Browse Source

更换播放视屏

lex 1 year ago
parent
commit
0dae387cbc

+ 0 - 1
src/teacher/main.ts

@@ -32,7 +32,6 @@ promisefiyPostMessage({ api: 'getToken' }).then((res: any) => {
   }
 })
 
-
 // 设置是否显示导航栏 0 不显示 1 显示
 postMessage({ api: 'setBarStatus', content: { status: 0 } })
 // 导航栏高度

+ 176 - 0
src/views/coursewarePlay/component/video-play-copy.tsx

@@ -0,0 +1,176 @@
+import { defineComponent, nextTick, onMounted, toRefs, watch } from 'vue'
+import 'plyr/dist/plyr.css'
+import Plyr from 'plyr'
+import { ref } from 'vue'
+import styles from './video.module.less'
+
+import iconLoop from '../image/icon-loop.svg'
+import iconLoopActive from '../image/icon-loop-active.svg'
+import iconplay from '../image/icon-play.svg'
+import iconpause from '../image/icon-pause.svg'
+
+export default defineComponent({
+  name: 'video-play',
+  props: {
+    item: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+    isEmtry: {
+      type: Boolean,
+      default: false
+    },
+    isActive: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['loadedmetadata', 'togglePlay', 'ended', 'reset'],
+  setup(props, { emit, expose }) {
+    const { item, isEmtry } = toRefs(props)
+    const videoRef = ref()
+    const videoItem = ref<Plyr>()
+    const controlID = 'v' + Date.now() + Math.floor(Math.random() * 100)
+    const playBtnId = 'play' + Date.now() + Math.floor(Math.random() * 100)
+    const loopBtnId = 'loop' + Date.now() + Math.floor(Math.random() * 100)
+    const toggleHideControl = (isShow: false) => {
+      videoItem.value?.toggleControls(isShow)
+    }
+    const togglePlay = (e: Event) => {
+      e.stopPropagation()
+      videoItem.value?.togglePlay()
+    }
+    const toggleLoop = (e: Event) => {
+      const loopBtn = document.getElementById(loopBtnId)
+      if (!loopBtn || !videoItem.value) return
+      const isLoop = videoItem.value.loop
+      if (isLoop) {
+        loopBtn.classList.remove(styles.active)
+      } else {
+        loopBtn.classList.add(styles.active)
+      }
+      videoItem.value.loop = !videoItem.value.loop
+    }
+    const onDefault = () => {
+      document.getElementById(controlID)?.addEventListener('click', (e: Event) => {
+        e.stopPropagation()
+        emit('reset')
+      })
+      document.getElementById(playBtnId)?.addEventListener('click', togglePlay)
+      document.getElementById(loopBtnId)?.addEventListener('click', toggleLoop)
+    }
+
+    const changePlayBtn = (code: string) => {
+      const playBtn = document.getElementById(playBtnId)
+      if (!playBtn) return
+      if (code == 'play') {
+        playBtn.classList.remove(styles.btnPause)
+        playBtn.classList.add(styles.btnPlay)
+      } else {
+        playBtn.classList.remove(styles.btnPlay)
+        playBtn.classList.add(styles.btnPause)
+      }
+    }
+    const controls = `
+            <div id="${controlID}" class="plyr__controls bottomFixed ${styles.controls}">
+                <div class="${styles.time}">
+                    <div class="plyr__time plyr__time--current" aria-label="Current time">00:00</div>
+                    <div class="plyr__time plyr__time--duration" aria-label="Duration">00:00</div>
+                </div>
+                <div class="${styles.slider}">
+                    <div class="plyr__progress">
+                        <input data-plyr="seek" type="range" min="0" max="100" step="0.01" value="0" aria-label="Seek">
+                        <progress class="plyr__progress__buffer" min="0" max="100" value="0">% buffered</progress>
+                        <span role="tooltip" class="plyr__tooltip">00:00</span>
+                    </div>
+                </div>
+                <div class="${styles.actions}">
+                    <div class="${styles.actionWrap}">
+                        <button id="${playBtnId}" class="${styles.actionBtn}">
+                            <div class="van-loading van-loading--circular" aria-live="polite" aria-busy="true"><span class="van-loading__spinner van-loading__spinner--circular" style="color: rgb(255, 255, 255);"><svg class="van-loading__circular" viewBox="25 25 50 50"><circle cx="50" cy="50" r="20" fill="none"></circle></svg></span></div>
+                            <img class="${styles.playIcon}" src="${iconplay}" />
+                            <img class="${styles.playIcon}" src="${iconpause}" />
+                        </button>
+                        <button id="${loopBtnId}" class="${styles.actionBtn} ${styles.loopBtn}">
+                            <img class="loop" src="${iconLoop}" />
+                            <img class="loopActive" src="${iconLoopActive}" />
+                        </button>
+                    </div>
+                    <div>${item.value.name}</div>
+                </div>
+            </div>`
+
+    onMounted(() => {
+      videoItem.value = new Plyr(videoRef.value, {
+        autoplay: true,
+        controls: controls,
+        autopause: true, // 一次只允许
+        ratio: '16:9', // 强制所有视频的纵横比
+        hideControls: false, // 在 2 秒没有鼠标或焦点移动、控制元素模糊(制表符退出)、播放开始或进入全屏时自动隐藏视频控件。只要移动鼠标、聚焦控制元素或暂停播放,控件就会立即重新出现。
+        clickToPlay: false, // 单击(或点击)视频容器将切换播放/暂停
+        fullscreen: { enabled: false, fallback: false, iosNative: false } // 不适用全屏
+      })
+      if (videoItem.value) {
+        videoItem.value.on('play', () => {
+          if (videoItem.value) {
+            videoItem.value.muted = false
+            videoItem.value.volume = 1
+          }
+
+          // console.log('开始播放', item.value)
+          if (!item.value.autoPlay && !item.value.isprepare && videoItem.value) {
+            // 加载完成后,取消静音播放
+
+            console.log(videoItem.value)
+            videoItem.value.pause()
+          }
+          changePlayBtn('')
+          emit('togglePlay', videoItem.value?.paused)
+        })
+        videoItem.value.on('pause', () => {
+          changePlayBtn('play')
+          emit('togglePlay', videoItem.value?.paused)
+        })
+        videoItem.value.on('ended', (e: Event) => {
+          emit('ended')
+          changePlayBtn('play')
+        })
+        videoItem.value.once('loadedmetadata', (e: Event) => {
+          changePlayBtn('play')
+          if (item.value.autoPlay && videoItem.value) {
+            videoItem.value.play()
+          }
+          emit('loadedmetadata', videoItem.value)
+        })
+
+        nextTick(() => {
+          onDefault()
+        })
+      }
+    })
+    expose({
+      changePlayBtn,
+      toggleHideControl
+    })
+    watch(
+      () => props.isActive,
+      (val) => {
+        if (!val) {
+          videoItem.value?.pause()
+        }
+      }
+    )
+    return () => (
+      <div class={styles.videoWrap}>
+        <video
+          style={{ width: '100%', height: '100%' }}
+          src={isEmtry.value ? '' : item.value.content}
+          ref={videoRef}
+          playsinline="false"
+        ></video>
+      </div>
+    )
+  }
+})

+ 167 - 91
src/views/coursewarePlay/component/video-play.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, nextTick, onMounted, toRefs, watch } from 'vue'
+import { defineComponent, nextTick, onMounted, reactive, toRefs, watch } from 'vue'
 import 'plyr/dist/plyr.css'
 import Plyr from 'plyr'
 import { ref } from 'vue'
