Kaynağa Gözat

fix 课程详情

he2802 3 yıl önce
ebeveyn
işleme
bc9521d01c

+ 3 - 1
common/api.js

@@ -7,6 +7,7 @@ import system from './httpList/system.js'
 import goods from './httpList/goods.js'
 import business from './httpList/business.js'
 import course from './httpList/course.js'
+import grade from './httpList/grade.js'
 export default {
 	...login,
 	...polyvVideo,
@@ -16,5 +17,6 @@ export default {
 	...wxpay,
 	...goods,
 	...business,
-	...course
+	...course,
+	...grade
 }

+ 16 - 0
common/httpList/course.js

@@ -10,5 +10,21 @@ export default {
 			noToken: true
 		})
 	},
+	chapterList(data) {
+		return myRequest({
+			url: '/app/common/course/chapterList',
+			method: 'get',
+			data: data,
+			noToken: true
+		})
+	},
+	sectionList(data) {
+		return myRequest({
+			url: '/app/common/course/sectionList',
+			method: 'get',
+			data: data,
+			noToken: true
+		})
+	}
 	
 }

+ 13 - 0
common/httpList/grade.js

@@ -0,0 +1,13 @@
+import {
+	myRequest
+} from '../request.js'
+export default {
+	goodsGradeList(data) {
+		return myRequest({
+			url: '/grade/grade/list',
+			method: 'get',
+			data: data
+		})
+	}
+	
+}

+ 4 - 1
common/methodTool.js

@@ -72,7 +72,7 @@ export default {
 		return Y + M + D + h + m + s;
 	},
 	//当前时间距离目标时间还有多久
