一、课程学习和课程详情展示
<template>
<div class="course_detail">
<y-study v-if="courseInfo.allowStudy" ref="watchVideo" :course-info="courseInfo" :play-period="playPeriod" @playfunc="videoPlay" />
<y-detail v-else ref="watchVideo" :course-info="courseInfo" :teacher-info="teacherInfo" />
<div class=" detail_info detail_box clearfix">
<div class="layout_left">
<ul class="course_tab clearfix">
<li :class="{on: tab == 'info'}"><a href="javascript:" @click="tab = 'info'">课程介绍</a></li>
<li :class="{on: tab == 'big'}"><a href="javascript:" @click="tab = 'big'">课程目录</a></li>
<li :class="{on: tab == 'comment'}"><a href="javascript:" @click="tab = 'comment'">课程评论</a></li>
</ul>
<div v-if="tab == 'info'" class="content_info">
<div class="introduce" v-html="courseInfo.introduce" />
</div>
<div v-if="tab == 'big'" class="content_info">
<y-catalog :list="courseInfo.chapterRespList" :play-period="playPeriod" @playfunc="videoPlay" />
</div>
<div v-if="tab == 'comment'" class="content_info">
<y-comment :id="courseInfo.id" />
</div>
</div>
<div class="layout_right">
<div class="teacher_info clearfix">
<span class="head">讲师简介</span>
<div class="teacher_msg">
<div v-if="teacherInfo" class="teacher_msg_right">
<img v-if="teacherInfo.lecturerHead" class="teacher_phone" :src="teacherInfo.lecturerHead" alt="">
<img v-else class="teacher_phone" src="~/assets/image/friend.jpg" alt="">
<div class="teacher_name">{{ teacherInfo.lecturerName }}</div>
<div class="teacher_position">{{ teacherInfo.lecturerPosition }}</div>
<div class="info_box" v-html="teacherInfo.introduce" />
</div>
</div>
</div>
</div>
</div>
<!-- <bottom /> -->
</div>
</template>
<script>
import YDetail from '@/components/course/Detail'
import YCatalog from '@/components/course/Catalog'
import YStudy from '@/components/course/Study'
import YComment from '@/components/course/Comment'
import cookies from '@/utils/cookies'
import { courseDetail, playSign, studyProgress, userCourseDetail } from '~/api/course.js'
export default {
components: {
YDetail,
YCatalog,
YStudy,
YComment
},
async asyncData(context) {
try {
const result = {}
const token = cookies.getInServerToken(context.req)
if (token) {
// 登录
const courseData = await userCourseDetail({ courseId: context.params.id }, token)
result.courseInfo = courseData
if (courseData.lecturerResp) {
result.teacherInfo = courseData.lecturerResp
}
} else {
// 没登录
const courseData = await courseDetail({ courseId: context.params.id })
result.courseInfo = courseData
if (courseData.lecturerResp) {
result.teacherInfo = courseData.lecturerResp
}
}
return result
} catch (e) {
context.error({ message: 'Data not found', statusCode: 404 })
}
},
data() {
return {
tab: 'info',
playPeriod: '', // 当前播放课时
userStudy: {} // 进度
}
},
head() {
return {
title: this.courseInfo.courseName + '-' + this.$store.state.websiteInfo.websiteName
}
},
mounted() {
if (this.courseInfo.allowStudy) {
this.tab = 'big'
}
if (this.courseInfo.allowStudy) {
// 可以播放,自动获取最后学习的课程
playSign({ courseId: this.courseInfo.id, clientIp: '172.0.0.1' }).then(res => {
this.playPeriod = res.periodId
if (res.periodId) {
this.play(res, false)
}
})
}
window.s2j_onVideoPause = (vid) => {
// 暂停
clearInterval(this.progressInterval)
}
window.s2j_onVideoPlay = (vid) => {
// 播放
this.progressInterval = setInterval(() => {
this.studyRecord()
}, 3000)
}
window.s2j_onPlayOver = (vid) => {
// 播放完成
clearInterval(this.progressInterval)
this.studyRecord()
}
},
methods: {
videoPlay(periodId) {
if (this.courseInfo.allowStudy) {
window.scrollTo(0, 0)
playSign({ periodId: periodId, courseId: this.courseInfo.id, clientIp: '172.0.0.1' }).then(res => {
this.playPeriod = res.periodId
if (res.periodId) {
this.play(res, true)
}
})
} else {
this.$msgBox({
content: '购买后才可以学习',
isShowCancelBtn: false
})
return false
}
},
play(data, autoplay) {
const box = this.$refs.watchVideo.$refs.videobox
this.player?.destroy()
const param = {
'width': box.offsetWidth,
'height': box.offsetHeight,
'forceH5': true,
'watchStartTime': data.startTime,
autoplay: autoplay
}
if (data.vodPlatform === 2) {
const vodPlayConfig = JSON.parse(data.vodPlayConfig)
param.code = vodPlayConfig.code
param.playsafe = vodPlayConfig.token
param.ts = vodPlayConfig.ts
param.sign = vodPlayConfig.sign
param.vid = vodPlayConfig.vid
} else {
const vodPlayConfig = JSON.parse(data.vodPlayConfig)
if (vodPlayConfig.hdUrl) {
param.url = vodPlayConfig.hdUrl
} else {
param.url = vodPlayConfig.sdUrl
}
}
this.player = window.polyvObject('#player').videoPlayer(param)
this.userStudy.studyId = data.studyId
this.userStudy.resourceId = data.resourceId
},
// 记录进度
studyRecord() {
// this.userStudy.totalDuration = this.getDuration()
this.userStudy.currentDuration = this.getCurrentTime()
studyProgress(this.userStudy).then(res => {
if (res === 'OK') {
// 完成,暂停同步
clearInterval(this.progressInterval)
}
})
},
// 获取视频当前播放时长
getCurrentTime() {
if (this.player && this.player.j2s_getCurrentTime) {
return this.player.j2s_getCurrentTime()
}
return 0
},
// 获取视频总时长
getDuration() {
if (this.player && this.player.j2s_getDuration) {
return this.player.j2s_getDuration()
}
return 0
}
}
}
</script>
<style lang="scss" rel="stylesheet/scss">
.course_detail {
.detail_info {
margin: 20px auto 60px;
width: 1200px;
}
.layout_left {
width: 920px;
float: left;
.info_body {
margin-bottom: 30px;
}
}
.course_tab {
padding-left: 30px;
background: #fff;
border-radius: 8px;
margin-bottom: 20px;
li {
float: left;
height: 66px;
line-height: 66px;
margin-right: 80px;
&.on {
border-bottom: 2px solid #D51423;
a {
color: #D51423;
}
}
a {
color: #000;
font-size: 14px;
font-weight: 700;
&:hover {
text-decoration: none;
color: #D51423;
}
}
}
}
.content_info {
padding: 30px;
background: #fff;
border-radius: 8px;
min-height: 400px;
.title {
border-left: 3px solid #000;
padding-left: 12px;
font-size: 16px;
color: #000;
font-weight: 700;
margin-bottom: 25px;
}
.introduce {
font-size: 14px;
line-height: 30px;
color: #333;
padding-left: 8px;
}
}
.layout_right {
width: 260px;
float: right;
}
.teacher_info {
background: #fff;
border-radius: 8px;
position: relative;
min-height: 180px;
.head {
display: block;
line-height: 60px;
height: 60px;
padding-left: 20px;
font-size: 14px;
color: #333;
border-bottom: 1px solid rgb(228, 228, 228);
}
.teacher_phone {
width: 50px;
height: 50px;
border-radius: 50%;
background: rgb(228, 228, 228);
text-align: center;
line-height: 46px;
font-size: 13px;
color: #999;
float: left;
margin: 0px 10px 0 10px;
img {
width: 46px;
height: 46px;
border-radius: 50%;
}
}
.teacher_msg_right {
width: 235px;
float: right;
margin: 12px 15px 12px 12px;
line-height: 20px;
}
.teacher_name {
font-size: 18px;
font-weight: 700;
color: #333;
margin: 5px 0;
}
.teacher_position {
font-size: 12px;
color: #333;
margin-bottom: 5px;
}
}
}
</style>
二、用户登录的前端界面
<template>
<div class="login_page">
<y-header :hide-top="true" :hide-search="true" />
<div class="login_body">
<div class="login_box">
<div class="login_logo"><img src="../assets/image/login_bg.png"></div>
<div class="center_box">
<div v-if="!(userInfo)" :class="{login_form: true, rotate: tab == 2}">
<div v-if="tabp == 1" style="height: 30px" />
<div :class="{tabs: true, clearfix: true, r180: tabp == 2}">
<div class="fl tab" @click="changetab(1)">
<span :class="{on: tab == 1}">登录</span>
</div>
<div class="fl tab" @click="changetab(2)">
<span :class="{on: tab == 2}">注册</span>
</div>
</div>
<div v-if="tabp == 1" class="form_body">
<form action="" @submit="loginSubmit">
<input v-model="loginObj.mobile" type="text" placeholder="请输入手机号或邮箱">
<div class="error_msg">{{ errTip1 }}</div>
<input v-model="loginObj.password" type="password" placeholder="6-20位密码,可用数字/字母/符号组合">
<div class="error_msg">{{ errTip2 }}</div>
<input v-if="subState" type="submit" disabled="disabled" value="登录中···" class="btn">
<input v-else type="submit" value="登录" class="btn">
</form>
<nuxt-link class="is_go" :to="{name: 'reset'}">忘记密码</nuxt-link>
<div style="margin-top: 20px;">注册即可体验</div>
</div>
<div v-if="tabp == 2" class="form_body r180">
<form action="" @submit="regSubmit">
<input v-model="registerObj.mobile" type="text" placeholder="请输入手机号">
<div class="phone_yzm">
<input v-model="registerObj.code" type="text" name="code" placeholder="请输入手机验证码" class="phone" maxlength="6">
<y-button :mobile="registerObj.mobile" />
</div>
<input v-model="registerObj.mobilePwd" type="password" placeholder="6-20位密码,可用数字/字母/符号组合">
<input v-model="registerObj.repassword" type="password" placeholder="确认密码">
<div class="mgt20 font_14">
<input id="tonyi" v-model="registerObj.check" type="checkbox">
<label for="tonyi">我已经阅读并同意</label><a href="jvascript:" class="c_blue" @click="xieyi = true">《用户协议》</a>
</div>
<input v-if="subState" type="submit" disabled="disabled" value="提交中···" class="btn">
<input v-else type="submit" value="注册" class="btn">
</form>
</div>
</div>
<div v-else class="login_form">
<div style="height: 60px" />
<div class="login_title is_login">领课教育系统(开源版)</div>
<div class="form_body">
<div class="img_box">
<img v-if="userInfo.userHead" :src="userInfo.userHead" alt="">
<img v-else src="../assets/image/friend.jpg" alt="">
</div>
<ul class="btn_box clearfix">
<li>
<nuxt-link :to="{name: 'account-course'}">我的课程</nuxt-link>
</li>
<li>
<nuxt-link :to="{name: 'account-order'}">我的订单</nuxt-link>
</li>
</ul>
<div>
<a href="javascript:" class="out_btn" @click="signOut">退出登录</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-if="xieyi" class="xieyi" @click.self="xieyi = false">
<div class="xieyi_content">
<div class="xieyi_title">用户协议</div>
<div v-if="websiteInfo && websiteInfo.websiteUserAgreement" class="xieyi_body" v-html="websiteInfo.websiteUserAgreement" />
<input type="button" class="xieyi_btn" value="确定" @click="xieyi = false">
</div>
</div>
<!-- <y-bottom /> -->
</div>
</template>
<script>
import YHeader from '~/components/common/Header'
import YButton from '@/components/common/Code'
import { getOsInfo, getBrowserInfo } from '@/utils/utils'
import { register, userLogin } from '@/api/login.js'
export default {
components: {
YHeader,
YButton
},
data() {
return {
tab: this.$route.query.tab || 1,
tabp: this.$route.query.tab || 1,
websiteInfo: this.$store.state.websiteInfo,
userInfo: this.$store.state.userInfo,
subState: false, // 提交状态
xieyi: false, // 用户协议
errTip1: '',
errTip2: '',
ipInfo: {},
loginObj: {
mobile: '',
password: ''
},
registerObj: {
mobile: '',
code: '',
mobilePwd: '',
repassword: '',
check: false
}
}
},
head() {
return {
title: '用户登录-' + this.websiteInfo.websiteName,
meta: [
{
hid: 'keywords',
name: 'keywords',
content: this.websiteInfo.websiteName
},
{
hid: 'description',
name: 'description',
content: this.websiteInfo.websiteDesc
}
]
}
},
mounted() {
if (this.$route.query.t === 'login') {
this.$store.commit('SIGN_OUT')
this.userInfo = ''
this.errTip1 = '未登录或登录超时,请重新登录'
} else if (this.$route.query.t) {
window.location.href = '/index'
}
},
methods: {
signOut() {
// this.SIGN_OUT();
this.$store.commit('SIGN_OUT')
this.userInfo = ''
},
changetab(int) {
this.tab = int
const _that = this
setTimeout(function() {
_that.tabp = int
}, 200)
},
loginSubmit(e) {
e.preventDefault()
if (this.subState) {
return false
}
this.errTip1 = ''
this.errTip2 = ''
if (!(/^1\d{10}$/.test(this.loginObj.mobile.trim())) && !(/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/).test(this.loginObj.mobile.trim())) {
this.errTip1 = '请输入正确手机号或者邮箱'
return false
}
if (this.loginObj.password.length < 6) {
this.errTip2 = '请输入正确的账号或密码'
return false
}
this.loginObj.loginIp = this.ipInfo.ip
this.loginObj.province = this.ipInfo.pro
this.loginObj.city = this.ipInfo.city
this.loginObj.os = getOsInfo().version
this.loginObj.browser = getBrowserInfo().name + getBrowserInfo().version
this.$nuxt.$loading.start()
this.subState = true
userLogin(this.loginObj).then(res => {
this.subState = false
this.$nuxt.$loading.finish()
this.$store.commit('SET_TOKEN', res.token)
this.$store.commit('GET_TEMPORARYURL')
this.$store.dispatch('GET_USERINFO', store => {
this.userInfo = this.$store.state.userInfo
window.location.href = this.$store.state.temporaryUrl
})
}).catch((error) => {
this.subState = false
this.$nuxt.$loading.finish()
this.$msgBox({
content: error.msg,
isShowCancelBtn: false
})
})
return false
},
// 注册
regSubmit: function(e) {
e.preventDefault()
if (this.subState) {
return false
}
if (!(/^1\d{10}$/.test(this.registerObj.mobile.trim())) || this.registerObj.mobile.trim().length !== 11) {
this.$msgBox({
content: '请输入正确手机',
isShowCancelBtn: false
}).catch(() => {
})
return false
}
if (!this.registerObj.code || this.registerObj.code.length !== 6) {
this.$msgBox({
content: '请输入正确的手机验证码',
isShowCancelBtn: false
}).catch(() => {
})
return false
}
if (this.registerObj.mobilePwd.length < 6 || this.registerObj.mobilePwd.length > 16) {
this.$msgBox({
content: '请输入6-16位的登录密码,区分大小写,不可有空格',
isShowCancelBtn: false
}).catch(() => {
})
return false
}
if (this.registerObj.mobilePwd !== this.registerObj.repassword) {
this.$msgBox({
content: '两次输入密码不一致',
isShowCancelBtn: false
}).catch(() => {
})
return false
}
if (!this.registerObj.check) {
this.$msgBox({
content: '请先阅读并同意用户协议',
isShowCancelBtn: false
}).catch(() => {
})
return false
}
this.$nuxt.$loading.start()
this.subState = true
this.registerObj.loginIp = this.ipInfo.ip
this.registerObj.province = this.ipInfo.pro
this.registerObj.city = this.ipInfo.city
this.registerObj.os = getOsInfo().version
this.registerObj.browser = getBrowserInfo().name + getBrowserInfo().version
register(this.registerObj).then(res => {
this.$nuxt.$loading.finish()
this.subState = false
this.$msgBox({
content: '注册成功!',
confirmBtnText: '立即登录',
isShowCancelBtn: false
}).then(() => {
this.changetab(1)
}).catch(() => {
this.changetab(1)
})
this.registerObj = {
mobile: '',
code: '',
mobilePwd: '',
repassword: '',
check: false
}
}).catch(res => {
this.$nuxt.$loading.finish()
this.subState = false
this.$msgBox({
content: res.msg,
isShowCancelBtn: false
})
})
}
}
}
</script>
<style lang="scss" rel="stylesheet/scss">
.login_body {
background-color: #2256f7;
height: calc(100vh - 120px);
}
.login_page {
.login_box {
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
.login_logo img {
width: 500px;
height: auto;
}
.center_box {
position: relative;
margin: 0 100px;
}
}
.login {
background: rgb(0, 153, 255);
.login_header {
width: 1200px;
margin: 0 auto;
height: 132px;
position: relative;
span {
position: absolute;
height: 28px;
line-height: 28px;
font-size: 26px;
color: #fff;
border-left: 2px solid #fff;
padding-left: 25px;
top: 46px;
left: 210px;
}
}
}
.login_logo {
img {
width: 186px;
height: 52px;
margin-top: 32px;
}
}
.login_body {
img {
width: 186px;
height: 52px;
display: block;
margin: 0 auto;
}
p {
text-align: center;
font-size: 14px;
color: #fff;
margin-top: 30px;
}
}
.login_form {
width: 380px;
border-radius: 6px;
transition: all 0.8s;
transform: perspective(600px);
&.rotate {
transform: rotateY(-180deg);
}
.login_title {
height: 95px;
line-height: 95px;
font-size: 18px;
padding-left: 30px;
color: #333;
background: #fff;
border-radius: 6px 6px 0 0;
&.is_login {
padding-left: 0px;
text-align: center;
}
}
}
.form_body {
background: #fff;
padding: 0 30px 20px;
border-radius: 0 0 6px 6px;
input[type='text'], input[type='password'], input[type='button'], input[type='submit'] {
width: 310px;
height: 46px;
padding-left: 10px;
margin-top: 20px;
border-radius: 6px;
font-size: 14px;
border-color: rgb(230, 230, 230);
&.btn {
background: rgb(213, 20, 35);
width: 320px;
color: #fff;
border: none;
outline: none;
cursor: pointer;
margin-bottom: 20px;
}
}
.error_msg {
width: 310px;
color: #D51423;
font-size: 12px;
}
.next_auto {
font-size: 14px;
color: #333;
}
.is_go {
float: right;
color: #0099FF;
height: 18px;
line-height: 18px;
font-size: 14px;
display: inline-block;
padding: 0 10px;
&:hover {
text-decoration: none;
}
}
}
.phone_yzm {
position: relative;
.phone {
padding-right: 100px;
width: 210px;
font-size: 14px;
border-color: rgb(230, 230, 230);
padding-left: 10px;
height: 46px;
border-radius: 6px;
margin-top: 20px;
}
}
.yzm_btn {
width: 100px;
height: 46px;
position: absolute;
left: 210px;
top: 21px;
line-height: 48px;
border-radius: 0 6px 6px 0;
text-align: center;
color: #fff;
cursor: pointer;
border: none;
}
.login_footer {
padding-bottom: 30px;
p {
text-align: center;
font-size: 12px;
color: #1E1E1E;
margin-top: 20px;
}
}
.img_box {
height: 70px;
img {
width: 70px;
height: 70px;
display: block;
border-radius: 50%;
margin: 0 auto;
}
}
.hellow_text {
text-align: center;
font-size: 14px;
padding: 20px 0;
border-bottom: 1px solid #ccc;
}
.btn_box {
border-bottom: 1px solid #ccc;
padding-top: 20px;
padding-bottom: 30px;
font-size: 14px;
li {
float: left;
width: 140px;
text-align: center;
line-height: 40px;
margin: 20px 10px 0;
border-radius: 6px;
a {
display: block;
background: #f6f8fb;
border-radius: 6px;
&:hover {
color: #fff;
text-decoration: none;
background: skyblue;
}
}
}
}
.out_btn {
display: inline-block;
margin: 10px 0 0 100px;
font-size: 16px;
color: #333;
width: 120px;
line-height: 30px;
text-align: center;
&:hover {
text-decoration: none;
}
}
.prn_icon {
width: 16px;
height: 16px;
position: relative;
top: 3px;
}
.tabs {
height: 60px;
line-height: 60px;
background: #fff;
border-radius: 8px 8px 0 0;
border-bottom: 1px solid rgb(230, 230, 230);
.tab {
font-size: 18px;
width: 50%;
text-align: center;
cursor: pointer;
line-height: 60px;
}
span {
display: inline-block;
height: 60px;
}
.on {
border-bottom: 2px solid #D51423;
color: #D51423;
}
}
.r180 {
transform: rotateY(-180deg);
}
.xieyi {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
z-index: 10003;
.xieyi_content {
width: 900px;
height: 500px;
margin: 70px auto 0;
border-radius: 8px;
background: #fff;
position: relative;
}
.xieyi_title {
color: #333;
font-size: 18px;
line-height: 70px;
text-align: center;
}
.xieyi_body {
height: 350px;
overflow-y: auto;
padding: 0 20px;
}
.xieyi_btn {
position: absolute;
bottom: 10px;
width: 166px;
height: 46px;
background: rgba(213, 20, 35, 1);
color: #fff;
border: none;
outline: none;
cursor: pointer;
left: 50%;
margin-left: -83px;
border-radius: 6px;
}
}
}
</style>
三、用户密码重设置
<template>
<div class="register_page">
<div class="register">
<div class="register_body">
<div class="register_header">
<div class="register_logo">
<nuxt-link :to="{name: 'index'}">
<img v-if="websiteInfo && websiteInfo.websiteLogo" :src="websiteInfo.websiteLogo" alt="">
</nuxt-link>
</div>
<p>
已有账号,
<nuxt-link :to="{name: 'login'}">立即登陆</nuxt-link>
<span />
<nuxt-link :to="{name: 'index'}">返回首页</nuxt-link>
</p>
</div>
<div class="register_content">
<div class="register_title">修改密码</div>
<form action="" @submit="resetPsw">
<div class="form_group">
<div class="label">手机号:</div>
<div class="form_ctl">
<input v-model="obj.mobile" type="text" maxlength="11" @change="enterPhone">
<p v-show="errTip0" class="err">{{ errTip0 }}</p>
</div>
</div>
<div class="form_group">
<div class="label">验证码:</div>
<div class="form_ctl">
<input v-model="obj.code" type="text" maxlength="6" name="code" @change="enter">
<y-button :mobile="obj.mobile" />
<p v-show="errTip1" class="err">{{ errTip1 }}</p>
</div>
</div>
<div class="form_group">
<div class="label">新密码:</div>
<div class="form_ctl">
<input v-model="obj.mobilePwd" type="password" name="mobilePwd" @change="enter">
<p v-show="errTip2" class="err">{{ errTip2 }}</p>
</div>
</div>
<div class="form_group">
<div class="label">重复密码:</div>
<div class="form_ctl">
<input v-model="obj.repassword" type="password" name="repassword" @change="enter">
<p v-show="errTip3" class="err">{{ errTip3 }}</p>
</div>
</div>
<div class="form_group">
<div class="label"> </div>
<div class="form_ctl">
<input type="submit" class="submit_btn" value="确定">
</div>
</div>
</form>
</div>
</div>
<!-- <y-bottom /> -->
</div>
</div>
</template>
<script>
import YButton from '@/components/common/Code'
// import YBottom from '@/components/common/Bottom'
import { updatePassword } from '~/api/login.js'
export default {
components: {
YButton
// YBottom
},
data() {
return {
errTip0: '',
errTip1: '',
errTip2: '',
errTip3: '',
obj: {
mobile: '',
code: '',
mobilePwd: '',
repassword: ''
},
websiteInfo: this.$store.state.websiteInfo
}
},
head() {
return {
title: '修改密码-' + this.websiteInfo.websiteName
}
},
mounted() {
},
methods: {
goLogin() {
this.$router.push({ path: '/login' })
},
// 输入内容
enter(e) {
const name = e.target.name
if (name === 'code') {
if (this.obj.code.length !== 6) {
this.errTip1 = '请输入正确的手机验证码'
return false
} else {
this.errTip1 = false
}
} else if (name === 'mobilePwd') {
if (this.obj.mobilePwd.length < 6 || this.obj.mobilePwd.length > 16) {
this.errTip2 = '请输入6-16位的登录密码,区分大小写,不可有空格'
return false
} else {
this.errTip2 = false
}
} else if (name === 'repassword') {
if (this.obj.mobilePwd !== this.obj.repassword) {
this.errTip3 = '两次输入密码不一致'
return false
} else {
this.errTip3 = false
}
}
},
// 输入手机
enterPhone() {
if (this.obj.mobile.length === 11) {
if ((/^1[3|4|5|8|7][0-9]\d{4,8}$/.test(this.obj.mobile.trim()))) {
this.errTip0 = false
this.getCodeBtn = true
} else {
this.errTip0 = '请输入正确手机'
this.getCodeBtn = false
}
} else {
this.errTip0 = false
this.getCodeBtn = false
}
},
showMsg(msg) {
this.$msgBox({
content: msg,
isShowCancelBtn: false
}).catch(() => {
})
},
resetPsw(e) {
e.preventDefault()
if (!(/^1[3|4|5|8|7][0-9]\d{4,8}$/.test(this.obj.mobile.trim()))) {
this.showMsg('请输入正确格式的手机号码')
return
}
if (this.obj.code.length !== 6) {
this.showMsg('请输入正确的验证码')
return
}
if (this.obj.mobilePwd.length < 6 || this.obj.mobilePwd.length > 16) {
this.showMsg('请输入6-16位的登录密码,区分大小写,不可有空格')
return
}
if (this.obj.mobilePwd !== this.obj.repassword) {
this.showMsg('两次输入密码不一致')
return
}
updatePassword(this.obj).then(res => {
this.$msgBox({
content: '修改成功,请重新登录',
isShowCancelBtn: false
}).then(() => {
this.$store.commit('SIGN_OUT')
this.$router.push({ name: 'login' })
}).catch(() => {
this.$store.commit('SIGN_OUT')
this.$router.push({ name: 'login' })
})
})
}
}
}
</script>
<style lang="scss" rel="stylesheet/scss">
.register_page {
.register {
background: rgb(247, 247, 247);
.register_body {
width: 900px;
margin: 0 auto 30px;
}
.footer_text {
text-align: center;
font-size: 12px;
color: #999;
padding: 10px 0;
}
.register_header {
height: 112px;
padding-top: 30px;
position: relative;
.register_logo {
position: absolute;
top: 60px;
img {
height: 52px;
}
}
p {
font-size: 14px;
text-align: right;
height: 18px;
line-height: 18px;
margin-top: 48px;
a {
color: #0099FF;
&:hover {
text-decoration: none;
}
}
span {
display: inline-block;
width: 1px;
height: 18px;
background: #333;
position: relative;
top: 4px;
margin: 0 10px;
}
}
}
.register_content {
border-radius: 8px;
background: #fff;
min-height: 500px;
margin-top: 20px;
.register_title {
border-radius: 8px 8px 0 0;
height: 80px;
line-height: 80px;
background: rgb(228, 228, 228);
font-size: 18px;
text-align: center;
color: #333;
}
}
.m_center {
width: 390px;
margin: 30px auto;
}
}
.prn_icon {
width: 16px;
height: 16px;
position: relative;
top: 3px;
}
.form_group {
width: 432px;
margin: 20px auto 0;
font-size: 14px;
.label {
text-align: right;
float: left;
display: block;
width: 110px;
line-height: 36px;
color: #333;
}
.form_ctl {
margin-left: 12px;
float: left;
position: relative;
input {
width: 250px;
height: 46px;
padding-left: 10px;
border-radius: 6px;
font-size: 14px;
margin-top: -5px;
&.reset_yzm {
width: 310px;
padding-right: 110px;
}
}
.submit_btn {
width: 250px;
background: rgb(213, 20, 35);
border: none;
color: #fff;
cursor: pointer;
}
.yzm_btn {
width: 100px;
height: 46px;
position: absolute;
left: 150px;
top: -5px;
line-height: 48px;
background: rgb(213, 20, 35);
border-radius: 0 6px 6px 0;
text-align: center;
color: #fff;
cursor: pointer;
border: none;
&:disabled {
background: rgb(204, 204, 204);
}
}
.err {
color: rgb(213, 20, 35);
}
}
.text {
color: #333;
line-height: 36px;
}
&:after {
content: '';
display: block;
clear: both;
}
}
.login_footer {
padding-bottom: 30px;
p {
text-align: center;
font-size: 12px;
color: #1E1E1E;
margin-top: 20px;
}
}
}
</style>
四、课程搜索界面
<script>
import YHeader from '~/components/common/Header'
import RightTap from '@/components/common/Top'
import { courseList } from '~/api/course'
import DPage from '~/components/common/Page'
export default {
name: 'Search',
components: {
YHeader,
DPage,
RightTap
},
async asyncData(context) {
const dataObj = {
map: {
courseName: '',
isHfield: 1
},
opts: {
list: []
},
page: {
pageCurrent: 1,
pageSize: 10,
totalPage: 1,
totalCount: 0
}
}
dataObj.websiteInfo = context.store.state.websiteInfo
try {
dataObj.map.courseName = context.query.search
const res = await courseList(dataObj.map, dataObj.page.pageCurrent, dataObj.page.pageSize)
dataObj.opts.list = res.list || []
dataObj.page.pageCurrent = res.pageCurrent || 1
dataObj.page.totalPage = res.totalPage || 1
dataObj.page.totalCount = res.totalCount || 0
return dataObj
} catch (e) {
context.error({ message: 'Data not found', statusCode: 404 })
}
},
data() {
return {
openVip: false,
ctrl: {
loading: false
}
}
},
head() {
return {
title: '搜索页面-' + this.$store.state.websiteInfo.websiteName,
meta: [
{
hid: 'keywords',
name: 'keywords',
content: this.$store.state.websiteInfo.websiteKeyword
},
{
hid: 'description',
name: 'description',
content: this.$store.state.websiteInfo.websiteDesc
}
]
}
},
watch: {
'$route'() {
this.getList()
}
},
mounted() {
if (this.websiteInfo && this.websiteInfo.isEnableVip) {
this.openVip = true
}
},
methods: {
getPage(int) {
window.scrollTo(0, 0)
this.page.pageCurrent = int
this.getList()
},
handleSearch() {
this.getList()
},
getList() {
this.ctrl.loading = true
courseList(this.map, this.page.pageCurrent, this.page.pageSize).then(res => {
this.ctrl.loading = false
this.opts.list = res.list || []
this.page.pageCurrent = res.pageCurrent
this.page.totalPage = res.totalPage || 1
this.page.totalCount = res.totalCount
}).catch(() => {
this.ctrl.loading = false
this.opts.list = []
this.page.pageCurrent = 1
this.page.totalPage = 1
this.page.totalCount = 0
})
}
}
}
</script>
<style scoped lang="scss">
// .search_page {
// min-height: 100vh;
// }
@keyframes bganimation {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
.search_input {
padding: 33px 0;
background-image: linear-gradient(125deg, #F44336, #E91E63, #9C27B0, #3F51B5, #2196F3);
animation: bganimation 15s infinite;
.search_box_content {
width: 607px;
margin: 0 auto;
}
.form {
width: 100%;
position: relative;
.iconfont {
position: absolute;
left: 10px;
top: 2px;
line-height: 44px;
font-size: 22px;
margin-right: 2px;
color: #999;
font-weight: 700;
}
.search_box_input {
width: 505px;
padding-left: 40px;
height: 44px;
line-height: 44px;
background: #fff;
border-radius: 6px 0 0 6px;
font-size: 13px;
color: #333;
}
.search_btn {
width: 96px;
height: 44px;
background-color: #47a6ee;
color: #fff;
font-weight: 700;
border-radius: 0 6px 6px 0;
position: relative;
top: 2px;
left: -5px;
border: none;
font-size: 14px;
cursor: pointer;
}
}
}
.course_info {
display: block;
width: 285px;
height: 240px;
border-radius: 6px;
position: relative;
&:hover {
text-decoration: none;
color: #000;
box-shadow: 0px 3px 18px rgba(0, 0, 0, 0.2);
transform: translateY(-2px);
transition: all .3s;
}
.img_box {
position: relative;
width: 285px;
height: 140px;
.qizi {
background: url(../assets/image/activity/qizi.png) no-repeat center;
position: absolute;
top: 0;
left: 10px;
width: 46px;
height: 43px;
padding-top: 3px;
color: #fff;
font-size: 14px;
text-align: center;
}
.course_img {
width: 285px;
height: 140px;
border-radius: 6px 6px 0 0;
}
}
p {
font-size: 16px;
margin-top: 5px;
padding: 0 10px;
word-break: break-all;
}
.price_box {
font-size: 16px;
position: absolute;
bottom: 15px;
left: 10px;
color: red;
}
}
.search_content {
background: #f6f8fb;
padding: 30px 0 10px 0;
min-height: calc(100vh - 130px - 120px - 380px - 1px);
ul {
width: 1200px;
margin: 0 auto;
}
li {
float: left;
border-radius: 6px;
background: #fff;
margin: 0px 20px 20px 0px;
&:nth-child(4n) {
margin-right: 0px;
}
}
}
.no_data {
text-align: center;
width: 1200px;
font-size: 20px;
line-height: 100px;
color: #999;
background: #fff;
margin: 20px auto;
border-radius: 6px;
box-shadow: 0 4px 10px 0 rgb(0 0 0 / 5%);
}
</style>
更多详细代码看idea