lex 1 year ago
parent
commit
14108b4b76

+ 14 - 52
src/views/coursewarePlay/component/video-item/index.tsx

@@ -1,8 +1,7 @@
-import { defineComponent, nextTick, onMounted, reactive, toRefs, watch } from 'vue'
+import { defineComponent, nextTick, onMounted, reactive, toRefs, watch, ref } from 'vue'
 import 'plyr/dist/plyr.css'
 import Plyr from 'plyr'
 import styles from './index.module.less'
-
 import { iconVideoBg, iconLoop, iconLoopActive, iconPlay, iconPause } from '../../image/icons.json'
 
 export default defineComponent({
@@ -19,7 +18,7 @@ export default defineComponent({
       default: true
     }
   },
-  emits: ['play', 'pause', 'ended', 'close'],
+  emits: ['play', 'pause', 'ended', 'close', 'seeked', 'seeking', 'waiting', 'timeupdate'],
   setup(props, { emit, expose }) {
     const { item } = toRefs(props)
     const data = reactive({
@@ -32,47 +31,6 @@ export default defineComponent({
     const playBtnId = 'play' + Date.now() + Math.floor(Math.random() * 100)
     const loopBtnId = 'loop' + Date.now() + Math.floor(Math.random() * 100)
 
-    /**
-     * 格式化视屏播放有效时间 - 合并区间
-     * @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)
-      // console.log(res, 'formatEffectiveTime')
-
-      return res
-    }
-    /**
-     * 获取数据有效期
-     * @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 togglePlay = (e: Event) => {
       e.stopPropagation()
       if (!data.videoContianerRef.paused) {
@@ -193,7 +151,7 @@ export default defineComponent({
       clearTimeout(videoTimer)
       nextTick(() => {
         data.videoContianerRef.play().catch((err) => {
-          console.log('🚀 ~ err:', err)
+          // console.log('🚀 ~ err:', err)
           videoTimer = setTimeout(() => {
             if (err?.message?.includes('play()')) {
               emit('play')
@@ -227,8 +185,12 @@ export default defineComponent({
       return data.videoContianerRef
     }
 
+    const getPlyrRef = () => {
+      return data.videoItem
+    }
     expose({
-      getVideoRef
+      getVideoRef,
+      getPlyrRef
     })
 
     return () => (
@@ -271,16 +233,16 @@ export default defineComponent({
             emit('ended')
           }}
           onSeeked={() => {
-            changePlayBtn('play')
-            data.videoItem?.pause()
+            emit('seeked')
           }}
           onSeeking={() => {
-            changePlayBtn('play')
-            data.videoItem?.pause()
+            emit('seeking')
+          }}
+          onTimeupdate={() => {
+            emit('timeupdate')
           }}
           onWaiting={() => {
-            changePlayBtn('play')
-            data.videoItem?.pause()
+            emit('waiting')
           }}
           onError={handleErrorVideo}
         ></video>

+ 174 - 1
src/views/coursewarePlay/index.tsx

@@ -26,12 +26,14 @@ import { browser } from '@/helpers/utils'
 import { Vue3Lottie } from 'vue3-lottie'
 import playLoadData from './datas/data.json'
 import { usePageVisibility } from '@vant/use'
+import { useInterval } from '@vueuse/core'
 import PlayRecordTime from './playRecordTime'
 import { handleCheckVip } from '../hook/useFee'
 import OGuide from '@/components/o-guide'
 import Tool, { ToolItem, ToolType } from './component/tool'
 import Pen from './component/tools/pen'
 import VideoItem from './component/video-item'
+import deepClone from '@/helpers/deep-clone'
 
 export default defineComponent({
   name: 'CoursewarePlay',
@@ -180,6 +182,8 @@ export default defineComponent({
 
         list.push({
           ...material,
+          moreTime: [],
+          videoTime: 0, // 视频时长
           iframeRef: null,
           videoEle: null,
           tabName: name,
@@ -232,6 +236,7 @@ export default defineComponent({
       popupData.itemActive = item.id
       popupData.itemName = item.name
       nextTick(() => {
+        console.log(list, 'list')
         data.itemList = list
         checkedAnimation(popupData.activeIndex)
         postMessage({
@@ -315,10 +320,28 @@ export default defineComponent({
       }
     }
 
+    // 获取学生观看数据
+    const getLookVideoData = async () => {
+      try {
+        const { data } = await request.get(
+          state.platformApi + `/studentCoursewareMaterialRelation/findByDetailId/${route.query.id}`,
+          {
+            hideLoading: true
+          }
+        )
+        console.log(data)
+      } catch {
+        //
+      }
+    }
+
     onMounted(async () => {
+      if (state.platformType === 'STUDENT') {
+        await getLookVideoData()
+      }
       await getDetail()
       const hasFree = String(data.detail?.accessScope) === '0'
-      if (!hasFree){
+      if (!hasFree) {
         const hasVip = handleCheckVip()
         if (!hasVip) {
           nextTick(() => {
@@ -623,6 +646,132 @@ export default defineComponent({
       return {}
     })
     let closeModelTimer: any = null
+
+    /**
+     * 统计视频播放时间段
+     */
+    // 播放视频总时长
+    const videoIntervalRef = useInterval(1000, { controls: true })
+    videoIntervalRef.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)
+      // console.log(res, 'formatEffectiveTime')
+
+      return res
+    }
+    /**
+     * 获取数据有效期
+     * @param intervals [[], []]
+     * @returns 0s
+     */
+    const formatTimer = (intervals: any[]) => {
+      const afterIntervals = formatEffectiveTime(intervals)
+      let time = 0
+      afterIntervals.forEach((t: any) => {
+        time += t[1] - t[0]
+      })
+      return time
+    }
+
+    // 保存零时时间
+    // const moreTime: any = ref([]) // 多个观看时间段 已经放到列表里面了
+    let tempTime: any = [] // 临时存储时间
+    const currentTimer = useInterval(1000, { controls: true })
+    // 监听播放状态,
+    watch(
+      () => videoIntervalRef.isActive.value,
+      (newVal: boolean) => {
+        initVideoCount(newVal)
+      }
+    )
+
+    /**
+     * 初始化视频时长
+     * @param newVal 播放状态
+     * @param repeat 是否为定时发送的
+     */
+    const initVideoCount = (newVal: any, repeat = false) => {
+      // console.log('watch', forms.player.currentTime)
+      const activeVideoRef = data.videoItemRef?.getPlyrRef()
+      const initTime = deepClone(tempTime)
+      if (repeat) {
+        if (tempTime.length > 0) {
+          // console.log('join video', tempTime, 'initTime', initTime)
+          tempTime[1] = Math.floor(activeVideoRef.currentTime)
+        }
+      } else {
+        if (newVal) {
+          tempTime[0] = Math.floor(activeVideoRef.currentTime)
+        } else {
+          tempTime[1] = Math.floor(activeVideoRef.currentTime)
+        }
+      }
+
+      // console.log(newVal, repeat, tempTime, tempTime.length, 'videoIntervalRef.isActive.value in')
+      // console.log(activeVideoRef.speed, 'speed')
+
+      if (tempTime.length >= 2) {
+        // console.log(tempTime, 'tempTime', moreTime.value)
+        // 处理在短时间内的时间差 【视屏拖动,点击可能会导致时间差太大】
+        const diffTime = tempTime[1] - tempTime[0] - currentTimer.counter.value > 2
+        // 结束时间,如果 大于开始时间则清除
+        if (tempTime[1] >= tempTime[0] && !diffTime) {
+          data.itemList[popupData.activeIndex].moreTime.push(tempTime)
+          // moreTime.value.push(tempTime)
+        }
+        if (repeat) {
+          tempTime = deepClone(initTime)
+        } else {
+          tempTime = []
+          currentTimer.counter.value = 0
+        }
+      }
+    }
+    // 更新时间
+    const updateStat = async (pageBrowseTime = 10) => {
+      try {
+        // const videoBrowseData = moreTime.value.length > 0 ? formatEffectiveTime(moreTime.value) : []
+        // console.log(moreTime.value, videoBrowseData, 'video')
+        // const time = videoBrowseData.length > 0 ? formatTimer(videoBrowseData) : 0
+        // const videoCountTime = videoIntervalRef?.counter.value
+        // 判断如何视屏播放时间大于视屏播放有效时间则说明数据有问题,进行重置数据
+        // const rate = Math.floor((time / Math.floor(forms.pointVideoTime)) * 100)
+        // console.log('videoIntervalRef?.counter.value', videoIntervalRef?.counter.value)
+        // await request.post('/api-student/studentCoursewareMaterialRelation/save', {
+        //   data: {
+        //     lessonCoursewareDetailId: route.query.id,
+        //     pageBrowseTime, // 固定10秒
+        //     videoBrowseData: JSON.stringify(videoBrowseData), // 视屏播放数据
+        //     videoBrowseDataTime: time || 0, // 有效的视频观看时长
+        //     // videoBrowseTime: videoIntervalRef?.counter.value, // 视频观看时长
+        //     // videoBrowsePoint: Math.floor(forms.player.currentTime || 0) // 视频最后观看点 - 向下取整
+        //   }
+        // })
+      } catch {
+        //
+      }
+    }
+    /** 统计视频播放时间段 */
     return () => (
       <div id="playContent" class={styles.playContent}>
         <div
@@ -663,10 +812,15 @@ export default defineComponent({
                 onClose={setModelOpen}
                 onPlay={() => {
                   data.videoState = 'play'
+
+                  // 设置视频时长
+                  const videoTime = data.videoItemRef.getPlyrRef().duration || 0
+                  data.itemList[popupData.activeIndex].videoTime = Math.floor(videoTime)
                 }}
                 onPause={() => {
                   clearTimeout(activeData.timer)
                   activeData.model = true
+                  videoIntervalRef.pause()
                 }}
                 onEnded={() => {
                   const _index = popupData.activeIndex + 1
@@ -674,6 +828,25 @@ export default defineComponent({
                     handleSwipeChange(_index)
                   }
                 }}
+                onSeeked={() => {
+                  videoIntervalRef.isActive.value && videoIntervalRef.pause()
+                }}
+                onSeeking={() => {
+                  videoIntervalRef.isActive.value && videoIntervalRef.pause()
+                }}
+                onWaiting={() => {
+                  videoIntervalRef.isActive.value && videoIntervalRef.pause()
+                }}
+                onTimeupdate={() => {
+                  const activeVideoRef = data.videoItemRef?.getPlyrRef()
+                  if (
+                    !videoIntervalRef.isActive.value &&
+                    activeVideoRef?.currentTime > 0 &&
+                    activeVideoRef?.playing
+                  ) {
+                    videoIntervalRef.resume()
+                  }
+                }}
               />
             </div>
             {data.itemList.map((m: any, mIndex: number) => {

+ 1 - 1
vite.config.ts

@@ -13,7 +13,7 @@ function resolve(dir: string) {
 // https://github.com/vitejs/vite/issues/1930 .env
 // const proxyUrl = 'https://online.lexiaoya.cn/'
 // const proxyUrl = 'https://test.lexiaoya.cn/'
-const proxyUrl = 'https://test.lexiaoya.cn/'
+const proxyUrl = 'https://dev.lexiaoya.cn/'
 // const proxyUrl = 'http://47.98.131.38:8989/'
 // const proxyUrl = 'http://192.168.3.20:8989/' // 邹旋
 // const proxyUrl = 'http://192.168.3.143:8989/' // 尚科