lex 11 months ago
parent
commit
c80cb56853
3 changed files with 9840 additions and 17 deletions
  1. 9415 1
      package-lock.json
  2. 77 0
      src/views/student-register/index.module.less
  3. 348 16
      src/views/student-register/index.tsx

File diff suppressed because it is too large
+ 9415 - 1
package-lock.json


+ 77 - 0
src/views/student-register/index.module.less

@@ -12,6 +12,83 @@
     background: url('./images/new/banner-bg.png') no-repeat top center;
     background-size: contain;
   }
+
+
+  .video-content {
+    width: 100%;
+    height: 300px;
+
+    .coverImg {
+      width: 100%;
+      height: 100%;
+      border-radius: 30px;
+    }
+
+    video {
+      width: 100%;
+    }
+
+    :global {
+      .video-back {
+        position: absolute;
+        left: 20px;
+        top: 20px;
+        color: #fff;
+        z-index: 99;
+        font-size: 24px;
+        width: 30px;
+        height: 30px;
+        background-color: rgba(0, 0, 0, 0.5);
+        border-radius: 50%;
+        padding: 4px 5px 4px 3px;
+      }
+
+
+      .video-js {
+        width: 100%;
+        height: 100%;
+        border-radius: 30px;
+        overflow: hidden;
+      }
+
+
+      .plyr__poster {
+        background-size: cover;
+      }
+
+      .plyr__control--overlaid {
+        border: 1px solid #fff;
+        background-color: rgba(0, 0, 0, 0.2) !important;
+      }
+
+      .plyr--video .plyr__control:hover {
+        background-color: transparent !important;
+      }
+
+      .video-js {
+        width: 100%;
+        height: 100%;
+      }
+
+      .tcp-skin .vjs-play-progress {
+        background-color: var(--van-primary) !important;
+      }
+
+      .vjs-slider:focus {
+        box-shadow: none !important;
+      }
+
+      .video-js .vjs-progress-control:hover .vjs-progress-holder {
+        font-size: 1em !important;
+      }
+
+    }
+
+    .video {
+      position: relative;
+      border-radius: 18px;
+    }
+  }
 }
 
 .countdownSection {

+ 348 - 16
src/views/student-register/index.tsx

@@ -20,8 +20,11 @@ import {
   onMounted,
   onUnmounted,
   reactive,
-  ref
+  ref,
+  watch
 } from 'vue';
+import TCPlayer from 'tcplayer.js';
+import 'tcplayer.js/dist/tcplayer.css';
 import qs from 'query-string';
 import {
   state as baseState,
@@ -53,7 +56,7 @@ import giftTip from './images/new/icon-4.png';
 import iconGift from './images/new/icon-gift.png';
 import dayjs from 'dayjs';
 // import MMessageTip from '@/components/m-message-tip';
-import { CurrentTime, useCountDown } from '@vant/use';
+import { CurrentTime, useCountDown, usePageVisibility } from '@vant/use';
 import Payment from '../adapay/payment';
 import QrcodePayment from './qrcode-payment';
 import MImgCode from '@/components/m-img-code';
@@ -113,6 +116,7 @@ export default defineComponent({
   name: 'student-register',
   setup() {
     const route = useRoute();
+    const pageVisibility = usePageVisibility();
     const studentRegisterStore = useStudentRegisterStore();
     const router = useRouter();
     // 初始化学校编号
@@ -212,17 +216,6 @@ export default defineComponent({
       orderTimer: null as any
     });
 
-    /*
-      新用户:
-      autoRegister: true
-      loginType: 'SMS'
-
-      已存在用户:
-      autoRegister: false
-      loginType: 'TOKEN'
-      password: xxx
-    */
-
     const studentInfo = reactive({
       autoRegister: true,
       multiUser: true, // 是否为多用户
@@ -241,10 +234,111 @@ export default defineComponent({
       password: '',
       username: ''
     });
+
+    const videoForms = reactive({
+      introductionVideo: '',
+      introductionVideoTime: 0, // 视频总时长
+      videoBrowsePoint: 0, // 视频最后观看点
+      player: null as any,
+      playerSpeed: 1,
+      intervalFnRef: null as any,
+      videoDetails: [] as any, // 节点列表
+      pointVideo: {} as any, // 需要处理有效的时间段
+      pointVideoTime: 0 // 有效时长
+    });
+
+    // 播放视频总时长
+    const videoIntervalRef = useInterval(1000, { controls: true });
+    videoIntervalRef.pause();
+
     // 页面定时
     const pageTimer = useInterval(1000, { controls: true });
     pageTimer.pause();
 
+    /**
+     * 格式化视屏播放有效时间 - 合并区间
+     * @param intervals [[], []]
+     * @example [[4, 8],[0, 4],[10, 30]]
+     * @returns [[0, 8], [10, 30]]
+     */
+    const formatEffectiveTime = (intervals: any[]) => {
+      const res: any = [];
+      intervals.sort((a, b) => a[0] - b[0]);
+      let prev = intervals[0];
+      for (let i = 1; i < intervals.length; i++) {
+        const cur = intervals[i];
+        if (prev[1] >= cur[0]) {
+          // 有重合
+          prev[1] = Math.max(cur[1], prev[1]);
+        } else {
+          // 不重合,prev推入res数组
+          res.push(prev);
+          prev = cur; // 更新 prev
+        }
+      }
+      res.push(prev);
+      return res;
+    };
+
+    const formatEffectiveTimeToAfter = (res: any[]) => {
+      // 格式化有效时间
+      const effective: any = [];
+      const startNode = videoForms.pointVideo.startNode;
+      const endNode = videoForms.pointVideo.endNode;
+      // console.log(startNode, endNode, 'startNode')
+      res.forEach((item: any) => {
+        // 开始时间大于 设置时间
+        if (item[1] >= item[0]) {
+          /**
+           * 1、开始时间
+           */
+          if (
+            item[0] >= startNode &&
+            item[0] <= endNode &&
+            item[1] <= endNode
+          ) {
+            // console.log(1)
+            effective.push(item);
+          }
+          if (item[0] >= startNode && item[0] <= endNode && item[1] > endNode) {
+            // console.log(3)
+            effective.push([item[0], endNode]);
+          }
+          if (
+            item[0] < startNode &&
+            item[1] > startNode &&
+            item[1] <= endNode
+          ) {
+            // console.log(4)
+            effective.push([startNode, item[1]]);
+          }
+
+          if (item[0] < startNode && item[1] > startNode && item[1] > endNode) {
+            // console.log(4)
+            effective.push([startNode, endNode]);
+          }
+        }
+      });
+
+      // console.log(effective, 'effective')
+      return effective;
+    };
+
+    /**
+     * 获取数据有效期
+     * @param intervals [[], []]
+     * @returns 0s
+     */
+    const formatTimer = (intervals: any[]) => {
+      const afterIntervals = formatEffectiveTime(intervals);
+      // console.log(afterIntervals, 'afterIntervals')
+      let time = 0;
+      afterIntervals.forEach((t: any) => {
+        time += t[1] - t[0];
+      });
+      return time;
+    };
+
     const overCountDown = useCountDown({
       time: forms.activeOverTime,
       onFinish() {
@@ -1132,15 +1226,35 @@ export default defineComponent({
       schoolId?: string
     ) => {
       try {
+        const videoBrowseData =
+          moreTime.value.length > 0 ? formatEffectiveTime(moreTime.value) : [];
+        const time =
+          videoBrowseData.length > 0 ? formatTimer(videoBrowseData) : 0;
+        console.log(moreTime.value, videoBrowseData, 'video', time);
+        // const videoCountTime = videoIntervalRef?.counter.value
+        // 判断 视屏播放时间大于视屏播放有效时间则说明数据有问题,进行重置数据
+        const rate = Math.floor(
+          (time / Math.floor(videoForms.player.duration())) * 100
+        );
+        console.log(
+          videoIntervalRef?.counter.value,
+          'videoIntervalRef?.counter.value'
+        );
         const { data } = await requestStudent.post(
           '/edu-app/open/studentRegisterPointRecord/update',
           {
             data: {
               id: forms.saveId,
-              useTime: pageBrowseTime, // 固定10秒
+              useTime: pageBrowseTime, // 固定5
               joinType,
               userId,
-              schoolId
+              schoolId,
+              // pageBrowseTime, // 固定10秒
+              videoBrowseData: JSON.stringify(videoBrowseData), // 视屏播放数据
+              videoBrowseDataTime: time || 0, // 有效的视频观看时长
+              videoBrowsePercentage: rate || 0, // 有效的视频观看时长百分比
+              videoBrowseTime: videoIntervalRef?.counter.value, // 视频观看时长
+              videoBrowsePoint: Math.floor(videoForms.player.currentTime() || 0) // 视频最后观看点 - 向下取整
             }
           }
         );
@@ -1196,6 +1310,200 @@ export default defineComponent({
       }
     };
 
+    /**
+     * 视屏累计时长
+     * 1、视屏开始播放时-开始计时
+     * 2、视频暂停时暂停-停止计时
+     * 3、视频加载时-停止计时
+     * 4、视频倍数播放时,时间正常计时
+     * 5、点击视频进度或拖动进度时,时间暂停
+     */
+    const _init = () => {
+      const Button = TCPlayer.getComponent('Button');
+      const BigPlayButton = TCPlayer.getComponent('BigPlayButton');
+      BigPlayButton.prototype.createEl = function () {
+        const el = Button.prototype.createEl.call(this);
+        const _html =
+          '<button><svg width="41px"height="41px"viewBox="0 0 41 41"version="1.1"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"><g stroke="none"stroke-width="1"fill="none"fill-rule="evenodd"><g transform="translate(-167.000000, -155.000000)"><g transform="translate(0.000000, 85.000000)"><g transform="translate(158.000000, 70.000000)"><g transform="translate(9.000000, 0.000000)"><circle id="椭圆形"stroke="#FFFFFF"fill-opacity="0.1"fill="#D8D8D8"cx="20.5"cy="20.5"r="20"></circle><path d="M14.5483871,27.6859997 L14.5483871,13.4342349 C14.5480523,12.8729571 14.8729597,12.356555 15.3949624,12.0887034 C15.9169651,11.8208518 16.5522696,11.8445472 17.0503046,12.1504437 L28.6530473,19.2778563 C29.1119763,19.5602271 29.3887725,20.0426422 29.3887725,20.5601173 C29.3887725,21.0775924 29.1119763,21.5600075 28.6530473,21.8423783 L17.0503046,28.9697909 C16.5522696,29.2756874 15.9169651,29.2993828 15.3949624,29.0315312 C14.8729597,28.7636796 14.5480523,28.2472775 14.5483871,27.6859997 Z"id="路径"fill="#FFFFFF"fill-rule="nonzero"></path></g></g></g></g></g></svg></button>';
+
+        el.appendChild(
+          TCPlayer.dom.createEl('div', {
+            className: 'vjs-button-icon',
+            innerHTML: _html
+          })
+        );
+        return el;
+      };
+      videoForms.player = TCPlayer('register-video', {
+        appID: '',
+        controls: true,
+        plugins: {}
+      }); // player-container-id 为播放器容器 ID,必须与 html 中一致
+      if (videoForms.player) {
+        videoForms.player.src(
+          videoForms.introductionVideo ||
+            'https://oss.dayaedu.com/ktqy/1712800085691.mp4'
+        ); // url 播放地址
+        // videoForms.player.poster(videoForms.coverImg || '');
+
+        videoForms.player.on('ready', (item: any) => {
+          console.log('ready', item);
+
+          // videoForms.player.pause()
+        });
+        videoForms.player.on('loadedmetadata', () => {
+          console.log('loadedmetadata');
+          // videoForms.loading = false;
+          videoForms.player.currentTime(videoForms.videoBrowsePoint);
+        });
+
+        // 速度变化时
+        videoForms.player.on('ratechange', () => {
+          videoForms.playerSpeed =
+            videoForms.playerSpeed < videoForms.player.speed
+              ? videoForms.player.speed
+              : videoForms.playerSpeed;
+        });
+
+        videoForms.player.on('seeking', () => {
+          console.log('seeking');
+          videoIntervalRef.isActive.value && videoIntervalRef.pause();
+        });
+
+        // // 拖动结束时
+        videoForms.player.on('seeked', () => {
+          console.log('seeked');
+          videoIntervalRef.isActive.value && videoIntervalRef.pause();
+        });
+
+        // 正在搜索中
+        videoForms.player.on('waiting', () => {
+          // console.log('waiting pause')
+          videoIntervalRef.isActive.value && videoIntervalRef.pause();
+        });
+
+        // 如何视频在缓存不会触发
+        videoForms.player.on('timeupdate', () => {
+          console.log('timeupdate', videoForms.player.currentTime());
+          // 判断视频计时器是否暂停,如果暂停则恢复
+          // 添加 「videoForms.player.playing」 是由会跳转到上次播放时间,会触发些方法
+          if (
+            !videoIntervalRef.isActive.value &&
+            videoForms.player.currentTime() > 0 &&
+            videoForms.player.playing
+          ) {
+            // console.log('timeupdate play')
+            videoIntervalRef.resume();
+          }
+        });
+
+        // 视屏播放时暂停
+        videoForms.player.on('ended', () => {
+          videoForms.player.pause();
+        });
+
+        // 开始播放
+        videoForms.player.on('play', () => {
+          console.log('play');
+          // 判断视频计时器是否暂停,如果暂停则恢复
+          videoIntervalRef.resume();
+        });
+
+        // 暂停播放
+        videoForms.player.on('pause', () => {
+          console.log('pause', videoIntervalRef.isActive.value);
+
+          videoIntervalRef.pause();
+        });
+
+        videoForms.player.on('fullscreenchange', () => {
+          if (videoForms.player.isFullscreen()) {
+            console.log('fullscreen');
+            const i = document.createElement('i');
+            i.id = 'fullscreen-back';
+            i.className = 'van-icon van-icon-arrow-left video-back';
+            i.addEventListener('click', () => {
+              videoForms.player.exitFullscreen();
+            });
+            document.getElementsByClassName('video-js')[0].appendChild(i);
+          } else {
+            console.log('exitfullscreen');
+            const i = document.getElementById('fullscreen-back');
+            i && i.remove();
+          }
+        });
+      }
+    };
+
+    // 保存零时时间
+    const moreTime: any = ref([]); // 多个观看时间段
+    let tempTime: any = []; // 临时存储时间
+
+    const currentTimer = useInterval(1000, { controls: true });
+    // 监听播放状态,
+    watch(
+      () => videoIntervalRef.isActive.value,
+      (newVal: boolean) => {
+        console.log(videoIntervalRef.isActive.value, 'videoIntervalRef');
+        initVideoCount(newVal);
+      }
+    );
+
+    /**
+     * 初始化视频时长
+     * @param newVal 播放状态
+     * @param repeat 是否为定时发送的
+     */
+    const initVideoCount = (newVal: any, repeat = false) => {
+      // console.log('watch', videoForms.player.currentTime())
+      const initTime = deepClone(tempTime);
+      if (repeat) {
+        if (tempTime.length > 0) {
+          // console.log('join video', tempTime, 'initTime', initTime)
+          tempTime[1] = Math.floor(videoForms.player.currentTime());
+        }
+      } else {
+        if (newVal) {
+          console.log(
+            videoForms.player.currentTime(),
+            'videoForms.player.currentTime()'
+          );
+          tempTime[0] = Math.floor(videoForms.player.currentTime());
+        } else {
+          tempTime[1] = Math.floor(videoForms.player.currentTime());
+        }
+      }
+
+      // console.log(newVal, repeat, tempTime, tempTime.length, 'videoIntervalRef.isActive.value in')
+      // console.log(videoForms.player.speed, 'speed')
+
+      if (tempTime.length >= 2) {
+        // console.log(tempTime, 'tempTime', moreTime.value)
+        // 处理在短时间内的时间差 【视屏拖动,点击可能会导致时间差太大】
+        const diffTime =
+          tempTime[1] -
+            tempTime[0] -
+            currentTimer.counter.value * videoForms.playerSpeed >
+          2;
+        // console.log(diffTime, 'diffTime', currentTimer.counter.value, videoForms.playerSpeed, 'value')
+        // 结束时间,如果 大于开始时间则清除
+        if (tempTime[1] >= tempTime[0] && !diffTime)
+          moreTime.value.push(tempTime);
+        if (repeat) {
+          tempTime = deepClone(initTime);
+        } else {
+          tempTime = [];
+          currentTimer.counter.value = 0;
+        }
+      }
+    };
+
+    watch(pageVisibility, (value: any) => {
+      if (value == 'hidden') {
+        videoForms.player.pause();
+      }
+    });
+
     const pagePointInit = async () => {
       try {
         // 判断是否获取微信code码
@@ -1212,6 +1520,15 @@ export default defineComponent({
         );
         forms.saveId = data.id;
         forms.openId = data.openId;
+
+        moreTime.value = data.videoBrowseData
+          ? JSON.parse(data.videoBrowseData)
+          : [];
+        videoForms.videoBrowsePoint = data.videoBrowsePoint || 0;
+        if (videoForms.player) {
+          videoForms.player.currentTime(data.videoBrowsePoint || 0);
+        }
+
         sessionStorage.setItem('active-open-id', data.openId);
 
         // 间隔多少时间同步数据
@@ -1220,8 +1537,10 @@ export default defineComponent({
           pageTimer.counter.value = 0;
           pageTimer.resume();
           // 同步数据时先进行有效时间进行保存
-
+          initVideoCount(false, true);
           await updateStat();
+
+          videoIntervalRef.counter.value = 0;
         }, 5000);
       } catch {}
     };
@@ -1283,6 +1602,9 @@ export default defineComponent({
           }
         }
 
+        // 初始化视频播放器
+        _init();
+
         await getRegisterGoods();
       } catch {}
     });
@@ -1321,6 +1643,16 @@ export default defineComponent({
             </div>
           )}
 
+          <div class={styles['video-content']}>
+            <video
+              id="register-video"
+              class={styles['video']}
+              src={'https://oss.dayaedu.com/ktqy/1712800085691.mp4'}
+              playsinline={true}
+              // poster={forms.coverImg}
+              preload="auto"></video>
+          </div>
+
           <div
             class={[
               styles.studentSection,

Some files were not shown because too many files changed in this diff