@@ -9,6 +9,26 @@ import iconLoopActive from '../image/icon-loop-active.svg'
 import iconplay from '../image/icon-play.svg'
 import iconpause from '../image/icon-pause.svg'
 
+import TCPlayer from 'tcplayer.js'
+import 'tcplayer.js/dist/tcplayer.min.css'
+import { Slider } from 'vant'
+
+// 秒转分
+export const getSecondRPM = (second: number, type?: string) => {
+  if (isNaN(second)) return '00:00'
+  const mm = Math.floor(second / 60)
+    .toString()
+    .padStart(2, '0')
+  const dd = Math.floor(second % 60)
+    .toString()
+    .padStart(2, '0')
+  if (type === 'cn') {
+    return mm + '分' + dd + '秒'
+  } else {
+    return mm + ':' + dd
+  }
+}
+
 export default defineComponent({
   name: 'video-play',
   props: {
@@ -27,126 +47,135 @@ export default defineComponent({
       default: false
     }
   },
-  emits: ['loadedmetadata', 'togglePlay', 'ended', 'reset'],
+  emits: ['loadedmetadata', 'togglePlay', 'ended', 'reset', 'error', 'close'],
   setup(props, { emit, expose }) {
     const { item, isEmtry } = toRefs(props)
+    const data = reactive({
+      timer: null as any,
+      currentTime: 0,
+      duration: 0.1,
+      loop: false,
+      playState: 'pause' as 'play' | 'pause',
+      vudio: null as any,
+      showBar: true
+    })
     const videoRef = ref()
-    const videoItem = ref<Plyr>()
-    const controlID = 'v' + Date.now() + Math.floor(Math.random() * 100)
-    const playBtnId = 'play' + Date.now() + Math.floor(Math.random() * 100)
-    const loopBtnId = 'loop' + Date.now() + Math.floor(Math.random() * 100)
+    const videoItem = ref()
+    const videoID = 'video' + Date.now() + Math.floor(Math.random() * 100)
     const toggleHideControl = (isShow: false) => {
-      videoItem.value?.toggleControls(isShow)
-    }
-    const togglePlay = (e: Event) => {
-      e.stopPropagation()
-      videoItem.value?.togglePlay()
+      data.showBar = isShow
     }
-    const toggleLoop = (e: Event) => {
-      const loopBtn = document.getElementById(loopBtnId)
-      if (!loopBtn || !videoItem.value) return
-      const isLoop = videoItem.value.loop
-      if (isLoop) {
-        loopBtn.classList.remove(styles.active)
+    // const togglePlay = (e: Event) => {
+    //   e.stopPropagation()
+
+    // }
+    let playTimer = null as any
+    // 切换音频播放
+    const onToggleAudio = (state: 'play' | 'pause') => {
+      console.log(state, 'state')
+      clearTimeout(playTimer)
+      if (state === 'play') {
+        playTimer = setTimeout(() => {
+          videoItem.value?.play()
+          data.playState = 'play'
+        }, 100)
       } else {
-        loopBtn.classList.add(styles.active)
+        videoItem.value?.pause()
+        data.playState = 'pause'
       }
-      videoItem.value.loop = !videoItem.value.loop
+
+      emit('togglePlay', data.playState)
     }
-    const onDefault = () => {
-      document.getElementById(controlID)?.addEventListener('click', (e: Event) => {
-        e.stopPropagation()
-        emit('reset')
-      })
-      document.getElementById(playBtnId)?.addEventListener('click', togglePlay)
-      document.getElementById(loopBtnId)?.addEventListener('click', toggleLoop)
+    const toggleLoop = () => {
+      if (!videoItem.value) return
+      if (data.loop) {
+        videoItem.value.loop(false)
+      } else {
+        videoItem.value.loop(true)
+      }
+      data.loop = !data.loop
     }
-
     const changePlayBtn = (code: string) => {
-      const playBtn = document.getElementById(playBtnId)
-      if (!playBtn) return
       if (code == 'play') {
-        playBtn.classList.remove(styles.btnPause)
-        playBtn.classList.add(styles.btnPlay)
+        data.playState = 'play'
       } else {
-        playBtn.classList.remove(styles.btnPlay)
-        playBtn.classList.add(styles.btnPause)
+        data.playState = 'pause'
       }
     }
-    const controls = `
-            <div id="${controlID}" class="plyr__controls bottomFixed ${styles.controls}">
-                <div class="${styles.time}">
-                    <div class="plyr__time plyr__time--current" aria-label="Current time">00:00</div>
-                    <div class="plyr__time plyr__time--duration" aria-label="Duration">00:00</div>
-                </div>
-                <div class="${styles.slider}">
-                    <div class="plyr__progress">
-                        <input data-plyr="seek" type="range" min="0" max="100" step="0.01" value="0" aria-label="Seek">
-                        <progress class="plyr__progress__buffer" min="0" max="100" value="0">% buffered</progress>
-                        <span role="tooltip" class="plyr__tooltip">00:00</span>
-                    </div>
-                </div>
-                <div class="${styles.actions}">
-                    <div class="${styles.actionWrap}">
-                        <button id="${playBtnId}" class="${styles.actionBtn}">
-                            <div class="van-loading van-loading--circular" aria-live="polite" aria-busy="true"><span class="van-loading__spinner van-loading__spinner--circular" style="color: rgb(255, 255, 255);"><svg class="van-loading__circular" viewBox="25 25 50 50"><circle cx="50" cy="50" r="20" fill="none"></circle></svg></span></div>
-                            <img class="${styles.playIcon}" src="${iconplay}" />
-                            <img class="${styles.playIcon}" src="${iconpause}" />
-                        </button>
-                        <button id="${loopBtnId}" class="${styles.actionBtn} ${styles.loopBtn}">
-                            <img class="loop" src="${iconLoop}" />
-                            <img class="loopActive" src="${iconLoopActive}" />
-                        </button>
-                    </div>
-                    <div>${item.value.name}</div>
-                </div>
-            </div>`
 
+    /** 改变播放时间 */
+    const handleChangeTime = (val: number) => {
+      data.currentTime = val
+      clearTimeout(data.timer)
+      data.timer = setTimeout(() => {
+        videoItem.value.currentTime(val)
+        data.timer = null
+      }, 300)
+    }
     onMounted(() => {
-      videoItem.value = new Plyr(videoRef.value, {
-        autoplay: true,
-        controls: controls,
-        autopause: true, // 一次只允许
-        ratio: '16:9', // 强制所有视频的纵横比
-        hideControls: false, // 在 2 秒没有鼠标或焦点移动、控制元素模糊(制表符退出)、播放开始或进入全屏时自动隐藏视频控件。只要移动鼠标、聚焦控制元素或暂停播放,控件就会立即重新出现。
-        clickToPlay: false, // 单击(或点击)视频容器将切换播放/暂停
-        fullscreen: { enabled: false, fallback: false, iosNative: false } // 不适用全屏
-      })
+      videoItem.value = TCPlayer(videoID, {
+        appID: '',
+        controls: false
+        // autoplay: true
+      }) // player-container-id 为播放器容器 ID,必须与 html 中一致
       if (videoItem.value) {
+        videoItem.value.poster(props.item.coverImg) // 封面
+        videoItem.value.src(isEmtry.value ? '' : item.value.content) // url 播放地址
+
+        // 初步加载时
+        videoItem.value.one('loadedmetadata', (e: any) => {
+          // console.log(' Loading metadata')
+
+          // 获取时长
+          data.duration = videoItem.value.duration()
+
+          // 必须在当前元素
+
+          if (item.value.autoPlay && videoItem.value && props.isActive) {
+            videoItem.value?.play()
+          }
+          emit('loadedmetadata', videoItem.value)
+        })
+
+        // 视频播放时加载
+        videoItem.value.on('timeupdate', () => {
+          if (data.timer) return
+          data.currentTime = videoItem.value.currentTime()
+        })
+
+        // 视频播放结束
+        videoItem.value.on('ended', () => {
+          changePlayBtn('play')
+          emit('ended')
+        })
+
+        //
+        videoItem.value.on('pause', () => {
+          data.playState = 'pause'
+          changePlayBtn('pause')
+          emit('togglePlay', true)
+        })
+
         videoItem.value.on('play', () => {
+          // console.log(play, 'playing')
+          changePlayBtn('play')
           if (videoItem.value) {
             videoItem.value.muted = false
             videoItem.value.volume = 1
           }
-
           // console.log('开始播放', item.value)
           if (!item.value.autoPlay && !item.value.isprepare && videoItem.value) {
             // 加载完成后,取消静音播放
-
             console.log(videoItem.value)
             videoItem.value.pause()
           }
-          changePlayBtn('')
           emit('togglePlay', videoItem.value?.paused)
         })
-        videoItem.value.on('pause', () => {
-          changePlayBtn('play')
-          emit('togglePlay', videoItem.value?.paused)
-        })
-        videoItem.value.on('ended', (e: Event) => {
-          emit('ended')
-          changePlayBtn('play')
-        })
-        videoItem.value.once('loadedmetadata', (e: Event) => {
-          changePlayBtn('play')
-          if (item.value.autoPlay && videoItem.value) {
-            videoItem.value.play()
-          }
-          emit('loadedmetadata', videoItem.value)
-        })
 
-        nextTick(() => {
-          onDefault()
+        // 视频播放异常
+        videoItem.value.on('error', (e: any) => {
+          emit('error')
+          console.log(e, 'error')
         })
       }
     })
@@ -168,8 +197,55 @@ export default defineComponent({
           style={{ width: '100%', height: '100%' }}
           src={isEmtry.value ? '' : item.value.content}
           ref={videoRef}
-          playsinline="false"
+          id={videoID}
+          preload="auto"
+          playsinline
+          webkit-playsinline
         ></video>
+        <div class={styles.videoSection}></div>
+
+        <div
+          class={[styles.controls, data.showBar ? '' : styles.hide]}
+          onClick={(e: Event) => {
+            e.stopPropagation()
+          }}
+          // onTouchmove={(e: TouchEvent) => {
+          //   emit('close')
+          // }}
+        >
+          <div class={styles.time}>
+            <div>{getSecondRPM(data.currentTime)}</div>
+            <div>{getSecondRPM(data.duration)}</div>
+          </div>
+          <div class={styles.slider}>
+            <Slider
+              step={0.01}
+              class={styles.timeProgress}
+              v-model={data.currentTime}
+              max={data.duration}
+              onUpdate:modelValue={(val) => {
+                handleChangeTime(val)
+              }}
+            />
+          </div>
+          <div class={styles.actionSection}>
+            <div class={styles.actions} onClick={() => emit('close')}>
+              <div
+                class={styles.actionBtn}
+                onClick={(e: any) => {
+                  e.stopPropagation()
+                  onToggleAudio(data.playState === 'pause' ? 'play' : 'pause')
+                }}
+              >
+                <img src={data.playState === 'pause' ? iconplay : iconpause} />
+              </div>
+              <div class={styles.actionBtn} onClick={toggleLoop}>
+                <img src={data.loop ? iconLoopActive : iconLoop} />
+              </div>
+            </div>
+            <div class={styles.name}>{item.value.name}</div>
+          </div>
+        </div>
       </div>
     )
   }

+ 0 - 284
src/views/coursewarePlay/component/video-play1.tsx

@@ -1,284 +0,0 @@
-import { defineComponent, nextTick, onMounted, reactive, toRefs, watch } from 'vue'
-import 'plyr/dist/plyr.css'
-import Plyr from 'plyr'
-import { ref } from 'vue'
-import styles from './video.module.less'
-
-import iconLoop from '../image/icon-loop.svg'
-import iconLoopActive from '../image/icon-loop-active.svg'
-import iconplay from '../image/icon-play.svg'
-import iconpause from '../image/icon-pause.svg'
-
-import TCPlayer from 'tcplayer.js'
-import 'tcplayer.js/dist/tcplayer.min.css'
-import { Slider } from 'vant'
-
-// 秒转分
-export const getSecondRPM = (second: number, type?: string) => {
-  if (isNaN(second)) return '00:00'
-  const mm = Math.floor(second / 60)
-    .toString()
-    .padStart(2, '0')
-  const dd = Math.floor(second % 60)
-    .toString()
-    .padStart(2, '0')
-  if (type === 'cn') {
-    return mm + '分' + dd + '秒'
-  } else {
-    return mm + ':' + dd
-  }
-}
-
-export default defineComponent({
-  name: 'video-play',
-  props: {
-    item: {
-      type: Object,
-      default: () => {
-        return {}
-      }
-    },
-    isEmtry: {
-      type: Boolean,
-      default: false
-    },
-    isActive: {
-      type: Boolean,
-      default: false
-    }
-  },
-  emits: ['loadedmetadata', 'togglePlay', 'ended', 'reset', 'error', 'close'],
-  setup(props, { emit, expose }) {
-    const { item, isEmtry } = toRefs(props)
-    const data = reactive({
-      timer: null as any,
-      currentTime: 0,
-      duration: 0,
-      loop: false,
-      playState: 'pause' as 'play' | 'pause',
-      vudio: null as any,
-      showBar: true
-    })
-    const videoRef = ref()
-    const videoItem = ref()
-    const videoID = 'video' + Date.now() + Math.floor(Math.random() * 100)
-    const toggleHideControl = (isShow: false) => {
-      videoItem.value?.toggleControls(isShow)
-    }
-    // const togglePlay = (e: Event) => {
-    //   e.stopPropagation()
-
-    // }
-    let playTimer = null as any
-    // 切换音频播放
-    const onToggleAudio = (state: 'play' | 'pause') => {
-      clearTimeout(playTimer)
-      if (state === 'play') {
-        playTimer = setTimeout(() => {
-          videoItem.value?.play()
-          data.playState = 'play'
-        }, 100)
-      } else {
-        videoItem.value?.pause()
-        data.playState = 'pause'
-      }
-      emit('togglePlay', data.playState)
-    }
-    // const toggleLoop = (e: Event) => {
-    //   const loopBtn = document.getElementById(loopBtnId)
-    //   if (!loopBtn || !videoItem.value) return
-    //   const isLoop = videoItem.value.loop
-    //   if (isLoop) {
-    //     loopBtn.classList.remove(styles.active)
-    //   } else {
-    //     loopBtn.classList.add(styles.active)
-    //   }
-    //   videoItem.value.loop = !videoItem.value.loop
-    // }
-    const changePlayBtn = (code: string) => {
-      // const playBtn = document.getElementById(playBtnId)
-      // if (!playBtn) return
-      // if (code == 'play') {
-      //   playBtn.classList.remove(styles.btnPause)
-      //   playBtn.classList.add(styles.btnPlay)
-      // } else {
-      //   playBtn.classList.remove(styles.btnPlay)
-      //   playBtn.classList.add(styles.btnPause)
-      // }
-    }
-    onMounted(() => {
-      // videoItem.value = new Plyr(videoRef.value, {
-      //   autoplay: true,
-      //   controls: controls,
-      //   autopause: true, // 一次只允许
-      //   ratio: '16:9', // 强制所有视频的纵横比
-      //   hideControls: false, // 在 2 秒没有鼠标或焦点移动、控制元素模糊(制表符退出)、播放开始或进入全屏时自动隐藏视频控件。只要移动鼠标、聚焦控制元素或暂停播放,控件就会立即重新出现。
-      //   clickToPlay: false, // 单击(或点击)视频容器将切换播放/暂停
-      //   fullscreen: { enabled: false, fallback: false, iosNative: false } // 不适用全屏
-      // })
-      // if (videoItem.value) {
-      //   videoItem.value.on('play', () => {
-      //     if (videoItem.value) {
-      //       videoItem.value.muted = false
-      //       videoItem.value.volume = 1
-      //     }
-      //     // console.log('开始播放', item.value)
-      //     if (!item.value.autoPlay && !item.value.isprepare && videoItem.value) {
-      //       // 加载完成后,取消静音播放
-      //       console.log(videoItem.value)
-      //       videoItem.value.pause()
-      //     }
-      //     changePlayBtn('')
-      //     emit('togglePlay', videoItem.value?.paused)
-      //   })
-      //   videoItem.value.on('pause', () => {
-      //     changePlayBtn('play')
-      //     emit('togglePlay', videoItem.value?.paused)
-      //   })
-      //   videoItem.value.on('ended', (e: Event) => {
-      //     emit('ended')
-      //     changePlayBtn('play')
-      //   })
-      //   videoItem.value.once('loadedmetadata', (e: Event) => {
-      //     changePlayBtn('play')
-      //     if (item.value.autoPlay && videoItem.value) {
-      //       videoItem.value.play()
-      //     }
-      //     emit('loadedmetadata', videoItem.value)
-      //   })
-      //   nextTick(() => {
-      //     onDefault()
-      //   })
-      // }
-      videoItem.value = TCPlayer(videoID, {
-        appID: '',
-        controls: false
-      }) // player-container-id 为播放器容器 ID,必须与 html 中一致
-      if (videoItem.value) {
-        videoItem.value.poster(props.item.coverImg) // 封面
-        videoItem.value.src(isEmtry.value ? '' : item.value.content) // url 播放地址
-
-        // 初步加载时
-        videoItem.value.one('loadedmetadata', (e: any) => {
-          console.log(' Loading metadata')
-
-          // 获取时长
-          data.duration = videoItem.value.duration()
-
-          if (item.value.autoPlay && videoItem.value) {
-            videoItem.value?.play()
-          }
-          emit('loadedmetadata', videoItem.value)
-        })
-
-        // 视频播放时加载
-        videoItem.value.on('timeupdate', () => {
-          if (data.timer) return
-          data.currentTime = videoItem.value.currentTime()
-        })
-
-        // 视频播放结束
-        videoItem.value.on('ended', () => {
-          data.playState = 'pause'
-          emit('ended')
-        })
-
-        //
-        videoItem.value.on('pause', () => {
-          data.playState = 'pause'
-          changePlayBtn('play')
-          emit('togglePlay', true)
-        })
-
-        videoItem.value.on('playing', () => {
-          data.playState = 'play'
-          if (videoItem.value) {
-            videoItem.value.muted = false
-            videoItem.value.volume = 1
-          }
-          // console.log('开始播放', item.value)
-          if (!item.value.autoPlay && !item.value.isprepare && videoItem.value) {
-            // 加载完成后,取消静音播放
-            console.log(videoItem.value)
-            videoItem.value.pause()
-          }
-          changePlayBtn('')
-          emit('togglePlay', videoItem.value?.paused)
-        })
-
-        // videoItem.value.on('canplay', (e: any) => {
-        //   console.log('canplay', e)
-        // })
-
-        // 视频播放异常
-        videoItem.value.on('error', (e: any) => {
-          emit('error')
-          console.log(e, 'error')
-        })
-      }
-    })
-    expose({
-      changePlayBtn,
-      toggleHideControl
-    })
-    watch(
-      () => props.isActive,
-      (val) => {
-        if (!val) {
-          videoItem.value?.pause()
-        }
-      }
-    )
-    return () => (
-      <div class={styles.videoWrap}>
-        <video
-          style={{ width: '100%', height: '100%' }}
-          src={isEmtry.value ? '' : item.value.content}
-          ref={videoRef}
-          id={videoID}
-          preload="auto"
-          playsinline
-          webkit-playsinline
-        ></video>
-
-        <div
-          class={[styles.controls, data.showBar ? '' : styles.hide]}
-          onClick={(e: Event) => {
-            e.stopPropagation()
-          }}
-          // onTouchmove={(e: TouchEvent) => {
-          //   emit('close')
-          // }}
-        >
-          <div class={styles.time}>
-            <div>{getSecondRPM(data.currentTime)}</div>
-            <div>{getSecondRPM(data.duration)}</div>
-          </div>
-          <div class={styles.slider}>
-            <Slider
-              step={0.01}
-              class={styles.timeProgress}
-              // value={data.currentTime}
-              // max={data.duration}
-              // onUpdate:value={(val) => handleChangeTime(val)}
-            />
-          </div>
-          <div class={styles.actions} onClick={() => emit('close')}>
-            <div
-              class={styles.actionBtn}
-              onClick={(e: any) => {
-                e.stopPropagation()
-                onToggleAudio(data.playState === 'pause' ? 'play' : 'pause')
-              }}
-            >
-              <img src={data.playState === 'pause' ? iconplay : iconpause} />
-            </div>
-            <div class={styles.actionBtn} onClick={() => (data.loop = !data.loop)}>
-              <img src={data.loop ? iconLoopActive : iconLoop} />
-            </div>
-          </div>
-        </div>
-      </div>
-    )
-  }
-})

+ 36 - 6
src/views/coursewarePlay/component/video.module.less

@@ -136,6 +136,15 @@
   position: relative;
   width: 100%;
   height: 100%;
+
+  .videoSection {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 8;
+  }
 }
 
 .content {
@@ -171,6 +180,8 @@
   flex-direction: column;
   justify-content: space-between;
   transition: all 0.5s;
+  width: 100%;
+  z-index: 9;
 
   &.hide {
     transform: translateY(100%);
@@ -179,15 +190,17 @@
   .time {
     display: flex;
     justify-content: space-between;
-    width: 100%;
+    // width: 100%;
     color: #fff;
     font-size: 10px;
     padding: 4px 20px;
   }
 
   .slider {
-    width: 100%;
+    // width: 100%;
     padding: 0 20px;
+    --van-slider-button-width: 13px !important;
+    --van-slider-button-height: 13px !important;
 
     :global {
       .n-slider {
@@ -203,12 +216,25 @@
     }
   }
 
+  .actionSection {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 0 16px 8px 15px;
+
+    .name {
+      font-size: 14px;
+      font-weight: 500;
+      color: #FFFFFF;
+    }
+  }
+
   .actions {
     display: flex;
-    width: 100%;
+    // width: 100%;
     color: #fff;
     font-size: 12px;
-    padding: 0 20px;
+
     align-items: center;
 
     .actionWrap {
@@ -217,10 +243,14 @@
 
     .actionBtn {
       display: flex;
-      width: 28px;
-      height: 28px;
+      width: 24px;
+      height: 24px;
       padding: 4px 0;
       background: transparent;
+
+      &+.actionBtn {
+        margin-left: 12px;
+      }
     }
 
     .actionBtn>img {

+ 17 - 3
src/views/coursewarePlay/index.tsx

@@ -445,8 +445,9 @@ export default defineComponent({
       for (let i = 0; i < data.itemList.length; i++) {
         const activeItem = data.itemList[i]
         if (activeItem.type === 'VIDEO') {
+          activeItem.videoEle?.currentTime(0)
           activeItem.videoEle?.pause()
-          activeItem.videoEle?.stop()
+          // activeItem.videoEle?.stop()
         }
         // console.log('🚀 ~ activeItem:', activeItem)
         // 停止曲谱的播放
@@ -608,6 +609,12 @@ export default defineComponent({
               clearTimeout(activeData.timer)
               closeToast()
               item.autoPlay = true
+              console.log(item, 'item')
+              // 当视屏异常时重置链接
+              if (item.error) {
+                item.videoEle?.src(item.content)
+                item.error = false
+              }
               nextTick(() => {
                 item.videoEle?.play()
               })
@@ -714,8 +721,8 @@ export default defineComponent({
           >
             <div class={styles.wraps}>
               {data.itemList.map((m: any, mIndex: number) => {
-                const isRender = Math.abs(popupData.activeIndex - mIndex) < 1
-                const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 3
+                const isRender = Math.abs(popupData.activeIndex - mIndex) < 5
+                const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4
                 // if (isRender) {
                 //   m.isRender = true
                 // }
@@ -763,6 +770,9 @@ export default defineComponent({
                           isEmtry={isEmtry}
                           onLoadedmetadata={(videoItem: any) => {
                             m.videoEle = videoItem
+                            if (!m.isprepare) {
+                              m.isprepare = true
+                            }
                           }}
                           onTogglePlay={(paused: boolean) => {
                             // console.log('播放切换', paused)
@@ -788,6 +798,10 @@ export default defineComponent({
                               setModelOpen()
                             }
                           }}
+                          onError={() => {
+                            // 视屏异常
+                            m.error = true
+                          }}
                         />
                         <Transition name="van-fade">
                           {!m.isprepare && (

+ 46 - 4
src/views/exercise-after-class/index.module.less

@@ -4,11 +4,13 @@
   background-color: #000;
   overflow: hidden;
 }
+
 .coursewarePlay {
   position: relative;
   height: 100vh;
   margin: 0 auto;
 }
+
 .playModel {
   position: absolute;
   left: 0;
@@ -18,6 +20,7 @@
   box-shadow: inset 0px 0px 164px 0px rgba(0, 0, 0, 1);
   pointer-events: none;
 }
+
 .headerContainer {
   position: fixed;
   top: 0;
@@ -31,6 +34,7 @@
   font-size: 12px;
   background: linear-gradient(180deg, rgba(0, 0, 0, 0.6), transparent);
 }
+
 .backBtn {
   color: #fff;
   width: 40px;
@@ -40,25 +44,30 @@
   align-items: center;
   z-index: 10;
 }
+
 .menu {
   flex: 1;
   display: flex;
   justify-content: center;
   color: #fff;
 }
+
 .tabsContent {
   width: 100vw;
   height: 100vh;
+
   :global {
     .van-tabs__wrap {
       display: none !important;
     }
+
     .van-tabs__content {
       width: 100%;
       height: 100%;
     }
   }
 }
+
 .loadWrap {
   position: absolute;
   left: 0;
@@ -70,21 +79,34 @@
   justify-content: center;
   align-items: center;
 }
+
 .itemDiv {
   position: relative;
   width: 100%;
   height: 100%;
+
   video {
     width: 100%;
     height: 100%;
   }
+
   img {
     display: block;
     width: 100%;
     height: 100%;
     object-fit: contain;
   }
+
+  .videoSection {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 8;
+  }
 }
+
 .videoModel {
   position: absolute;
   top: 0;
@@ -94,35 +116,42 @@
   display: flex;
   justify-content: center;
   align-items: center;
-  & > img {
+
+  &>img {
     width: 50px;
     height: 50px;
   }
 }
+
 .rightFixedBtns {
   position: fixed;
   top: 50%;
   transform: translateY(-50%);
   right: 20px;
+
   .point {
     margin-top: 10px;
     border-bottom-left-radius: 0;
     border-bottom-right-radius: 0;
   }
-  .point + .fullBtn {
+
+  .point+.fullBtn {
     border-top-left-radius: 0;
     border-top-right-radius: 0;
   }
 }
+
 .leftFixedBtns {
   position: fixed;
   top: 50%;
   transform: translateY(-50%);
   left: 20px;
+
   .prePoint {
     margin-bottom: 8px;
   }
 }
+
 .fullBtn {
   width: 38px;
   height: 55px;
@@ -133,10 +162,12 @@
   align-items: center;
   color: #fff;
   justify-content: space-evenly;
+
   &:active {
     opacity: 0.8;
   }
 }
+
 .bottomFixedContainer {
   position: absolute;
   left: 0;
@@ -145,6 +176,7 @@
   z-index: 10;
   background: linear-gradient(0deg, rgba(0, 0, 0, 0.5), transparent);
   padding: 0 30px;
+
   .time {
     display: flex;
     justify-content: space-between;
@@ -152,19 +184,23 @@
     font-size: 10px;
     padding: 4px 0;
   }
+
   .slider {
     padding: 10px 0;
   }
+
   .actions {
     display: flex;
     justify-content: space-between;
     color: #fff;
     font-size: 12px;
     align-items: center;
+
     .actionBtn {
       display: flex;
     }
-    .actionBtn > img {
+
+    .actionBtn>img {
       width: 26px;
       height: 26px;
       display: block;
@@ -172,17 +208,22 @@
     }
   }
 }
+
 .popup {
   background: rgba(0, 0, 0, 0.5);
 }
+
 .overlayClass {
   --van-overlay-background: transparent;
 }
+
 :global {
+
   .top-enter-active,
   .top-leave-active {
     transition: transform 0.5s;
   }
+
   .top-enter-from,
   .top-leave-to {
     transform: translateY(-100%);
@@ -192,6 +233,7 @@
   .left-leave-active {
     transition: all 0.5s;
   }
+
   .left-enter-from,
   .left-leave-to {
     left: -60px;
@@ -216,4 +258,4 @@
   .bottom-leave-to {
     transform: translateY(100%);
   }
-}
+}

+ 16 - 117
src/views/exercise-after-class/index.tsx

@@ -1,33 +1,18 @@
-import { Icon, showConfirmDialog, showDialog, Slider, Swipe, SwipeItem } from 'vant'
-import {
-  defineComponent,
-  onMounted,
-  reactive,
-  onUnmounted,
-  ref,
-  watch,
-  Transition,
-  nextTick,
-  computed
-} from 'vue'
+import { Icon, showConfirmDialog, Swipe, SwipeItem } from 'vant'
+import { defineComponent, onMounted, reactive, onUnmounted, ref, Transition } from 'vue'
 import styles from './index.module.less'
 import 'plyr/dist/plyr.css'
 import request from '@/helpers/request'
 import { state } from '@/state'
 import { useRoute, useRouter } from 'vue-router'
 import iconBack from '../coursewarePlay/image/back.svg'
-import { postMessage, promisefiyPostMessage } from '@/helpers/native-message'
-import iconLoop from '../coursewarePlay/image/icon-loop.svg'
-import iconLoopActive from '../coursewarePlay/image/icon-loop-active.svg'
-import iconplay from '../coursewarePlay/image/icon-play.svg'
-import iconpause from '../coursewarePlay/image/icon-pause.svg'
-import iconVideobg from '../coursewarePlay/image/icon-videobg.png'
-import { browser, getSecondRPM } from '@/helpers/utils'
+import { postMessage } from '@/helpers/native-message'
+import { browser } from '@/helpers/utils'
 import qs from 'query-string'
 import { Vue3Lottie } from 'vue3-lottie'
 import playLoadData from '../coursewarePlay/datas/data.json'
 import { handleCheckVip } from '../hook/useFee'
-import item from '@/student/coupons/item'
+import VideoClass from './video-class'
 
 const materialType = {
   视频: 'VIDEO',
@@ -43,9 +28,9 @@ export default defineComponent({
       width: '100vw'
     })
     const setContainer = () => {
-      let min = Math.min(screen.width, screen.height)
-      let max = Math.max(screen.width, screen.height)
-      let width = min * (16 / 9)
+      const min = Math.min(screen.width, screen.height)
+      const max = Math.max(screen.width, screen.height)
+      const width = min * (16 / 9)
       if (width > max) {
         parentContainer.width = '100vw'
         return
@@ -306,7 +291,7 @@ export default defineComponent({
                   lessonTrainingId: query.lessonTrainingId,
                   materialId: nextItem.materialId
                 })
-                let src = `${location.origin}/orchestra-music-score/?` + parmas
+                const src = `${location.origin}/orchestra-music-score/?` + parmas
                 postMessage({
                   api: 'openAccompanyWebView',
                   content: {
@@ -345,100 +330,14 @@ export default defineComponent({
               return (
                 <SwipeItem>
                   <>
-                    <div
-                      class={styles.itemDiv}
-                      onClick={() => {
-                        clearTimeout(m.timer)
-                        activeData.model = !activeData.model
+                    <VideoClass
+                      item={m}
+                      modal={activeData.model}
+                      onEnded={(m: any) => addTrainingRecord(m)}
+                      onChangeModal={(status: boolean) => {
+                        activeData.model = status
                       }}
-                    >
-                      <video
-                        playsinline="false"
-                        preload="auto"
-                        class="player"
-                        poster={iconVideobg}
-                        data-vid={m.id}
-                        src={m.content}
-                        loop={m.loop}
-                        autoplay={m.autoplay}
-                        muted={m.muted}
-                        onLoadedmetadata={async (e: Event) => {
-                          const videoEle = e.target as unknown as HTMLVideoElement
-                          m.duration = videoEle.duration
-                          m.videoEle = videoEle
-                          m.loaded = true
-                        }}
-                        onTimeupdate={(e: Event) => {
-                          if (!m.loaded) return
-                          const videoEle = e.target as unknown as HTMLVideoElement
-                          m.currentTime = videoEle.currentTime
-                        }}
-                        onPlay={() => {
-                          console.log('播放')
-                          // 播放
-                          m.paused = false
-                          if (m.muted) {
-                            m.muted = false
-                            m.videoEle.pause()
-                          }
-                        }}
-                        onPause={() => {
-                          console.log('暂停')
-                          //暂停
-                          m.paused = true
-                        }}
-                        onEnded={() => addTrainingRecord(m)}
-                      >
-                        <source src={m.content} type="video/mp4" />
-                      </video>
-                    </div>
-                    <Transition name="bottom">
-                      {activeData.model && !m.muted && (
-                        <div class={styles.bottomFixedContainer}>
-                          <div class={styles.time}>
-                            <span>{getSecondRPM(m.currentTime)}</span>
-                            <span>{getSecondRPM(m.duration)}</span>
-                          </div>
-                          <div class={styles.slider}>
-                            {m.duration && (
-                              <Slider
-                                buttonSize={16}
-                                modelValue={m.currentTime}
-                                min={0}
-                                max={m.duration}
-                              />
-                            )}
-                          </div>
-
-                          <div class={styles.actions}>
-                            <div class={styles.actionBtn}>
-                              {m.paused ? (
-                                <img
-                                  src={iconplay}
-                                  onClick={(e: Event) => {
-                                    clearTimeout(m.timer)
-                                    m.videoEle?.play()
-                                    m.paused = false
-                                    m.timer = setTimeout(() => {
-                                      activeData.model = false
-                                    }, 4000)
-                                  }}
-                                />
-                              ) : (
-                                <img
-                                  src={iconpause}
-                                  onClick={(e: Event) => {
-                                    clearTimeout(m.timer)
-                                    m.videoEle?.pause()
-                                    m.paused = true
-                                  }}
-                                />
-                              )}
-                            </div>
-                          </div>
-                        </div>
-                      )}
-                    </Transition>
+                    />
                     {m.muted && (
                       <div class={styles.loadWrap}>
                         <Vue3Lottie animationData={playLoadData}></Vue3Lottie>

+ 169 - 0
src/views/exercise-after-class/video-class.tsx

@@ -0,0 +1,169 @@
+import { defineComponent, onMounted, ref, watch, Transition, toRefs } from 'vue'
+import styles from './index.module.less'
+import { Slider } from 'vant'
+import iconplay from '../coursewarePlay/image/icon-play.svg'
+import iconpause from '../coursewarePlay/image/icon-pause.svg'
+import iconVideobg from '../coursewarePlay/image/icon-videobg.png'
+import { getSecondRPM } from '@/helpers/utils'
+import TCPlayer from 'tcplayer.js'
+import 'tcplayer.js/dist/tcplayer.min.css'
+export default defineComponent({
+  name: 'video-class',
+  props: {
+    item: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+    modal: {
+      type: Boolean,
+      default: true
+    }
+  },
+  emits: ['loadedmetadata', 'togglePlay', 'ended', 'reset', 'error', 'close', 'changeModal'],
+  setup(props, { emit }) {
+    const { item, modal } = toRefs(props)
+    const videoItem = ref()
+    const videoID = 'video' + Date.now() + Math.floor(Math.random() * 100)
+
+    onMounted(() => {
+      videoItem.value = TCPlayer(videoID, {
+        appID: '',
+        controls: false,
+        loop: item.value.loop,
+        muted: item.value.muted
+        // autoplay: true
+      }) // player-container-id 为播放器容器 ID,必须与 html 中一致
+      if (videoItem.value) {
+        videoItem.value.poster(props.item.coverImg) // 封面
+        videoItem.value.src(item.value.content) // url 播放地址
+
+        // 初步加载时
+        videoItem.value.one('loadedmetadata', (e: any) => {
+          // console.log(' Loading metadata')
+
+          if (item.value.autoPlay && videoItem.value) {
+            videoItem.value?.play()
+          }
+          // 获取时长
+          const videoEle = videoItem.value
+          item.value.duration = videoEle.duration()
+          item.value.videoEle = videoEle
+          item.value.loaded = true
+          emit('loadedmetadata', videoItem.value)
+        })
+
+        // 视频播放时加载
+        videoItem.value.on('timeupdate', () => {
+          if (!item.value.loaded) return
+          const videoEle = videoItem.value
+          item.value.currentTime = videoEle.currentTime()
+        })
+
+        // 视频播放结束
+        videoItem.value.on('ended', () => {
+          emit('ended', item.value)
+        })
+
+        //
+        videoItem.value.on('pause', () => {
+          console.log('暂停')
+          //暂停
+          item.value.paused = true
+        })
+
+        videoItem.value.on('play', () => {
+          console.log('播放')
+          // 播放
+          item.value.paused = false
+          if (item.value.muted) {
+            item.value.muted = false
+            item.value.videoEle.pause()
+          }
+        })
+
+        // 视频播放异常
+        videoItem.value.on('error', () => {
+          emit('error')
+        })
+      }
+    })
+    return () => (
+      <>
+        <div
+          class={styles.itemDiv}
+          onClick={() => {
+            clearTimeout(item.value.timer)
+            // activeData.model = !activeData.model
+            emit('changeModal', !modal.value)
+          }}
+        >
+          <video
+            id={videoID}
+            playsinline="false"
+            preload="auto"
+            class="player"
+            poster={iconVideobg}
+            data-vid={item.value.id}
+            src={item.value.content}
+            loop={item.value.loop}
+            autoplay={item.value.autoplay}
+            muted={item.value.muted}
+          >
+            <source src={item.value.content} type="video/mp4" />
+          </video>
+          <div class={styles.videoSection}></div>
+        </div>
+        <Transition name="bottom">
+          {modal.value && !item.value.muted && (
+            <div class={styles.bottomFixedContainer}>
+              <div class={styles.time}>
+                <span>{getSecondRPM(item.value.currentTime)}</span>
+                <span>{getSecondRPM(item.value.duration)}</span>
+              </div>
+              <div class={styles.slider}>
+                {item.value.duration && (
+                  <Slider
+                    buttonSize={16}
+                    modelValue={item.value.currentTime}
+                    min={0}
+                    max={item.value.duration}
+                  />
+                )}
+              </div>
+
+              <div class={styles.actions}>
+                <div class={styles.actionBtn}>
+                  {item.value.paused ? (
+                    <img
+                      src={iconplay}
+                      onClick={() => {
+                        clearTimeout(item.value.timer)
+                        item.value.videoEle?.play()
+                        item.value.paused = false
+                        item.value.timer = setTimeout(() => {
+                          // activeData.model = false
+                          emit('changeModal', false)
+                        }, 4000)
+                      }}
+                    />
+                  ) : (
+                    <img
+                      src={iconpause}
+                      onClick={() => {
+                        clearTimeout(item.value.timer)
+                        item.value.videoEle?.pause()
+                        item.value.paused = true
+                      }}
+                    />
+                  )}
+                </div>
+              </div>
+            </div>
+          )}
+        </Transition>
+      </>
+    )
+  }
+})

+ 1 - 1
vite.config.ts

@@ -50,7 +50,7 @@ export default defineConfig({
     port: 1000,
     strictPort: true,
     cors: true,
-    https: true,
+    // https: true,
     proxy: {
       '/api-oauth': {
         target: proxyUrl,