前端vue页面的编写
一 、课程收藏列表页面
<template>
<div>
<y-header />
<div class="person_body container account_cont clearfix">
<y-side :type="'wdsc'" />
<div class="main_box">
<ul class="tabs clearfix">
<a class="tab on">我的收藏</a>
</ul>
<div class="main_cont">
<div v-if="notdata" class="notdata">
<i class="iconfont"></i>暂时没有数据
</div>
<table v-else class="course_table table">
<tbody>
<tr v-for="(item, index) in pageObj.list" :key="index">
<td v-if="item.courseResp">
<img :src="item.courseResp.courseLogo" :alt="item.courseResp.courseName" :height="80">
<div>
<div class="title">{{ item.courseResp.courseName }}</div>
<div v-if="item.courseResp.isFree === 1" style="margin: 0">【免费课】</div>
<br><br>
<div>购买人数:{{ item.courseResp.countBuy }}</div>
<br><br>
<div>学习人数:{{ item.courseResp.countStudy }}</div>
</div>
</td>
<td v-if="item.courseResp" style="float: right;margin-top: 10px">
<nuxt-link target="_blank" :to="{name: 'course-id', params: {id: item.courseResp.id}}" class="go_btn">马上学习</nuxt-link>
</td>
</tr>
</tbody>
</table>
<d-page v-if="pageObj.totalPage > 1" :page="pageObj" @btnClick="getPage" />
</div>
</div>
</div>
</div>
</template>
<script>
import YSide from '~/components/account/Side'
import YHeader from '~/components/common/Header'
import DPage from '~/components/common/Page'
import { userCourseCollectPage } from '@/api/user.js'
export default {
components: {
YHeader,
YSide,
DPage
},
data() {
return {
notdata: true,
pageCurrent: 1,
pageObj: {}
}
},
head() {
return {
title: '我的收藏-' + this.$store.state.websiteInfo.websiteName
}
},
mounted() {
this.getList()
},
methods: {
getPage(int) {
this.pageCurrent = int
this.getList()
},
getList() {
userCourseCollectPage({ pageCurrent: this.pageCurrent, pageSize: 20 }).then(res => {
if (res.totalCount > 0) {
this.notdata = false
this.pageObj = res
console.log('res', res)
}
}).catch(err => {
if (err.code >= 300 && err.code <= 400) {
this.$store.dispatch('REDIRECT_LOGIN')
}
})
}
}
}
</script>
<style lang="scss">
@import '~/assets/css/account.scss';
</style>
-
组件结构:
YHeader
组件:显示页面顶部的通用头部。.person_body
包裹容器:整体布局容器,包含侧边栏、主内容区域。YSide
组件:侧边栏组件,根据type
属性显示特定类型的侧边栏。.main_box
:主内容区域的容器。.tabs
:选项卡区域,目前只有一个“我的收藏”选项卡。.main_cont
:主内容区域,包含课程列表或暂无数据的提示信息。v-if="notdata"
:根据notdata
的值决定显示暂无数据的提示信息。v-else
:如果有数据,则显示课程列表表格。v-for="(item, index) in pageObj.list"
:遍历pageObj.list
中的课程数据,显示每个课程的相关信息和链接。
-
表格数据展示:
v-if="item.courseResp"
:确保课程数据存在时才显示该列。:src="item.courseResp.courseLogo"
:显示课程的Logo。{{ item.courseResp.courseName }}
:显示课程名称。v-if="item.courseResp.isFree === 1"
:如果课程是免费的,则显示免费课字样。{{ item.courseResp.countBuy }}
:显示购买人数。{{ item.courseResp.countStudy }}
:显示学习人数。<nuxt-link>
:使用Nuxt.js的链接组件,跳转到课程详情页。
-
分页组件:
d-page
组件:显示分页控件,根据pageObj
对象中的总页数来决定是否显示。
JavaScript部分(script)
-
导入组件和API:
- 导入了
YSide
、YHeader
、DPage
组件以及一个名为userCourseCollectPage
的API方法,用于获取用户收藏的课程列表数据。
- 导入了
-
数据和状态:
notdata
:控制是否显示暂无数据的提示信息。pageCurrent
:当前页码。pageObj
:包含从API返回的分页数据对象,如当前页数据列表、总页数等。
-
生命周期钩子:
mounted
:组件挂载后调用getList()
方法获取初始数据。
-
方法:
getPage(int)
:处理分页点击事件,更新pageCurrent
并重新获取列表数据。getList()
:调用userCourseCollectPage
方法获取用户收藏的课程数据,并更新组件的状态(如是否显示暂无数据信息)。
样式部分(style)
- 使用了SCSS预处理器,并导入了特定的账户样式文件
二、我的课程页面设计
<template>
<div>
<y-header />
<div class="person_body container account_cont clearfix">
<y-side :type="'wdkc'" />
<div class="main_box">
<ul class="tabs clearfix">
<a class="tab on">我的课程</a>
</ul>
<div class="main_cont">
<div v-if="notdata" class="notdata">
<i class="iconfont"></i>暂时没有数据
</div>
<table v-else class="course_table table">
<tbody>
<tr v-for="(item, index) in pageObj.list" :key="item.periodName + index">
<td v-if="item.courseResp">
<img :src="item.courseResp.courseLogo" :alt="item.courseResp.courseName" :height="80">
<div>
<div class="title">{{ item.courseResp.courseName }}</div>
<div v-if="item.courseResp.isFree === 1" style="margin: 0">【免费课】</div>
<br><br>
<div v-if="item.periodName">学习至:{{ item.periodName }}({{ item.periodProgress }}%)| {{ item.periodTime }}</div>
<br><br>
<div>总进度:{{ item.courseProgress ? item.courseProgress : 0 }}%</div>
</div>
</td>
<td v-if="item.courseResp" style="float: right; margin-top: 10px">
<nuxt-link :to="{ name: 'course-id', params: { id: item.courseResp.id }}" class="go_btn">继续学习</nuxt-link>
</td>
<td v-if="item.courseResp" style="float: right; margin-top: 10px">
<el-button plain type="primary" @click="studyRecord(item)">进度明细</el-button>
</td>
</tr>
</tbody>
</table>
<d-page v-if="pageObj.totalPage > 1" :page="pageObj" @btnClick="getPage" />
</div>
</div>
</div>
<study :visible="study.visible" :info="study.info" @close="studyCallback" />
</div>
</template>
<script>
import YSide from '~/components/account/Side'
import YHeader from '~/components/common/Header'
import DPage from '~/components/common/Page'
import { userCoursePage } from '@/api/user.js'
import Study from './study.vue'
import { userStudyePage } from '@/api/course.js'
export default {
components: {
Study,
YHeader,
YSide,
DPage
},
data() {
return {
study: {
visible: false,
info: []
},
notdata: true,
pageCurrent: 1,
pageObj: {
list: [],
totalPage: 0
}
}
},
head() {
return {
title: '我的课程-' + this.$store.state.websiteInfo.websiteName
}
},
mounted() {
this.getStudyList()
},
methods: {
getPage(int) {
this.pageCurrent = int
this.getStudyList()
},
getStudyList() {
userCoursePage({ pageCurrent: this.pageCurrent, pageSize: 20 }).then(res => {
if (res.totalCount > 0) {
this.notdata = false
this.pageObj = res
} else {
this.notdata = true
}
}).catch(err => {
if (err.code >= 300 && err.code <= 400) {
this.$store.dispatch('REDIRECT_LOGIN')
}
})
},
studyRecord(item) {
userStudyePage({ userId: this.$store.state.userInfo.id, courseId: item.courseResp.id }).then((res) => {
this.study.info = res.list || []
this.study.visible = true
console.log(this.study.info)
})
},
studyCallback() {
this.study.visible = false
}
}
}
</script>
<style lang="scss">
@import '~/assets/css/account.scss'
</style>
实现了一个简单的“我的课程”页面,包括课程列表的展示、分页、以及学习记录的查看
三、个人信息查看及编辑页面
<template>
<div class="">
<y-header />
<div class="container account_cont clearfix">
<y-side :type="'grxx'" />
<div class="main_box">
<ul class="tabs clearfix">
<a class="tab on">基础信息</a>
</ul>
<div v-if="editStatus" class="main_cont form">
<form action="" @submit="userInfoUpdate">
<div class="form_group">
<div class="label">手机号:</div>
<div class="form_ctl">
<div class="text">{{ obj.mobile }}</div>
</div>
</div>
<div class="form_group">
<div class="label">昵称:</div>
<div class="form_ctl">
<input v-model="obj.nickname" type="text" class="form_input" placeholder="请输入昵称">
</div>
</div>
<div class="form_group">
<div class="label">年龄:</div>
<div class="form_ctl">
<input v-model="obj.userAge" type="number" class="form_input" placeholder="请输入年龄">
</div>
</div>
<div class="form_group">
<div class="label">性别:</div>
<div class="form_ctl form_ctl_radio">
<input id="sex1" v-model="obj.userSex" type="radio" class="radiobox" value="1" name="sex">
<label for="sex1">男</label>
<input id="sex2" v-model="obj.userSex" type="radio" class="radiobox" value="2" name="sex">
<label for="sex2">女</label>
<input id="sex3" v-model="obj.userSex" type="radio" class="radiobox" value="3" name="sex">
<label for="sex3">保密</label>
</div>
</div>
<div class="form_group">
<div class="label">头像:</div>
<div class="form_ctl upload_ctl" style="float: none;">
<input v-model="obj.userHead" type="hidden">
<div class="preview" style="display: flex;align-items: center; width: 300px;">
<img v-if="obj.userHead" :src="obj.userHead" alt="" style="height: 100px; margin-left: 10px">
<i v-else class="iconfont"></i>
<d-upload style="margin-left: 50px" :btntxt="'选择头像'" @rtnUrl="setUrl" />
</div>
</div>
<p class="tip" style="padding-left:110px;">* 图片尺寸为800x800,图片大小<500KB,建议使用真人照片,便于品牌宣传效果</p>
</div>
<div class="form_group">
<div class="label">学号:</div>
<div class="form_ctl">
<input v-model="obj.studentNumber" type="text" class="form_input" placeholder="请输入学号">
</div>
</div>
<div class="form_group">
<div class="label">学校:</div>
<div class="form_ctl">
<input v-model="obj.school" type="text" class="form_input" placeholder="请输入学校">
</div>
</div>
<div class="form_group">
<div class="label"> </div>
<div class="form_ctl">
<button type="submit" class="submit_btn">保存</button>
</div>
</div>
</form>
</div>
<div v-else class="main_cont form">
<div class="form_group">
<div class="label">手机号:</div>
<div class="form_ctl">
<div class="text">{{ obj.mobile }}</div>
</div>
</div>
<div class="form_group">
<div class="label">昵称:</div>
<div class="form_ctl">
<div class="text">{{ obj.nickname }}</div>
</div>
</div>
<div class="form_group">
<div class="label">年龄:</div>
<div class="form_ctl">
<div class="text">{{ obj.userAge }}</div>
</div>
</div>
<div class="form_group">
<div class="label">性别:</div>
<div class="form_ctl">
<div v-if="obj.userSex === 1" class="text">男</div>
<div v-else-if="obj.userSex === 2" class="text">女</div>
<div v-else-if="obj.userSex === 3" class="text">保密</div>
</div>
</div>
<div class="form_group">
<div class="label">头像:</div>
<div class="form_ctl upload_ctl">
<div class="preview">
<img v-if="obj.userHead" :src="obj.userHead" alt="">
<i v-else class="iconfont"></i>
</div>
</div>
</div>
<div class="form_group">
<div class="label">学号</div>
<div class="form_ctl">
<div class="text">{{ obj.studentNumber }}</div>
</div>
</div>
<div class="form_group">
<div class="label">学校:</div>
<div class="form_ctl">
<div class="text">{{ obj.school }}</div>
</div>
</div>
<div class="form_group">
<div class="label"> </div>
<div class="form_ctl">
<button href="javascript:" class="submit_btn" @click="editInfo">修改</button>
</div>
</div>
</div>
</div>
</div>
<!-- <bottom /> -->
</div>
</template>
<script>
import YSide from '~/components/account/Side'
import YHeader from '~/components/common/Header'
import DUpload from '~/components/common/Upload'
import { getUserInfo, usersUpdata } from '@/api/user.js'
export default {
components: {
YHeader,
YSide,
DUpload
},
data() {
return {
editStatus: false,
region1: [],
region2: [],
obj: {}
}
},
head() {
return {
title: '个人信息-' + this.$store.state.websiteInfo.websiteName
}
},
mounted() {
this.userInfo()
},
methods: {
userInfo() {
getUserInfo().then(res => {
this.obj = res || {}
}).catch(err => {
if (err.code >= 300 && err.code <= 400) {
this.$store.dispatch('REDIRECT_LOGIN')
}
})
},
editInfo() {
this.editStatus = true
},
userInfoUpdate(e) {
e.preventDefault()
usersUpdata(this.obj).then(res => {
this.$msgBox({
content: '信息修改成功',
isShowCancelBtn: false
}).then(async(val) => {
window.location.reload()
}).catch(() => {
window.location.reload()
})
})
},
setUrl(res) {
this.obj.userHead = res.url
}
}
}
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
@import '~/assets/css/account.scss';
.upload_ctl {
.preview {
width: 100px;
height: 100px;
text-align: center;
margin-bottom: 10px;
i {
line-height: 120px;
font-size: 100px;
color: #ddd;
}
}
}
</style>
四、课程订单查看界面
<div>
<y-header />
<div class="container account_cont clearfix">
<y-side :type="'wddd'" />
<div class="main_box">
<ul class="tabs clearfix">
<a class="tab" :class="{on: num == 0}" @click="clicktab(0)">所有订单</a>
<a class="tab" :class="{on: num == 1}" @click="clicktab(1)">待支付订单</a>
<a class="tab" :class="{on: num == 2}" @click="clicktab(2)">已完成订单</a>
</ul>
<div v-if="notdata" class="notdata">
<i class="iconfont"></i>暂时没有数据
</div>
<div v-if="!notdata" class="person_info">
<div v-for="(item, index) in pageObj.list" :key="index" class="order_content">
<div class="order_title clearfix">
<span class="order_num">订单号:{{ item.orderNo }}</span>
<span class="time">{{ item.gmtCreate }}</span>
</div>
<div class="order_body clearfix">
<div class="body_left clearfix">
<nuxt-link target="_blank" :to="{name: 'course-id', params: {id: item.courseId}}">
<div v-if="item.courseLogo" class="img_box fl">
<img :src="item.courseLogo" :alt="item.courseName">
</div>
<p class="fl">
{{ item.courseName }}<br><br>
原价:<span style="text-decoration-line: line-through; margin-right: 20px">¥{{ item.rulingPrice }}</span>实付:¥{{ item.coursePrice }}
</p>
</nuxt-link>
</div>
<ul class="body_right clearfix">
<li>
<br>
<span v-if="item.orderStatus == 1">待支付</span>
<span v-if="item.orderStatus == 2"><nuxt-link target="_blank" :to="{name: 'course-id', params: {id: item.courseId}}" class="go_btn">马上学习</nuxt-link></span>
<span v-if="item.orderStatus == 3">支付失败</span>
<span v-if="item.orderStatus == 4">已关闭</span>
</li>
<li v-if="item.orderStatus == 1 || item.orderStatus == 3">
<a href="javascript:" class="go_btn go_pay" @click="continuePay(item)">继续支付</a>
<a href="javascript:" class="cancel" @click="closeOrder(item.orderNo)">关闭订单</a>
</li>
</ul>
</div>
</div>
<d-page v-if="pageObj.totalPage > 1 && !notdata" :page="pageObj" @btnClick="getPage" />
</div>
</div>
</div>
<d-paymodal v-if="showPay" class="" :data="payData" @hidefun="showPay = false" />
<!-- <bottom /> -->
</div>
</template>
<script>
import YSide from '~/components/account/Side'
import YHeader from '~/components/common/Header'
import DPaymodal from '@/components/common/Order'
import { cancelOrder, orderPage } from '@/api/user.js'
import DPage from '~/components/common/Page'
export default {
components: {
YHeader,
YSide,
DPage,
DPaymodal
},
data() {
return {
num: 0,
showPay: false,
payData: null,
notdata: true,
obj: {
orderStatus: 0,
pageCurrent: 1,
pageSize: 20
},
pageObj: {
pageCurrent: '',
pageSize: '',
totalCount: '',
totalPage: ''
}
}
},
head() {
return {
title: '我的订单-' + this.$store.state.websiteInfo.websiteName
}
},
mounted() {
this.obj = {
orderStatus: '',
pageCurrent: 1,
pageSize: 10
}
this.getOrderList()
},
methods: {
continuePay(item) {
this.payData = item
this.showPay = true
},
clicktab(int) {
this.num = int
if (int === 0) {
this.obj.orderStatus = ''
} else {
this.obj.orderStatus = int
}
this.obj.pageCurrent = 1
this.getOrderList()
},
getPage: function(int) {
this.obj.pageCurrent = int
this.getOrderList()
},
getOrderList() {
orderPage(this.obj).then(res => {
if (res.totalCount > 0) {
this.notdata = false
this.pageObj = res
}
}).catch(err => {
if (err.code >= 300 && err.code <= 400) {
this.$store.dispatch('REDIRECT_LOGIN')
}
})
},
closeOrder(orderNo) {
cancelOrder({ orderNo: orderNo }).then(res => {
this.$msgBox({
content: '关闭成功',
isShowCancelBtn: false,
edit: false
}).then(async(val) => {
this.getOrderList()
}).catch(() => {
this.getOrderList()
})
})
}
}
}
</script>
五、学习记录信息。
<template>
<el-dialog :visible="visible" :append-to-body="true" :title="title" width="900px" @close="closeDialog">
<el-table :data="info" row-key="id" :tree-props="{ children: 'userStudyPeriodPageRespList' }" default-expand-all>
<el-table-column label="章节名称" prop="chapterName" width="150">
<template #default="scope">
<span>{{ scope.row.chapterName }}</span>
<span>{{ scope.row.periodName }}</span>
</template>
</el-table-column>
<el-table-column label="学习时间" prop="gmtCreate" width="200">
<template #default="scope">
<span v-if="scope.row.progress > 0">{{ scope.row.gmtCreate }}</span>
</template>
</el-table-column>
<el-table-column label="学习进度" prop="courseProgress" width="200">
<template #default="scope">
<el-progress v-if="scope.row.progress" :percentage="scope.row.progress" :stroke-width="25" :text-inside="true" />
</template>
</el-table-column>
<!-- <el-table-column label="眼动数据上传" width="150">
<template #default="scope">
<uploader-btn v-if="scope.row.progress" :disabled="scope.row.eyeData" icon="" :plain="false" btn-text="上传" :userStudyId="scope.row.progressId" mode="async" class="mgl10"/>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button v-if="scope.row.progress" :disabled="!scope.row.report" @click="viewReport(scope.row.report)" type="primary">查看报告</el-button>
</template>
</el-table-column> -->
<el-table-column label="状态" width="140">
<template #default="scope">
<div v-if="scope.row.periodName">
<span v-if="!scope.row.progress">未观看视频</span>
<span v-if="scope.row.progress&&scope.row.report">报告已生成</span>
<span v-if="scope.row.progress&&!scope.row.report&&scope.row.eyeData">报告已上传</span>
<span v-if="scope.row.progress&&!scope.row.report&&!scope.row.eyeData">未上传眼动数据</span>
</div>
</template>
</el-table-column>
<el-table-column label="更多操作" width="170">
<template #default="scope">
<el-dropdown v-if="scope.row.progress">
<el-button>更多操作<i class="el-icon-arrow-down" /></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<uploader-btn v-if="scope.row.progress" :disabled="scope.row.eyeData" icon="" :plain="false" btn-text="上传眼动数据" :userStudyId="scope.row.progressId" mode="async" class="mgl10"/>
</el-dropdown-item>
<el-dropdown-item>
<el-button v-if="scope.row.progress" :disabled="!scope.row.report" @click="viewReport(scope.row.report)" type="primary" size="small">查看报告</el-button>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-table-column>
</el-table>
</el-dialog>
</template>
<script>
import { defineComponent, ref, watch } from 'vue'
import UploaderBtn from '@/components/upload/UploaderBtn.vue'
export default defineComponent({
components: {
UploaderBtn
},
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '学习记录'
},
info: {
type: Array,
default: () => []
}
},
emits: ['close'],
setup(props, { emit }) {
const dialogVisible = ref(props.visible)
watch(() => props.visible, (newValue) => {
dialogVisible.value = newValue
})
const closeDialog = () => {
emit('close')
}
const viewReport = (link) => {
if (link) {
window.open(link, '_blank')
} else {
console.warn('报告不存在')
}
}
return {
dialogVisible,
closeDialog,
viewReport
}
}
})
</script>