Browse Source

Merge branch 'iteration_0307' into jenkins-main

liushengqiang 1 year ago
parent
commit
5d8fcf4f23

+ 7 - 8
src/views/coursewarePlay/component/musicScore.tsx

@@ -1,10 +1,9 @@
-import { defineComponent, ref, nextTick, onMounted, watch } from 'vue'
+import { defineComponent, ref, watch } from 'vue'
 import styles from './musicScore.module.less'
 import qs from 'query-string'
-import OEmpty from '@/components/o-empty'
 import iconStart from '../image/icon-start.svg'
 import { listenerMessage, postMessage } from '@/helpers/native-message'
-import { Loading, Skeleton } from 'vant'
+import { Skeleton } from 'vant'
 import { usePageVisibility } from '@vant/use'
 import { useRoute } from 'vue-router'
 import { browser } from '@/helpers/utils'
@@ -14,7 +13,7 @@ export default defineComponent({
   props: {
     music: {
       type: Object,
-      default: () => {}
+      default: () => ({})
     },
     activeModel: {
       type: Boolean
@@ -47,14 +46,14 @@ export default defineComponent({
       headerHeight: 32,
       Authorization: Authorization
     })
-    let src = `${origin}/orchestra-music-score/?` + query
+    const src = `${origin}/orchestra-music-score/?` + query
     const checkView = () => {
       fetch(src)
         .then(() => {
           renderSuccess.value = true
           renderError.value = false
         })
-        .catch((err) => {
+        .catch(() => {
           renderError.value = true
         })
     }
@@ -75,7 +74,7 @@ export default defineComponent({
       const parmas = qs.stringify({
         id: props.music.content
       })
-      let src = `${location.origin}/orchestra-music-score/?` + parmas
+      const src = `${location.origin}/orchestra-music-score/?` + parmas
       postMessage({
         api: 'openAccompanyWebView',
         content: {
@@ -99,7 +98,7 @@ export default defineComponent({
       <div class={styles.musicScore}>
         <iframe
           ref={iframeRef}
-          onLoad={(e: Event) => {
+          onLoad={() => {
             emit('setIframe', iframeRef.value)
             isLoaded.value = true
           }}

+ 133 - 0
src/views/coursewarePlay/component/video-item/index.module.less

@@ -0,0 +1,133 @@
+.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;
+                }
+            }
+        }
+
+    }
+}

+ 183 - 0
src/views/coursewarePlay/component/video-item/index.tsx

@@ -0,0 +1,183 @@
+import { defineComponent, nextTick, onMounted, reactive, toRefs, watch } from 'vue'
+import 'plyr/dist/plyr.css'
+import Plyr from 'plyr'
+import styles from './index.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 {}
+      }
+    },
+    activeModel: {
+      type: Boolean,
+      default: true
+    }
+  },
+  emits: ['play', 'pause', 'ended', 'close'],
+  setup(props, { emit }) {
+    const { item } = toRefs(props)
+    const data = reactive({
+      videoContianerRef: null as unknown as HTMLAudioElement,
+      videoState: 'pause' as 'init' | 'play' | 'pause',
+      animationState: 'start' as 'start' | 'end',
+      videoItem: null as unknown as 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 togglePlay = (e: Event) => {
+      e.stopPropagation()
+      if (!data.videoContianerRef.paused) {
+        data.videoItem?.pause()
+      } else {
+        data.videoContianerRef?.play()
+      }
+    }
+    const toggleLoop = () => {
+      const loopBtn = document.getElementById(loopBtnId)
+      if (!loopBtn || !data.videoItem) return
+      const isLoop = data.videoItem.loop
+      if (isLoop) {
+        loopBtn.classList.remove(styles.active)
+      } else {
+        loopBtn.classList.add(styles.active)
+      }
+      data.videoItem.loop = !data.videoItem.loop
+    }
+    const onDefault = () => {
+      document.getElementById(controlID)?.addEventListener('click', (e: Event) => {
+        e.stopPropagation()
+        if (data.videoContianerRef.paused) return
+        emit('close')
+      })
+      document.getElementById(controlID)?.addEventListener('touchmove', () => {
+        if (data.videoContianerRef.paused) return
+        emit('close')
+      })
+      document.getElementById(playBtnId)?.addEventListener('click', togglePlay)
+      document.getElementById(loopBtnId)?.addEventListener('click', toggleLoop)
+      setName();
+    }
+    const setName = () => {
+      const nameEl = document.getElementById('videoItemName');
+      if (nameEl){
+        nameEl.innerHTML = item.value.name || ''
+      }
+    }
+
+    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 id="videoItemName"></div>
+                </div>
+            </div>`
+
+    onMounted(() => {
+      data.videoItem = new Plyr(data.videoContianerRef, {
+        autoplay: true,
+        controls: controls,
+        ratio: '16:9', // 强制所有视频的纵横比
+        hideControls: false, // 在 2 秒没有鼠标或焦点移动、控制元素模糊(制表符退出)、播放开始或进入全屏时自动隐藏视频控件。只要移动鼠标、聚焦控制元素或暂停播放,控件就会立即重新出现。
+        clickToPlay: false, // 单击(或点击)视频容器将切换播放/暂停
+        fullscreen: { enabled: false, fallback: false, iosNative: false } // 不适用全屏
+      })
+
+      nextTick(() => {
+        onDefault()
+      })
+    })
+
+    const toggleHideControl = (isShow: boolean) => {
+      data.videoItem?.toggleControls(isShow)
+    }
+    watch(() => props.activeModel, () => {
+      toggleHideControl(props.activeModel)
+    })
+
+    watch(() => props.item, () => {
+      setName();
+    })
+
+    return () => (
+      <div class={styles.videoWrap}>
+        <video
+          ref={(el) => (data.videoContianerRef = el as unknown as HTMLAudioElement)}
+          class={styles.itemDiv}
+          src={props.item?.content}
+          webkit-playsinline
+          playsinline
+          x5-video-player-type="h5"
+          onLoadedmetadata={() => {
+            data.videoState = 'pause'
+            changePlayBtn('play');
+            nextTick(() => {
+              data.videoContianerRef.play();
+            })
+          }}
+          onPlay={() => {
+            console.log('开始播放')
+            data.videoState = 'play'
+            changePlayBtn('pause')
+            emit('close')
+            emit('play')
+          }}
+          onPause={() => {
+            console.log('暂停播放')
+            data.videoState = 'pause'
+            changePlayBtn('play')
+            emit('pause')
+          }}
+          onEnded={() => {
+            console.log('播放结束')
+            data.videoState = 'pause'
+            changePlayBtn('play')
+            emit('ended')
+          }}
+        ></video>
+      </div>
+    )
+  }
+})

+ 177 - 0
src/views/coursewarePlay/component/video-item/video-play.tsx

@@ -0,0 +1,177 @@
+import { defineComponent, nextTick, onMounted, toRefs } 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', 'prepare'],
+  setup(props, { emit, expose }) {
+    const { item, isEmtry } = toRefs(props)
+    const videoRef = ref()
+    const videoItem: any = ref()
+    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 = () => {
+      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(() => {
+      emit('prepare', false)
+      videoItem.value = new Plyr(videoRef.value, {
+        autoplay: false,
+        controls: controls,
+        autopause: false, // 一次只允许
+        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) {
+            videoItem.value.muted = false
+            videoItem.value.volume = 1
+          }
+
+          // console.log('开始播放', item.value)
+          if (!item.value.autoPlay && !item.value.isprepare && videoItem.value) {
+            // 加载完成后,取消静音播放
+            videoItem.value.pause()
+            console.log(videoItem.value?.paused, 'video status')
+          }
+          changePlayBtn('')
+          emit('togglePlay', videoItem.value?.paused)
+        })
+        videoItem.value.on('pause', () => {
+          changePlayBtn('play')
+          emit('togglePlay', videoItem.value?.paused)
+        })
+        videoItem.value.on('ended', () => {
+          emit('ended')
+          changePlayBtn('play')
+        })
+        videoItem.value.once('loadedmetadata', () => {
+          console.log('loadedmetadata')
+          changePlayBtn('play')
+          videoItem.value.currentTime = 0
+          if (item.value.autoPlay && videoItem.value && props.isActive) {
+            videoItem.value.play()
+          }
+          emit('loadedmetadata', videoItem.value)
+        })
+        videoItem.value.on('timeupdate', () => {
+          if (!props.isActive) {
+            console.log('不是激活的视频,如果在播放,就暂停')
+            videoRef.value.pause()
+          }
+        })
+
+        nextTick(() => {
+          onDefault()
+        })
+      }
+    })
+    expose({
+      changePlayBtn,
+      toggleHideControl
+    })
+
+    return () => (
+      <div class={styles.videoWrap}>
+        <video
+          style={{ width: '100%', height: '100%' }}
+          src={isEmtry.value ? '' : item.value.content}
+          ref={videoRef}
+          playsinline="false"
+        ></video>
+      </div>
+    )
+  }
+})

+ 4 - 4
src/views/coursewarePlay/component/video-play.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, nextTick, onMounted, onUnmounted, toRefs, watch } from 'vue'
+import { defineComponent, nextTick, onMounted, toRefs } from 'vue'
 import 'plyr/dist/plyr.css'
 import Plyr from 'plyr'
 import { ref } from 'vue'
@@ -42,7 +42,7 @@ export default defineComponent({
       e.stopPropagation()
       videoItem.value?.togglePlay()
     }
-    const toggleLoop = (e: Event) => {
+    const toggleLoop = () => {
       const loopBtn = document.getElementById(loopBtnId)
       if (!loopBtn || !videoItem.value) return
       const isLoop = videoItem.value.loop
@@ -133,11 +133,11 @@ export default defineComponent({
           changePlayBtn('play')
           emit('togglePlay', videoItem.value?.paused)
         })
-        videoItem.value.on('ended', (e: Event) => {
+        videoItem.value.on('ended', () => {
           emit('ended')
           changePlayBtn('play')
         })
-        videoItem.value.once('loadedmetadata', (e: Event) => {
+        videoItem.value.once('loadedmetadata', () => {
           console.log('loadedmetadata')
           changePlayBtn('play')
           videoItem.value.currentTime = 0

+ 214 - 246
src/views/coursewarePlay/index.tsx

@@ -1,15 +1,4 @@
-import {
-  closeToast,
-  Icon,
-  Loading,
-  Popup,
-  showDialog,
-  showToast,
-  Slider,
-  Swipe,
-  SwipeInstance,
-  SwipeItem
-} from 'vant'
+import { closeToast, Icon, Popup, showDialog } from 'vant'
 import {
   defineComponent,
   onMounted,
@@ -19,16 +8,15 @@ import {
   ref,
   watch,
   Transition,
-  TransitionGroup,
-  onBeforeUnmount
+  computed
 } from 'vue'
 import iconBack from './image/back.svg'
 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 { listenerMessage, postMessage, promisefiyPostMessage } from '@/helpers/native-message'
+import { useRoute } from 'vue-router'
+import { postMessage, promisefiyPostMessage } from '@/helpers/native-message'
 import MusicScore from './component/musicScore'
 import iconMenu from './image/icon-menu.svg'
 import iconDian from './image/icon-dian.svg'
@@ -36,46 +24,32 @@ import iconTouping from './image/icon-touping.svg'
 import iconPoint from './image/icon-point.svg'
 import iconUp from './image/icon-up.svg'
 import iconDown from './image/icon-down.svg'
-import iconMore from './image/icon-more.png'
 import Points from './component/points'
-import { browser, getSecondRPM } from '@/helpers/utils'
+import { browser } from '@/helpers/utils'
 import { Vue3Lottie } from 'vue3-lottie'
 import playLoadData from './datas/data.json'
-import { usePageVisibility, useRect } from '@vant/use'
+import { usePageVisibility } from '@vant/use'
 import PlayRecordTime from './playRecordTime'
-import VideoPlay from './component/video-play'
-import {
-  Pagination,
-  Navigation,
-  Virtual,
-  EffectFade,
-  EffectFlip,
-  EffectCreative,
-  Lazy
-} from 'swiper'
-import { Swiper, SwiperSlide } from 'swiper/vue'
-import 'swiper/less'
-import 'swiper/less/effect-fade'
-import 'swiper/less/effect-flip'
-import 'swiper/less/effect-creative'
 import { handleCheckVip } from '../hook/useFee'
 import OGuide from '@/components/o-guide'
 import Tool, { ToolItem, ToolType } from './component/tool'
-import Tools from './component/tools/pen'
 import Pen from './component/tools/pen'
 import iconPen from './image/icon-pen.png'
-import { useThrottle, useThrottleFn, useDebounceFn } from '@vueuse/core'
+import VideoItem from './component/video-item'
 
 export default defineComponent({
   name: 'CoursewarePlay',
   setup() {
     const pageVisibility = usePageVisibility()
     /** 页面显示和隐藏 */
-    watch(() => pageVisibility.value, (value) => {
-      if (value == 'hidden') {
-        handleStop()
+    watch(
+      () => pageVisibility.value,
+      (value) => {
+        if (value == 'hidden') {
+          handleStop()
+        }
       }
-    })
+    )
     /** 设置播放容器 16:9 */
     const parentContainer = reactive({
       width: '100vw'
@@ -135,7 +109,6 @@ export default defineComponent({
     })
 
     const route = useRoute()
-    const router = useRouter()
     const headeRef = ref()
     const data = reactive({
       detail: null,
@@ -144,7 +117,10 @@ export default defineComponent({
       showHead: true,
       isCourse: false,
       isRecordPlay: false,
-      videoRefs: {}
+      videoRefs: {},
+
+      videoState: 'init' as 'init' | 'play',
+      animationState: 'start' as 'start' | 'end'
     })
     const activeData = reactive({
       isAutoPlay: true, // 是否自动播放
@@ -268,6 +244,9 @@ export default defineComponent({
             type: 'fullscreen'
           }
         })
+        setTimeout(() => {
+          data.animationState = 'end'
+        }, 500)
       })
     }
     const getDetail = async () => {
@@ -290,7 +269,7 @@ export default defineComponent({
           showDialog({
             title: '温馨提示',
             message: '课件已锁定'
-          }).then((value) => {
+          }).then(() => {
             goback()
           })
           return
@@ -327,7 +306,9 @@ export default defineComponent({
           })
           getItemList()
         }
-      } catch (error) {}
+      } catch (error) {
+        console.log(error)
+      }
     }
 
     // ifram事件处理
@@ -354,7 +335,6 @@ export default defineComponent({
       getDetail()
       getCourseSchedule()
       window.addEventListener('message', iframeHandle)
-
     })
 
     const playRef = ref()
@@ -362,7 +342,9 @@ export default defineComponent({
     const goback = () => {
       try {
         playRef.value?.handleOut()
-      } catch (error) {}
+      } catch (error) {
+        console.log(error)
+      }
       postMessage({ api: 'goBack' })
     }
 
@@ -380,7 +362,7 @@ export default defineComponent({
 
     const stopVideo = (el: HTMLVideoElement) => {
       return new Promise((resolve) => {
-        if(el.paused) return resolve(true)
+        if (el.paused) return resolve(true)
         el.onpause = () => {
           console.log('暂停')
           resolve(true)
@@ -416,7 +398,6 @@ export default defineComponent({
       closeToast()
       activeData.timer = setTimeout(() => {
         activeData.model = false
-        Object.values(data.videoRefs).map((n: any) => n?.toggleHideControl(false))
       }, 4000)
     }
     /** 立即收起所有的模态框 */
@@ -424,11 +405,9 @@ export default defineComponent({
       clearTimeout(activeData.timer)
       closeToast()
       activeData.model = false
-      Object.values(data.videoRefs).map((n: any) => n?.toggleHideControl(false))
     }
     const toggleModel = (type = true) => {
       activeData.model = type
-      Object.values(data.videoRefs).map((n: any) => n?.toggleHideControl(type))
     }
 
     // 去点名,签退
@@ -445,31 +424,12 @@ export default defineComponent({
     }
 
     // 双击
-    const handleDbClick = (item: any) => {
-      if (item && item.type === 'VIDEO') {
-        const videoEle: HTMLVideoElement = item.videoEle
-        if (videoEle) {
-          if (videoEle?.paused) {
-            closeToast()
-            videoEle.play()
-          } else {
-            showToast('已暂停')
-            videoEle.pause()
-          }
-        }
-      }
-    }
-
-    // 切换播放
-    const togglePlay = (m: any, isPlay: boolean) => {
-      if (isPlay) {
-        m.videoEle?.play()
-      } else {
-        m.videoEle?.pause()
+    const handleDbClick = () => {
+      if (activeVideoItem.value.type === 'VIDEO') {
+        handleStop()
       }
     }
 
-    const showIndex = ref(-4)
     const effectIndex = ref(0)
     const effects = [
       {
@@ -535,6 +495,8 @@ export default defineComponent({
       // 如果是当前正在播放 或者是视频最后一个
       if (popupData.activeIndex == index) return
       await handleStop()
+      data.animationState = 'start'
+      data.videoState = 'init'
       clearTimeout(acitveTimer.value)
       checkedAnimation(popupData.activeIndex, index)
       nextTick(() => {
@@ -562,33 +524,20 @@ export default defineComponent({
                 clearTimeout(activeData.timer)
                 closeToast()
                 item.autoPlay = true
-                nextTick(() => {
-                  item.videoEle?.play()
-                })
+                data.animationState = 'end'
               }
             })
           },
-          activeData.isAnimation ? 800 : 0
+          activeData.isAnimation ? 850 : 0
         )
       })
     }
 
-    const onChangeSwiper = useDebounceFn((type: string, itemActive?: any) => {
-      if (type === 'up') {
-        handlePreAndNext('up')
-      }
-      if (type === 'down') {
-        handlePreAndNext('down')
-      }
-      if (type === 'change') {
-        popupData.open = false
-        toggleMaterial(itemActive)
-      }
-    }, 200)
     /** 是否有转场动画 */
     const checkedAnimation = (index: number, nextIndex?: number) => {
+      nextIndex = nextIndex ? nextIndex : index + 1;
       const item = data.itemList[index]
-      const nextItem = data.itemList[nextIndex!]
+      const nextItem = data.itemList[nextIndex]
       if (nextItem) {
         if (nextItem.knowledgePointId != item.knowledgePointId) {
           activeData.isAnimation = true
@@ -596,9 +545,9 @@ export default defineComponent({
         }
         const videoEle = item.videoEle
         const nextVideo = nextItem.videoEle
-        if (videoEle && videoEle.duration < 8 && index < nextIndex!) {
+        if (videoEle && videoEle.duration < 8 && index < nextIndex) {
           activeData.isAnimation = false
-        } else if (nextVideo && nextVideo.duration < 8 && index > nextIndex!) {
+        } else if (nextVideo && nextVideo.duration < 8 && index > nextIndex) {
           activeData.isAnimation = false
         } else {
           activeData.isAnimation = true
@@ -658,68 +607,87 @@ export default defineComponent({
       toggleModel()
     }
 
+    const activeVideoItem = computed(() => {
+      const item = data.itemList[popupData.activeIndex]
+      if (item && item.type && item.type.toLocaleUpperCase() === 'VIDEO') {
+        return item
+      }
+      return {}
+    })
+
     return () => (
       <div id="playContent" class={styles.playContent}>
         <div
+          class={styles.coursewarePlay}
+          style={{ width: parentContainer.width }}
           onClick={() => {
             clearTimeout(activeData.timer)
+            if (Date.now() - activeData.nowTime < 300) {
+              activeData.model = false
+              handleDbClick()
+              return
+            }
+            activeData.nowTime = Date.now()
             activeData.model = !activeData.model
-            Object.values(data.videoRefs).map((n: any) => n?.toggleHideControl(activeData.model))
           }}
         >
-          <div
-            class={styles.coursewarePlay}
-            style={{ width: parentContainer.width }}
-            onClick={(e: Event) => {
-              e.stopPropagation()
-              setModelOpen()
-            }}
-          >
-            <div class={styles.wraps}>
-              {data.itemList.map((m: any, mIndex: number) => {
-                const isRenderItem = Math.abs(popupData.activeIndex - mIndex) < 2
-                const isRender = Math.abs(popupData.playIndex - mIndex) < 2
-                const isEmtry = popupData.activeIndex != mIndex
-                // 判断是否是当前选中的元素
-                const activeEle = popupData.playIndex === mIndex ? true : false
-
-                return isRenderItem ? (
-                  <div
-                    key={'index' + mIndex}
-                    data-id={'data' + mIndex}
-                    class={[
-                      styles.itemDiv,
-                      activeEle && styles.itemActive,
-                      activeData.isAnimation && styles.acitveAnimation,
-                      isRenderItem ? styles.show : styles.hide
-                    ]}
-                    style={
-                      mIndex < popupData.activeIndex
-                        ? effects[effectIndex.value].prev
-                        : mIndex > popupData.activeIndex
-                        ? effects[effectIndex.value].next
-                        : {}
-                    }
-                    onClick={(e: Event) => {
-                      e.stopPropagation()
-                      clearTimeout(activeData.timer)
-                      if (Date.now() - activeData.nowTime < 300) {
-                        handleDbClick(m)
-                        return
-                      }
-                      activeData.nowTime = Date.now()
-                      activeData.timer = setTimeout(() => {
-                        activeData.model = !activeData.model
-                        Object.values(data.videoRefs).map((n: any) =>
-                          n?.toggleHideControl(activeData.model)
-                        )
-                        if (activeData.model) {
-                          setModelOpen()
-                        }
-                      }, 300)
-                    }}
-                  >
-                    {m.type === 'VIDEO' && (
+          <div class={styles.wraps}>
+            <div
+              style={{
+                zIndex: 15,
+                opacity:
+                  activeVideoItem.value.type &&
+                  data.animationState === 'end' &&
+                  data.videoState === 'play'
+                    ? 1
+                    : 0
+              }}
+              class={styles.itemDiv}
+            >
+              <VideoItem
+                item={activeVideoItem.value}
+                activeModel={activeData.model}
+                onClose={setModelOpen}
+                onPlay={() => {
+                  data.videoState = 'play'
+                }}
+                onPause={() => {
+                  clearTimeout(activeData.timer)
+                  activeData.model = true
+                }}
+                onEnded={() => {
+                  const _index = popupData.activeIndex + 1
+                  if (_index < data.itemList.length) {
+                    handleSwipeChange(_index)
+                  }
+                }}
+              />
+            </div>
+            {data.itemList.map((m: any, mIndex: number) => {
+              const isRenderItem = Math.abs(popupData.activeIndex - mIndex) < 2
+              const isRender = Math.abs(popupData.playIndex - mIndex) < 2
+              // 判断是否是当前选中的元素
+              const activeEle = popupData.playIndex === mIndex ? true : false
+
+              return isRenderItem ? (
+                <div
+                  key={'index' + mIndex}
+                  data-id={'data' + mIndex}
+                  class={[
+                    styles.itemDiv,
+                    activeEle && styles.itemActive,
+                    activeData.isAnimation && styles.acitveAnimation,
+                    isRenderItem ? styles.show : styles.hide
+                  ]}
+                  style={
+                    mIndex < popupData.activeIndex
+                      ? effects[effectIndex.value].prev
+                      : mIndex > popupData.activeIndex
+                      ? effects[effectIndex.value].next
+                      : {}
+                  }
+                >
+                  {/* {m.type === 'VIDEO' && (
                       <>
                         <VideoPlay
                           ref={(v: any) => (data.videoRefs[mIndex] = v)}
@@ -764,116 +732,116 @@ export default defineComponent({
                           )}
                         </Transition>
                       </>
-                    )}
-                    {m.type === "VIDEO" && isEmtry && (
-                      <div class={styles.loadWrap}>
-                      <Vue3Lottie animationData={playLoadData}></Vue3Lottie>
-                    </div>
-                    )}
-                    {isRender && m.type === 'IMG' && <img src={m.content} />}
-                    {isRender && m.type === 'SONG' && (
-                      <MusicScore
-                        activeModel={activeData.model}
-                        data-vid={m.id}
-                        music={m}
-                        onSetIframe={(el: any) => {
-                          m.iframeRef = el
-                        }}
-                      />
-                    )}
-                  </div>
-                ) : (
-                  ''
-                )
-              })}
-            </div>
-            <Transition name="right">
-              {activeData.model && (
-                <div
-                  class={styles.rightFixedBtns}
-                  onClick={(e: Event) => {
-                    e.stopPropagation()
-                    clearTimeout(activeData.timer)
-                  }}
-                >
-                  <div class={styles.btnsWrap}>
-                    <div
-                      class={[styles.fullBtn, styles.point]}
-                      onClick={() => (popupData.open = true)}
-                    >
-                      <img src={iconMenu} />
-                      <span>知识点</span>
-                    </div>
+                    )} */}
+                  <Transition name="van-fade">
+                    {m.type === 'VIDEO' &&
+                      data.animationState !== 'end' &&
+                      data.videoState != 'play' && (
+                        <div class={styles.loadWrap}>
+                          <Vue3Lottie animationData={playLoadData}></Vue3Lottie>
+                        </div>
+                      )}
+                  </Transition>
+                  {isRender && m.type === 'IMG' && <img src={m.content} />}
+                  {isRender && m.type === 'SONG' && (
+                    <MusicScore
+                      activeModel={activeData.model}
+                      data-vid={m.id}
+                      music={m}
+                      onSetIframe={(el: any) => {
+                        m.iframeRef = el
+                      }}
+                    />
+                  )}
+                </div>
+              ) : (
+                ''
+              )
+            })}
+          </div>
+          <Transition name="right">
+            {activeData.model && (
+              <div
+                class={styles.rightFixedBtns}
+                onClick={(e: Event) => {
+                  e.stopPropagation()
+                  clearTimeout(activeData.timer)
+                }}
+              >
+                <div class={styles.btnsWrap}>
+                  <div
+                    class={[styles.fullBtn, styles.point]}
+                    onClick={() => (popupData.open = true)}
+                  >
+                    <img src={iconMenu} />
+                    <span>知识点</span>
                   </div>
+                </div>
 
-                  <div class={[styles.btnsWrap, styles.btnsBottom]}>
-                    {/* <div class={styles.fullBtn} onClick={() => (popupData.guideOpen = true)}>
+                <div class={[styles.btnsWrap, styles.btnsBottom]}>
+                  {/* <div class={styles.fullBtn} onClick={() => (popupData.guideOpen = true)}>
                       <img src={iconTouping} />
                       <span>投屏</span>
                     </div> */}
-                    {data.isCourse && (
-                      <>
-                        <div
-                          class={styles.fullBtn}
-                          onClick={() => gotoRollCall('student_roll_call')}
-                        >
-                          <img src={iconDian} />
-                          <span>点名</span>
-                        </div>
-                        <div class={styles.fullBtn} onClick={() => gotoRollCall('sign_out')}>
-                          <img src={iconPoint} />
-                          <span>签退</span>
-                        </div>
-                      </>
-                    )}
-                  </div>
-                </div>
-              )}
-            </Transition>
-
-            <Transition name="left">
-              {activeData.model && (
-                <div class={styles.leftFixedBtns} onClick={(e: Event) => e.stopPropagation()}>
-                  {popupData.activeIndex != 0 && (
-                    <div class={[styles.btnsWrap, styles.prePoint]}>
-                      <div
-                        class={styles.fullBtn}
-                        onClick={() => {
-                          // useThrottleFn(() => {
-                          //   handlePreAndNext('up')
-                          // }, 300)
-                          // onChangeSwiper('up')
-                          handlePreAndNext('up')
-                        }}
-                      >
-                        <img src={iconUp} />
-                        <span style={{ textAlign: 'center' }}>上一个</span>
+                  {data.isCourse && (
+                    <>
+                      <div class={styles.fullBtn} onClick={() => gotoRollCall('student_roll_call')}>
+                        <img src={iconDian} />
+                        <span>点名</span>
                       </div>
-                    </div>
-                  )}
-                  {popupData.activeIndex != data.itemList.length - 1 && (
-                    <div class={styles.btnsWrap}>
-                      <div
-                        class={styles.fullBtn}
-                        onClick={() => {
-                          // console.log('click down')
-                          // useThrottleFn(() => {
-                          //   console.log('click down pass')
-                          //   handlePreAndNext('down')
-                          // }, 300)
-                          // onChangeSwiper('down')
-                          handlePreAndNext('down')
-                        }}
-                      >
-                        <span style={{ textAlign: 'center' }}>下一个</span>
-                        <img src={iconDown} />
+                      <div class={styles.fullBtn} onClick={() => gotoRollCall('sign_out')}>
+                        <img src={iconPoint} />
+                        <span>签退</span>
                       </div>
-                    </div>
+                    </>
                   )}
                 </div>
-              )}
-            </Transition>
-          </div>
+              </div>
+            )}
+          </Transition>
+
+          <Transition name="left">
+            {activeData.model && (
+              <div class={styles.leftFixedBtns} onClick={(e: Event) => e.stopPropagation()}>
+                {popupData.activeIndex != 0 && (
+                  <div class={[styles.btnsWrap, styles.prePoint]}>
+                    <div
+                      class={styles.fullBtn}
+                      onClick={() => {
+                        // useThrottleFn(() => {
+                        //   handlePreAndNext('up')
+                        // }, 300)
+                        // onChangeSwiper('up')
+                        handlePreAndNext('up')
+                      }}
+                    >
+                      <img src={iconUp} />
+                      <span style={{ textAlign: 'center' }}>上一个</span>
+                    </div>
+                  </div>
+                )}
+                {popupData.activeIndex != data.itemList.length - 1 && (
+                  <div class={styles.btnsWrap}>
+                    <div
+                      class={styles.fullBtn}
+                      onClick={() => {
+                        // console.log('click down')
+                        // useThrottleFn(() => {
+                        //   console.log('click down pass')
+                        //   handlePreAndNext('down')
+                        // }, 300)
+                        // onChangeSwiper('down')
+                        handlePreAndNext('down')
+                      }}
+                    >
+                      <span style={{ textAlign: 'center' }}>下一个</span>
+                      <img src={iconDown} />
+                    </div>
+                  </div>
+                )}
+              </div>
+            )}
+          </Transition>
         </div>
 
         <div