lex 1 year ago
parent
commit
2d00a935b8

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

@@ -0,0 +1,284 @@
+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>
+    )
+  }
+})

+ 254 - 103
src/views/coursewarePlay/component/video.module.less

@@ -1,133 +1,284 @@
+// .videoWrap {
+//     width: 100%;
+//     height: 100%;
+
+//     :global {
+//         .plyr--video {
+//             width: 100%;
+//             height: 100%;
+//         }
+
+//         .plyr__time {
+//             display: block !important;
+//         }
+//         .plyr__video-wrapper{
+//             pointer-events: none;
+//         }
+//     }
+// }
+
+// :global(.bottomFixed).controls {
+//     width: 100%;
+//     background: linear-gradient(0deg, rgba(0, 0, 0, 0.5), transparent);
+//     padding: 0 !important;
+//     flex-direction: column;
+//     transition: all 0.5s;
+
+//     .time {
+//         display: flex;
+//         justify-content: space-between;
+//         width: 100%;
+//         color: #fff;
+//         font-size: 10px;
+//         padding: 4px 20px;
+
+//         :global {
+//             .plyr__time+.plyr__time:before {
+//                 content: '';
+//             }
+//         }
+//     }
+
+//     .slider {
+//         width: 100%;
+//         padding: 0 20px;
+
+//         :global {
+//             .van-slider__button {
+//                 background: var(--van-primary);
+//             }
+
+//             .van-loading {
+//                 width: 100%;
+//                 height: 100%;
+//             }
+//         }
+//     }
+
+//     .actions {
+//         display: flex;
+//         justify-content: space-between;
+//         width: 100%;
+//         color: #fff;
+//         font-size: 12px;
+//         padding: 0 20px;
+//         align-items: center;
+
+//         .actionWrap {
+//             display: flex;
+//         }
+
+//         .actionBtn {
+//             display: flex;
+//             width: 38px;
+//             height: 38px;
+//             padding: 4px 0;
+//             background: transparent;
+//         }
+
+//         .actionBtn>img {
+//             width: 100%;
+//             height: 100%;
+//         }
+
+//         :global {
+//             .van-loading__circular {
+//                 width: 100%;
+//                 height: 100%;
+//             }
+//         }
+
+//         .playIcon {
+//             display: none;
+//         }
+
+//         .btnPlay img:nth-child(2) {
+//             display: block;
+//         }
+
+//         .btnPause img:nth-child(3) {
+//             display: block;
+//         }
+
+//         .btnPlay,
+//         .btnPause {
+//             :global {
+//                 .van-loading {
+//                     display: none;
+//                 }
+//             }
+//         }
+//         .loopBtn{
+//             :global{
+//                 .loop{
+//                     display: block;
+//                 }
+//                 .loopActive{
+//                     display: none;
+//                 }
+//             }
+//         }
+//         .loopBtn.active{
+//             :global{
+//                 .loop{
+//                     display: none;
+//                 }
+//                 .loopActive{
+//                     display: block;
+//                 }
+//             }
+//         }
+
+//     }
+// }
+
 .videoWrap {
+  position: relative;
+  width: 100%;
+  height: 100%;
+}
+
+.content {
+  position: relative;
+  height: 100%;
+}
+
+.contentWrap {
+  height: 100%;
+
+  video {
     width: 100%;
     height: 100%;
+  }
+}
 
-    :global {
-        .plyr--video {
-            width: 100%;
-            height: 100%;
-        }
-
-        .plyr__time {
-            display: block !important;
-        }
-        .plyr__video-wrapper{
-            pointer-events: none;
-        }
-    }
+.videoSection {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
 }
 
