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.
309 lines
8.1 KiB
309 lines
8.1 KiB
<template>
|
|
<view>
|
|
<video :custom-cache="false" @loadedmetadata="onLoadedmetadata" id="nodeVideo" @timeupdate="onVideoTimeupdate" :src="currentNode.videoUrl" :title="currentNode.nodeName" play-btn-position="center" show-mute-btn show-center-play-btn controls autoplay enable-play-gesture class="video" ></video>
|
|
<view class="subject">
|
|
<view class="subject-name u-line-2">
|
|
{{subject.subjectName}}
|
|
</view>
|
|
<view class="subject-introduce u-line-2">
|
|
{{subject.introduce}}
|
|
</view>
|
|
</view>
|
|
<u-gap v-if="subject.product" bg-color="#efefef" height="8"></u-gap>
|
|
<view v-if="subject.product" class="product">
|
|
<view @click="toProductDetail" class="volume">
|
|
<u--image :src="subject.product.image" width="168.75rpx" height="168.75rpx" mode="aspectFill" radius="8rpx"></u--image>
|
|
<view style="width: 500rpx;" class="volume-info">
|
|
<view class="volume-title">{{subject.product.storeName}}</view>
|
|
<view class="volume-level">{{subject.product.storeInfo}}</view>
|
|
<view class="volume-price">
|
|
<view style="height: 30rpx;"><text style="font-size: 16rpx;">¥</text>{{subject.product.price}}</view>
|
|
<view class="btn-pay"><u-button type="primary" size="mini">查看</u-button> </view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<u-gap bg-color="#efefef" height="8"></u-gap>
|
|
<u-tabs @change=onTabChange :list="tabLIst" :current="currentTabIndex" :activeStyle="{color: '#eb3729','font-weight': 'bold'}" lineColor="#eb3729" :scrollable="false" itemStyle="width: 33.33%;height: 100rpx;font-size: 32rpx !important;"></u-tabs>
|
|
<view v-show="currentTabIndex == 0">
|
|
<view @click="onVolumeChange(item.id, index)" class="volume" v-for="(item ,index) in subject.volumeList" :key="index">
|
|
<u--image :src="item.poster" width="300rpx" height="168.75rpx" mode="aspectFill" radius="8rpx"></u--image>
|
|
<view class="volume-info">
|
|
<view class="volume-title">{{item.volumeName}}</view>
|
|
<view class="volume-level">{{item.level}} | {{item.nodeList?item.nodeList.length: 0}}小节</view>
|
|
<view class="volume-price">
|
|
<view style="height: 30rpx;"><text v-if="!item.isPaid" style="font-size: 16rpx;">¥</text>{{item.isSpike?item.spikePrice:item.price}}</view>
|
|
<view v-if="!item.isPaid" @click.stop="onBuy(item.id)" class="btn-pay"><u-button type="primary" size="mini">立即报名</u-button> </view>
|
|
<view v-if="item.isPaid" class="btn-pay"><u-button type="success" size="mini">学习</u-button> </view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view v-show="currentTabIndex == 1">
|
|
<u-cell-group>
|
|
<u-cell @click="onPlayNode(item)" :label="`${(item.duration / 60).toFixed(0)}分钟`" :title="item.nodeName"
|
|
v-for="(item, index) in subject.volumeList[volumeIndex].nodeList" :key="index">
|
|
<view slot="value" style="padding-right: 20rpx;display: flex; align-items: center;">
|
|
<template v-if="item.isFree || subject.volumeList[volumeIndex].isPaid">
|
|
<u-icon size="50rpx" name="play-right"></u-icon>立即播放
|
|
</template>
|
|
<template v-else>
|
|
<u-icon size="50rpx" name="lock"></u-icon>解锁专栏
|
|
</template>
|
|
</view>
|
|
</u-cell>
|
|
</u-cell-group>
|
|
|
|
</view>
|
|
<view v-show="currentTabIndex == 2">
|
|
<view class="subject-detail">
|
|
<rich-text :nodes="subject.detail"></rich-text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
|
|
import {
|
|
getProductDetail,
|
|
postCartAdd,
|
|
getCartCount,
|
|
getProductCode
|
|
} from '@/api/store'
|
|
import * as SubjectApi from '@/api/subject.js'
|
|
import parseHtml from '@/utils/html-parser.js'
|
|
let videoContext = null
|
|
let timer;
|
|
export default {
|
|
data() {
|
|
return {
|
|
tabLIst: [{name:'专栏'},{name:'目录'},{name: '介绍'}],
|
|
currentTabIndex: 0,
|
|
currentNode: {},
|
|
subjectId: undefined,
|
|
volumeId: undefined,
|
|
nodeId: undefined,
|
|
volumeIndex: 0,
|
|
nodeIndex: undefined,
|
|
volume: {},
|
|
subject: {
|
|
subjectName: '',
|
|
introduce:'',
|
|
}
|
|
}
|
|
},
|
|
computed: mapGetters(['userInfo']),
|
|
onLoad(options) {
|
|
const {sid, vid, nid} = options
|
|
this.subjectId = sid
|
|
this.volumeId = vid
|
|
this.nodeId = nid
|
|
this.getSubjectInfo()
|
|
uni.$on('VolumePaid', (volumeId) => {
|
|
console.log('volumeId: ',volumeId);
|
|
this.getSubjectInfo()
|
|
})
|
|
|
|
|
|
timer = setInterval(() => {
|
|
this.now += 1000
|
|
})
|
|
},
|
|
onReady() {
|
|
videoContext = uni.createVideoContext("nodeVideo", this)
|
|
},
|
|
onUnload() {
|
|
uni.$off('VolumePaid')
|
|
},
|
|
destroyed() {
|
|
if(timer) {
|
|
clearInterval(timer);
|
|
timer = null
|
|
}
|
|
},
|
|
methods: {
|
|
toProductDetail() {
|
|
this.$yrouter.push({
|
|
path: '/pages/shop/GoodsCon/index',
|
|
query: { id: this.subject.product.id },
|
|
})
|
|
},
|
|
onTabChange({index, name}) {
|
|
this.currentTabIndex = index
|
|
},
|
|
onLoadedmetadata() {
|
|
if(this.currentNode.position) {
|
|
console.log("改变进度");
|
|
videoContext.seek(this.currentNode.position)
|
|
}
|
|
|
|
videoContext.play()
|
|
},
|
|
onVideoTimeupdate(e) {
|
|
const {currentTime, duration} = e.detail
|
|
if(currentTime < 20) {
|
|
return
|
|
}
|
|
// 30秒保存一次记录
|
|
uni.$u.throttle(()=>{
|
|
console.log('currentTime: ',currentTime);
|
|
SubjectApi.saveNodeViews({
|
|
nodeId: this.currentNode.id,
|
|
volumeId: this.currentNode.volumeId,
|
|
subjectId: this.subjectId,
|
|
position: currentTime,
|
|
duration: duration
|
|
})
|
|
}, 30000)
|
|
},
|
|
async getSubjectInfo() {
|
|
const res = await SubjectApi.getSubject(this.subjectId)
|
|
this.subject = res.data
|
|
|
|
|
|
|
|
|
|
res.data.detail = res.data.detail.replace(/\<img/gi,
|
|
'<img style="max-width:100%;height:auto;"')
|
|
res.data.detail = res.data.detail.replace(/\<video/gi,
|
|
'<video style="max-width:100%;height:auto;"')
|
|
res.data.detail = res.data.detail.replace(/style=""/gi,
|
|
'')
|
|
|
|
|
|
|
|
|
|
this.subject.detail = parseHtml(res.data.detail);
|
|
|
|
uni.setNavigationBarTitle({
|
|
title: this.subject.subjectName
|
|
})
|
|
if(res.data.videoUrl){
|
|
this.currentVideo = res.data.videoUrl
|
|
}
|
|
|
|
if(!this.volumeId) {
|
|
this.volumeId = this.subject.volumeList[0].id
|
|
}
|
|
|
|
this.volumeIndex = this.subject.volumeList.findIndex(item => item.id == this.volumeId)
|
|
this.onVolumeChange(this.volumeId, this.volumeIndex)
|
|
if(this.nodeId) {
|
|
|
|
const node = this.subject.volumeList[this.volumeIndex].nodeList.find(item => item.id == this.nodeId)
|
|
this.onPlayNode(node)
|
|
}
|
|
|
|
},
|
|
onVolumeChange(volumeId, index) {
|
|
this.volumeId = volumeId
|
|
this.volumeIndex = index
|
|
this.currentTabIndex = 1
|
|
|
|
|
|
},
|
|
async onPlayNode(node) {
|
|
const volume = this.subject.volumeList[this.volumeIndex]
|
|
|
|
|
|
if(volume.isPaid || node.isFree) {
|
|
if(node.id != this.currentNode.id) {
|
|
|
|
// 获取进度
|
|
const res = await SubjectApi.getNodeViews(node.id)
|
|
node.position = 0
|
|
if(res.data) {
|
|
node.position = res.data.position
|
|
}
|
|
this.currentNode = node
|
|
videoContext.stop()
|
|
}
|
|
}else {
|
|
this.onBuy(volume.id)
|
|
}
|
|
},
|
|
|
|
async onBuy(volumeId) {
|
|
this.$yrouter.push({
|
|
path: '/pages/subject/OrderSubmission/index',
|
|
query: {
|
|
volumeId: volumeId
|
|
},
|
|
})
|
|
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.video{
|
|
width: 750rpx;
|
|
height: 420rpx;
|
|
|
|
}
|
|
|
|
.subject{
|
|
position: relative;
|
|
padding: 20rpx 0;
|
|
&-name {
|
|
// width: 400rpx ;
|
|
color: #333333;
|
|
font-size: 36rpx;
|
|
padding-left: 30rpx;
|
|
}
|
|
|
|
&-introduce{
|
|
// width: 400rpx;
|
|
color: #6c6c6c;
|
|
font-size: 24rpx;
|
|
padding-left: 30rpx;
|
|
margin-top: 10rpx;
|
|
}
|
|
}
|
|
|
|
.volume{
|
|
display: flex;
|
|
padding: 30rpx;
|
|
justify-content: space-between;
|
|
&-info {
|
|
width: 350rpx;
|
|
}
|
|
&-title {
|
|
font-size: 32rpx;
|
|
height: 70rpx;
|
|
}
|
|
|
|
&-level {
|
|
font-size: 24rpx;
|
|
color: #6c6c6c;
|
|
margin-bottom: 10rpx;
|
|
}
|
|
|
|
&-price {
|
|
font-size: 26rpx;
|
|
color: #eb3729;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.btn-pay{
|
|
width: 130rpx;
|
|
}
|
|
}
|
|
|
|
.node {
|
|
padding: 30rpx 0;
|
|
margin: 0 30rpx;
|
|
border-bottom: #e8e8e8 solid 1px;
|
|
&-title {
|
|
}
|
|
}
|
|
|
|
.subject-detail{
|
|
padding: 30rpx;
|
|
}
|
|
|
|
</style>
|