| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- <template>
- <view class="page_body">
- <!-- 头部 -->
- <nav-bar height="96" title="视频详情" :onlyTitle="true" :showHeaderborder="true" @GoBack="GoBack"></nav-bar>
- <view class="page_content">
- <view class="video_content">
- <!-- duration:指定视频时长,单位为秒(s) initial-time:指定视频初始播放位置,单位为秒(s) -->
- <video
- ref="myVideoRef"
- id="myVideoRef"
- :title="state?.videoInfo?.courseName || ''"
- :initial-time="state?.videoInfo?.initialTime"
- :duration="state?.videoInfo?.duration"
- :show-mute-btn="true"
- :poster="state?.videoInfo?.videoCoverUrl"
- :src="state?.videoInfo?.attachmentUrl"
- style="width: 100%; height: 100%; object-fit: contain;"
- @timeupdate="videoTimeupdate"
- >
- </video>
- </view>
- <view class="video_title">{{state?.videoInfo?.courseName}}</view>
- <view class="video_progress">
- <view class="video_progress_box">
- <text class="video_progress_text" v-if="state?.videoInfo?.courseType===0">必修任务:{{state.videoInfo.courseStatus===0?`剩余${state.videoInfo.remainingTime}`:'已结束'}}</text>
- <text class="video_progress_text">观看进度:{{state?.videoInfo?.progress}}</text>
- </view>
- <view class="task_collect">
- <view class="preview">
- <image class="img" src="@/static/image/icon/preview.png"></image>
- <text class="text">{{state?.videoInfo?.watchCount || 0}}</text>
- </view>
- <view class="collect" v-if="state?.videoInfo?.courseType===1" @click="ChangeFollowStatus()">
- <uni-icons v-if="state?.videoInfo?.followStatus==1" class="img filled" type="heart-filled"></uni-icons>
- <uni-icons v-else class="img" type="heart"></uni-icons>
- <text :class="['text',{'filled':state?.videoInfo?.followStatus==1}]">{{state?.videoInfo?.followCount || 0}}</text>
- </view>
- </view>
- </view>
- <view class="video_class_name">
- <text class="class_name_item">{{state?.videoInfo?.teachCourseTypeName}}</text>
- <text class="class_name_item">{{state?.videoInfo?.teachCourseTypeClassName}}</text>
- <text class="class_name_item">{{state?.videoInfo?.createdBy}}</text>
- </view>
- <view class="video_time">
- <text class="time_item">{{state?.videoInfo?.courseType===0 ?'开始':'上传'}}时间:{{state?.videoInfo?.courseStartDatetime}}</text>
- <text class="time_item" v-if="state?.videoInfo?.courseType===0">结束时间:{{state.videoInfo.courseEndDatetime}}</text>
- </view>
- <view class="video_desc">
- 课程简介:{{state?.videoInfo?.courseContent}}
- </view>
- </view>
- <!-- 提示框 -->
- <custom-popup-dialog ref="popupDialogRef" title="试题弹出" dialogWidth="702" :isMaskClick="false" :showDialogClose="false" :showCloseBtn="false" @DialogConfirm="DialogConfirm">
- <view class="dialog_questions_input">
- <view class="questions_title">{{state?.question?.codeName}}:{{state?.question?.questionsName}}</view>
- <uni-easyinput type="textarea" v-model="state.questionsAnswer" :style="state.easyinputStyle" placeholder="请输入你的回答" placeholderStyle="font-weight: 400;font-size: 28rpx;color: #999999;"></uni-easyinput>
- </view>
- </custom-popup-dialog>
- </view>
- </template>
- <script setup>
- import uniEasyinput from '@/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue';
- import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
- import navBar from '@/components/nav-bar.vue';
- import customPopupDialog from '@/components/custom-popup-dialog.vue';
- import { teacherSelectCourseDetail,addQuestionAnswerData,updateFollowCountTeacher,synTeachLearnData } from '@/reqApi/studentStudy.js';
- import {
- onLoad,
- onUnload
- } from '@dcloudio/uni-app';
- import {
- reactive,
- ref,
- getCurrentInstance
- } from 'vue';
- const state = reactive({
- id: '', //视频id
- videoInfo:{},
- currentTime:'',//播放进度时分秒
- question:null,//试题弹出信息
- easyinputStyle:{
- color: '#303133',
- borderColor: '#E9E9E9'
- },
- questionsAnswer:'',
- lastValidTime:'',//上一次的播放进度
- });
- //提示框
- const popupDialogRef = ref(null);
- const myVideoRef = ref(null);
- const videoCtx = ref(null);//播放器
- onLoad((option) => {
- state.id = option.id;
- const instance = getCurrentInstance();
- videoCtx.value = uni.createVideoContext('myVideoRef', instance.proxy);
- GetCourseDetail();
- })
- //视频详情
- const GetCourseDetail = () => {
- teacherSelectCourseDetail(state.id).then(res => {
- if(res.code == 200){
- const resData = res.data;
- resData.initialTime = timeToSecond(resData.courseLearnTime ?? '');//视频初始播放位置
- state.lastValidTime = timeToSecond(resData.courseLearnTime ?? '');
- resData.duration = timeToSecond(resData.videoTime ?? '');//视频时长,单位为秒(s)
- if(resData?.questionsList?.length > 0){
- resData.questionsList.forEach(item=>{
- const showTimeDate = timeToSecond(item.showTime);
- item.isWriteAnswer = showTimeDate < resData.initialTime;//是否已作答 true 已作答
- })
- }
- state.videoInfo = resData;
- }
- })
- }
- //计算秒数
- const timeToSecond = (timeStr) => {
- if(timeStr == '') return 0
- const timeArr = timeStr.split(':');
- const [h, m, s] = timeArr.map(item => Number(item));
- return h * 3600 + m * 60 + s;
- }
- //播放进度变化时触发,event.detail = {currentTime, duration} 。触发频率 250ms 一次
- const videoTimeupdate = (event) => {
- const currentTime = event.detail.currentTime;
- if(currentTime > 0){
- // 1. 判断:如果当前时间小于记录值,说明在回退
- const diff = (Number(currentTime) - Number(state.lastValidTime)).toFixed(2);
- const dragStatus = state.videoInfo.dragStatus || 0;//0 不可拖拽、默认不可拖拽
- const learnStatus = state.videoInfo.learnStatus ?? 3;//学习状态
- const progress = state.videoInfo.progress;//进度
- if (diff > 0.35 && dragStatus == 0 && (learnStatus!=4 || (learnStatus!=1 && progress!='100%'))) {
- videoCtx.value.seek(state.lastValidTime);
- }
- state.currentTime = formatSecondsToHms(currentTime);
- const questionsList = state?.videoInfo?.questionsList || [];
- const question = questionsList.find(item=>item.showTime == state.currentTime);//视频显示的时间
- if(question && !question.isWriteAnswer){//未作答
- videoCtx?.value?.pause();//暂停播放
- state.questionsAnswer = '';
- state.question = question;//试题弹框信息
- popupDialogRef.value.OpenDialog();
- }
- state.lastValidTime = currentTime;//上一次播放进度
- // 教师学习进度更新接口
- if(learnStatus!=4 || (learnStatus!=1 && progress!='100%')){
- state.videoInfo.progress = Math.min(currentTime / Number(state.videoInfo.duration) * 100, 100).toFixed(2) + '%';
- setSynTeachLearnData();
- }
- }
-
- }
- //提交试题
- const DialogConfirm = () => {
- if(state.questionsAnswer == ''){
- uni.showToast({
- title: '请输入答案!',
- icon: 'none'
- })
- return false
- }
- addQuestionAnswerData({
- teachCourseId: state.id,
- teachQuestionsId: state?.question?.id || '',
- teachQuestionsAnswer: state.questionsAnswer
- }).then(res=>{
- if(res.code == 200){
- state.question.isWriteAnswer = true;//状态修改成已作答
- popupDialogRef.value.CloseDialog();//关闭弹框
- videoCtx?.value?.play();//播放
- }else{
- uni.showToast({
- title: res.msg || '请求异常',
- icon: 'none'
- })
- }
- })
- }
- //教师学习进度更新接口
- const setSynTeachLearnData = () => {
- const videoTime = state.videoInfo.videoTime ?? '';//视频时长
- // 学习状态: 1-已结束(原已过期状态-课程结束了,但是用户没学完),2-待开始,3-未完成,4-已完成
- const learnStatus = state.videoInfo.learnStatus ?? 3;
- synTeachLearnData({
- teachCourseId:state.id,
- courseLearnTime:state.currentTime,
- learnStatus:state.currentTime==videoTime && learnStatus!=1 && learnStatus!=4?4:learnStatus
- }).then(res=>{
- if(res.code==200 && state.currentTime==videoTime && learnStatus!=1 && learnStatus!=4){
- state.videoInfo.learnStatus = 4;//学习状态
- state.videoInfo.progress = '100%';//进度
- }
- })
- }
- // 数字补零工具
- const addZero = (num) => {
- return num < 10 ? '0' + num : String(num);
- }
- //转换时分秒
- const formatSecondsToHms = (seconds) => {
- let total = Math.floor(seconds);
- const h = Math.floor(total / 3600);
- const m = Math.floor((total % 3600) / 60);
- const s = total % 60;
-
- const hh = addZero(h);
- const mm = addZero(m);
- const ss = addZero(s);
- return `${hh}:${mm}:${ss}`;
- }
- //收藏
- const ChangeFollowStatus = () => {
- updateFollowCountTeacher(state.id).then(res=>{
- if(res.code == 200){
- state.videoInfo.followStatus = state.videoInfo.followStatus===1?0:1;
- state.videoInfo.followCount = res.data;
- }
- })
- }
- //页面销毁时触发
- onUnload(()=>{
- videoCtx?.value?.pause();//暂停播放
- })
- //返回
- const GoBack = () => {
- uni.navigateBack({
- delta: 1
- });
- }
- </script>
- <style scoped lang="scss">
- .video_content {
- width: 100%;
- border-radius: 8rpx;
- margin-top: 24rpx;
- height: 398rpx;
- overflow: hidden;
- }
- .video_title{
- width: 100%;
- margin-top: 32rpx;
- font-weight: 500;
- font-size: 36rpx;
- color: #333333;
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .video_progress{
- display: flex;
- justify-content: space-between;
- width: 100%;
- margin-top: 16rpx;
- align-items: flex-start;
- .video_progress_box{
- display: inline-flex;
- flex-wrap: wrap;
- gap: 20rpx;
- .video_progress_text{
- font-weight: 400;
- font-size: 24rpx;
- color: #2E64FA;
- }
- }
- .task_collect {
- display: inline-flex;
- gap: 20rpx;
-
- .preview,
- .collect {
- display: inline-flex;
- align-items: center;
- }
-
- .img {
- width: 24rpx;
- height: 24rpx;
- margin-right: 12rpx;
- font-size: 24rpx !important;
- color: #999999 !important;
- &.filled{
- color: #2e64fa !important;
- }
- }
-
- .text {
- font-weight: 400;
- font-size: 24rpx;
- color: #999999;
- &.filled{
- color: #2e64fa;
- }
- }
- }
-
- }
- .video_class_name{
- display: flex;
- width: 100%;
- margin-top: 16rpx;
- gap: 16rpx 24rpx;
- flex-wrap: wrap;
- .class_name_item{
- font-weight: 400;
- font-size: 24rpx;
- color: #666666;
- }
- }
- .video_time{
- display: flex;
- width: 100%;
- margin-top: 16rpx;
- gap: 16rpx;
- flex-wrap: wrap;
- .time_item{
- font-weight: 400;
- font-size: 24rpx;
- color: #666666;
- }
- }
- .video_desc{
- display: flex;
- width: 100%;
- margin-top: 32rpx;
- flex-wrap: wrap;
- word-break: break-all;
- font-weight: 400;
- font-size: 24rpx;
- color: #999999;
- line-height: 44rpx;
- }
- .dialog_questions_input{
- display: flex;
- width: 100%;
- flex-direction: column;
- .questions_title{
- display: flex;
- width: 100%;
- font-weight: 400;
- font-size: 32rpx;
- color: #303133;
- text-align: left;
- word-break: break-all;
- }
- :deep(.uni-easyinput){
- margin-top: 20rpx;
- font-size: 28rpx;
- .is-input-border{
- border-radius: 8rpx;
- border-color: #E9E9E9 !important;
- }
- .uni-easyinput__content-textarea{
- height: 440rpx;
- min-height: 440rpx;
- font-size: 28rpx;
- margin:20rpx 24rpx 20rpx 14rpx;
- }
- .input-padding{
- padding-left: 10rpx;
- }
- }
- }
- </style>
|