|
|
@@ -1,463 +0,0 @@
|
|
|
-<template>
|
|
|
- <view class="kecheng-study-page">
|
|
|
-
|
|
|
- <customNavbarVue title="课程" :show-back-btn="true" @back="goUpPage"></customNavbarVue>
|
|
|
- <!-- 播放器 -->
|
|
|
- <!-- <videoPlayVue v-if="showVideo" ref="videoRef" class="phone-video-box" @play-end="onPlayEnd" @play-play="onPlay"
|
|
|
- @play-pause="onPause" @play-timeupdate="onTimeupdate"></videoPlayVue> -->
|
|
|
- <view class="video-wrapper">
|
|
|
- <video v-if="showVideo" :src="videoUrl" :poster="videoPoster" controls class="phone-video-box"
|
|
|
- @play="onVideoPlay" @pause="onVideoPause" @ended="onVideoEnd" @error="onVideoError" id="myVideo"
|
|
|
- object-fit="contain" custom-cache @fullscreenchange="onVideoFullscreenChange"></video>
|
|
|
- <view v-else class="phone-video-box master-image" :style="{ backgroundImage: 'url(' + imgsArr.videoBj + ')' }">
|
|
|
- <!-- <view class="video-player-icon" @click="handlePlayFirst"></view> -->
|
|
|
- </view>
|
|
|
- <zmm-watermark :watermark="watermark" class="watermark-component" ></zmm-watermark>
|
|
|
- <view class="static-watermark">保密内容 · 请勿外传</view>
|
|
|
- <!-- 视频背景图 -->
|
|
|
-
|
|
|
- </view>
|
|
|
- <!-- 播放引导 -->
|
|
|
- <view class="yindao-shadow-image" v-if="!shaDowImgFlag"
|
|
|
- :style="{ backgroundImage: 'url(' + imgsArr.pointIcon + ')' }"> </view>
|
|
|
- <!-- 中间区域 -->
|
|
|
- <view class="kc-name-box">
|
|
|
- <view>{{name}}</view>
|
|
|
- </view>
|
|
|
- <view class="kc-info-box">
|
|
|
- <view>时长:{{period}}</view>
|
|
|
- <view>{{userCount}}人学习</view>
|
|
|
- </view>
|
|
|
- <!-- 大纲 -->
|
|
|
- <view class="phone-tab-box">
|
|
|
- <uni-segmented-control :current="current" :values="items" style-type="text" :active-color="activeColor"
|
|
|
- @clickItem="onClickItem" />
|
|
|
- </view>
|
|
|
- <view class="kecheng-content-box">
|
|
|
- <!-- 目录 -->
|
|
|
- <kechengMuluVue v-if="current === 0" :chapterArr="list" @play="handlePlay" :isHasProgress="true"
|
|
|
- :activeKjId="curPlayData&&curPlayData.kjId"></kechengMuluVue>
|
|
|
- <!-- 介绍 -->
|
|
|
- <rich-text :nodes="intro || '暂无内容'" v-if="current === 1 && intro" class="kecheng-jieshao-box"></rich-text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
- import cacheManager from '@/utils/cacheManager.js';
|
|
|
- import * as kechengApi from "@/api/kecheng.js";
|
|
|
- import customNavbarVue from "@/components/custom-navbar/custom-navbar.vue";
|
|
|
- import videoPlayVue from "@/components/videoPlay/videoPlay.vue";
|
|
|
- import kechengMuluVue from "@/components/kecheng-mulu/kecheng-mulu.vue";
|
|
|
- import {
|
|
|
- useUserCache
|
|
|
- } from "@/utils/userCache.js"
|
|
|
- import {
|
|
|
- formatSecondsToCnhms
|
|
|
- } from "@/utils/common.js"
|
|
|
- import {
|
|
|
- useKechengTools
|
|
|
- } from "./useKechengCache.js"
|
|
|
-
|
|
|
- const {
|
|
|
- getCurKjIndex,
|
|
|
- saveKechengData,
|
|
|
- getKechengDataFromHistory,
|
|
|
- saveKechengSectionPage,
|
|
|
- getKechengSectionPageFromHistory,
|
|
|
- mergeProgress,
|
|
|
- initCourseProgressAll,
|
|
|
- saveCourseProgress,
|
|
|
- updateSectionProgress,
|
|
|
- } = useKechengTools();
|
|
|
-
|
|
|
- export default {
|
|
|
- components: {
|
|
|
- videoPlayVue,
|
|
|
- kechengMuluVue,
|
|
|
- customNavbarVue
|
|
|
- },
|
|
|
- data() {
|
|
|
- return {
|
|
|
- showVideo: false,
|
|
|
- shaDowImgFlag: false,
|
|
|
- items: ['目录', '介绍'],
|
|
|
- colors: ['#007aff', '#4cd964', '#dd524d'],
|
|
|
- activeColor: '#3fd2a1',
|
|
|
- current: 0, // 激活的选项卡
|
|
|
- operId: '', // 课程
|
|
|
- name: '',
|
|
|
- period: 0, // 时长
|
|
|
- userCount: 0, // 学习人数
|
|
|
- list: [],
|
|
|
- intro: '',
|
|
|
- curPlayData: null,
|
|
|
- timer1: null,
|
|
|
- kcId: null,
|
|
|
- from: '',
|
|
|
- jzId: null,
|
|
|
- watermark: '<h5>我是h5标签我是h5标签我是h5标签我是h5标签</h5><p style="color:#f00">我是p标签</p><br>/>',
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- videoUrl: '', // 视频地址
|
|
|
- videoPoster: '', // 视频封面
|
|
|
- videoInfo: {}, // 视频信息
|
|
|
- loading: false, // 加载状态
|
|
|
- error: false, // 错误状态
|
|
|
- errorMessage: '', // 错误信息
|
|
|
- videoContext: null, // 视频上下文
|
|
|
- imgsArr: {}
|
|
|
-
|
|
|
- }
|
|
|
- },
|
|
|
- onLoad(options) {
|
|
|
- this.kcId = options.kcId;
|
|
|
- this.from = options.from;
|
|
|
- this.jzId = options.jzId;
|
|
|
- this.imgsArr.videoBj = cacheManager.get('projectImg').video_bj;
|
|
|
- this.imgsArr.pointIcon = cacheManager.get('projectImg').course_point_icon;
|
|
|
- this.init();
|
|
|
- },
|
|
|
- methods: {
|
|
|
- onVideoFullscreenChange(e) {
|
|
|
- console.log('全屏状态变化:', e.detail.fullScreen);
|
|
|
- // 改用uniapp专属选择器,获取视频容器(避免document兼容问题)
|
|
|
- const query = uni.createSelectorQuery().in(this);
|
|
|
- query.select('.video-wrapper')
|
|
|
- .fields({ node: true, dataset: true })
|
|
|
- .exec((res) => {
|
|
|
- if (res[0] && res[0].node) {
|
|
|
- const videoWrapper = res[0].node;
|
|
|
- if (e.detail.fullScreen) {
|
|
|
- // 全屏:添加wx-video-fullscreen类(匹配样式)
|
|
|
- videoWrapper.classList.add('wx-video-fullscreen');
|
|
|
- } else {
|
|
|
- // 退出全屏:移除类,恢复默认样式
|
|
|
- videoWrapper.classList.remove('wx-video-fullscreen');
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
- // 播放视频
|
|
|
- playVideo() {
|
|
|
- if (this.videoUrl) {
|
|
|
- this.videoContext.play()
|
|
|
- } else {
|
|
|
- uni.showToast({
|
|
|
- title: '暂无视频可播放',
|
|
|
- icon: 'none'
|
|
|
- })
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 暂停视频
|
|
|
- pauseVideo() {
|
|
|
- this.videoContext.pause()
|
|
|
- },
|
|
|
-
|
|
|
- // 重新播放
|
|
|
- replayVideo() {
|
|
|
- this.videoContext.seek(0)
|
|
|
- this.videoContext.play()
|
|
|
- },
|
|
|
-
|
|
|
- // 视频开始播放
|
|
|
- onVideoPlay() {
|
|
|
- console.log('视频开始播放')
|
|
|
- },
|
|
|
-
|
|
|
- // 视频暂停
|
|
|
- onVideoPause() {
|
|
|
- console.log('视频暂停')
|
|
|
- },
|
|
|
-
|
|
|
- // 视频播放结束
|
|
|
- onVideoEnd() {
|
|
|
- console.log('视频播放结束')
|
|
|
- uni.showToast({
|
|
|
- title: '播放完成',
|
|
|
- icon: 'success'
|
|
|
- })
|
|
|
- },
|
|
|
-
|
|
|
- // 视频播放错误
|
|
|
- onVideoError(e) {
|
|
|
- console.error('视频播放错误:', e.detail.errMsg)
|
|
|
- this.error = true
|
|
|
- this.errorMessage = '视频播放出错,请检查视频地址或格式'
|
|
|
- },
|
|
|
-
|
|
|
- // 重新加载视频
|
|
|
- retryLoadVideo() {
|
|
|
- this.getVideoData()
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- handlePlayFirst() {
|
|
|
-
|
|
|
- console.log('this.list', this.list);
|
|
|
- if (this.list && this.list[0].jieList && this.list[0].jieList[0].kejianList) {
|
|
|
- // 设置默认展开项
|
|
|
- this.list[0].open = true;
|
|
|
- this.list[0].jieList[0].open = true;
|
|
|
- // 设置播放可见
|
|
|
- const kejian = this.list[0].jieList[0].kejianList[0];
|
|
|
- this.handlePlay(kejian)
|
|
|
-
|
|
|
- }
|
|
|
- },
|
|
|
- onPause() {
|
|
|
- clearInterval(this.timer1);
|
|
|
- this.timer1 = null;
|
|
|
- },
|
|
|
- onPlay() {
|
|
|
- clearInterval(this.timer1);
|
|
|
- this.timer1 = null;
|
|
|
- // this.timer = setInterval(() => {
|
|
|
- // updateSectionProgress(this.operId);
|
|
|
- // }, 1000 * 1 * 60) // 自动保存进度 1分钟已保存
|
|
|
-
|
|
|
- },
|
|
|
- sectionPlayerProgress(progress) {
|
|
|
- let sectionPage = getKechengSectionPageFromHistory(this.operId);
|
|
|
- let maxProcess = this.list[sectionPage.iChapter].jieList[sectionPage.iSection].kejianList.find(it1 => it1
|
|
|
- .kjId == sectionPage.kjId).maxProcess;
|
|
|
- // 更新缓存进度
|
|
|
- this.list[sectionPage.iChapter].jieList[sectionPage.iSection].kejianList.find(it1 => it1.kjId ==
|
|
|
- sectionPage.kjId).curProcess = progress;
|
|
|
- if (progress < 100) {
|
|
|
- // 播放进度小于100
|
|
|
- if (progress < maxProcess) {
|
|
|
- this.list[sectionPage.iChapter].jieList[sectionPage.iSection].kejianList.find(it1 => it1.kjId ==
|
|
|
- sectionPage.kjId).maxProcess = maxProcess
|
|
|
- } else {
|
|
|
- this.list[sectionPage.iChapter].jieList[sectionPage.iSection].kejianList.find(it1 => it1.kjId ==
|
|
|
- sectionPage.kjId).maxProcess = progress
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 播放进度大于100
|
|
|
- this.list[sectionPage.iChapter].jieList[sectionPage.iSection].kejianList.find(it1 => it1.kjId ==
|
|
|
- sectionPage.kjId).maxProcess = 100
|
|
|
- }
|
|
|
- },
|
|
|
- onTimeupdate(time) {
|
|
|
- const progress = parseInt(time / this.curPlayData.duration * 100);
|
|
|
- this.curPlayData.curProgress = parseInt(progress >= 100 ? '99' : progress);
|
|
|
- // 保存进度
|
|
|
- // saveCourseProgress(time, this.curPlayData.duration, this.operId)
|
|
|
- // 更新进度
|
|
|
- // this.sectionPlayerProgress(progress)
|
|
|
- },
|
|
|
- onPlayEnd() {
|
|
|
- clearInterval(this.timer1);
|
|
|
- this.timer1 = null;
|
|
|
- // saveCourseProgress(this.curPlayData.duration, this.curPlayData.duration, this.operId, 'end');
|
|
|
- // updateSectionProgress(this.operId, 'end', 'video', () => {
|
|
|
- // this.curPlayData.maxProcess = 99;
|
|
|
- // this.curPlayData.curProcess = 99;
|
|
|
- // });
|
|
|
- console.log('end')
|
|
|
- },
|
|
|
- goUpPage() {
|
|
|
- const pages = getCurrentPages();
|
|
|
- if (pages.length > 1) {
|
|
|
- uni.navigateBack()
|
|
|
- } else {
|
|
|
- /* if (this.from == 'kechengList') {
|
|
|
- uni.redirectTo({
|
|
|
- url: '/pages/client/Kecheng/list'
|
|
|
- })
|
|
|
- } else if (this.from == 'shouye') {
|
|
|
- uni.redirectTo({
|
|
|
- url: '/pages/client/ShouYe/shouye'
|
|
|
- })
|
|
|
- } else {
|
|
|
- uni.redirectTo({
|
|
|
- url: '/pages/client/ShouYe/shouye'
|
|
|
- })
|
|
|
- } */
|
|
|
- history.back();
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
- onClickItem(e) {
|
|
|
- if (this.current !== e.currentIndex) {
|
|
|
- this.current = e.currentIndex
|
|
|
- }
|
|
|
- // if (this.current == 0) {
|
|
|
- // if (!this.showVideo) {
|
|
|
- // this.shaDowImgFlag = false
|
|
|
- // } else {
|
|
|
- // this.shaDowImgFlag = true
|
|
|
- // }
|
|
|
- // } else {
|
|
|
- // this.shaDowImgFlag = true
|
|
|
- // }
|
|
|
- this.shaDowImgFlag = this.current !== 0 || this.showVideo
|
|
|
- },
|
|
|
- formatData(data) {
|
|
|
- data.forEach(zhang => {
|
|
|
- zhang.open = false;
|
|
|
- zhang.jieList.forEach(jie => {
|
|
|
- jie.open = false;
|
|
|
- })
|
|
|
- })
|
|
|
- return data;
|
|
|
- },
|
|
|
- handlePlay(data) {
|
|
|
- this.showVideo = true;
|
|
|
- this.shaDowImgFlag = true;
|
|
|
- if (this.curPlayData && this.curPlayData.url == data.url) {
|
|
|
- return;
|
|
|
- }
|
|
|
- console.log('data', data);
|
|
|
- this.curPlayData = data;
|
|
|
-
|
|
|
- //this.videoUrl = 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4'
|
|
|
-
|
|
|
- kechengApi.getVideoId({
|
|
|
- videoId: data.url
|
|
|
- }).then(res => {
|
|
|
- this.videoUrl = res.data
|
|
|
- console.log('res', res);
|
|
|
- // const progress = data.curProcess || 0;
|
|
|
- // this.$refs.videoRef.init({
|
|
|
- // videoId: data.url,
|
|
|
- // playAuth: res.data,
|
|
|
- // seekTime: data.duration * progress / 100,
|
|
|
- // isPlay: false
|
|
|
- // })
|
|
|
-
|
|
|
- })
|
|
|
- },
|
|
|
- initFirstVideo() {
|
|
|
-
|
|
|
- if (this.list && this.list[0].jieList && this.list[0].jieList[0].kejianList) {
|
|
|
- // 设置默认展开项
|
|
|
- this.list[0].open = true;
|
|
|
- this.list[0].jieList[0].open = true;
|
|
|
- // 设置播放可见
|
|
|
- const kejian = this.list[0].jieList[0].kejianList[0];
|
|
|
- // this.handlePlay(kejian)
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- //saveKechengSectionPage(this.operId, sectionPage)
|
|
|
- },
|
|
|
-
|
|
|
- init() {
|
|
|
- kechengApi.getClientKechengStart({
|
|
|
- kcId: this.kcId,
|
|
|
- jzId: this.jzId
|
|
|
- }).then(res => {
|
|
|
- const {
|
|
|
- userCount,
|
|
|
- period,
|
|
|
- name,
|
|
|
- kejianUserVo,
|
|
|
- intro,
|
|
|
- operId,
|
|
|
- } = res.data;
|
|
|
- this.userCount = userCount;
|
|
|
- this.period = formatSecondsToCnhms(period, true);
|
|
|
- this.name = name;
|
|
|
- this.formatData(kejianUserVo.zhangList)
|
|
|
- this.list = kejianUserVo.zhangList;
|
|
|
- this.intro = intro;
|
|
|
- this.operId = operId;
|
|
|
-
|
|
|
- // 获取课程缓存 && 课件缓存(课件缓存点击后产生)
|
|
|
- //let historyArrKecheng = getKechengDataFromHistory(this.operId)
|
|
|
- // let sectionPageHistory = getKechengSectionPageFromHistory(this.operId)
|
|
|
- // // 判断是否有前台缓存
|
|
|
- // if (historyArrKecheng && sectionPageHistory) {
|
|
|
- // // 有缓存---- 把start接口中,返回数据进度100%,更新到前台缓存
|
|
|
- // const arrKecheng = mergeProgress(kejianUserVo && kejianUserVo.zhangList,
|
|
|
- // historyArrKecheng);
|
|
|
- // // 后台数据 同步前台缓存
|
|
|
- // saveKechengData(this.operId, arrKecheng)
|
|
|
- // } else {
|
|
|
- // // 无缓存----把start接口中,返回的所有数据,更新到前台缓存
|
|
|
- // saveKechengData(this.operId, kejianUserVo && kejianUserVo.zhangList)
|
|
|
- // }
|
|
|
- // 初始化页面 常规数据
|
|
|
- //initCourseProgressAll(this.operId)
|
|
|
-
|
|
|
- console.log('初始化播放首1123次')
|
|
|
- // 设置播放视频
|
|
|
- this.initFirstVideo();
|
|
|
- }).catch(err => {
|
|
|
- this.goUpPage();
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-</script>
|
|
|
-
|
|
|
-
|
|
|
-<style lang="scss" scoped>
|
|
|
-.video-wrapper {
|
|
|
- position: relative;
|
|
|
- width: 100%;
|
|
|
- height: 400rpx;
|
|
|
- background-color: #000;
|
|
|
- overflow: hidden; /* 防止水印溢出 */
|
|
|
-
|
|
|
- .phone-video-box {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- z-index: 1; /* 明确视频层级,低于水印 */
|
|
|
- }
|
|
|
-
|
|
|
- // 1. 确保水印组件样式完全生效(铺满、置顶、不遮挡操作)
|
|
|
- .watermark-component {
|
|
|
- position: absolute !important; /* 强制绝对定位,避免组件自身样式覆盖 */
|
|
|
- top: 0 !important;
|
|
|
- left: 0 !important;
|
|
|
- width: 100% !important;
|
|
|
- height: 100% !important;
|
|
|
- z-index: 999 !important; /* 高于视频z-index:1 */
|
|
|
- pointer-events: none !important;
|
|
|
- background: transparent !important; /* 避免组件背景遮挡视频 */
|
|
|
- }
|
|
|
-
|
|
|
- // 2. 优化静态水印:提高对比度,确保可见
|
|
|
- .static-watermark {
|
|
|
- position: absolute;
|
|
|
- top: 50%;
|
|
|
- left: 50%;
|
|
|
- transform: translate(-50%, -50%) rotate(-30deg);
|
|
|
- font-size: 32rpx;
|
|
|
- color: rgba(255, 255, 255, 0.3) !important; /* 提高不透明度,增强可见性 */
|
|
|
- font-weight: bold;
|
|
|
- white-space: nowrap;
|
|
|
- pointer-events: none;
|
|
|
- z-index: 999 !important;
|
|
|
- transition: all 0.3s ease;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 全屏样式优化:确保水印铺满屏幕
|
|
|
-.video-wrapper.wx-video-fullscreen {
|
|
|
- position: fixed !important;
|
|
|
- top: 0 !important;
|
|
|
- left: 0 !important;
|
|
|
- width: 100vw !important;
|
|
|
- height: 100vh !important;
|
|
|
- z-index: 9999 !important;
|
|
|
- background-color: #000 !important;
|
|
|
-
|
|
|
- .static-watermark {
|
|
|
- font-size: 80rpx !important;
|
|
|
- color: rgba(255, 255, 255, 0.4) !important; /* 全屏时进一步提高可见性 */
|
|
|
- }
|
|
|
-
|
|
|
- .watermark-component {
|
|
|
- width: 100vw !important;
|
|
|
- height: 100vh !important;
|
|
|
- }
|
|
|
-}
|
|
|
-</style>
|