-	GetRTime(EndTime) {
+	GetRTime(EndTime, isDay = true) {
 		var EndTime = EndTime //结束时间
 		var NowTime = new Date(); //当前时间
 		//后台给我的是10位 精确到秒的 所有下面我就除以了1000,不要小数点后面的
@@ -97,6 +97,9 @@ export default {
 		if (parseInt(s) < 10) {
 			s = "0" + s;
 		}
+		if (isDay) {
+			return d;
+		}
 		return d + '天' + h + '小时' + m + '分' + s + '秒'
 	},
 	TimeTotimestamp(date) {

+ 1 - 1
manifest.json

@@ -75,7 +75,7 @@
     "quickapp" : {},
     /* 小程序特有相关 */
     "mp-weixin" : {
-        "appid" : "wx8295c6fa6b0b3106",
+        "appid" : "wxd0fac11d52c8808b",
         "setting" : {
             "urlCheck" : false,
             "postcss" : true,

+ 1 - 1
pages2/bank/detail.vue

@@ -26,7 +26,7 @@
 		<view class="intro" v-if="current == 0">
 			<view class="content">
 				<view class="top">本题库为广东省建筑施工企业安全生产管理人员安全生产考试第三批参考题库(可两行)</view>
-				<image class="img"  src="/static/intro.png" ></image>
+				<image class="img"  src="/static/wd_bg.png" ></image>
 			</view>
 		</view>
 		<view class="title-list"  v-if="current == 1">

+ 87 - 0
pages2/components/course/courseChapter.vue

@@ -0,0 +1,87 @@
+<template>
+	<view style="margin: 20rpx 0;">
+		<view class="title" @click="openChapter(menuItem)">
+		<image src="/static/icon/up1.png" class="icon_up" v-if="down"></image>
+		<image src="/static/icon/down1.png" class="icon_up" v-if="!down"></image>
+		<text style="margin-left: 30rpx;">{{menuItem.name}}</text>
+		</view>
+		<view v-if="!down">
+			<view v-for="(itemM,indexM) in list" >
+				<courseSection  :menuItem="itemM"></courseSection>
+				<u-line v-if="indexM<list.length-1"></u-line>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { mapGetters } from 'vuex';
+import courseSection from '@/pages2/components/course/courseSection.vue';
+export default {
+	name: 'courseChapter',
+	props: {
+		menuItem: {
+			type: Object,
+			default: {}
+		}
+	},
+	components: {
+		courseSection
+	},
+	data() {
+		return {
+			down:true,
+			list:[]
+	
+		};
+	},
+	onLoad() {},
+	created() {
+		
+	},
+	mounted() {
+		
+	},
+	methods: {
+		openChapter(item){
+			this.down = !this.down
+			if(!this.down&&this.list.length==0){
+				this.getSectionList(item.id)
+			}
+		},
+		getSectionList(chapterId) {
+			let self = this
+			this.$api.sectionList({chapterId:chapterId}).then(res => {
+				if(res.data.code==200){
+					for(let i=0;i<res.data.data.length;i++){
+						let item = res.data.data[i]
+						item.id = item.sectionId
+						//判断是否试听
+						item.tryListen = false
+						if(self.goodsAuditionConfigIdList.indexOf(item.id)!==-1){
+							item.tryListen = true
+						}	
+					}
+					self.list = res.data.data
+				}
+			});
+		},
+	},computed: { ...mapGetters(['goodsAuditionConfigIdList']) },
+};
+</script>
+
+<style scoped>
+	.icon_up{
+		width: 24rpx;
+		height: 24rpx;
+	}
+.title{
+	font-size: 24rpx;
+	font-family: PingFang SC;
+	font-weight: bold;
+	color: #666666;
+	white-space:nowrap;
+	overflow:hidden;
+	text-overflow:ellipsis; 
+}
+</style>

+ 81 - 0
pages2/components/course/courseModule.vue

@@ -0,0 +1,81 @@
+<template>
+	<view style="margin: 20rpx 0;">
+		<view class="title" @click="openModule(menuItem)">
+		<image src="/static/icon/up1.png" class="icon_up" v-if="down"></image>
+		<image src="/static/icon/down1.png" class="icon_up" v-if="!down"></image>
+		<text style="margin-left: 10rpx;">{{menuItem.name}}</text>
+		</view>
+		<view v-if="!down">
+			<view v-for="(itemM,indexM) in list" >
+				<courseChapter  :menuItem="itemM"></courseChapter>
+				<u-line v-if="indexM<list.length-1"></u-line>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import courseChapter from '@/pages2/components/course/courseChapter.vue';
+export default {
+	name: 'courseModule',
+	props: {
+		menuItem: {
+			type: Object,
+			default: {}
+		}
+	},
+	components: {
+		courseChapter
+	},
+	data() {
+		return {
+			 down:true,
+			 list:[]
+		};
+	},
+	onLoad() {},
+	created() {
+		
+	},
+	mounted() {
+		
+	},
+	methods: {
+		openModule(item){
+			this.down = !this.down
+			if(!this.down&&this.list.length==0){
+				this.getChapterList(item.id)
+			}
+		},
+		getChapterList(moduleId) {
+			let self = this
+			this.$api.chapterList({moduleId:moduleId}).then(res => {
+				if(res.data.code==200){
+					for(let i=0;i<res.data.data.length;i++){
+						let item = res.data.data[i]
+						item.id = item.chapterId
+						
+					}
+					self.list = res.data.data
+				}
+			});
+		},
+	}
+};
+</script>
+
+<style scoped>
+	.icon_up{
+		width: 24rpx;
+		height: 24rpx;
+	}
+.title{
+	font-size: 30rpx;
+	font-family: PingFang SC;
+	font-weight: bold;
+	color: #333333;
+	white-space:nowrap;
+	overflow:hidden;
+	text-overflow:ellipsis; 
+}
+</style>

+ 106 - 0
pages2/components/course/courseSection.vue

@@ -0,0 +1,106 @@
+<template>
+	<view style="display: flex;justify-content: space-between;align-items: center;">
+		<view style="display: flex;align-items: center;margin: 20rpx 0;">
+			<view class="tag tagColor1" v-if="menuItem.sectionType==1">录播</view>
+			<view class="tag tagColor2" v-if="menuItem.sectionType==2">直播</view>
+			<view class="tag tagColor3" v-if="menuItem.sectionType==3">回放</view>
+			<view class="t_content">
+				<view>{{menuItem.name}}</view>
+				<view style="font-size: 20rpx;color: #FF3B30;" v-if="menuItem.liveStartTime">
+					<view v-if="menuItem.liveStartTime>nowTime">
+						<text>{{$method.timestampToTime(menuItem.liveStartTime)}}</text>-
+						<text>{{$method.timestampToTime(menuItem.liveEndTime)}}</text>
+					</view>
+					<view v-if="menuItem.liveStartTime<=nowTime&&menuItem.liveEndTime>nowTime">
+						<text>直播中</text>
+					</view>
+					<view v-if="menuItem.liveEndTime<nowTime">
+						<text>回放</text>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view v-if="menuItem.tryListen" class="tryBox">
+			试看
+		</view>
+	</view>
+</template>
+
+<script>
+
+export default {
+	name: 'courseSection',
+	props: {
+		menuItem: {
+			type: Object,
+			default: {}
+		}
+	},
+	data() {
+		return {
+			nowTime:0
+		};
+	},
+	onLoad() {},
+	created() {
+		
+	},
+	mounted() {
+		this.nowTime = Number(new  Date().getTime()/1000).toFixed(0)
+	},
+	methods: {
+		
+	}
+	
+};
+</script>
+
+<style scoped>
+	.tryBox{
+		width: 96rpx;
+		height: 48rpx;
+		background: #007AFF;
+		border-radius: 24rpx;
+		color: #FFFFFF;
+		font-size: 30rpx;
+		line-height: 48rpx;
+		text-align: center;
+	}
+	.icon_up{
+		width: 24rpx;
+		height: 24rpx;
+	}
+	.t_content3{
+		color: #007AFF;
+	}
+	.t_content2{
+		color: #007AFF;
+	}
+	.t_content1{
+		color: #007AFF;
+	}
+	.t_content{
+		margin-left: 10rpx;
+		color: #666666;
+		white-space:nowrap;
+		overflow:hidden;
+		text-overflow:ellipsis; 
+	}
+	.tagColor3{
+		border: 2rpx solid #FF9500;
+		color: #FF9500;
+	}
+	.tagColor2{
+		border: 2rpx solid #FF3B30;
+		color: #FF3B30;
+	}
+	.tagColor1{
+		border: 2rpx solid #007AFF;
+		color: #007AFF;
+	}
+	.tag{
+		border-radius: 8rpx;
+		font-size: 20rpx;
+		padding: 5rpx;
+	}
+</style>

+ 86 - 31
pages2/course/detail.vue

@@ -1,10 +1,10 @@
 <template>
 	<view>
-		<view style="background-color: #FFFFFF;">
+		<view class="videoBox" >
 			<view >
-				<image :src="$method.splitImgHost(detail.coverUrl)" style="height: 461rpx;width: 100%;"></image>
-				<view style="padding:20rpx">
-					<view style="display: flex;margin-top: 13rpx;">
+				<image :src="$method.splitImgHost(detail.coverUrl)" style="height: 460rpx;width: 100%;"></image>
+				<view style="padding:20rpx;height: 120rpx;">
+					<view style="display: flex;">
 						<view class="yearTag">{{detail.year}}</view>
 						<view class="titleTag">{{detail.goodsName}}</view>
 					</view>
@@ -16,29 +16,37 @@
 				</view>
 			</view>
 			<u-line color="#D6D6DB" />
-			<view>
+			<view style="height: 80rpx;">
 				<view style="width: 160px;margin: 0 auto;"><u-tabs :list="list" item-width="150" font-size="24" bar-width="110" :current="current" @change="change" active-color="#007AFF"></u-tabs></view>
 			</view>
-			<u-line color="#D6D6DB" />
+			
 		</view>
-		<view style="padding: 20rpx;" v-show="current==0">
+		<view style="padding: 20rpx;position: relative;top: 680rpx;" v-show="current==0">
 			<view class="content">
 				<view v-html="detail.mobileDetailHtml"></view>
 			</view>
 		</view>
-		<view style="padding: 20rpx;" v-show="current==1">
+		<view style="padding: 20rpx;padding-bottom: 100rpx;position: relative;top: 680rpx;" v-show="current==1">
 			<view >
 				<view v-for="(item,index) in courseList" >
-					<view class="courseItem" @click="openCourse(item)">
-						<view>{{item.courseName}}</view>
-						<view>
-							<u-icon name="arrow-down" v-if="item.down"></u-icon>
-							<u-icon name="arrow-up" v-if="!item.down"></u-icon>
+					<view class="courseItemBox" >
+						<view class="courseItem" @click="openCourse(item)">
+							<view class="courseName">{{item.courseName}}</view>
+							<view>
+								<image src="/static/icon/up.png" class="icon_up" v-if="item.down"></image>
+								<image src="/static/icon/down.png" class="icon_up" v-if="!item.down"></image>
+							</view>
+						</view>
+						<view v-show="!item.down">
+							<view v-for="(itemM,indexM) in menuList" >
+								<courseModule v-if="itemM.type==1" :menuItem="itemM"></courseModule>
+								<courseChapter v-if="itemM.type==2" :menuItem="itemM"></courseChapter>
+								<courseSection v-if="itemM.type==3" :menuItem="itemM"></courseSection>
+								<u-line></u-line>
+							</view>
 						</view>
 					</view>
-					<view v-if="item.down">
-						
-					</view>
+					
 				</view>
 			</view>
 		</view>
@@ -46,15 +54,23 @@
 			<view class="priceTag">¥ {{detail.standPrice}}</view>
 			<view style="display: flex;color: #FFFFFF;align-items: center;">
 				<view class="btn1" @click="addCart">加购物车</view>
-				<view class="btn2" @click="buy">立即购买</view>
+				<view class="btn2" @click="buy">立即购买 </view>
 			</view>
 		</view>
 	</view>
 </template>
 
 <script>
+import courseModule from '@/pages2/components/course/courseModule.vue';
+import courseChapter from '@/pages2/components/course/courseChapter.vue';
+import courseSection from '@/pages2/components/course/courseSection.vue';
 import { mapGetters } from 'vuex';
 export default {
+	components: {
+		courseModule,
+		courseChapter,
+		courseSection
+	},
 	data() {
 		return {
 			id:0,
@@ -68,13 +84,14 @@ export default {
 			],
 			current:0,
 			detail:{},
-			courseList:[]	
+			courseList:[],
+			menuList:[]
 		};
 	},
 	onUnload() {
 		
 	},
-	computed: { ...mapGetters(['userInfo']) },
+	computed: { ...mapGetters(['userInfo','goodsAuditionConfigIdList']) },
 	onLoad(option) {
 		this.id = option.id;
 		this.getDetail()
@@ -86,8 +103,8 @@ export default {
 	methods: {
 		openCourse(item){
 			item.down = !item.down
-			if(!item.down){
-				this.menuList(item.courseId)
+			if(!item.down&&this.menuList.length==0){
+				this.getMenuList(item.courseId)
 			}
 			
 		},
@@ -108,7 +125,6 @@ export default {
 			let self = this
 			this.$api.goodsCourseList(this.id).then(res => {
 				if(res.data.code==200){
-					
 					for(let i=0;i<res.data.rows.length;i++){
 						let item = res.data.rows[i]
 						item.down = true
@@ -117,21 +133,41 @@ export default {
 				}
 			});
 		},
-		menuList(courseId) {
+		getMenuList(courseId) {
 			let self = this
 			this.$api.menuList({courseId:courseId}).then(res => {
 				if(res.data.code==200){
-					
-					console.log(res.data)
+					for(let i=0;i<res.data.rows.length;i++){
+						let item = res.data.rows[i]
+						item.down = true
+						item.id = item.menuId
+						item.name = item.menuName
+						
+						if(item.type==3){
+							//判断是否试听
+							item.tryListen = false
+							if(self.goodsAuditionConfigIdList.indexOf(item.id)!==-1){
+								item.tryListen = true
+							}	
+						}
+					}
+					self.menuList = res.data.rows
 				}
 			});
 		},
 		getDetail() {
 			let self = this
+			let sectionIdList = []
 			this.$api.goodsDetail(this.id).then(res => {
 				if(res.data.code==200){
 					self.detail = res.data.data
-					console.log(self.detail.mobileDetailHtml)
+					if(self.detail.goodsAuditionConfig){
+						let configList = JSON.parse(self.detail.goodsAuditionConfig)
+						for (var itemChild of configList) {
+							sectionIdList.push(itemChild.sectionId)//存储试听节ID
+						}
+						self.$store.commit('setGoodsAuditionConfigIdList', {goodsAuditionConfigIdList:sectionIdList});
+					}
 				}
 			});
 		},
@@ -139,7 +175,7 @@ export default {
 			if(this.$method.isGoLogin()){
 				return
 			}
-			this.$navTo.togo('/pages2/order/confirm_list');
+			this.$navTo.togo('/pages2/order/confirm_list?id='+this.id);
 		},
 		addCart(){
 			if(this.$method.isGoLogin()){
@@ -162,21 +198,40 @@ export default {
 	}
 </style>
 <style scope>
+	.courseName{
+		white-space:nowrap;
+		overflow:hidden;
+		text-overflow:ellipsis; 
+	}
+	.videoBox{
+		position: fixed;
+		background-color: #FFFFFF;
+		width: 100%;
+		height: 680rpx;
+		z-index: 999;
+	}
+	.icon_up{
+		width: 32rpx;
+		height: 32rpx;
+	}
 	.contentBox{
 		
 	}
-	.courseItem{
-		width: 100%;
-		height: 80rpx;
+	.courseItemBox{
 		background: #FFFFFF;
 		border-radius: 16rpx;
+		padding: 0 10rpx;
+		margin-bottom: 20rpx;
+	}
+	.courseItem{
+		height: 80rpx;
 		color: #333333;
 		font-size: 32rpx;
 		line-height: 80rpx;
 		font-weight: bold;
-		padding: 0 10rpx;
 		display: flex;
 		justify-content: space-between;
+
 	}
 	.content{
 		background-color: #FFFFFF;

+ 1 - 1
pages2/learn/details.vue

@@ -9,7 +9,7 @@
 				<u-line color="#EEEEEE" />
 				<view class="info">
 					<view class="item" v-for="item in 7">
-						<view class="imgbox"><image src="@/static/intro.png" style="width:100%" mode="heightFix"></image></view>
+						<view class="imgbox"><image src="@/static/wd_bg.png" style="width:100%" mode="heightFix"></image></view>
 						<view class="time">2021/10/25 14:25:20</view>
 					</view>
 				</view>

+ 44 - 18
pages2/order/confirm_list.vue

@@ -1,30 +1,35 @@
 <template>
 	<view>
 		<view style="padding: 30rpx;padding-bottom: 98rpx;">
-			<view v-for="(item,index) in list" :key="index" >
+			<view  >
 				<view class="item">
 					<view style="display: flex;justify-content: space-between;padding-bottom: 15rpx;">
-						<image src="/static/login_bg.jpg" style="height: 134rpx;width: 388rpx;border-radius: 16rpx;"></image>
+						<image :src="$method.splitImgHost(detail.coverUrl)" style="height: 134rpx;width: 278rpx;border-radius: 16rpx;"></image>
 						<view style="margin-left: 20rpx;">
 							<view style="color: #333333;font-size: 30rpx;font-weight: bold;">
-								2020年二建建筑工程管理与实
-								务(实务专题班)
+								{{detail.goodsName}}
 							</view>
 							<view class="priceTag">
-								¥ 999.00
+								¥ {{detail.standPrice}}
 							</view>
 						</view>
 					</view>
-					<u-line color="#D6D6DB" />
-					<view style="display: flex;justify-content: space-between;align-items: center;height: 50rpx;" @click="openPopup(index)">
-						<view style="color: #666666;font-size: 24rpx;">选择班级</view>
-						<view><u-icon name="arrow-right" color="#999999" size="28"></u-icon></view>
+					<view v-if="detail.templateType!=null">
+						<u-line color="#D6D6DB" />
+						<view v-if="detail.templateType=='class'" style="display: flex;justify-content: space-between;align-items: center;height: 50rpx;" @click="openPopup(0)">
+							<view style="color: #666666;font-size: 24rpx;">选择班级</view>
+							<view><u-icon name="arrow-right" color="#999999" size="28"></u-icon></view>
+						</view>
+						<view v-if="detail.templateType=='apply'" style="display: flex;justify-content: space-between;align-items: center;height: 50rpx;" @click="openPopup(1)">
+							<view style="color: #666666;font-size: 24rpx;">报考地区</view>
+							<view><u-icon name="arrow-right" color="#999999" size="28"></u-icon></view>
+						</view>
 					</view>
 				</view>
 			</view>
 		</view>
 		<view class="bottomBox safeArea">
-			<view class="priceTag">¥ 999.00</view>
+			<view class="priceTag">¥ {{detail.standPrice}}</view>
 			<view style="display: flex;color: #FFFFFF;align-items: center;">
 				<view class="btn2" @click="goBuy()">确认购买</view>
 			</view>
@@ -41,21 +46,21 @@
 					<scroll-view scroll-y="true" style="height: 500rpx;">
 						<view>
 							<u-checkbox-group @change="checkboxGroupChange">
-								<view v-for="(item, index) in list" :key="index" >
+								<view v-for="(item, index) in gradeList" :key="index" >
 									<view style="display: flex;align-items: center;padding: 20rpx;">
 										<view>
 											<u-checkbox
 												shape="circle"
 												@change="checkboxChange" 
-												v-model="item.checked" 
-												:name="item.name"
+												v-model="gradeList[index].checked" 
+												:name="gradeList[index].name"
 											></u-checkbox>
 										</view>
 										<view :class="item.checked?'white-box blue-box':'white-box'" >
 											<view>
-												<view class="blackTxt">2021年第1期二级建造师继续教育选修课(市政)标题过长换行显示</view>
-												<view class="redTxt">有效期至:2021/11/30</view>
-												<view class="redTxt">本班还剩41天将结束学习</view>
+												<view class="blackTxt">{{item.className}}</view>
+												<view class="redTxt">有效期至:{{$method.timestampToTime(item.classEndTime)}}</view>
+												<view class="redTxt">本班还剩{{$method.GetRTime(item.classEndTime)}}天将结束学习</view>
 											</view>
 										</view>
 									</view>
@@ -118,6 +123,7 @@ export default {
 	},
 	data() {
 		return {
+			id:0,
 			indicatorStyle: `height: 50px;`,
 			showArea:true,
 			show1:false,
@@ -185,13 +191,16 @@ export default {
 					}
 				],
 				value1:'',
-			show:false
+			show:false,
+			detail:{},
+			gradeList:[]
 		};
 	},
 	onPullDownRefresh(){
 	},
 	onLoad(option) {
-		
+		this.id = option.id;
+		this.getDetail()
 	},
 	onShow() {
 		/* if(this.current === 2 && this.$method.isLogin()){
@@ -199,9 +208,25 @@ export default {
 		} */
 	},
 	methods: {
+		goodsGradeList() {
+			let self = this
+			this.$api.goodsGradeList({goodsId:this.id}).then(res => {
+				if(res.data.code==200){
+					self.gradeList = res.data.rows
+				}
+			});
+		},
 		goBuy(){
 			this.$navTo.togo('/pages2/order/confirm_pay');
 		},
+		getDetail() {
+			let self = this
+			this.$api.goodsDetail(this.id).then(res => {
+				if(res.data.code==200){
+					self.detail = res.data.data
+				}
+			});
+		},
 		bindChange(e) {
 			const val = e.detail.value
 		},
@@ -221,6 +246,7 @@ export default {
 		openPopup(index){
 			if(index==0){
 				this.show = true
+				this.goodsGradeList()
 			}else{
 				this.show1 = true
 			}

BIN
static/goods.png


BIN
static/icon/down.png


BIN
static/icon/down1.png


BIN
static/icon/up.png


BIN
static/icon/up1.png


BIN
static/intro.png


BIN
static/job@3xactive.png


+ 8 - 1
store/index.js

@@ -10,7 +10,8 @@ const store = new Vuex.Store({
         userName: '',
 		userInfo:null,
 		dictObj:null,
-		allowLoading:true
+		allowLoading:true,
+		goodsAuditionConfigIdList:[] //当前访问页面的试听节ID
     },  
 	 getters: {
 		userInfo: state => {
@@ -27,11 +28,17 @@ const store = new Vuex.Store({
 		allowLoading:state => {
 			return state.allowLoading
 		},
+		goodsAuditionConfigIdList:state => {
+			return state.goodsAuditionConfigIdList
+		}
 	},
     mutations: { 
         updateUserInfo(state, provider) {
             state.userInfo = provider.userInfo;  
         },
+		setGoodsAuditionConfigIdList(state, provider){
+			state.goodsAuditionConfigIdList = provider.goodsAuditionConfigIdList;  
+		},
     }  
 })  
 async function getUserInfo(state){