-:global(.bottomFixed).controls {
+.controls {
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  height: 80px;
+  background: linear-gradient(0deg, rgba(0, 0, 0, 0.5), transparent);
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  transition: all 0.5s;
+
+  &.hide {
+    transform: translateY(100%);
+  }
+
+  .time {
+    display: flex;
+    justify-content: space-between;
+    width: 100%;
+    color: #fff;
+    font-size: 10px;
+    padding: 4px 20px;
+  }
+
+  .slider {
     width: 100%;
-    background: linear-gradient(0deg, rgba(0, 0, 0, 0.5), transparent);
-    padding: 0 !important;
-    flex-direction: column;
-    transition: all 0.5s;
-
-    .time {
-        display: flex;
-        justify-content: space-between;
+    padding: 0 20px;
+
+    :global {
+      .n-slider {
+        --n-handle-size: 13px !important;
+        --n-fill-color: var(--van-primary-color) !important;
+        --n-fill-color-hover: var(--van-primary-color) !important;
+      }
+
+      .van-loading {
         width: 100%;
-        color: #fff;
-        font-size: 10px;
-        padding: 4px 20px;
-
-        :global {
-            .plyr__time+.plyr__time:before {
-                content: '';
-            }
-        }
+        height: 100%;
+      }
     }
+  }
 
-    .slider {
-        width: 100%;
-        padding: 0 20px;
+  .actions {
+    display: flex;
+    width: 100%;
+    color: #fff;
+    font-size: 12px;
+    padding: 0 20px;
+    align-items: center;
 
-        :global {
-            .van-slider__button {
-                background: var(--van-primary);
-            }
+    .actionWrap {
+      display: flex;
+    }
 
-            .van-loading {
-                width: 100%;
-                height: 100%;
-            }
-        }
+    .actionBtn {
+      display: flex;
+      width: 28px;
+      height: 28px;
+      padding: 4px 0;
+      background: transparent;
+    }
+
+    .actionBtn>img {
+      width: 100%;
+      height: 100%;
     }
 
-    .actions {
-        display: flex;
-        justify-content: space-between;
+    :global {
+      .van-loading__circular {
         width: 100%;
-        color: #fff;
-        font-size: 12px;
-        padding: 0 20px;
-        align-items: center;
+        height: 100%;
+      }
+    }
 
-        .actionWrap {
-            display: flex;
-        }
+    .playIcon {
+      display: none;
+    }
 
-        .actionBtn {
-            display: flex;
-            width: 38px;
-            height: 38px;
-            padding: 4px 0;
-            background: transparent;
-        }
+    .btnPlay img:nth-child(2) {
+      display: block;
+    }
 
-        .actionBtn>img {
-            width: 100%;
-            height: 100%;
-        }
+    .btnPause img:nth-child(3) {
+      display: block;
+    }
 
-        :global {
-            .van-loading__circular {
-                width: 100%;
-                height: 100%;
-            }
+    .btnPlay,
+    .btnPause {
+      :global {
+        .van-loading {
+          display: none;
         }
+      }
+    }
 
-        .playIcon {
-            display: none;
+    .loopBtn {
+      :global {
+        .loop {
+          display: block;
         }
 
-        .btnPlay img:nth-child(2) {
-            display: block;
+        .loopActive {
+          display: none;
         }
+      }
+    }
 
-        .btnPause img:nth-child(3) {
-            display: block;
+    .loopBtn.active {
+      :global {
+        .loop {
+          display: none;
         }
 
-        .btnPlay,
-        .btnPause {
-            :global {
-                .van-loading {
-                    display: none;
-                }
-            }
-        }
-        .loopBtn{
-            :global{
-                .loop{
-                    display: block;
-                }
-                .loopActive{
-                    display: none;
-                }
-            }
+        .loopActive {
+          display: block;
         }
-        .loopBtn.active{
-            :global{
-                .loop{
-                    display: none;
-                }
-                .loopActive{
-                    display: block;
-                }
-            }
-        }
-
+      }
     }
+
+  }
 }

+ 9 - 6
src/views/coursewarePlay/index.tsx

@@ -87,9 +87,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
@@ -246,7 +246,10 @@ export default defineComponent({
 
       console.log(list, 'list')
 
-      let _firstIndex = list.findIndex((n: any) => n.knowledgePointMaterialRelationId == route.query.kId || n.materialId == route.query.kId)
+      let _firstIndex = list.findIndex(
+        (n: any) =>
+          n.knowledgePointMaterialRelationId == route.query.kId || n.materialId == route.query.kId
+      )
       _firstIndex = _firstIndex > -1 ? _firstIndex : 0
       const item = list[_firstIndex]
 
@@ -475,7 +478,7 @@ export default defineComponent({
       activeData.model = false
       Object.values(data.videoRefs).map((n: any) => n.toggleHideControl(false))
     }
-    const toggleModel = (type: boolean = true) => {
+    const toggleModel = (type = true) => {
       activeData.model = type
       Object.values(data.videoRefs).map((n: any) => n.toggleHideControl(type))
     }
@@ -711,7 +714,7 @@ export default defineComponent({
           >
             <div class={styles.wraps}>
               {data.itemList.map((m: any, mIndex: number) => {
-                const isRender = Math.abs(popupData.activeIndex - mIndex) < 5
+                const isRender = Math.abs(popupData.activeIndex - mIndex) < 1
                 const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 3
                 // if (isRender) {
                 //   m.isRender = true

+ 2 - 1
vite.config.ts

@@ -12,7 +12,7 @@ function resolve(dir: string) {
 // https://vitejs.dev/config/
 // 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 = 'http://47.98.131.38:8989/'
 // const proxyUrl = 'http://192.168.3.20:8989/' // 邹旋
 // const proxyUrl = 'http://192.168.3.143:8989/' // 尚科
@@ -50,6 +50,7 @@ export default defineConfig({
     port: 1000,
     strictPort: true,
     cors: true,
+    https: true,
     proxy: {
       '/api-oauth': {
         target: proxyUrl,