You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

820 lines
21 KiB

<template>
<view class="course-detail-index">
<view v-if="$store.getters.token || userInfo.uid">
<view class="swiper-box">
<swiper :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000">
<swiper-item v-for="(item,index) in detail.imageArr" :key="index">
<view class="swiper-item"><image :src="item" mode="aspectFill"></image></view>
</swiper-item>
</swiper>
</view>
<view class="course-info-box">
<view class="course-title">{{detail.courseName}}</view>
<view class="tips-box">
<view class="tip-item blue">{{detail.categoryName}}</view>
<view class="tip-item orange">{{detail.levelName}}</view>
</view>
</view>
<view class="tab-box acea-row row-around" :class="isFixedTop?'fixed':''">
<view class="tab-item" :class="active == 0 ? 'tab-item-active' : ''" @click="handelScroll('content1','0')">简介</view>
<view class="tab-item" :class="active == 1 ? 'tab-item-active' : ''" @click="handelScroll('content2','1')">课程安排</view>
<view class="tab-item" :class="active == 2 ? 'tab-item-active' : ''" @click="handelScroll('content3','2')">相关推荐</view>
</view>
<view class="content-box" bindscroll="scroll">
<scroll-view scroll-y="true" scroll-with-animation="true" style="height:100%;" :scroll-into-view="toView" >
<view class="teacher-info-box content" id="content1">
<view class="title-box acea-row row-middle">
<view class="left-line"></view>
<span>主讲人介绍</span>
</view>
<scroll-view class="scroll-view_H" scroll-x="true">
<view class="info-box" v-for="(item,index) in detail.masterDtos" :key="index">
<image :src="item.photoPath"></image>
<view class="name">{{item.name}}</view>
<view class="tip">禪易能量高级讲师</view>
<view class="desc">{{item.introduction}}</view>
</view>
</scroll-view>
</view>
<view class="course-desc-box">
<view class="title-box acea-row row-middle">
<view class="left-line"></view>
<span>课程简介</span>
</view>
<text class="desc">{{detail.courseIntroduce}}</text>
</view>
<view class="course-desc-box content" id="content2">
<view class="title-box acea-row row-middle">
<view class="left-line"></view>
<span>课程表</span>
</view>
<view class="course-card-list">
<view class="course-card-item acea-row row-middle">
<image src="../../static/course-icon1.png"></image>
<view class="label">课程时间</view>
<view class="info">{{courseStartTime}} 至 {{courseEndTime}}</view>
</view>
<view class="course-card-item acea-row row-middle">
<image src="../../static/course-icon2.png"></image>
<view class="label">课程地点</view>
<view class="info">{{detail.coursePlace}}</view>
</view>
<view class="course-card-item acea-row row-middle">
<image src="../../static/course-icon3.png"></image>
<view class="label">签到时间</view>
<view class="info">{{detail.signStartTime}} 至 {{detail.signEndTime}}</view>
</view>
<view class="course-card-item acea-row row-middle">
<image src="../../static/course-icon4.png"></image>
<view class="label">报名截止</view>
<view class="info">{{detail.enterEndTime}}</view>
</view>
</view>
</view>
<view class="course-desc-box" >
<view class="title-box acea-row row-middle">
<view class="left-line"></view>
<span>课程安排</span>
</view>
<view class="plan-box acea-row-nowrap" v-for="(item,index) in detail.courseScheduleList" :key="index">
<!-- <text class="plan-time">{{index+1}}</text> -->
<text>{{item.courseContent}}</text>
</view>
</view>
<view class="recommend-box content" id="content3">
<view class="title-box acea-row row-middle row-center" v-if="detail.similarCourseList.length > 0">
<view class="row-line"></view>
<view class="title">相关推荐</view>
<view class="row-line"></view>
</view>
<view class="knowledge-list">
<view class="knowledge-item acea-row" v-for="(item,index) in detail.similarCourseList" :key="index" @click="toDetail(item.id)">
<view class="img-box">
<image :src="item.imageArr[0]" mode="aspectFill"></image>
</view>
<view class="knowledge-info-box">
<view class="title line1">{{item.courseName}}</view>
<view class="time">{{courseStartTime}}~{{courseEndTime}}</view>
<view class="address">{{item.coursePlace}}</view>
<view class="type-box acea-row row-between row-middle">
<view class="price">{{item.levelName}}</view>
<!-- <view class="price" v-if="item.level == 2">¥{{item.courseCharge}}</view>
<view class="price colG" v-if="item.chargeType == 0">免费</view> -->
<view class="signin-btn red">去报名</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<view class="footer-box acea-row row-between-wrapper">
<view class="footer-left">
<view class="price" v-if="detail.signState == 1">座位号:{{detail.seat}}</view>
<!-- <view class="price" v-if="detail.level > 2">{{detail.level}}级课程</view>
<view class="price" v-if="detail.level == 2"><span class="fz24">¥</span>{{detail.courseCharge}} </view>
<view class="price colG" v-if="detail.chargeType == 0">免费</view> -->
</view>
<view class="footer-right">
<view class="btn-box acea-row row-between-wrapper" v-if="userInfo.phone != '' && teacherId != null && detail.haveSeller == 0 && detail.enterState == 0">
<view class="count-down">
<view>距离截止时间还剩</view>
<uni-countdown color="#EB5744" :day="day" :hour="hour" :minute="minute" :second="second" />
</view>
<view class="signin-btn" @click="signInClick(2)">立即报名</view>
</view>
<view class="btn-box acea-row row-between-wrapper" v-if="userInfo.phone != '' && detail.haveSeller == 1 && detail.enterState == 0">
<view class="count-down">
<view>距离截止时间还剩</view>
<uni-countdown color="#EB5744" :day="day" :hour="hour" :minute="minute" :second="second" />
</view>
<view class="signin-btn" @click="signInClick(2)">立即报名</view>
</view>
<view class="signin-btn call-btn default-btn" v-if="detail.enterState == 1 && detail.signState == 2" >已报名</view>
<view class="signin-btn call-btn default-btn" v-if="detail.signState == 1" >已签到</view>
<view class="signin-btn call-btn"
@click="signInClick(1)"
v-if="userInfo.phone != '' && detail.haveSeller == 0 && detail.enterState == 0 && teacherId == null">
联系服务老师
</view>
<!-- #ifdef MP-WEIXIN -->
<button open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber" v-if="userInfo.phone == '' && detail.haveSeller == 0 && detail.enterState == 0" class="binding signin-btn call-btn" v-else>
<text>绑定手机号</text>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<button class="binding" @click="goBindPhone()" v-if="userInfo.phone == '' && detail.haveSeller == 0">
<text>绑定手机号</text>
</button>
<!-- #endif -->
</view>
</view>
<view class="mask-box" v-if="maskDialog">
<view class="dialog-box">
<view class="close" @click="closeDialog()">×</view>
<view class="dialog-top acea-row-nowrap row-center-wrapper">
<image src="../../static/error-icon.png"></image>
<view class="word">{{teacher.msg}}</view>
</view>
<view class="dialog-btm">
<view class="l-box acea-row row-between-wrapper">
<view class="acea-row row-middle">
<image :src="teacher.imgPath"></image>
<view>
<view>{{teacher.name}}</view>
<view>{{teacher.phone}}</view>
</view>
</view>
<view class="call-btn" @click="call">拨打电话</view>
</view>
</view>
</view>
</view>
</view>
<Authorization v-else />
</view>
</template>
<script>
// import WucTab from '@/components/wuc-tab/wuc-tab.vue';
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
import { getUserInfo, bindingPhone, wxappBindingPhone } from '@/api/user'
import { getCourseDetail ,enterCourse } from '@/api/knowledge';
import Authorization from '@/pages/authorization/index'
export default {
components: {
Authorization,
},
data() {
return {
id: '',
teacherId:null,
validCode:null,
detail:{courseStartTime:[],courseEndTime:[],similarCourseList:[]},
active: 0,
toView: '',
heightArr:[],
tabInitTop:'',
isFixedTop: false,
maskDialog:false,
day:'',
hour:'',
minute:'',
second:'',
teacher:{}
}
},
onLoad() {
//获取节点距离顶部的距离
uni.createSelectorQuery().select('.tab-box').boundingClientRect((res) => {
if (res && res.top > 0) {
this.tabInitTop= res.top;
}
}).exec();
if(this.$yroute.query.teacherId){
this.courseId = this.$yroute.query.courseId;
this.validCode = this.$yroute.query.validCode;
this.teacherId = this.$yroute.query.teacherId;
} else{
this.courseId = this.$yroute.query.id;
}
if(this.userInfo.uid){
this.getDetail()
this.$nextTick(()=>{
this.getHeightArr()
})
}
},
computed:{
userInfo(){
return this.$store.getters["userInfo"]
},
courseStartTime(){
return this.detail.courseStartTime.split(' ')[0]
},
courseEndTime(){
return this.detail.courseEndTime.split(' ')[0]
},
},
mounted() {
},
methods: {
getPhoneNumber: function(e) {
let thit = this
// 判断一下这里是不是小程序 如果是小程序,走获取微信手机号进行绑定
if (e.mp.detail.errMsg == 'getPhoneNumber:ok') {
uni.showLoading({
title: '绑定中',
})
wxappBindingPhone({
encryptedData: e.mp.detail.encryptedData,
iv: e.mp.detail.iv,
})
.then(res => {
// this.User();
thit.$store.dispatch('userInfo', true)
uni.hideLoading()
uni.showToast({
title: res.msg,
icon: 'success',
duration: 2000,
})
})
.catch(error => {
uni.hideLoading()
thit.$store.dispatch('userInfo', true)
uni.showToast({
title: error.msg || error.response.data.msg || error.response.data.message,
icon: 'none',
duration: 2000,
})
})
} else {
uni.showToast({
title: '已拒绝授权',
icon: 'none',
duration: 2000,
})
}
},
setDate(t){
let str = t.split(" ")[0].split("-")[1] + "." + t.split(" ")[0].split("-")[2]
return str
},
toDetail(id){
this.$yrouter.push({
path: '/pages/course/detail',
query: {
id:id,
},
})
},
getDetail(){
uni.showLoading({
title:'正在加载中...'
})
let nowTime = Date.parse(new Date())/1000;
getCourseDetail({id:this.courseId}).then((res)=>{
uni.hideLoading()
this.detail = res.data
console.log(this.detail,'this.detail')
let t = res.data.signEndTime.replace(/-/g, '/');
let endTime = Date.parse(new Date(t))/1000
this.times = endTime - nowTime;
this.day = Math.floor(this.times / (60 * 60 * 24));
this.hour = Math.floor(this.times / (60 * 60)) - (this.day * 24);
this.minute = Math.floor(this.times / 60) - (this.day * 24 * 60) - (this.hour * 60);
this.second = Math.floor(this.times) - (this.day * 24 * 60 * 60) - (this.hour * 60 * 60) - (this.minute * 60);
})
},
signInClick(type){
if(this.userInfo.realName == null){
uni.showModal({
title: '提示!',
content: '请完善个人信息后报名!',
success(res) {
if (res.confirm) {
uni.navigateTo({
url:'/pages/user/PersonalData/index'
})
}
}
});
} else{
if(type == 2){
uni.showModal({
title:'提示!',
content:'是否确定报名?',
success:(res)=>{
if(res.confirm){
this.sign()
}else if (res.cancel) {
console.log('用户点击取消');
}
}
})
} else{
this.sign()
}
}
},
sign(){
enterCourse({
courseId:this.courseId,
sellerId:this.teacherId,
validCode:this.validCode
}).then((res)=>{
if(res.data.state == 0){
this.maskDialog = true
this.teacher = res.data
} else if(res.data.state == 1){
uni.showToast({
title: '报名成功!',
duration: 2000,
type: "success"
});
setTimeout(()=>{
this.getDetail()
},2000)
} else{
uni.showToast({
title: res.msg,
icon: 'none',
duration: 2000
});
}
})
},
call(){
uni.makePhoneCall({
phoneNumber: this.teacher.phone
});
},
closeDialog(){
this.maskDialog = false
},
handelScroll(id,idx){
this.isFixedTop = true
uni.createSelectorQuery().select('#'+id).boundingClientRect(data=>{//目标位置的节点:类或者id
uni.createSelectorQuery().select(".course-detail-index").boundingClientRect(res=>{//最外层盒子的节点:类或者id
uni.pageScrollTo({
duration: 500,//过渡时间
scrollTop:data.top - res.top - 50 ,//返回顶部的top值
})
}).exec()
}).exec();
this.toView = id;
this.active = idx;
},
getHeightArr(){
let h=0;
let heightArr = [];
//selectAll会选择所要含有该类名的盒子
uni.createSelectorQuery().selectAll('.content').boundingClientRect((rect) => {
}).exec((res) => {
console.log('res',res)
res[0].forEach((item)=>{
h+=item.top;
heightArr.push(h);
})
this.heightArr = heightArr;
})
},
onPageScroll(res) {
this.scrollTop = res.scrollTop
let scrollTop = res.scrollTop;
let scrollArr = this.heightArr;
let timer = setTimeout(()=>{
if(this.scrollTop===scrollTop){
for  (let  i = 0; i < scrollArr.length; i++) {
if  (scrollTop < scrollArr[0]) {
this.active = 0;
} else if (scrollTop >= (scrollArr[i - 1]) && scrollTop < scrollArr[i]) {
this.active= i;
}
}
clearTimeout(timer)
}
},100)
var isSatisfy = scrollTop >= this.tabInitTop? true : false;
//只有处于吸顶的临界值才会不相等
if (this.isFixedTop === isSatisfy) {
return false;
}
this.isFixedTop = isSatisfy
},
onHide() {
clearInterval(this.timer);
},
}
}
</script>
<style scoped lang="less">
.course-detail-index{
width: 100%;
min-height: 100vh;
padding-bottom: 200rpx;
box-sizing: border-box;
.container {
height: 100%;
}
.mask-box{
width: 100%;
height: 100%;
background: rgba(0,0,0,.6);
position: fixed;
top: 0;
z-index: 99;
.dialog-box{
width: 600rpx;
border-radius: 12rpx;
overflow: hidden;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
.close{
width: 38rpx;
height: 38rpx;
position: absolute;
right: 12rpx;
top: 12rpx;
font-size: 36rpx;
}
.dialog-top{
width: 100%;
height: 212rpx;
box-sizing: border-box;
padding: 30rpx 38rpx 0;
background: linear-gradient(180deg, #FFE8E8 0%, #FFFFF8 100%);
image{
width: 154rpx;
height: 162rpx;
margin-right: 12rpx;
}
.word{
width: calc(100% - 164rpx);
height: 108rpx;
font-size: 28rpx;
color: #666;
}
}
.dialog-btm{
background-color: #fff;
padding: 30rpx 32rpx;
color: #666;
font-size: 28rpx;
image{
width: 100rpx;
height: 100rpx;
border-radius: 25px;
margin-right: 20rpx;
}
.call-btn{
width: 192rpx;
height: 60rpx;
background: #ECEFFD;
border-radius: 12rpx;
text-align: center;
line-height: 60rpx;
color: #6E85EB;
}
}
}
}
.swiper-box{
width: 100%;
height: 340rpx;
swiper{
height: 100%;
.swiper-item{
width: 100%;
height: 100%;
image{
width: 100%;
height: 100%;
}
}
}
}
.course-info-box{
box-sizing: border-box;
padding: 30rpx 30rpx 50rpx;
background: #fff;
.course-title{
font-size: 40rpx;
font-weight: 500;
color: #333333;
line-height: 56rpx;
margin: 0rpx 0 20rpx;
}
.tips-box{
display: flex;
.tip-item{
min-width: 60rpx;
height: 34rpx;
text-align: center;
line-height: 34rpx;
font-size: 24rpx;
padding: 0 5rpx;
border-radius: 5rpx;
}
.blue{
color: #6E85EB;
border: 1px solid #6E85EB;
background: #EDEFF8;
}
.orange{
margin-left: 10rpx;
border: 1px solid #F99C10;
color: #F99C10;
background: #FFEDCC;
}
}
}
.tab-box{
white-space: nowrap;
width: 100%;
background: #fff;
padding: 18rpx 34rpx 0;
.tab-item{
width: 33%;
text-align: center;
display: inline-block;
padding-bottom: 18rpx;
font-size: 32rpx;
color: #222;
}
.tab-item-active{
font-size: 32rpx;
font-weight: bold;
border-bottom: 4rpx solid #EB5744;
}
}
.fixed{
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 99;
}
.content-box{
width: 100%;
box-sizing: border-box;
background: #F5F6F7;
padding: 24rpx 22rpx;
.teacher-info-box,.course-desc-box{
width: 100%;
background:#fff;
border-radius: 8rpx;
padding: 18rpx 24rpx 30rpx;
margin-bottom: 20rpx;
}
.desc{
text-indent: 2em;
font-size: 28rpx;
color: #666666;
line-height: 24px;
}
.scroll-view_H{
white-space: nowrap;
width: 100%;
}
.info-box{
width: 100%;
display: inline-block;
text-align: center;
image{
width: 100rpx;
height: 100rpx;
border-radius: 50px;
}
.name{
width: 100%;
font-size: 32rpx;
color: #3C464F;
line-height: 44rpx;
}
.tip{
width: 100%;
font-size: 24rpx;
color: #999999;
line-height: 34px;
}
.desc{
width: 100%;
white-space: pre-wrap;
}
}
.title-box{
font-size: 36rpx;
font-weight: bold;
color: #3C464F;
line-height: 50rpx;
margin-bottom: 20rpx;
.left-line{
width: 6rpx;
height: 32rpx;
background-color: #EB5744;
margin-right: 16rpx;
}
}
.plan-box{
font-size: 28rpx;
color: #666;
line-height: 32rpx;
margin-bottom: 12rpx;
.plan-time{
width: 170rpx;
margin-right: 22rpx;
}
}
.course-card-item{
font-size: 26rpx;
margin-bottom: 12rpx;
image{
width: 30rpx;
height: 30rpx;
}
.label{
color: #999999;
margin: 0 24rpx 0 16rpx;
}
.info{
color: #666666;
}
}
.recommend-box{
.title-box{
font-size: 32rpx;
color: #EB5744;
.title{
margin: 0 10rpx;
}
.row-line{
width: 36rpx;
height: 2rpx;
border: 2rpx solid #EB5744;
}
}
.knowledge-list {
width: 100%;
.knowledge-item {
background: #fff;
padding: 32rpx;
.img-box {
width: 204rpx;
height: 200rpx;
margin-right: 20rpx;
image {
width: 100%;
height: 100%;
border-radius: 10rpx;
}
}
.knowledge-info-box {
width: calc(100% - 224rpx);
.title {
font-size: 28rpx;
color: #333;
font-size: 500;
line-height: 40rpx;
}
.time,.address{
color: #999;
font-size: 24rpx;
margin-top: 5rpx;
line-height: 34rpx;
}
.intro {
font-size: 28rpx;
color: #999999;
line-height: 40rpx;
margin: 8rpx 0 36rpx;
}
.type-box {
font-size: 24rpx;
color: #999;
margin-top: 28rpx;
.price{
font-size: 28rpx;
color: #EA533E;
font-weight: 500;
}
.signin-btn{
width: 156rpx;
height: 60rpx;
text-align: center;
line-height: 60rpx;
background: #FFEAE7;
border-radius: 12rpx;
font-size: 32rpx;
}
.red{
background: #FFEAE7;
color: #EE7060;
}
.blue {
color: #6E85EB;
background: #ECEFFD;
}
.yellow {
color: #F99C10;
background: #FFEDCC;
}
}
}
}
}
}
}
.colG{
color: #8FB85B !important;
}
.footer-box{
width: 100%;
padding: 30rpx 26rpx 60rpx 44rpx;
background: #fff;
position: fixed;
bottom: 0;
left: 0;
.price{
font-size: 44rpx;
color: #EA533E;
}
.fz24{
font-size: 24rpx;
}
.footer-right{
.btn-box{
text-align: center;
width: 410rpx;
height: 80rpx;
border-radius: 44px;
border: 2rpx solid #EB5744;
box-sizing: border-box;
overflow: hidden;
.count-down{
width: calc(100% - 178rpx);
text-align: center;
font-size: 24rpx;
color: #999;
}
.colR{
color: #EB5744;
}
.signin-btn{
width: 178rpx;
height: 100%;
line-height: 84rpx;
font-size: 32rpx;
color: #fff;
background: #EB5744;
}
}
.call-btn{
width: 268rpx;
height: 80rpx;
background: #F99C10;
border-radius: 44rpx;
text-align: center;
line-height: 80rpx;
color: #fff;
font-size: 30rpx;
}
.default-btn{
background: #f6f6f6;
color: #222;
}
}
}
}
</style>