integrated_traffic_uniapp/pages/application/onlinexxks/video_study.vue

638 lines
20 KiB
Vue
Raw Normal View History

2024-03-25 19:16:41 +08:00
<template>
<view>
<!-- <cu-custom bgColor="bg-gradual-blueness" :isBack="true">-->
<!-- <block slot="backText">返回</block>-->
<!-- <block slot="content">学习详情</block>-->
<!-- </cu-custom>-->
<view id="fixed">
<view v-if="videoSrc">
2024-05-31 16:07:31 +08:00
<web-view-video
ref="video"
2024-03-25 19:16:41 +08:00
:src="videoSrc"
:poster="videoPoster"
2024-05-31 16:07:31 +08:00
:play-time="serverVideoPlayTime"
2024-03-25 19:16:41 +08:00
@play="fnPlay"
@pause="fnPause"
@ended="fnEnded"
@timeupdate="fnTimeUpdate"
/>
2024-05-31 16:07:31 +08:00
<!-- <video-->
<!-- id="video"-->
<!-- :src="videoSrc"-->
<!-- :poster="videoPoster"-->
<!-- autoplay-->
<!-- controls-->
<!-- style="width: 100vw"-->
<!-- :show-progress="false"-->
<!-- :enable-progress-gesture="false"-->
<!-- @play="fnPlay"-->
<!-- @pause="fnPause"-->
<!-- @ended="fnEnded"-->
<!-- @timeupdate="fnTimeUpdate"-->
<!-- />-->
2024-03-25 19:16:41 +08:00
</view>
<image
v-if="!videoSrc"
:src="baseImgPath+info.COVERPATH"
class="video_bg"
/>
<view class="title">
<text>{{ info.CURRICULUMNAME }}</text>
</view>
<view class="tabs">
<uv-tabs :list="[{name:'视频目录'},{name:'详情'}]" @change="tabsCurrent=$event.index" :scrollable="false"/>
</view>
</view>
<view class="show_container" v-show="tabsCurrent === 1">
<scroll-view scroll-y :style="{ height: `calc(100vh - ${scrollHeight})` }">
<view class="introduce">{{ info.CURRICULUMINTRODUCE }}</view>
<view class="image">
<image :src="baseImgPath+info.COVERPATH" alt=""/>
</view>
</scroll-view>
</view>
<view v-show="tabsCurrent === 0">
<scroll-view scroll-y :style="{ height: `calc(100vh - ${scrollHeight})` }">
<view class="video_container">
<view
class="video_list"
v-for="(item, index) in videoList"
:key="index"
>
<view class="video_list_title">
<image src="/static/study/copy-one.png"/>
<view>{{ item.NAME }}</view>
</view>
<view class="video_list_main">
<view v-if="item.nodes && item.nodes.length > 0">
<view
class="video_list_main_wrap"
v-for="(item1, index1) in item.nodes"
:key="index1"
@click="fnVideoSwitching(item1, 1, index, index1)"
>
<view class="video_list_main_wrap_tit line-1">
{{ item1.COURSEWARENAME }}
</view>
<view class="video_list_main_wrap_info">
<text class="fcb">进度{{ item1.percent }}%</text>
<text>{{ secondsCount(item1.VIDEOTIME) }}</text>
<image src="/static/study/play.png"/>
<button
class="cu-btn round bg-blue sm"
@click.stop="
fnNavigationExercises(item1.VIDEOCOURSEWARE_ID)
"
>
课后练习
</button>
</view>
</view>
</view>
<view v-else>
<view
class="video_list_main_wrap"
@click="fnVideoSwitching(item, 0, index, 0)"
>
<view class="video_list_main_wrap_tit line-1">
{{ item.COURSEWARENAME }}
</view>
<view class="video_list_main_wrap_info">
<text class="fcb">进度{{ item.percent }}%</text>
<text>{{ secondsCount(item.VIDEOTIME) }}</text>
<image src="/static/study/play.png"/>
<button
class="cu-btn round bg-blue sm"
@click.stop="fnNavigationExercises(item.VIDEOCOURSEWARE_ID)"
>
课后练习
</button>
</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import {basePath, loginUser, baseImgPath} from "@/common/tool";
2024-05-31 16:07:31 +08:00
import WebViewVideo from './web_view_video.vue'
2024-03-25 19:16:41 +08:00
let faceAuthTimer; // 人脸认证计时器
let throttleTimer;
let throttleFlag;
export default {
2024-05-31 16:07:31 +08:00
components: {
WebViewVideo,
},
2024-03-25 19:16:41 +08:00
data() {
return {
baseImgPath,
CLASSCURRICULUM_ID: '',
CLASS_ID: '',
STUDENT_ID: '',
ISFACE: '',
info: {}, // 课程信息
videoList: [], // 视频列表
tabsCurrent: 0, // 当前tabs
randomTime: 0, // 随机人脸识别认证时间
// randomTime: new Date().getTime() + 60 * 1000, // 1分钟 随机人脸识别认证时间 测试
videoData: {
CURRICULUM_ID: '',
VIDEOCOURSEWARE_ID: '',
CHAPTER_ID: '',
VIDEOTIME: '',
}, // 当前播放视频的对象
index: 0, // 一级目录index
nodeIndex: 0, // 二级目录index
hasNodes: '0', // 是否存在二级目录1是 0否
changeVideoPlayTime: 0, // 当前视频播放进度
serverVideoPlayTime: 0, // 服务器的当前视频播放进度
videoSrc: "", // 视频地址
videoPoster: "", // 视频封面图
scrollHeight: '0px', // scroll-view减去的高度
2024-05-20 18:03:21 +08:00
submitTimeWaitForCount: 0, // 提交时间等待次数
2024-03-25 19:16:41 +08:00
verification:false,// 是否人脸认证
}
},
onLoad(options) {
this.CLASSCURRICULUM_ID = options.CLASSCURRICULUM_ID;
this.CLASS_ID = options.CLASS_ID;
this.STUDENT_ID = options.STUDENT_ID;
2024-05-28 17:34:39 +08:00
this.ISFACE = options.ISFACE;
2024-05-31 16:07:31 +08:00
// this.ISFACE = '0';
2024-03-25 19:16:41 +08:00
this.fnInit();
},
onReady() {
uni.createSelectorQuery()
.select("#fixed")
.boundingClientRect((data) => {
this.scrollHeight = data.height + 'px';
})
.exec();
},
mounted() {
uni.$on('verification', () => {
this.verification = true
})
},
beforeDestroy() {
uni.$off('verification')
2024-05-31 16:07:31 +08:00
this.fnClearInterval()
2024-03-25 19:16:41 +08:00
},
watch: {
verification() {
if (this.verification) {
this.fnInit();
}
},
},
methods: {
post(url, data) {
return new Promise((resolve, reject) => {
if (data && data.loading !== false) {
uni.showLoading({
title: "加载中",
});
}
uni.request({
url: basePath + url,
data: {
CORPINFO_ID: loginUser.CORPINFO_ID,
USER_ID: loginUser.USER_ID,
...data,
},
method: "POST",
header: {
"Content-type": "application/x-www-form-urlencoded",
},
success: (res) => {
if (res.statusCode !== 200) {
uni.showToast({
title: "网络错误请重试," + res.statusCode,
icon: "error",
duration: 2000,
});
}
if (data && data.loading !== false) {
uni.hideLoading();
}
if (res.data.result === "success") {
resolve(res.data);
} else {
uni.showToast({
title: res.data.msg || "系统开小差了",
icon: "none",
duration: 2000,
});
reject(res.data);
}
},
fail: (err) => {
if (data && data.loading !== false) {
uni.hideLoading();
}
uni.showToast({
title: "网络错误请重试",
icon: "none",
duration: 2000,
});
reject(err);
},
});
});
},
throttle(func, wait = 500, immediate = true) {
if (immediate) {
if (!throttleFlag) {
throttleFlag = true
// 如果是立即执行则在wait毫秒内开始时执行
typeof func === 'function' && func()
throttleTimer = setTimeout(() => {
throttleFlag = false
}, wait)
}
} else if (!throttleFlag) {
throttleFlag = true
// 如果是非立即执行则在wait毫秒内的结束处执行
throttleTimer = setTimeout(() => {
throttleFlag = false
typeof func === 'function' && func()
}, wait)
}
},
secondsCount(second) {
if (!second) return 0;
const h = parseInt((second / 60 / 60) % 24, 10);
const m = parseInt((second / 60) % 60, 10);
const s = parseInt(second % 60, 10);
return (
(h < 10 ? "0" + h : h) +
":" +
(m < 10 ? "0" + m : m) +
":" +
(s < 10 ? "0" + s : s)
);
},
async fnInit() {
await this.fnGetData();
if (this.ISFACE === "1") {
if (this.verification) {
await this.fnGetVideoPlayInfo();
} else {
uni.showModal({
title: "温馨提示",
confirmText: "同意并继续",
content:
"重要提醒:尊敬的用户,根据规定我们会在您学习过程中多次进行人脸识别认证,为了保护您的隐私请您在摄像设备视野内确保衣冠整齐。",
success: (res) => {
if (res.cancel) {
uni.navigateBack();
}
},
});
}
}
},
async fnGetData() {
const resData = await this.post('/app/stagestudentrelation/getMyTask',{
CLASSCURRICULUM_ID: this.CLASSCURRICULUM_ID,
CLASS_ID: this.CLASS_ID,
STUDENT_ID: this.STUDENT_ID,
});
if (resData.pd.VIDEOLIST && resData.pd.VIDEOLIST.length > 0) {
resData.pd.VIDEOLIST.forEach((item) => {
if (item.nodes && item.nodes.length > 0) {
item.nodes.forEach((nodeItem) => {
let percent;
if (nodeItem.PLAYCOUNT > 0) {
percent = 100;
} else {
const resourceTime = parseFloat(nodeItem.RESOURCETIME || 0);
const videoTime = parseFloat(nodeItem.VIDEOTIME || 0);
const temp = Math.floor(
(resourceTime / videoTime) * 10000,
).toString();
percent = temp.substring(0, temp.length) / 100;
}
nodeItem.percent = percent;
});
} else {
let percent;
if (item.PLAYCOUNT > 0) {
percent = 100;
} else {
const resourceTime = parseFloat(item.RESOURCETIME || 0);
const videoTime = parseFloat(item.VIDEOTIME || 0);
const temp = Math.floor(
(resourceTime / videoTime) * 10000,
).toString();
percent = temp.substring(0, temp.length) / 100;
}
item.percent = percent;
}
});
}
this.info = resData.pd;
this.videoList = resData.pd.VIDEOLIST;
},
async fnVideoSwitching(videoData, hasNodes, index, index1) {
2024-05-31 16:07:31 +08:00
this.$refs.video && this.$refs.video.fn('destroy')
this.videoSrc = "";
this.videoPoster = "";
2024-05-20 18:03:21 +08:00
this.submitTimeWaitForCount = 0;
2024-03-25 19:16:41 +08:00
if (this.changeVideoPlayTime !== 0) {
await this.fnSubmitPlayTime("0", this.changeVideoPlayTime);
this.changeVideoPlayTime = 0;
}
this.videoData = videoData;
this.firstIndex = index;
this.nodeIndex = index1;
this.hasNodes = hasNodes.toString();
if (this.ISFACE === "1") {
await this.fnNavigationFaceAuth();
} else {
await this.fnGetVideoPlayInfo();
}
},
async fnGetVideoPlayInfo() {
const resData = await this.post('/app/audioOrVideo/getPlayInfo',{
VIDEOCOURSEWARE_ID: this.videoData.VIDEOCOURSEWARE_ID,
CURRICULUM_ID: this.videoData.CURRICULUM_ID,
});
2024-05-31 16:07:31 +08:00
await this.fnGetVideoPlayProgress(resData.PLAYURL, resData.COVERURL);
2024-03-25 19:16:41 +08:00
},
2024-05-31 16:07:31 +08:00
async fnGetVideoPlayProgress(PLAYURL, COVERURL) {
2024-03-25 19:16:41 +08:00
const resData = await this.post('/app/coursestudyvideorecord/getVideoProgress',{
VIDEOCOURSEWARE_ID: this.videoData.VIDEOCOURSEWARE_ID,
CURRICULUM_ID: this.videoData.CURRICULUM_ID,
CLASS_ID: this.CLASS_ID,
STUDENT_ID: this.STUDENT_ID,
});
if (!resData.pd.RESOURCETIME) {
await this.fnSubmitPlayTime("0", 0);
}
2024-05-20 18:03:21 +08:00
this.serverVideoPlayTime = Math.floor(resData.pd.RESOURCETIME) || 0
2024-05-31 16:07:31 +08:00
this.videoSrc = PLAYURL;
this.videoPoster = COVERURL;
2024-03-25 19:16:41 +08:00
},
2024-05-23 15:04:26 +08:00
fnTimeUpdate(event) {
this.throttle(async () => {
2024-05-31 16:07:31 +08:00
if (event - this.serverVideoPlayTime >= 10) {
2024-03-25 19:16:41 +08:00
uni.navigateBack();
return;
}
2024-05-31 16:07:31 +08:00
this.changeVideoPlayTime = event;
if (event - this.serverVideoPlayTime >= 5) {
this.serverVideoPlayTime = event;
2024-05-20 18:03:21 +08:00
this.submitTimeWaitForCount = ++this.submitTimeWaitForCount;
}
if (this.submitTimeWaitForCount === 6) {
this.submitTimeWaitForCount = 0;
2024-05-31 16:07:31 +08:00
await this.fnSubmitPlayTime("0", event);
2024-03-25 19:16:41 +08:00
}
2024-05-23 15:04:26 +08:00
}, 1000);
2024-03-25 19:16:41 +08:00
},
async fnSubmitPlayTime(IS_END, RESOURCETIME) {
2024-05-20 18:03:21 +08:00
if(!this.videoData.VIDEOCOURSEWARE_ID) {
return
}
2024-03-25 19:16:41 +08:00
const resData = await this.post('/app/coursestudyvideorecord/save',{
USERNAME: loginUser.NAME,
VIDEOCOURSEWARE_ID: this.videoData.VIDEOCOURSEWARE_ID,
CURRICULUM_ID: this.videoData.CURRICULUM_ID,
CHAPTER_ID: this.videoData.CHAPTER_ID,
RESOURCETIME,
IS_END,
CLASS_ID: this.CLASS_ID,
CLASSCURRICULUM_ID: this.CLASSCURRICULUM_ID,
STUDENT_ID: this.STUDENT_ID,
loading: false,
});
this.serverVideoPlayTime = RESOURCETIME;
if (this.hasNodes === "1") {
let percent = 0;
if (resData.pd.PLAYCOUNT > 0) {
percent = 100;
} else {
const resourceTime = parseFloat(resData.pd.RESOURCETIME || 0);
const videoTime = parseFloat(
this.videoList[this.firstIndex].nodes[this.nodeIndex].VIDEOTIME ||
0,
);
const temp = Math.floor(
(resourceTime / videoTime) * 10000,
).toString();
percent = temp.substring(0, temp.length) / 100;
}
this.videoList[this.firstIndex].nodes[this.nodeIndex].percent = percent;
} else {
let percent = 0;
if (resData.pd.PLAYCOUNT > 0) {
percent = 100;
} else {
const resourceTime = parseFloat(resData.pd.RESOURCETIME || 0);
const videoTime = parseFloat(
this.videoList[this.firstIndex].VIDEOTIME || 0,
);
const temp = Math.floor(
(resourceTime / videoTime) * 10000,
).toString();
percent = temp.substring(0, temp.length) / 100;
}
this.videoList[this.firstIndex].percent = percent;
}
if (resData.pd.CANEXAM === "1") {
2024-05-31 16:07:31 +08:00
this.$refs.video && this.$refs.video.fn('pause')
this.$refs.video && this.$refs.video.fn('fullscreen.exit')
2024-03-25 19:16:41 +08:00
uni.showModal({
title: "提示",
content: "当前班级内所有课程均已学完,是否直接参加考试?",
confirmButtonText: "是",
cancelButtonText: "否",
success: (res) => {
if (res.confirm) {
uni.navigateTo({
url: '/pages/application/onlinexxks/course_exam?STAGEEXAMPAPERINPUT_ID=' + resData.paper.STAGEEXAMPAPERINPUT_ID + '&STAGEEXAMPAPER_ID=' + resData.paper.STAGEEXAMPAPERINPUT_ID + '&CLASS_ID=' + this.CLASS_ID + '&POST_ID=' + resData.pd.POST_ID + '&STUDENT_ID=' + this.STUDENT_ID + '&NUMBEROFEXAMS=' + resData.pd.NUMBEROFEXAMS + '&entrySite=video_study'
})
} else if (res.cancel) {
2024-05-31 16:07:31 +08:00
this.$refs.video && this.$refs.video.fn('play')
2024-03-25 19:16:41 +08:00
}
},
});
}
},
fnEnded() {
2024-05-20 18:03:21 +08:00
if (this.changeVideoPlayTime - this.serverVideoPlayTime >= 10) {
uni.navigateBack();
return;
}
2024-03-25 19:16:41 +08:00
this.changeVideoPlayTime = 0;
this.fnSubmitPlayTime("1", 0);
this.fnClearInterval();
},
fnPlay() {
if (this.ISFACE === "1") {
this.fnClearInterval();
if (this.randomTime === 0) {
this.randomTime = new Date().getTime() + 60 * 1000 * 10;
}
faceAuthTimer = setInterval(async () => {
if (new Date().getTime() >= this.randomTime) {
2024-05-31 16:07:31 +08:00
this.fnClearInterval();
this.$refs.video && this.$refs.video.fn('fullscreen.exit')
this.videoSrc = ''
this.videoPoster = ''
2024-05-20 18:03:21 +08:00
this.randomTime = 0;
2024-03-25 19:16:41 +08:00
await this.fnSubmitPlayTime("0", this.changeVideoPlayTime);
await this.fnNavigationFaceAuth();
}
}, 1000);
}
},
fnPause() {
this.fnClearInterval();
},
fnClearInterval() {
faceAuthTimer && clearInterval(faceAuthTimer);
faceAuthTimer = null;
},
async fnNavigationFaceAuth() {
this.verification = false
const params = JSON.stringify({
CLASS_ID: this.CLASS_ID,
STUDENT_ID: this.STUDENT_ID,
CURRICULUM_ID: this.videoData.CURRICULUM_ID,
CHAPTER_ID: this.videoData.CHAPTER_ID,
VIDEOCOURSEWARE_ID: this.videoData.VIDEOCOURSEWARE_ID,
})
2024-05-15 14:16:58 +08:00
const resData = await this.post('/app/user/getUserFace', {
USERNAME: loginUser.NAME,
USER_ID: loginUser.USER_ID,
});
if (resData.pd.PORTRAIT) {
uni.navigateTo({
url: '/pages/application/onlinexxks/face/index?params=' + params
})
} else {
2024-05-15 14:16:58 +08:00
uni.showModal({
title: "温馨提示",
content: "您当前还未进行人脸认证,请先进行认证",
confirmText: "前往人脸认证",
cancelText: "确定",
success: (res) => {
if(res.confirm) {
uni.navigateTo({
url: `/pages/my/set/set`,
});
}
}
2024-05-15 14:16:58 +08:00
});
}
2024-03-25 19:16:41 +08:00
},
fnNavigationExercises(VIDEOCOURSEWARE_ID) {
uni.navigateTo({
url: '/pages/application/onlinexxks/exercises?VIDEOCOURSEWARE_ID=' + VIDEOCOURSEWARE_ID
})
},
},
}
</script>
<style scoped lang="scss">
.video_bg, #video {
width: 100%;
2024-05-31 16:07:31 +08:00
height: 250px;
2024-03-25 19:16:41 +08:00
}
.title {
font-weight: bold;
background: #ffffff;
padding: 20upx;
font-size: 34upx;
}
.tabs {
background: #ffffff;
padding: 0 40upx;
margin-top: 20upx;
}
.video_container {
width: 100%;
background: #ffffff;
padding: 40upx;
margin-top: 20upx;
box-sizing: border-box;
.video_list_title {
display: flex;
align-items: center;
margin-left: -10upx;
image {
width: 48upx;
height: 48upx;
}
}
.video_list_main {
margin-top: 20upx;
.video_list_main_wrap {
width: 100%;
background: #fafafa;
padding: 20upx;
border-radius: 10upx;
font-size: 26upx;
box-sizing: border-box;
color: #666666;
margin-bottom: 20upx;
.video_list_main_wrap_tit {
width: 100%;
}
.video_list_main_wrap_info {
flex: 1;
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10upx;
image {
width: 28upx;
height: 28upx;
}
}
}
}
}
.fcb {
color: #3c9cff;
}
.show_container {
background: #ffffff;
padding: 40upx;
.introduce {
word-break: break-all;
}
.image {
margin-top: 20upx;
image {
width: 100%;
height: 400upx;
}
}
}
</style>