Browse Source

Merge branch 'iteration-20240522-download' into online

lex 11 tháng trước cách đây
mục cha
commit
212c627f57

+ 1 - 1
public/version.json

@@ -1 +1 @@
-{"version":1716814081921}
+{ "version": 1716458790006 }

+ 285 - 10
src/views/attend-class/component/audio-pay.tsx

@@ -4,7 +4,8 @@ import {
   ref,
   nextTick,
   onMounted,
-  watch
+  watch,
+  onUnmounted
 } from 'vue';
 import styles from './audio.module.less';
 import iconplay from '../image/icon-pause.png';
@@ -40,17 +41,23 @@ export default defineComponent({
   setup(props, { emit, expose }) {
     const audioForms = reactive({
       paused: true,
+      speedInKbps: '',
       currentTimeNum: 0,
+      isOnline: false,
       currentTime: '00:00',
       durationNum: 0,
       duration: '00:00',
       showBar: true,
       afterMa3: true,
-      count: 0
+      count: 0,
+      previousBytesLoaded: 0,
+      previousTime: Date.now()
     });
     const canvas: any = ref();
     const audio: any = ref();
     let vudio: any = null;
+    // const audioSlider =
+    //   'audioSlider' + Date.now() + Math.floor(Math.random() * 100);
 
     // 切换音频播放
     const onToggleAudio = (e?: any) => {
@@ -147,9 +154,275 @@ export default defineComponent({
       }
     );
 
-    // onMounted(() => {
-    //   console.log(props.item, 'eeeee');
-    // });
+    const calculateSpeed = (element: any) => {
+      let previousBytesLoaded = 0;
+      let timer: any = null;
+      let previousTime = Date.now();
+      let buffterCatchArray = [] as any; // 缓存数据显示
+      let isWaiting = false;
+
+      // 缓存检测状态
+      let isBuffering = false;
+      // 缓存检测计时器
+      let bufferTimeout: any = null;
+      // 设定一个检测缓存停止的时间间隔,这里我们设置为2500毫秒(2秒)
+      const BUFFER_CHECK_INTERVAL = 2500;
+
+      function resetDownloadSpeed() {
+        timer = setTimeout(() => {
+          // displayElement.textContent = `视屏下载速度: 0 KB/s`;
+          audioForms.speedInKbps = `0 KB/s`;
+        }, 1500);
+      }
+
+      function buffterCatch(time = 0) {
+        // 设定一个计时器,检查是否在指定的时间内再次触发了progress事件
+        bufferTimeout = setTimeout(() => {
+          if (isBuffering) {
+            // 如果计时器到达且isBuffering仍为true,则认为缓存停止
+            console.log('停止缓存数据。');
+            isBuffering = false;
+            audioForms.speedInKbps = '';
+          }
+        }, time || BUFFER_CHECK_INTERVAL);
+      }
+
+      function resetBuffterCatch() {
+        // 如果有缓存检测计时器,则清除它
+        if (bufferTimeout) {
+          clearTimeout(bufferTimeout);
+        }
+        // 标记为正在缓存
+        isBuffering = true;
+
+        buffterCatch();
+      }
+
+      /**
+       * 格式化视屏播放有效时间 - 合并区间
+       * @param intervals [[], []]
+       * @example [[4, 8],[0, 4],[10, 30]]
+       * @returns [[0, 8], [10, 30]]
+       */
+      const formatEffectiveTime = (intervals: any[]) => {
+        const res: any = [];
+        intervals.sort((a, b) => a[0] - b[0]);
+        let prev = intervals[0];
+        for (let i = 1; i < intervals.length; i++) {
+          const cur = intervals[i];
+          if (prev[1] >= cur[0]) {
+            // 有重合
+            prev[1] = Math.max(cur[1], prev[1]);
+          } else {
+            // 不重合,prev推入res数组
+            res.push(prev);
+            prev = cur; // 更新 prev
+          }
+        }
+        res.push(prev);
+        return res;
+      };
+
+      // 获取视频总时长
+      let noData = false;
+      const onProgress = () => {
+        const duration = element.duration;
+        const currentTime = Date.now();
+        const buffered = element.buffered;
+        let currentBytesLoaded = 0;
+        const currentLength = 0;
+
+        // 计算视频已缓存的总时长
+        let cachedDuration = 0;
+        if (buffered.length > 0) {
+          for (let i = 0; i < buffered.length; i++) {
+            currentBytesLoaded += buffered.end(i) - buffered.start(i);
+            cachedDuration += buffered.end(i) - buffered.start(i);
+            buffterCatchArray = formatEffectiveTime([
+              ...buffterCatchArray,
+              [buffered.start(i), buffered.end(i)]
+            ]);
+          }
+          // for (let i = 0; i < buffered.length; i++) {
+          //   // 寻找当前时间之后最近的点
+          //   if (buffered.start(buffered.length - 1 - i) < element.currentTime) {
+          //     currentLength =
+          //       (buffered.end(buffered.length - 1 - i) / duration) * 100;
+          //     break;
+          //   }
+          // }
+          currentBytesLoaded *= element.duration * element.seekable.end(0); // 更精确地近似字节加载量
+        }
+
+        // 计算未缓存的时间段
+        const uncachedDuration = duration - cachedDuration;
+        let uncachedTime = true; // 没有缓存时间
+        buffterCatchArray.forEach((item: any) => {
+          if (element.currentTime >= item[0] && uncachedTime <= item[1]) {
+            uncachedTime = false;
+          }
+        });
+        // if (duration) {
+        //   const sliderDom: any = document.querySelector(
+        //     '#' + audioSlider + ' .n-slider'
+        //   );
+        //   if (sliderDom) {
+        //     sliderDom.style.setProperty(
+        //       '--catch-width',
+        //       uncachedDuration > 0 ? `${currentLength}%` : 'calc(100% + 17px)'
+        //     );
+        //   }
+        // }
+        console.log(
+          uncachedTime,
+          duration,
+          cachedDuration,
+          'duration',
+          buffterCatchArray,
+          element.currentTime,
+          currentLength + '%',
+          isWaiting,
+          currentBytesLoaded <= previousBytesLoaded
+        );
+
+        const isNoBuffer = currentBytesLoaded <= previousBytesLoaded;
+        // console.log(
+        //   'progress',
+        //   currentBytesLoaded,
+        //   previousBytesLoaded,
+        //   currentBytesLoaded > previousBytesLoaded
+        // );
+        // 计算未缓存的时间段
+        // const uncachedDuration = duration - cachedDuration;
+        // console.log(uncachedDuration, duration, cachedDuration, 'duration');
+        // 如果存在未缓存的时间段,可以根据具体情况做出相应处理
+        if (uncachedDuration > 0) {
+          if (currentBytesLoaded > previousBytesLoaded && audioForms.isOnline) {
+            const timeDiff = (currentTime - previousTime) / 1000; // 时间差转换为秒
+            const bytesDiff = currentBytesLoaded - previousBytesLoaded; // 字节差值
+            const speed = bytesDiff / timeDiff; // 字节每秒
+
+            if (!element.paused) {
+              const kbps = speed / 1024;
+              const speedInKbps = kbps.toFixed(2); // 转换为千字节每秒并保留两位小数
+              if (kbps > 1024) {
+                audioForms.speedInKbps = `${Number(
+                  (kbps / 1024).toFixed(2)
+                )} M/s`;
+              } else {
+                audioForms.speedInKbps = `${Number(speedInKbps)} KB/s`;
+              }
+
+              // 如果1秒钟没有返回就重置数据
+              clearTimeout(timer);
+              resetDownloadSpeed();
+            }
+
+            previousBytesLoaded = currentBytesLoaded;
+            previousTime = currentTime;
+            noData = false;
+          }
+
+          if (isNoBuffer && !uncachedTime) {
+            // 如果1秒钟没有返回就重置数据
+
+            if (!noData) {
+              clearTimeout(timer);
+              clearTimeout(bufferTimeout);
+              setTimeout(() => {
+                if (isBuffering) {
+                  // 如果计时器到达且isBuffering仍为true,则认为缓存停止
+                  console.log('停止缓存数据');
+                  isBuffering = false;
+                  audioForms.speedInKbps = '';
+                }
+              }, 800);
+            }
+
+            noData = true;
+          }
+
+          if (element.paused || !audioForms.isOnline) {
+            clearTimeout(timer);
+            audioForms.speedInKbps = '';
+          }
+
+          if (!isWaiting) {
+            resetBuffterCatch();
+          }
+        } else {
+          clearTimeout(timer);
+          buffterCatch(1000);
+        }
+      };
+
+      const onWaiting = () => {
+        console.log('waiting');
+        isWaiting = true;
+        let uncachedTime = true; // 没有缓存时间
+        buffterCatchArray.forEach((item: any) => {
+          if (element.currentTime >= item[0] && uncachedTime <= item[1]) {
+            uncachedTime = false;
+          }
+        });
+
+        if (!element.paused && audioForms.isOnline && uncachedTime) {
+          // 如果1秒钟没有返回就重置数据
+          clearTimeout(timer);
+          resetDownloadSpeed();
+        }
+
+        // 如果有缓存检测计时器,则清除它
+        if (bufferTimeout) {
+          clearTimeout(bufferTimeout);
+        }
+      };
+
+      const onCanplay = () => {
+        console.log('canplay');
+        isWaiting = false;
+        resetBuffterCatch();
+      };
+
+      const onPause = () => {
+        clearTimeout(timer);
+        // 如果有缓存检测计时器,则清除它
+        if (bufferTimeout) {
+          clearTimeout(bufferTimeout);
+        }
+        audioForms.speedInKbps = '';
+      };
+
+      element.removeEventListener('progress', onProgress);
+      element.removeEventListener('waiting', onWaiting);
+      element.removeEventListener('canplay', onCanplay);
+      element.removeEventListener('pause', onPause);
+
+      element.addEventListener('progress', onProgress);
+      element.addEventListener('waiting', onWaiting);
+      element.addEventListener('canplay', onCanplay);
+      element.addEventListener('pause', onPause);
+    };
+
+    const onChangeOnlineStatus = (val: any) => {
+      if (val.type === 'online') {
+        audioForms.isOnline = true;
+      } else if (val.type === 'offline') {
+        audioForms.isOnline = false;
+      }
+    };
+    onMounted(() => {
+      nextTick(() => {
+        calculateSpeed(audio.value);
+      });
+      window.addEventListener('online', onChangeOnlineStatus);
+      window.addEventListener('offline', onChangeOnlineStatus);
+    });
+
+    onUnmounted(() => {
+      window.removeEventListener('online', onChangeOnlineStatus);
+      window.removeEventListener('offline', onChangeOnlineStatus);
+    });
 
     expose({
       toggleHideControl
@@ -161,7 +434,7 @@ export default defineComponent({
           <audio
             ref={audio}
             crossorigin="anonymous"
-            src={props.item.content + '?time=1'}
+            src={props.item.content}
             onEnded={() => {
               audioForms.paused = true;
               emit('ended');
@@ -200,11 +473,7 @@ export default defineComponent({
                   emit('togglePlay', audioForms.paused);
                 };
               }
-
               emit('loadedmetadata', audio.value);
-            }}
-            onProgress={(e: any) => {
-              console.log(e, 'loadedmetadata onProgress');
             }}></audio>
 
           <canvas ref={canvas}></canvas>
@@ -259,6 +528,9 @@ export default defineComponent({
                 </div>
                 <div class={styles.actions}>
                   <div class={styles.actionWrap}>
+                    <div class={styles.downloadSpeed}>
+                      {audioForms.speedInKbps}
+                    </div>
                     <button class={styles.iconReplay} onClick={onReplay}>
                       <img src={iconReplay} />
                     </button>
@@ -286,6 +558,9 @@ export default defineComponent({
                     <button class={styles.iconReplay} onClick={onReplay}>
                       <img src={iconReplay} />
                     </button>
+                    <div class={styles.downloadSpeed}>
+                      {audioForms.speedInKbps}
+                    </div>
                   </div>
                 </div>
                 <div class={styles.actions}>

+ 177 - 147
src/views/attend-class/component/audio.module.less

@@ -1,147 +1,177 @@
-.audioWrap {
-  width: 100%;
-  height: 100%;
-  background-color: #fff;
-}
-
-.audioContainer {
-  position: relative;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  height: 100%;
-  padding: 0 240px;
-
-  &>div {
-    flex: 1;
-  }
-
-  .audio {
-    position: absolute;
-    top: 0;
-    opacity: 0;
-  }
-
-  .tempVudio {
-    position: absolute;
-    top: 0;
-    right: 0;
-    bottom: 0;
-    left: 0;
-    padding: 0 240px;
-  }
-
-  canvas {
-    width: 100%;
-    height: 100%;
-  }
-}
-
-.sectionAnimate {
-  opacity: 0;
-  pointer-events: none;
-  transform: translateY(126px);
-  transition: all .2s;
-}
-
-.controls {
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  width: 100%;
-  background: url('../image/../image/bg.png') no-repeat;
-  background-size: 100% 100%;
-  // backdrop-filter: blur(26px);
-  height: 120px;
-  padding: 0 40px !important;
-  transition: all 0.301s;
-  display: flex;
-  justify-content: center;
-  flex-direction: column;
-  .time {
-    display: flex;
-    justify-content: space-between;
-    color: #fff;
-    // font-size: 10px;
-    padding: 4px 20px 4px;
-    font-size: 24px;
-    font-weight: 600;
-    line-height: 33px;
-
-    &>div {
-      font-size: 20px !important;
-      color: rgba(255,255,255,0.8);
-    }
-
-    .line {
-      font-size: 12px;
-    }
-
-    :global {
-      .plyr__time+.plyr__time:before {
-        content: '';
-        margin-right: 0;
-      }
-    }
-  }
-}
-.tools{
-  display: flex;
-  justify-content: space-between;
-  padding: 0 10px;
-  margin-top: 10px;
-}
-.actions {
-  display: flex;
-  justify-content: space-between;
-  // width: 100%;
-  height: 100%;
-  color: #fff;
-  font-size: 12px;
-  align-items: center;
-
-  .actionWrap {
-    display: flex;
-    align-items: center;
-  }
-
-  .actionBtn {
-    width: 40px;
-    height: 40px;
-    background: transparent;
-    cursor: pointer;
-
-    &>img {
-      width: 100%;
-      height: 100%;
-    }
-  }
-
-
-  .iconReplay {
-    width: 40px;
-    height: 40px;
-    background-color: transparent;
-    cursor: pointer;
-    margin: 0 22px;
-
-    &>img {
-      width: 100%;
-      height: 100%;
-    }
-  }
-}
-
-.slider {
-  width: 100%;
-  padding-top: 6px;
-
-  :global {
-
-    .n-slider .n-slider-rail .n-slider-rail__fill,
-    .n-slider .n-slider-handles .n-slider-handle-wrapper {
-      transition: all .2s;
-    }
-  }
-}
+.audioWrap {
+  width: 100%;
+  height: 100%;
+  background-color: #fff;
+}
+
+.audioContainer {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+  padding: 0 240px;
+
+  &>div {
+    flex: 1;
+  }
+
+  .audio {
+    position: absolute;
+    top: 0;
+    opacity: 0;
+  }
+
+  .tempVudio {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    padding: 0 240px;
+  }
+
+  canvas {
+    width: 100%;
+    height: 100%;
+  }
+}
+
+.sectionAnimate {
+  opacity: 0;
+  pointer-events: none;
+  transform: translateY(126px);
+  transition: all .2s;
+}
+
+.controls {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  width: 100%;
+  background: url('../image/../image/bg.png') no-repeat;
+  background-size: 100% 100%;
+  // backdrop-filter: blur(26px);
+  height: 120px;
+  padding: 0 40px !important;
+  transition: all 0.301s;
+  display: flex;
+  justify-content: center;
+  flex-direction: column;
+
+  .time {
+    display: flex;
+    justify-content: space-between;
+    color: #fff;
+    // font-size: 10px;
+    padding: 4px 20px 4px;
+    font-size: 24px;
+    font-weight: 600;
+    line-height: 33px;
+
+    &>div {
+      font-size: 20px !important;
+      color: rgba(255, 255, 255, 0.8);
+    }
+
+    .line {
+      font-size: 12px;
+    }
+
+    :global {
+      .plyr__time+.plyr__time:before {
+        content: '';
+        margin-right: 0;
+      }
+    }
+  }
+}
+
+.tools {
+  display: flex;
+  justify-content: space-between;
+  padding: 0 10px;
+  margin-top: 10px;
+}
+
+.actions {
+  display: flex;
+  justify-content: space-between;
+  // width: 100%;
+  height: 100%;
+  color: #fff;
+  font-size: 12px;
+  align-items: center;
+
+  .actionWrap {
+    display: flex;
+    align-items: center;
+  }
+
+  .downloadSpeed {
+    font-weight: 600;
+    font-size: max(18px, 14Px);
+    color: #FFFFFF;
+    line-height: 25px;
+  }
+
+  .actionBtn {
+    width: 40px;
+    height: 40px;
+    background: transparent;
+    cursor: pointer;
+
+    &>img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+
+  .iconReplay {
+    width: 40px;
+    height: 40px;
+    background-color: transparent;
+    cursor: pointer;
+    margin: 0 22px;
+
+    &>img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+}
+
+.slider {
+  width: 100%;
+  padding-top: 6px;
+
+  :global {
+
+    .n-slider .n-slider-rail .n-slider-rail__fill,
+    .n-slider .n-slider-handles .n-slider-handle-wrapper {
+      transition: all .2s;
+    }
+
+    .n-slider .n-slider-rail .n-slider-rail__fill {
+      z-index: 1;
+    }
+
+    .n-slider-handle-wrapper {
+      z-index: 1 !important;
+    }
+
+    .n-slider-handles::after {
+      content: '';
+      width: var(--catch-width);
+      position: absolute;
+      border-radius: calc(var(--n-rail-height) / 2);
+      top: 0;
+      bottom: 0;
+      left: -8Px;
+      // right: -8Px;
+      background-color: #909090;
+    }
+  }
+}

+ 838 - 544
src/views/attend-class/component/video-play.tsx

@@ -1,544 +1,838 @@
-import {
-  defineComponent,
-  nextTick,
-  onMounted,
-  onUnmounted,
-  reactive,
-  toRefs,
-  watch
-} from 'vue';
-import TCPlayer from 'tcplayer.js';
-import 'tcplayer.js/dist/tcplayer.min.css';
-// import 'plyr/dist/plyr.css';
-// import Plyr from 'plyr';
-import { ref } from 'vue';
-import styles from './video.module.less';
-import iconplay from '../image/icon-pause.png';
-import iconpause from '../image/icon-play.png';
-// import iconReplay from '../image/icon-replay.png';
-import iconLoop from '../image/icon-loop.svg';
-import iconLoopActive from '../image/icon-loop-active.svg';
-import iconSpeed from '../image/icon-speed.png';
-import { NSlider } from 'naive-ui';
-
-export default defineComponent({
-  name: 'video-play',
-  props: {
-    item: {
-      type: Object,
-      default: () => {
-        return {};
-      }
-    },
-    showModel: {
-      type: Boolean,
-      default: false
-    },
-    isEmtry: {
-      type: Boolean,
-      default: false
-    },
-    imagePos: {
-      type: String,
-      default: 'left'
-    }
-  },
-  emits: [
-    'canplay',
-    'pause',
-    'togglePlay',
-    'ended',
-    'reset',
-    'error',
-    'close',
-    'loadedmetadata'
-  ],
-  setup(props, { emit, expose }) {
-    const { item, isEmtry } = toRefs(props);
-    const videoFroms = reactive({
-      paused: true,
-      currentTimeNum: 0,
-      currentTime: '00:00',
-      durationNum: 0,
-      duration: '00:00',
-      showBar: true,
-      showAction: true,
-      loop: false,
-      speedControl: false,
-      speedStyle: {
-        left: '1px'
-      },
-      defaultSpeed: 1 // 默认速度
-    });
-    const videoRef = ref();
-    const videoItem = ref();
-    const videoID = ref('video' + Date.now() + Math.floor(Math.random() * 100));
-
-    // 对时间进行格式化
-    const timeFormat = (num: number) => {
-      if (num > 0) {
-        const m = Math.floor(num / 60);
-        const s = num % 60;
-        return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
-      } else {
-        return '00:00';
-      }
-    };
-
-    // 如果视屏异常后,需要重新播放视屏
-    const onPlay = () => {
-      if (videoItem.value) {
-        videoItem.value.src(item.value.content);
-        emit('reset');
-      }
-    };
-
-    //
-    const toggleHideControl = (isShow: false) => {
-      videoFroms.showBar = isShow;
-      videoFroms.speedControl = false;
-    };
-
-    const onReplay = () => {
-      videoFroms.speedControl = false;
-      if (!videoItem.value) return;
-      videoItem.value.currentTime(0);
-    };
-
-    // 切换音频播放
-    const onToggleVideo = (e?: MouseEvent) => {
-      e?.stopPropagation();
-      if (videoFroms.paused) {
-        videoItem.value.play();
-        videoFroms.paused = false;
-      } else {
-        videoItem.value.pause();
-        videoFroms.paused = true;
-      }
-      emit('togglePlay', videoFroms.paused);
-    };
-
-    const videoTimer = null as any;
-    let videoTimerErrorCount = 0;
-    const handlePlayVideo = () => {
-      if (videoTimerErrorCount > 5) {
-        return;
-      }
-      clearTimeout(videoTimer);
-      nextTick(() => {
-        videoItem.value?.play().catch((err: any) => {
-          // console.log('🚀 ~ err:', err)
-          // videoTimer = setTimeout(() => {
-          //   if (err?.message?.includes('play()')) {
-          //     // emit('play');
-          //   }
-          //   handlePlayVideo();
-          // }, 1000);
-        });
-      });
-      videoTimerErrorCount++;
-    };
-
-    const __init = () => {
-      if (videoItem.value) {
-        videoItem.value.poster(props.item.coverImg); // 封面
-        videoItem.value.src(item.value.content); // url 播放地址
-        videoItem.value.playbackRate(videoFroms.defaultSpeed);
-        // 初步加载时
-        videoItem.value.one('loadedmetadata', () => {
-          // console.log(' Loading metadata');
-          videoItem.value.playbackRate(videoFroms.defaultSpeed);
-
-          // 获取时长
-          videoFroms.duration = timeFormat(
-            Math.round(videoItem.value.duration())
-          );
-          videoFroms.durationNum = videoItem.value.duration();
-
-          emit('canplay');
-          emit('loadedmetadata', videoItem.value);
-
-          if (item.value.autoPlay && videoItem.value) {
-            // videoItem.value?.play()
-            nextTick(() => {
-              videoTimerErrorCount = 0;
-              videoItem.value.currentTime(0);
-              nextTick(handlePlayVideo);
-            });
-          }
-        });
-
-        // 视频开始播放
-        videoItem.value.on('play', () => {
-          emit('close');
-          emit('canplay');
-        });
-
-        // 视频播放时加载
-        videoItem.value.on('timeupdate', () => {
-          videoFroms.currentTime = timeFormat(
-            Math.round(videoItem.value?.currentTime() || 0)
-          );
-          videoFroms.currentTimeNum = videoItem.value.currentTime();
-        });
-
-        // 视频播放结束
-        videoItem.value.on('ended', () => {
-          videoFroms.paused = true;
-          emit('ended');
-        });
-
-        //
-        videoItem.value.on('pause', () => {
-          videoFroms.paused = true;
-          emit('pause');
-        });
-
-        videoItem.value.on('playing', () => {
-          videoFroms.paused = false;
-        });
-
-        videoItem.value.on('canplay', (e: any) => {
-          // 获取时长
-          videoFroms.duration = timeFormat(
-            Math.round(videoItem.value.duration())
-          );
-          videoFroms.durationNum = videoItem.value.duration();
-          emit('canplay');
-        });
-
-        // 视频播放异常
-        videoItem.value.on('error', (e: any) => {
-          emit('error');
-          console.log(e, 'error');
-        });
-      }
-    };
-
-    onMounted(() => {
-      videoItem.value = TCPlayer(videoID.value, {
-        appID: '',
-        controls: false
-      }); // player-container-id 为播放器容器 ID,必须与 html 中一致
-
-      __init();
-    });
-    const stop = () => {
-      videoItem.value.currentTime(0);
-      videoItem.value.pause();
-    };
-
-    const pause = () => {
-      videoItem.value.pause();
-    };
-
-    onUnmounted(() => {
-      if (videoItem.value) {
-        videoItem.value.pause();
-        videoItem.value.src('');
-        videoItem.value.dispose();
-      }
-    });
-
-    watch(
-      () => props.item,
-      () => {
-        // console.log(item.value, 'value----');
-        videoItem.value.pause();
-        videoItem.value.currentTime(0);
-        if (item.value?.id) {
-          // videoItem.value.poster(props.item.coverImg); // 封面
-          // videoItem.value.src(item.value.content); // url 播放地址
-          __init();
-
-          videoFroms.paused = true;
-        }
-      }
-    );
-    watch(
-      () => props.showModel,
-      () => {
-        // console.log(props.showModel, 'props.showModel')
-        videoFroms.showAction = props.showModel;
-        videoFroms.speedControl = false;
-      }
-    );
-    expose({
-      onPlay,
-      stop,
-      pause,
-      // changePlayBtn,
-      toggleHideControl
-    });
-    return () => (
-      <div class={styles.videoWrap}>
-        <video
-          style={{ width: '100%', height: '100%' }}
-          ref={videoRef}
-          id={videoID.value}
-          preload="auto"
-          playsinline
-          webkit-playsinline
-          x5-video-player-type="h5"></video>
-        <div class={styles.videoPop}></div>
-
-        <div
-          class={[
-            styles.controls,
-            videoFroms.showAction ? '' : styles.sectionAnimate
-          ]}
-          onClick={(e: MouseEvent) => {
-            e.stopPropagation();
-            if (videoItem.value.paused()) return;
-            emit('close');
-            emit('reset');
-          }}>
-          <div class={styles.slider}>
-            <NSlider
-              value={videoFroms.currentTimeNum}
-              step={0.01}
-              max={videoFroms.durationNum}
-              tooltip={false}
-              onUpdate:value={(val: number) => {
-                videoFroms.speedControl = false;
-                videoItem.value.currentTime(val);
-                videoFroms.currentTimeNum = val;
-                videoFroms.currentTime = timeFormat(Math.round(val || 0));
-              }}
-            />
-          </div>
-          <div class={styles.tools}>
-            {props.imagePos === 'right' ? (
-              <>
-                <div class={styles.actions}>
-                  <div class={styles.actionWrap}>
-                    <div class={styles.time}>
-                      <div
-                        class="plyr__time plyr__time--current"
-                        aria-label="Current time">
-                        {videoFroms.currentTime}
-                      </div>
-                      <span class={styles.line}>/</span>
-                      <div
-                        class="plyr__time plyr__time--duration"
-                        aria-label="Duration">
-                        {videoFroms.duration}
-                      </div>
-                    </div>
-                  </div>
-                </div>
-                <div class={styles.actions}>
-                  <div class={styles.actionWrap}>
-                    <div
-                      class={styles.actionBtnSpeed}
-                      onClick={e => {
-                        e.stopPropagation();
-                        videoFroms.speedControl = !videoFroms.speedControl;
-                      }}>
-                      <img src={iconSpeed} />
-                      <div
-                        style={{
-                          display: videoFroms.speedControl ? 'block' : 'none'
-                        }}>
-                        <div
-                          class={styles.sliderPopup}
-                          onClick={(e: Event) => {
-                            e.stopPropagation();
-                          }}>
-                          <i
-                            class={styles.iconAdd}
-                            onClick={() => {
-                              if (videoFroms.defaultSpeed >= 1.5) {
-                                return;
-                              }
-
-                              if (videoItem.value) {
-                                videoFroms.defaultSpeed =
-                                  (videoFroms.defaultSpeed * 10 + 1) / 10;
-                                videoItem.value.playbackRate(
-                                  videoFroms.defaultSpeed
-                                );
-                              }
-                            }}></i>
-                          <NSlider
-                            value={videoFroms.defaultSpeed}
-                            step={0.1}
-                            max={1.5}
-                            min={0.5}
-                            vertical
-                            tooltip={false}
-                            onUpdate:value={(val: number) => {
-                              videoFroms.defaultSpeed = val;
-
-                              if (videoItem.value) {
-                                videoItem.value.playbackRate(
-                                  videoFroms.defaultSpeed
-                                );
-                              }
-                            }}>
-                            {{
-                              thumb: () => (
-                                <div class={styles.sliderPoint}>
-                                  {videoFroms.defaultSpeed}
-                                  <span>x</span>
-                                </div>
-                              )
-                            }}
-                          </NSlider>
-                          <i
-                            class={[styles.iconCut]}
-                            onClick={() => {
-                              if (videoFroms.defaultSpeed <= 0.5) {
-                                return;
-                              }
-                              if (videoItem.value) {
-                                videoFroms.defaultSpeed =
-                                  (videoFroms.defaultSpeed * 10 - 1) / 10;
-                                videoItem.value.playbackRate(
-                                  videoFroms.defaultSpeed
-                                );
-                              }
-                            }}></i>
-                        </div>
-                      </div>
-                    </div>
-                    <button class={styles.iconReplay} onClick={onReplay}>
-                      <img src={iconLoop} />
-                    </button>
-                    <div
-                      class={styles.actionBtn}
-                      onClick={() => {
-                        videoFroms.speedControl = false;
-                        onToggleVideo();
-                      }}>
-                      {videoFroms.paused ? (
-                        <img class={styles.playIcon} src={iconplay} />
-                      ) : (
-                        <img class={styles.playIcon} src={iconpause} />
-                      )}
-                    </div>
-                  </div>
-                </div>
-              </>
-            ) : (
-              <>
-                <div class={styles.actions}>
-                  <div class={styles.actionWrap}>
-                    <div
-                      class={styles.actionBtn}
-                      onClick={() => {
-                        videoFroms.speedControl = false;
-                        onToggleVideo();
-                      }}>
-                      {videoFroms.paused ? (
-                        <img class={styles.playIcon} src={iconplay} />
-                      ) : (
-                        <img class={styles.playIcon} src={iconpause} />
-                      )}
-                    </div>
-
-                    <button class={styles.iconReplay} onClick={onReplay}>
-                      <img src={iconLoop} />
-                    </button>
-
-                    <div
-                      class={styles.actionBtnSpeed}
-                      onClick={e => {
-                        e.stopPropagation();
-                        videoFroms.speedControl = !videoFroms.speedControl;
-                      }}>
-                      <img src={iconSpeed} />
-
-                      <div
-                        style={{
-                          display: videoFroms.speedControl ? 'block' : 'none'
-                        }}>
-                        <div
-                          class={styles.sliderPopup}
-                          onClick={(e: Event) => {
-                            e.stopPropagation();
-                          }}>
-                          <i
-                            class={styles.iconAdd}
-                            onClick={() => {
-                              if (videoFroms.defaultSpeed >= 1.5) {
-                                return;
-                              }
-
-                              if (videoItem.value) {
-                                videoFroms.defaultSpeed =
-                                  (videoFroms.defaultSpeed * 10 + 1) / 10;
-                                videoItem.value.playbackRate(
-                                  videoFroms.defaultSpeed
-                                );
-                              }
-                            }}></i>
-                          <NSlider
-                            value={videoFroms.defaultSpeed}
-                            step={0.1}
-                            max={1.5}
-                            min={0.5}
-                            vertical
-                            tooltip={false}
-                            onUpdate:value={(val: number) => {
-                              videoFroms.defaultSpeed = val;
-
-                              if (videoItem.value) {
-                                videoItem.value.playbackRate(
-                                  videoFroms.defaultSpeed
-                                );
-                              }
-                            }}>
-                            {{
-                              thumb: () => (
-                                <div class={styles.sliderPoint}>
-                                  {videoFroms.defaultSpeed}
-                                  <span>x</span>
-                                </div>
-                              )
-                            }}
-                          </NSlider>
-                          <i
-                            class={[styles.iconCut]}
-                            onClick={() => {
-                              if (videoFroms.defaultSpeed <= 0.5) {
-                                return;
-                              }
-                              if (videoItem.value) {
-                                videoFroms.defaultSpeed =
-                                  (videoFroms.defaultSpeed * 10 - 1) / 10;
-                                videoItem.value.playbackRate(
-                                  videoFroms.defaultSpeed
-                                );
-                              }
-                            }}></i>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-                <div class={styles.actions}>
-                  <div class={styles.actionWrap}>
-                    <div class={styles.time}>
-                      <div
-                        class="plyr__time plyr__time--current"
-                        aria-label="Current time">
-                        {videoFroms.currentTime}
-                      </div>
-                      <span class={styles.line}>/</span>
-                      <div
-                        class="plyr__time plyr__time--duration"
-                        aria-label="Duration">
-                        {videoFroms.duration}
-                      </div>
-                    </div>
-                  </div>
-                </div>
-              </>
-            )}
-          </div>
-        </div>
-      </div>
-    );
-  }
-});
+import {
+  defineComponent,
+  nextTick,
+  onMounted,
+  onUnmounted,
+  reactive,
+  toRefs,
+  watch
+} from 'vue';
+import TCPlayer from 'tcplayer.js';
+import 'tcplayer.js/dist/tcplayer.min.css';
+// import 'plyr/dist/plyr.css';
+// import Plyr from 'plyr';
+import { ref } from 'vue';
+import styles from './video.module.less';
+import iconplay from '../image/icon-pause.png';
+import iconpause from '../image/icon-play.png';
+// import iconReplay from '../image/icon-replay.png';
+import iconLoop from '../image/icon-loop.svg';
+import iconLoopActive from '../image/icon-loop-active.svg';
+import iconSpeed from '../image/icon-speed.png';
+import { NSlider } from 'naive-ui';
+
+export default defineComponent({
+  name: 'video-play',
+  props: {
+    item: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    },
+    showModel: {
+      type: Boolean,
+      default: false
+    },
+    isEmtry: {
+      type: Boolean,
+      default: false
+    },
+    imagePos: {
+      type: String,
+      default: 'left'
+    }
+  },
+  emits: [
+    'canplay',
+    'pause',
+    'togglePlay',
+    'ended',
+    'reset',
+    'error',
+    'close',
+    'loadedmetadata'
+  ],
+  setup(props, { emit, expose }) {
+    const { item, isEmtry } = toRefs(props);
+    const videoFroms = reactive({
+      paused: true,
+      speedInKbps: '',
+      isOnline: true, // 是否在线
+      currentTimeNum: 0,
+      currentTime: '00:00',
+      durationNum: 0,
+      duration: '00:00',
+      showBar: true,
+      showAction: true,
+      loop: false,
+      speedControl: false,
+      speedStyle: {
+        left: '1px'
+      },
+      defaultSpeed: 1 // 默认速度
+    });
+    const videoRef = ref();
+    const videoItem = ref();
+    const videoID = ref('video' + Date.now() + Math.floor(Math.random() * 100));
+    // const videoSlider =
+    //   'videoSlider' + Date.now() + Math.floor(Math.random() * 100);
+
+    // 对时间进行格式化
+    const timeFormat = (num: number) => {
+      if (num > 0) {
+        const m = Math.floor(num / 60);
+        const s = num % 60;
+        return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
+      } else {
+        return '00:00';
+      }
+    };
+
+    // 如果视屏异常后,需要重新播放视屏
+    const onPlay = () => {
+      if (videoItem.value) {
+        videoItem.value.src(item.value.content);
+        emit('reset');
+      }
+    };
+
+    //
+    const toggleHideControl = (isShow: false) => {
+      videoFroms.showBar = isShow;
+      videoFroms.speedControl = false;
+    };
+
+    const onReplay = () => {
+      videoFroms.speedControl = false;
+      if (!videoItem.value) return;
+      videoItem.value.currentTime(0);
+    };
+
+    // 切换音频播放
+    const onToggleVideo = (e?: MouseEvent) => {
+      e?.stopPropagation();
+      if (videoFroms.paused) {
+        videoItem.value.play();
+        videoFroms.paused = false;
+      } else {
+        videoItem.value.pause();
+        videoFroms.paused = true;
+      }
+      emit('togglePlay', videoFroms.paused);
+    };
+
+    const videoTimer = null as any;
+    let videoTimerErrorCount = 0;
+    const handlePlayVideo = () => {
+      if (videoTimerErrorCount > 5) {
+        return;
+      }
+      clearTimeout(videoTimer);
+      nextTick(() => {
+        videoItem.value?.play().catch((err: any) => {
+          // console.log('🚀 ~ err:', err)
+          // videoTimer = setTimeout(() => {
+          //   if (err?.message?.includes('play()')) {
+          //     // emit('play');
+          //   }
+          //   handlePlayVideo();
+          // }, 1000);
+        });
+      });
+      videoTimerErrorCount++;
+    };
+
+    const __init = () => {
+      if (videoItem.value) {
+        videoItem.value.poster(props.item.coverImg); // 封面
+        videoItem.value.src(item.value.content + '?t=4'); // url 播放地址
+        videoItem.value.playbackRate(videoFroms.defaultSpeed);
+        // 初步加载时
+        videoItem.value.one('loadedmetadata', () => {
+          // console.log(' Loading metadata');
+          videoItem.value.playbackRate(videoFroms.defaultSpeed);
+
+          // 获取时长
+          videoFroms.duration = timeFormat(
+            Math.round(videoItem.value.duration())
+          );
+          videoFroms.durationNum = videoItem.value.duration();
+
+          emit('canplay');
+          emit('loadedmetadata', videoItem.value);
+
+          if (item.value.autoPlay && videoItem.value) {
+            // videoItem.value?.play()
+            nextTick(() => {
+              videoTimerErrorCount = 0;
+              videoItem.value.currentTime(0);
+              nextTick(handlePlayVideo);
+            });
+          }
+        });
+
+        // 视频开始播放
+        videoItem.value.on('play', () => {
+          emit('close');
+          emit('canplay');
+        });
+
+        // 视频播放时加载
+        videoItem.value.on('timeupdate', () => {
+          videoFroms.currentTime = timeFormat(
+            Math.round(videoItem.value?.currentTime() || 0)
+          );
+          videoFroms.currentTimeNum = videoItem.value.currentTime();
+        });
+
+        // 视频播放结束
+        videoItem.value.on('ended', () => {
+          videoFroms.paused = true;
+          emit('ended');
+        });
+
+        //
+        videoItem.value.on('pause', () => {
+          videoFroms.paused = true;
+          emit('pause');
+        });
+
+        videoItem.value.on('playing', () => {
+          videoFroms.paused = false;
+        });
+
+        videoItem.value.on('canplay', (e: any) => {
+          // 获取时长
+          videoFroms.duration = timeFormat(
+            Math.round(videoItem.value.duration())
+          );
+          videoFroms.durationNum = videoItem.value.duration();
+          emit('canplay');
+        });
+
+        // 视频播放异常
+        videoItem.value.on('error', (e: any) => {
+          emit('error');
+          console.log(e, 'error');
+          // element.pause();
+          // videoItem.value?.pause();
+        });
+      }
+    };
+
+    const calculateSpeed = (element: any) => {
+      let previousBytesLoaded = 0;
+      let timer: any = null;
+      let previousTime = Date.now();
+      let buffterCatchArray = [] as any; // 缓存数据显示
+      let isWaiting = false;
+
+      // 缓存检测状态
+      let isBuffering = false;
+      // 缓存检测计时器
+      let bufferTimeout: any = null;
+      // 设定一个检测缓存停止的时间间隔,这里我们设置为2500毫秒(2秒)
+      const BUFFER_CHECK_INTERVAL = 2500;
+
+      function resetDownloadSpeed(time = 1500) {
+        timer = setTimeout(() => {
+          // displayElement.textContent = `视屏下载速度: 0 KB/s`;
+          videoFroms.speedInKbps = `0 KB/s`;
+        }, time);
+      }
+
+      function buffterCatch(time = 0) {
+        // 设定一个计时器,检查是否在指定的时间内再次触发了progress事件
+        bufferTimeout = setTimeout(() => {
+          if (isBuffering) {
+            // 如果计时器到达且isBuffering仍为true,则认为缓存停止
+            console.log('停止缓存数据。');
+            isBuffering = false;
+            videoFroms.speedInKbps = '';
+          }
+        }, time || BUFFER_CHECK_INTERVAL);
+      }
+
+      function resetBuffterCatch() {
+        // 如果有缓存检测计时器,则清除它
+        if (bufferTimeout) {
+          clearTimeout(bufferTimeout);
+        }
+        // 标记为正在缓存
+        isBuffering = true;
+
+        buffterCatch();
+      }
+
+      /**
+       * 格式化视屏播放有效时间 - 合并区间
+       * @param intervals [[], []]
+       * @example [[4, 8],[0, 4],[10, 30]]
+       * @returns [[0, 8], [10, 30]]
+       */
+      const formatEffectiveTime = (intervals: any[]) => {
+        const res: any = [];
+        intervals.sort((a, b) => a[0] - b[0]);
+        let prev = intervals[0];
+        for (let i = 1; i < intervals.length; i++) {
+          const cur = intervals[i];
+          if (prev[1] >= cur[0]) {
+            // 有重合
+            prev[1] = Math.max(cur[1], prev[1]);
+          } else {
+            // 不重合,prev推入res数组
+            res.push(prev);
+            prev = cur; // 更新 prev
+          }
+        }
+        res.push(prev);
+        return res;
+      };
+
+      // 获取视频总时长
+      let noData = false;
+
+      const onProgress = () => {
+        const duration = element.duration;
+        const currentTime = Date.now();
+        const buffered = element.buffered;
+        let currentBytesLoaded = 0;
+        const currentLength = 0;
+
+        // 计算视频已缓存的总时长
+        let cachedDuration = 0;
+        if (buffered.length > 0) {
+          for (let i = 0; i < buffered.length; i++) {
+            currentBytesLoaded += buffered.end(i) - buffered.start(i);
+            cachedDuration += buffered.end(i) - buffered.start(i);
+            // console.log(buffered.end(i), buffered.start(i), 'bufferd');
+            buffterCatchArray = formatEffectiveTime([
+              ...buffterCatchArray,
+              [buffered.start(i), buffered.end(i)]
+            ]);
+          }
+          // for (let i = 0; i < buffered.length; i++) {
+          //   // 寻找当前时间之后最近的点
+          //   if (buffered.start(buffered.length - 1 - i) < element.currentTime) {
+          //     cachedDuration += buffered.end(i) - buffered.start(i);
+          //     currentLength =
+          //       (buffered.end(buffered.length - 1 - i) / duration) * 100;
+          //     break;
+          //   }
+          // }
+          currentBytesLoaded *= element.duration * element.seekable.end(0); // 更精确地近似字节加载量
+        }
+
+        // 计算未缓存的时间段
+        const uncachedDuration = duration - cachedDuration;
+        let uncachedTime = true; // 没有缓存时间
+        buffterCatchArray.forEach((item: any) => {
+          if (element.currentTime >= item[0] && uncachedTime <= item[1]) {
+            uncachedTime = false;
+          }
+        });
+
+        // if (duration) {
+        //   const sliderDom: any = document.querySelector(
+        //     '#' + videoSlider + ' .n-slider'
+        //   );
+        //   if (sliderDom) {
+        //     sliderDom.style.setProperty(
+        //       '--catch-width',
+        //       uncachedDuration > 0 ? `${currentLength}%` : 'calc(100% + 17px)'
+        //     );
+        //   }
+
+        // }
+        console.log(
+          uncachedTime,
+          duration,
+          cachedDuration,
+          'duration',
+          buffterCatchArray,
+          element.currentTime,
+          currentLength + '%',
+          isWaiting,
+          currentBytesLoaded <= previousBytesLoaded
+        );
+
+        const isNoBuffer = currentBytesLoaded <= previousBytesLoaded;
+        // 如果存在未缓存的时间段,可以根据具体情况做出相应处理
+        if (uncachedDuration > 0) {
+          // console.log(
+          //   'progress',
+          //   currentBytesLoaded > previousBytesLoaded,
+          //   currentBytesLoaded,
+          //   previousBytesLoaded,
+          //   '------ 加载中'
+          // );
+          if (currentBytesLoaded > previousBytesLoaded && videoFroms.isOnline) {
+            const timeDiff = (currentTime - previousTime) / 1000; // 时间差转换为秒
+            const bytesDiff = currentBytesLoaded - previousBytesLoaded; // 字节差值
+            const speed = bytesDiff / timeDiff; // 字节每秒
+            // console.log(timeDiff, bytesDiff, speed);
+            // console.log(element.paused, 'element.paused');
+            if (!element.paused) {
+              const kbps = speed / 1024;
+              const speedInKbps = kbps.toFixed(2); // 转换为千字节每秒并保留两位小数
+              if (kbps > 1024) {
+                videoFroms.speedInKbps = `${Number(
+                  (kbps / 1024).toFixed(2)
+                )} M/s`;
+              } else {
+                videoFroms.speedInKbps = `${Number(speedInKbps)} KB/s`;
+              }
+
+              // 如果1秒钟没有返回就重置数据
+              clearTimeout(timer);
+              resetDownloadSpeed();
+            }
+
+            previousBytesLoaded = currentBytesLoaded;
+            previousTime = currentTime;
+            noData = false;
+          }
+          if (isNoBuffer && !uncachedTime) {
+            // 如果1秒钟没有返回就重置数据
+            if (!noData) {
+              clearTimeout(timer);
+              clearTimeout(bufferTimeout);
+              setTimeout(() => {
+                if (isBuffering) {
+                  // 如果计时器到达且isBuffering仍为true,则认为缓存停止
+                  console.log('停止缓存数据-。');
+                  isBuffering = false;
+                  videoFroms.speedInKbps = '';
+                }
+              }, 800);
+            }
+
+            noData = true;
+          }
+          if (element.paused || !videoFroms.isOnline) {
+            clearTimeout(timer);
+            videoFroms.speedInKbps = '';
+          }
+
+          if (!isWaiting) {
+            resetBuffterCatch();
+          }
+        } else {
+          clearTimeout(timer);
+          buffterCatch(1000);
+        }
+      };
+
+      const onWaiting = () => {
+        console.log('waiting');
+        isWaiting = true;
+        let uncachedTime = true; // 没有缓存时间
+        buffterCatchArray.forEach((item: any) => {
+          if (element.currentTime >= item[0] && uncachedTime <= item[1]) {
+            uncachedTime = false;
+          }
+        });
+
+        if (!element.paused && videoFroms.isOnline && uncachedTime) {
+          // 如果1秒钟没有返回就重置数据
+          clearTimeout(timer);
+          resetDownloadSpeed();
+        }
+
+        // 如果有缓存检测计时器,则清除它
+        if (bufferTimeout) {
+          clearTimeout(bufferTimeout);
+        }
+      };
+
+      const onCanplay = () => {
+        console.log('canplay');
+        isWaiting = false;
+        resetBuffterCatch();
+      };
+
+      const onPause = () => {
+        clearTimeout(timer);
+        // 如果有缓存检测计时器,则清除它
+        if (bufferTimeout) {
+          clearTimeout(bufferTimeout);
+        }
+        videoFroms.speedInKbps = '';
+      };
+
+      element.removeEventListener('progress', onProgress);
+      element.removeEventListener('waiting', onWaiting);
+      element.removeEventListener('canplay', onCanplay);
+      element.removeEventListener('pause', onPause);
+      // if (type !== 'remove') {
+      element.addEventListener('progress', onProgress);
+      element.addEventListener('waiting', onWaiting);
+      element.addEventListener('canplay', onCanplay);
+      element.addEventListener('pause', onPause);
+      // } else {
+      //   element.removeEventListener('progress', onProgress);
+      //   element.removeEventListener('waiting', onWaiting);
+      //   element.removeEventListener('canplay', onCanplay);
+      //   element.removeEventListener('pause', onPause);
+      // }
+    };
+
+    const onChangeOnlineStatus = (val: any) => {
+      if (val.type === 'online') {
+        videoFroms.isOnline = true;
+        // const currentTime = videoItem.value.currentTime();
+        // videoItem.value.load();
+        // videoItem.value.currentTime(currentTime);
+      } else if (val.type === 'offline') {
+        videoFroms.isOnline = false;
+      }
+    };
+    onMounted(() => {
+      videoItem.value = TCPlayer(videoID.value, {
+        appID: '',
+        controls: false
+      }); // player-container-id 为播放器容器 ID,必须与 html 中一致
+
+      __init();
+
+      nextTick(() => {
+        calculateSpeed(videoRef.value);
+      });
+
+      window.addEventListener('online', onChangeOnlineStatus);
+      window.addEventListener('offline', onChangeOnlineStatus);
+    });
+    const stop = () => {
+      videoItem.value.currentTime(0);
+      videoItem.value.pause();
+    };
+
+    const pause = () => {
+      videoItem.value.pause();
+    };
+
+    onUnmounted(() => {
+      if (videoItem.value) {
+        videoItem.value.pause();
+        videoItem.value.src('');
+        videoItem.value.dispose();
+      }
+
+      window.removeEventListener('online', onChangeOnlineStatus);
+      window.removeEventListener('offline', onChangeOnlineStatus);
+    });
+
+    watch(
+      () => props.item,
+      () => {
+        videoItem.value.pause();
+        videoItem.value.currentTime(0);
+        if (item.value?.id) {
+          // videoItem.value.poster(props.item.coverImg); // 封面
+          // videoItem.value.src(item.value.content); // url 播放地址
+          __init();
+
+          videoFroms.paused = true;
+        }
+        nextTick(() => {
+          calculateSpeed(videoRef.value);
+        });
+      }
+    );
+    watch(
+      () => props.showModel,
+      () => {
+        // console.log(props.showModel, 'props.showModel')
+        videoFroms.showAction = props.showModel;
+        videoFroms.speedControl = false;
+      }
+    );
+    expose({
+      onPlay,
+      stop,
+      pause,
+      // changePlayBtn,
+      toggleHideControl
+    });
+    return () => (
+      <div class={styles.videoWrap}>
+        <video
+          style={{ width: '100%', height: '100%' }}
+          ref={videoRef}
+          id={videoID.value}
+          preload="auto"
+          playsinline
+          webkit-playsinline
+          x5-video-player-type="h5"></video>
+        <div class={styles.videoPop}></div>
+
+        <div
+          class={[
+            styles.controls,
+            videoFroms.showAction ? '' : styles.sectionAnimate
+          ]}
+          onClick={(e: MouseEvent) => {
+            e.stopPropagation();
+            if (videoItem.value.paused()) return;
+            emit('close');
+            emit('reset');
+          }}>
+          <div class={styles.slider}>
+            <NSlider
+              value={videoFroms.currentTimeNum}
+              step={0.01}
+              max={videoFroms.durationNum}
+              tooltip={false}
+              onUpdate:value={(val: number) => {
+                videoFroms.speedControl = false;
+                videoItem.value.currentTime(val);
+                videoFroms.currentTimeNum = val;
+                videoFroms.currentTime = timeFormat(Math.round(val || 0));
+              }}
+            />
+          </div>
+          <div class={styles.tools}>
+            {props.imagePos === 'right' ? (
+              <>
+                <div class={styles.actions}>
+                  <div class={styles.actionWrap}>
+                    <div class={styles.time}>
+                      <div
+                        class="plyr__time plyr__time--current"
+                        aria-label="Current time">
+                        {videoFroms.currentTime}
+                      </div>
+                      <span class={styles.line}>/</span>
+                      <div
+                        class="plyr__time plyr__time--duration"
+                        aria-label="Duration">
+                        {videoFroms.duration}
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <div class={styles.actions}>
+                  <div class={styles.actionWrap}>
+                    <div
+                      class={styles.downloadSpeed}
+                      style={{ paddingRight: '20px' }}>
+                      {videoFroms.speedInKbps}
+                    </div>
+                    <div
+                      class={styles.actionBtnSpeed}
+                      onClick={e => {
+                        e.stopPropagation();
+                        videoFroms.speedControl = !videoFroms.speedControl;
+                      }}>
+                      <img src={iconSpeed} />
+                      <div
+                        style={{
+                          display: videoFroms.speedControl ? 'block' : 'none'
+                        }}>
+                        <div
+                          class={styles.sliderPopup}
+                          onClick={(e: Event) => {
+                            e.stopPropagation();
+                          }}>
+                          <i
+                            class={styles.iconAdd}
+                            onClick={() => {
+                              if (videoFroms.defaultSpeed >= 1.5) {
+                                return;
+                              }
+
+                              if (videoItem.value) {
+                                videoFroms.defaultSpeed =
+                                  (videoFroms.defaultSpeed * 10 + 1) / 10;
+                                videoItem.value.playbackRate(
+                                  videoFroms.defaultSpeed
+                                );
+                              }
+                            }}></i>
+                          <NSlider
+                            value={videoFroms.defaultSpeed}
+                            step={0.1}
+                            max={1.5}
+                            min={0.5}
+                            vertical
+                            tooltip={false}
+                            onUpdate:value={(val: number) => {
+                              videoFroms.defaultSpeed = val;
+
+                              if (videoItem.value) {
+                                videoItem.value.playbackRate(
+                                  videoFroms.defaultSpeed
+                                );
+                              }
+                            }}>
+                            {{
+                              thumb: () => (
+                                <div class={styles.sliderPoint}>
+                                  {videoFroms.defaultSpeed}
+                                  <span>x</span>
+                                </div>
+                              )
+                            }}
+                          </NSlider>
+                          <i
+                            class={[styles.iconCut]}
+                            onClick={() => {
+                              if (videoFroms.defaultSpeed <= 0.5) {
+                                return;
+                              }
+                              if (videoItem.value) {
+                                videoFroms.defaultSpeed =
+                                  (videoFroms.defaultSpeed * 10 - 1) / 10;
+                                videoItem.value.playbackRate(
+                                  videoFroms.defaultSpeed
+                                );
+                              }
+                            }}></i>
+                        </div>
+                      </div>
+                    </div>
+                    <button class={styles.iconReplay} onClick={onReplay}>
+                      <img src={iconLoop} />
+                    </button>
+                    <div
+                      class={styles.actionBtn}
+                      onClick={() => {
+                        videoFroms.speedControl = false;
+                        onToggleVideo();
+                      }}>
+                      {videoFroms.paused ? (
+                        <img class={styles.playIcon} src={iconplay} />
+                      ) : (
+                        <img class={styles.playIcon} src={iconpause} />
+                      )}
+                    </div>
+                  </div>
+                </div>
+              </>
+            ) : (
+              <>
+                <div class={styles.actions}>
+                  <div class={styles.actionWrap}>
+                    <div
+                      class={styles.actionBtn}
+                      onClick={() => {
+                        videoFroms.speedControl = false;
+                        onToggleVideo();
+                      }}>
+                      {videoFroms.paused ? (
+                        <img class={styles.playIcon} src={iconplay} />
+                      ) : (
+                        <img class={styles.playIcon} src={iconpause} />
+                      )}
+                    </div>
+
+                    <button class={styles.iconReplay} onClick={onReplay}>
+                      <img src={iconLoop} />
+                    </button>
+
+                    <div
+                      class={styles.actionBtnSpeed}
+                      onClick={e => {
+                        e.stopPropagation();
+                        videoFroms.speedControl = !videoFroms.speedControl;
+                      }}>
+                      <img src={iconSpeed} />
+
+                      <div
+                        style={{
+                          display: videoFroms.speedControl ? 'block' : 'none'
+                        }}>
+                        <div
+                          class={styles.sliderPopup}
+                          onClick={(e: Event) => {
+                            e.stopPropagation();
+                          }}>
+                          <i
+                            class={styles.iconAdd}
+                            onClick={() => {
+                              if (videoFroms.defaultSpeed >= 1.5) {
+                                return;
+                              }
+
+                              if (videoItem.value) {
+                                videoFroms.defaultSpeed =
+                                  (videoFroms.defaultSpeed * 10 + 1) / 10;
+                                videoItem.value.playbackRate(
+                                  videoFroms.defaultSpeed
+                                );
+                              }
+                            }}></i>
+                          <NSlider
+                            value={videoFroms.defaultSpeed}
+                            step={0.1}
+                            max={1.5}
+                            min={0.5}
+                            vertical
+                            tooltip={false}
+                            onUpdate:value={(val: number) => {
+                              videoFroms.defaultSpeed = val;
+
+                              if (videoItem.value) {
+                                videoItem.value.playbackRate(
+                                  videoFroms.defaultSpeed
+                                );
+                              }
+                            }}>
+                            {{
+                              thumb: () => (
+                                <div class={styles.sliderPoint}>
+                                  {videoFroms.defaultSpeed}
+                                  <span>x</span>
+                                </div>
+                              )
+                            }}
+                          </NSlider>
+                          <i
+                            class={[styles.iconCut]}
+                            onClick={() => {
+                              if (videoFroms.defaultSpeed <= 0.5) {
+                                return;
+                              }
+                              if (videoItem.value) {
+                                videoFroms.defaultSpeed =
+                                  (videoFroms.defaultSpeed * 10 - 1) / 10;
+                                videoItem.value.playbackRate(
+                                  videoFroms.defaultSpeed
+                                );
+                              }
+                            }}></i>
+                        </div>
+                      </div>
+                    </div>
+
+                    <div class={styles.downloadSpeed}>
+                      {videoFroms.speedInKbps}
+                    </div>
+                  </div>
+                </div>
+                <div class={styles.actions}>
+                  <div class={styles.actionWrap}>
+                    <div class={styles.time}>
+                      <div
+                        class="plyr__time plyr__time--current"
+                        aria-label="Current time">
+                        {videoFroms.currentTime}
+                      </div>
+                      <span class={styles.line}>/</span>
+                      <div
+                        class="plyr__time plyr__time--duration"
+                        aria-label="Duration">
+                        {videoFroms.duration}
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </>
+            )}
+          </div>
+        </div>
+      </div>
+    );
+  }
+});

+ 383 - 353
src/views/attend-class/component/video.module.less

@@ -1,353 +1,383 @@
-.videoWrap {
-  width: 100%;
-  height: 100%;
-  position: relative;
-
-  .videoPop {
-    position: absolute;
-    inset: 0;
-  }
-
-  :global {
-
-    .vjs-poster,
-    .vjs-text-track-display {
-      cursor: default !important;
-    }
-  }
-
-  .sectionAnimate {
-    opacity: 0;
-    pointer-events: none;
-    transform: translateY(126px);
-    transition: all .2s;
-  }
-
-  .controls {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    width: 100%;
-    background: url('../image/../image/bg.png') no-repeat;
-    background-size: 100% 100%;
-    // backdrop-filter: blur(26px);
-    height: 120px;
-    padding: 0 40px !important;
-    transition: all 0.301s;
-    display: flex;
-    justify-content: center;
-    flex-direction: column;
-
-    .time {
-      display: flex;
-      justify-content: space-between;
-      color: #fff;
-      // font-size: 10px;
-      padding: 4px 20px 4px;
-      font-size: 24px;
-      font-weight: 600;
-      line-height: 33px;
-
-      &>div {
-        font-size: 20px !important;
-        color: rgba(255,255,255,0.8);
-      }
-
-      .line {
-        font-size: 20px;
-      }
-
-      :global {
-        .plyr__time+.plyr__time:before {
-          content: '';
-          margin-right: 0;
-        }
-      }
-    }
-  }
-  .tools{
-    display: flex;
-    justify-content: space-between;
-    padding: 0 10px;
-    margin-top: 10px;
-  }
-  .actions {
-    display: flex;
-    justify-content: space-between;
-    // width: 100%;
-    height: 100%;
-    color: #fff;
-    font-size: 12px;
-    align-items: center;
-
-    .actionWrap {
-      display: flex;
-      align-items: center;
-    }
-
-    .actionBtn {
-      width: 40px;
-      height: 40px;
-      background: transparent;
-      cursor: pointer;
-
-      &>img {
-        width: 100%;
-        height: 100%;
-      }
-    }
-
-    .actionBtnSpeed {
-      position: relative;
-      width: 40px;
-      height: 40px;
-      background-color: transparent;
-      cursor: pointer;
-
-      &>img {
-        width: 100%;
-        height: 100%;
-      }
-    }
-
-
-    .iconReplay {
-      width: 40px;
-      height: 40px;
-      background-color: transparent;
-      cursor: pointer;
-      margin: 0 22px;
-
-      &>img {
-        width: 100%;
-        height: 100%;
-      }
-    }
-  }
-
-  .slider {
-    width: 100%;
-    padding-top: 6px;
-
-    :global {
-
-      .n-slider .n-slider-rail .n-slider-rail__fill,
-      .n-slider .n-slider-handles .n-slider-handle-wrapper {
-        transition: all .2s;
-      }
-    }
-  }
-
-
-}
-
-
-.sliderPopup {
-  position: absolute;
-  z-index: 9999;
-  left: -10px;
-  bottom: 55px;
-  display: flex;
-  align-items: center;
-  flex-direction: column;
-  height: 252px;
-  width: 59px;
-  padding: 12Px 0 15Px;
-  background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAI4AAAJcCAMAAAAYSmw3AAAAaVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnbPKNAAAAI3RSTlOzAAUJiqaplqF6V0c4nSevpJN+GAZtX1EQGpqDPTQVc2QhqyTybJ0AAAKuSURBVHja7NRJVsJQAAXRRxMg9BwgGWX/2xT0KCKII5M/uHcFNaqMntm25+mm2uVf7KrN9NxuR8885ozX9Sw9mNXr8Z85p2WV3lTL0+uc1SK9Wqxe5HTz9G7e/ZbTZBDN05xJnYHUk8ec4yGDORx/5hz3GdD+eJ8zOWRQh8ldTp2B1d9zmgyuueV0KUD3lTNPAeafOasUYfWRc1qkCIvTe84yhVhec8ZVClGNLznrFGM9yvAHvKkvObMUYzbKNgXZpk1B2pxTkHOmKcg0mxRkk2ImeFVll4IUFQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAG3twIAAAAAAA5P/aCKqqqqqqqqqqqqqqqtJ+nSQ3CgVRFH2XjwCjHnWoCTXe/yJrUlEljCz7R9riD/Ks4E7yRaRzzjnnnHPOOeecc845zZWQuUZKyEhTJWSqXAnJtVFCNtopITvNlJCZKJSMAvGmZLwhJkrGBJElM4SjDEGtRNQgCJWSUAUQMFYSxoAASiWgBBCQxnFN/uWw1eC2/M+h0cAa7nNCqUGVoZPDaqEBLVZ0c9iXGky552MOodFAmkA/B64axBUe5jA56OUOE+6IjvFCL7UY0yG6Ql3oZYo68DwH1qfmXS/w3pzWfCQeyGb1Ja8K/ZKiyi/1LOMB8UTWFzcH4+wBnhCRsmNEDbFErPW3e3ZEE9FCrm+p6THkWHu29BlyjD1XIthyCEt9YUMEaw7tFz0XIthzaMvnz3YUew7tQZ86ZsSx53Ce6hP5mqcsOfE9eSCePYdVpQeWhhqEwX6hnrLFQFjcej2HFgthchupY3rGRNjcCt2pVtgIo1lxV7PHSFjtl/orP2Ml7E7HuTRvTtiJH9G2/AiRlD84lRqqt/0KYQAAAABJRU5ErkJggg==') no-repeat top center;
-  background-size: contain;
-
-  .iconAdd,
-  .iconCut {
-    display: inline-block;
-    width: 24Px;
-    height: 24Px;
-    background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAABMlBMVEUAAAAkqP8ckv8zu/8op/81v/8ajv8elP82v/8rrv8elP4npv8yuf8elP8elf81v/8sr/8elf8vuv8dlv8srv8jn/8zu/8wtf82wP8oqP8npf8Zjf8mpP8Zjf8hmv8bj/40vP8wtP81wP8Zjf41vv8sr/8jnf8aj/0zuf8dlf81vv8rq/4kof4vsf8imP8aj/////8usv8qrP4jn/4inP4dlP4yuf8el/4srv8pqv4gmf4bkf40vf8ssP4wtP8lov4koP4xt/8npv4mpP4zu/8hmv4ajv4ckv4wtv4op/58x/5+y/6Cxv5/zf57xP6Dx/6BxP7u+P/t9/6a1f6Bz/56w/5iuP5zwP5Bpv4yoP6x3/6w3f6d2/6Wzv5tyf5pwv5nvv5svP5Rsv5Pr/5Mq/43p/49/opPAAAAMHRSTlMACoRW+Pf38dTUurGDWEhHR0cjI/b28fHr6+vr19fQ0Lq6sbGoqKiolZWGhoZSUlKk1yinAAAB+UlEQVQ4y43S6VbaQACG4S9E9h3c933fatISI4sWYxUjAVQQ99r2/m+hGQfGMJNEHw4M+XhPTn4AjpQNLM1MhUJTM0uBrAQ/qbXQmUNoLQUve3Nngrk9uMnHjlzF8hAkw0cewklwAr98BDBg47uvjYH72oNx1/WuHfdOFgqFu3qj8bfgiT13Pmy3DbNhtr3jcB5UTNe7DdM0f6s69dRqPemcGG13dV3t2G2737b3bW2+3gUxaxh/LHJfo+eWxLcGZxa2lKqqHcsyuyph2G8aq71rdpL/yar9tW5ZbypTJ3Fd5a0C0oiiKG9W51hhaKzwRiRkFeJfVRFiQRbbx4IaiWvivo14RUBjcY9julKlKtXXGvVA4ofexWuV/T6NyWrf9b6raxZMYuhnX8s9brFgyBE33eOmI5646PN6DBZMIHpIXJCPl0vqnjT3vYuXj9+jiB8KLkl8Je5xbB0Irt5jcd9C5utxBtKwph1oBDtprPH7sASsaFpJs5XYSeMSv68AyJR4NyS+EeYMbNFvHBrzaxTEDj8/k/iZX3fwboGby4/N5mOZGxdA5cbKnxrLoUcunxMn596nDCZx8okEHNZ/+FrHgIRfmwBHHj31MCpDkJsvntqvov0eOOdzcCNHioKIDC/p5aCzDC6n4UdKby5GxoPB8cjiZlrCoP+meld2tFTGwgAAAABJRU5ErkJggg==') no-repeat center;
-    background-size: contain;
-    flex-shrink: 0;
-    cursor: pointer;
-
-    &.disabled {
-      opacity: 0.7;
-    }
-  }
-
-  .iconCut {
-    background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAABGlBMVEUAAAAkqP8zu/8op/8ajv8aj/8yuP8elP4npv4dlP8elP8yuP8elP81v/8sr/8jnf8Zjf81v/81v/8jn/8zu/8wtf8gl/82wP8oqP8npf83wP8qqv8mpP8Zjf81vf8tsv8hmv8bj/41wP8qqv8lof8Zjf41vv8sr/8jnf8aj/0zuf8dlf8bjf00vP8xt/8zvf8stv8dmf8dkv8srv8trf8qq/7///8tsP4jn/4us/8srv8gmf4lov4yuf8dlf4wt/8inf4hm/4ck/4bkf4wtP8koP4mpf4fl/4op/40vP8ajv4pqf4zuv98x/5+y/6Bxf40vv80u/6Dx/6a1f5owP5PsP4/p/5zwP4oo/4yoP7u+P7u9/5svP5it/4TfkURAAAANXRSTlMACoT49+66uoaDWFJSR0dHR/j29vHx8evr69fX19fQ0NDQsbGxsaioqKiVlYZYWCMjIyP29l0yDtwAAAGnSURBVDjLjc6HVsIwGAXg27I37r33nkilWloXCGK17OH7v4YpSKSQtPnS5v7n5J6cYIyUPt1bC/j9gbW907QEN+kjf2mE/ygNnvhWacJWHCzXoWem0DUmRAPPHIEoxkSeXETgcPJEaGSx88Rxr6ZpaqOtcUVARTWt0DBNs06yoBGTSd99NV8gXeKnwDV/hYGQqrZNoqerfCH0xVXVaNr3GqqbOGybhlG3LKunG642QaQMXW9altkmaRhk42UKwKGu6xXLquteDgFpRlGUutV8VbzMSEj1h0ZL8ZZC+FVYGLsPwnaxSudWrcJQa9HCKpbehqp3TFVaWIKPzl12uUsLPviKQx12uUMLPiwWi+XBzHsGOR9YxEqZqn0y1MrUCnYy5Uyfd+4gnBEWRlK8nIR0kxN0KwEHuVzWnrNeeQAgmRWUBLEh1t2ALXYvJIa+oEg3iIHLuRdPc5f4c/6Sz+fd/3NQct6DjBHHj66O4SC7dWWMOZv94Jg9w4SL4Ps3WeRzZvACLLH1d8Iu/Od6DDyJ/emvEdP7CbiREvL28sLU1MLytpyQ4PQLtc9vYI2HRk0AAAAASUVORK5CYII=') no-repeat center;
-    background-size: contain;
-  }
-
-  .sliderPoint {
-    background: #FFFFFF;
-    box-shadow: 0px 2px 4px 0px rgba(102, 102, 102, 0.77);
-    border-radius: 14px;
-    font-size: 14Px;
-    font-weight: 500;
-    height: 22Px;
-    color: #198CFE;
-    min-width: 40px;
-    text-align: center;
-    vertical-align: text-bottom;
-
-    span {
-      font-size: 12Px;
-    }
-  }
-
-  :global {
-    .n-slider {
-      margin: 7px 0;
-      padding: 0;
-    }
-  }
-}
-
-// .videoWrap {
-//   width: 100%;
-//   height: 100%;
-//   --plyr-color-main: #198CFE;
-//   --plyr-range-track-height: 4px;
-//   --plyr-tooltip-radius: 3px;
-//   --plyr-range-thumb-height: 18px;
-
-
-//   :global {
-//     .plyr--video {
-//       width: 100%;
-//       height: 100%;
-//     }
-
-//     .plyr__time {
-//       display: block !important;
-//     }
-
-//     .plyr__video-wrapper {
-//       pointer-events: none;
-//     }
-//   }
-// }
-
-// :global(.bottomFixed).controls {
-//   width: 100%;
-//   background: rgba(0, 0, 0, 0.6);
-//   backdrop-filter: blur(26px);
-//   height: 150px;
-//   padding: 0 250px 0 40px !important;
-//   transition: all 0.5s;
-
-//   .time {
-//     display: flex;
-//     justify-content: space-between;
-//     color: #fff;
-//     // font-size: 10px;
-//     padding: 4px 0 4px 20px;
-//     font-size: 24px;
-//     font-weight: 600;
-//     line-height: 33px;
-
-//     &>div {
-//       font-size: 24px !important;
-//     }
-
-//     .line {
-//       font-size: 12px;
-//     }
-
-//     :global {
-//       .plyr__time+.plyr__time:before {
-//         content: '';
-//         margin-right: 0;
-//       }
-//     }
-//   }
-
-//   .slider {
-//     width: 100%;
-//     padding: 0 20px 0 12px;
-
-//     :global {
-//       .van-slider__button {
-//         background: var(--van-primary);
-//       }
-
-//       .van-loading {
-//         width: 100%;
-//         height: 100%;
-//       }
-//     }
-//   }
-
-//   .actions {
-//     display: flex;
-//     justify-content: space-between;
-//     color: #fff;
-//     font-size: 12px;
-//     padding-right: 20px;
-//     align-items: center;
-
-//     .actionWrap {
-//       display: flex;
-//     }
-
-//     .actionBtn {
-//       display: flex;
-//       // width: 43px !important;
-//       // height: 43px !important;
-//       width: 82px;
-//       height: 82px;
-//       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 {
-//       background-color: transparent;
-//       width: 43px;
-//       height: 42px;
-//       padding: 0;
-//       cursor: pointer;
-
-//       :global {
-//         .loop {
-//           display: block;
-//         }
-
-//         .loopActive {
-//           display: none;
-//         }
-//       }
-//     }
-//   }
-// }
+.videoWrap {
+  width: 100%;
+  height: 100%;
+  position: relative;
+
+  .videoPop {
+    position: absolute;
+    inset: 0;
+  }
+
+  :global {
+
+    .vjs-poster,
+    .vjs-text-track-display {
+      cursor: default !important;
+    }
+  }
+
+  .sectionAnimate {
+    opacity: 0;
+    pointer-events: none;
+    transform: translateY(126px);
+    transition: all .2s;
+  }
+
+  .controls {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    width: 100%;
+    background: url('../image/../image/bg.png') no-repeat;
+    background-size: 100% 100%;
+    // backdrop-filter: blur(26px);
+    height: 120px;
+    padding: 0 40px !important;
+    transition: all 0.301s;
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+
+    .time {
+      display: flex;
+      justify-content: space-between;
+      color: #fff;
+      // font-size: 10px;
+      padding: 4px 20px 4px;
+      font-size: 24px;
+      font-weight: 600;
+      line-height: 33px;
+
+      &>div {
+        font-size: 20px !important;
+        color: rgba(255, 255, 255, 0.8);
+      }
+
+      .line {
+        font-size: 20px;
+      }
+
+      :global {
+        .plyr__time+.plyr__time:before {
+          content: '';
+          margin-right: 0;
+        }
+      }
+    }
+  }
+
+  .tools {
+    display: flex;
+    justify-content: space-between;
+    padding: 0 10px;
+    margin-top: 10px;
+  }
+
+  .actions {
+    display: flex;
+    justify-content: space-between;
+    // width: 100%;
+    height: 100%;
+    color: #fff;
+    font-size: 12px;
+    align-items: center;
+
+    .actionWrap {
+      display: flex;
+      align-items: center;
+    }
+
+    .downloadSpeed {
+      font-weight: 600;
+      font-size: max(18px, 14Px);
+      color: #FFFFFF;
+      line-height: 25px;
+      padding-left: 22px;
+    }
+
+    .actionBtn {
+      width: 40px;
+      height: 40px;
+      background: transparent;
+      cursor: pointer;
+
+      &>img {
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+    .actionBtnSpeed {
+      position: relative;
+      width: 40px;
+      height: 40px;
+      background-color: transparent;
+      cursor: pointer;
+
+      &>img {
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+
+    .iconReplay {
+      width: 40px;
+      height: 40px;
+      background-color: transparent;
+      cursor: pointer;
+      margin: 0 22px;
+
+      &>img {
+        width: 100%;
+        height: 100%;
+      }
+    }
+  }
+
+  .slider {
+    width: 100%;
+    padding-top: 6px;
+
+    :global {
+
+      .n-slider .n-slider-rail .n-slider-rail__fill,
+      .n-slider .n-slider-handles .n-slider-handle-wrapper {
+        transition: all .2s;
+      }
+
+      .n-slider .n-slider-rail .n-slider-rail__fill {
+        z-index: 1;
+      }
+
+      .n-slider-handle-wrapper {
+        z-index: 1 !important;
+      }
+
+      .n-slider-handles::after {
+        content: '';
+        width: var(--catch-width);
+        position: absolute;
+        border-radius: calc(var(--n-rail-height) / 2);
+        top: 0;
+        bottom: 0;
+        left: -8Px;
+        // right: -8Px;
+        background-color: #909090;
+      }
+    }
+  }
+
+
+}
+
+
+.sliderPopup {
+  position: absolute;
+  z-index: 9999;
+  left: -10px;
+  bottom: 55px;
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+  height: 252px;
+  width: 59px;
+  padding: 12Px 0 15Px;
+  background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAI4AAAJcCAMAAAAYSmw3AAAAaVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnbPKNAAAAI3RSTlOzAAUJiqaplqF6V0c4nSevpJN+GAZtX1EQGpqDPTQVc2QhqyTybJ0AAAKuSURBVHja7NRJVsJQAAXRRxMg9BwgGWX/2xT0KCKII5M/uHcFNaqMntm25+mm2uVf7KrN9NxuR8885ozX9Sw9mNXr8Z85p2WV3lTL0+uc1SK9Wqxe5HTz9G7e/ZbTZBDN05xJnYHUk8ec4yGDORx/5hz3GdD+eJ8zOWRQh8ldTp2B1d9zmgyuueV0KUD3lTNPAeafOasUYfWRc1qkCIvTe84yhVhec8ZVClGNLznrFGM9yvAHvKkvObMUYzbKNgXZpk1B2pxTkHOmKcg0mxRkk2ImeFVll4IUFQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAG3twIAAAAAAA5P/aCKqqqqqqqqqqqqqqqtJ+nSQ3CgVRFH2XjwCjHnWoCTXe/yJrUlEljCz7R9riD/Ks4E7yRaRzzjnnnHPOOeecc845zZWQuUZKyEhTJWSqXAnJtVFCNtopITvNlJCZKJSMAvGmZLwhJkrGBJElM4SjDEGtRNQgCJWSUAUQMFYSxoAASiWgBBCQxnFN/uWw1eC2/M+h0cAa7nNCqUGVoZPDaqEBLVZ0c9iXGky552MOodFAmkA/B64axBUe5jA56OUOE+6IjvFCL7UY0yG6Ql3oZYo68DwH1qfmXS/w3pzWfCQeyGb1Ja8K/ZKiyi/1LOMB8UTWFzcH4+wBnhCRsmNEDbFErPW3e3ZEE9FCrm+p6THkWHu29BlyjD1XIthyCEt9YUMEaw7tFz0XIthzaMvnz3YUew7tQZ86ZsSx53Ce6hP5mqcsOfE9eSCePYdVpQeWhhqEwX6hnrLFQFjcej2HFgthchupY3rGRNjcCt2pVtgIo1lxV7PHSFjtl/orP2Ml7E7HuTRvTtiJH9G2/AiRlD84lRqqt/0KYQAAAABJRU5ErkJggg==') no-repeat top center;
+  background-size: contain;
+
+  .iconAdd,
+  .iconCut {
+    display: inline-block;
+    width: 24Px;
+    height: 24Px;
+    background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAABMlBMVEUAAAAkqP8ckv8zu/8op/81v/8ajv8elP82v/8rrv8elP4npv8yuf8elP8elf81v/8sr/8elf8vuv8dlv8srv8jn/8zu/8wtf82wP8oqP8npf8Zjf8mpP8Zjf8hmv8bj/40vP8wtP81wP8Zjf41vv8sr/8jnf8aj/0zuf8dlf81vv8rq/4kof4vsf8imP8aj/////8usv8qrP4jn/4inP4dlP4yuf8el/4srv8pqv4gmf4bkf40vf8ssP4wtP8lov4koP4xt/8npv4mpP4zu/8hmv4ajv4ckv4wtv4op/58x/5+y/6Cxv5/zf57xP6Dx/6BxP7u+P/t9/6a1f6Bz/56w/5iuP5zwP5Bpv4yoP6x3/6w3f6d2/6Wzv5tyf5pwv5nvv5svP5Rsv5Pr/5Mq/43p/49/opPAAAAMHRSTlMACoRW+Pf38dTUurGDWEhHR0cjI/b28fHr6+vr19fQ0Lq6sbGoqKiolZWGhoZSUlKk1yinAAAB+UlEQVQ4y43S6VbaQACG4S9E9h3c933fatISI4sWYxUjAVQQ99r2/m+hGQfGMJNEHw4M+XhPTn4AjpQNLM1MhUJTM0uBrAQ/qbXQmUNoLQUve3Nngrk9uMnHjlzF8hAkw0cewklwAr98BDBg47uvjYH72oNx1/WuHfdOFgqFu3qj8bfgiT13Pmy3DbNhtr3jcB5UTNe7DdM0f6s69dRqPemcGG13dV3t2G2737b3bW2+3gUxaxh/LHJfo+eWxLcGZxa2lKqqHcsyuyph2G8aq71rdpL/yar9tW5ZbypTJ3Fd5a0C0oiiKG9W51hhaKzwRiRkFeJfVRFiQRbbx4IaiWvivo14RUBjcY9julKlKtXXGvVA4ofexWuV/T6NyWrf9b6raxZMYuhnX8s9brFgyBE33eOmI5646PN6DBZMIHpIXJCPl0vqnjT3vYuXj9+jiB8KLkl8Je5xbB0Irt5jcd9C5utxBtKwph1oBDtprPH7sASsaFpJs5XYSeMSv68AyJR4NyS+EeYMbNFvHBrzaxTEDj8/k/iZX3fwboGby4/N5mOZGxdA5cbKnxrLoUcunxMn596nDCZx8okEHNZ/+FrHgIRfmwBHHj31MCpDkJsvntqvov0eOOdzcCNHioKIDC/p5aCzDC6n4UdKby5GxoPB8cjiZlrCoP+meld2tFTGwgAAAABJRU5ErkJggg==') no-repeat center;
+    background-size: contain;
+    flex-shrink: 0;
+    cursor: pointer;
+
+    &.disabled {
+      opacity: 0.7;
+    }
+  }
+
+  .iconCut {
+    background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAABGlBMVEUAAAAkqP8zu/8op/8ajv8aj/8yuP8elP4npv4dlP8elP8yuP8elP81v/8sr/8jnf8Zjf81v/81v/8jn/8zu/8wtf8gl/82wP8oqP8npf83wP8qqv8mpP8Zjf81vf8tsv8hmv8bj/41wP8qqv8lof8Zjf41vv8sr/8jnf8aj/0zuf8dlf8bjf00vP8xt/8zvf8stv8dmf8dkv8srv8trf8qq/7///8tsP4jn/4us/8srv8gmf4lov4yuf8dlf4wt/8inf4hm/4ck/4bkf4wtP8koP4mpf4fl/4op/40vP8ajv4pqf4zuv98x/5+y/6Bxf40vv80u/6Dx/6a1f5owP5PsP4/p/5zwP4oo/4yoP7u+P7u9/5svP5it/4TfkURAAAANXRSTlMACoT49+66uoaDWFJSR0dHR/j29vHx8evr69fX19fQ0NDQsbGxsaioqKiVlYZYWCMjIyP29l0yDtwAAAGnSURBVDjLjc6HVsIwGAXg27I37r33nkilWloXCGK17OH7v4YpSKSQtPnS5v7n5J6cYIyUPt1bC/j9gbW907QEN+kjf2mE/ygNnvhWacJWHCzXoWem0DUmRAPPHIEoxkSeXETgcPJEaGSx88Rxr6ZpaqOtcUVARTWt0DBNs06yoBGTSd99NV8gXeKnwDV/hYGQqrZNoqerfCH0xVXVaNr3GqqbOGybhlG3LKunG642QaQMXW9altkmaRhk42UKwKGu6xXLquteDgFpRlGUutV8VbzMSEj1h0ZL8ZZC+FVYGLsPwnaxSudWrcJQa9HCKpbehqp3TFVaWIKPzl12uUsLPviKQx12uUMLPiwWi+XBzHsGOR9YxEqZqn0y1MrUCnYy5Uyfd+4gnBEWRlK8nIR0kxN0KwEHuVzWnrNeeQAgmRWUBLEh1t2ALXYvJIa+oEg3iIHLuRdPc5f4c/6Sz+fd/3NQct6DjBHHj66O4SC7dWWMOZv94Jg9w4SL4Ps3WeRzZvACLLH1d8Iu/Od6DDyJ/emvEdP7CbiREvL28sLU1MLytpyQ4PQLtc9vYI2HRk0AAAAASUVORK5CYII=') no-repeat center;
+    background-size: contain;
+  }
+
+  .sliderPoint {
+    background: #FFFFFF;
+    box-shadow: 0px 2px 4px 0px rgba(102, 102, 102, 0.77);
+    border-radius: 14px;
+    font-size: 14Px;
+    font-weight: 500;
+    height: 22Px;
+    color: #198CFE;
+    min-width: 40px;
+    text-align: center;
+    vertical-align: text-bottom;
+
+    span {
+      font-size: 12Px;
+    }
+  }
+
+  :global {
+    .n-slider {
+      margin: 7px 0;
+      padding: 0;
+    }
+  }
+}
+
+// .videoWrap {
+//   width: 100%;
+//   height: 100%;
+//   --plyr-color-main: #198CFE;
+//   --plyr-range-track-height: 4px;
+//   --plyr-tooltip-radius: 3px;
+//   --plyr-range-thumb-height: 18px;
+
+
+//   :global {
+//     .plyr--video {
+//       width: 100%;
+//       height: 100%;
+//     }
+
+//     .plyr__time {
+//       display: block !important;
+//     }
+
+//     .plyr__video-wrapper {
+//       pointer-events: none;
+//     }
+//   }
+// }
+
+// :global(.bottomFixed).controls {
+//   width: 100%;
+//   background: rgba(0, 0, 0, 0.6);
+//   backdrop-filter: blur(26px);
+//   height: 150px;
+//   padding: 0 250px 0 40px !important;
+//   transition: all 0.5s;
+
+//   .time {
+//     display: flex;
+//     justify-content: space-between;
+//     color: #fff;
+//     // font-size: 10px;
+//     padding: 4px 0 4px 20px;
+//     font-size: 24px;
+//     font-weight: 600;
+//     line-height: 33px;
+
+//     &>div {
+//       font-size: 24px !important;
+//     }
+
+//     .line {
+//       font-size: 12px;
+//     }
+
+//     :global {
+//       .plyr__time+.plyr__time:before {
+//         content: '';
+//         margin-right: 0;
+//       }
+//     }
+//   }
+
+//   .slider {
+//     width: 100%;
+//     padding: 0 20px 0 12px;
+
+//     :global {
+//       .van-slider__button {
+//         background: var(--van-primary);
+//       }
+
+//       .van-loading {
+//         width: 100%;
+//         height: 100%;
+//       }
+//     }
+//   }
+
+//   .actions {
+//     display: flex;
+//     justify-content: space-between;
+//     color: #fff;
+//     font-size: 12px;
+//     padding-right: 20px;
+//     align-items: center;
+
+//     .actionWrap {
+//       display: flex;
+//     }
+
+//     .actionBtn {
+//       display: flex;
+//       // width: 43px !important;
+//       // height: 43px !important;
+//       width: 82px;
+//       height: 82px;
+//       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 {
+//       background-color: transparent;
+//       width: 43px;
+//       height: 42px;
+//       padding: 0;
+//       cursor: pointer;
+
+//       :global {
+//         .loop {
+//           display: block;
+//         }
+
+//         .loopActive {
+//           display: none;
+//         }
+//       }
+//     }
+//   }
+// }

+ 2445 - 2422
src/views/attend-class/index.tsx

@@ -1,2422 +1,2445 @@
-import {
-  defineComponent,
-  onMounted,
-  reactive,
-  onUnmounted,
-  ref,
-  Transition,
-  computed,
-  nextTick,
-  watch,
-  toRef
-} from 'vue';
-import styles from './index.module.less';
-import 'plyr/dist/plyr.css';
-import MusicScore from './component/musicScore';
-// import iconChange from './image/icon-change.png';
-// import iconMenu from './image/icon-menu.png';
-// import iconUp from './image/icon-up.png';
-// import iconDown from './image/icon-down.png';
-// import iconNote from './image/icon-note.png';
-// import iconWhiteboard from './image/icon-whiteboard.png';
-// import iconAssignHomework from './image/icon-assignHomework.png';
-// import iconClose from './image/icon-close.png';
-// import iconOverPreivew from './image/icon-over-preview.png';
-import { Vue3Lottie } from 'vue3-lottie';
-import playLoadData from './datas/data.json';
-// import Moveable from 'moveable';
-import VideoPlay from './component/video-play';
-import {
-  useMessage,
-  NDrawer,
-  NDrawerContent,
-  NModal,
-  NSpace,
-  NButton,
-  NCollapse,
-  NCollapseItem,
-  NTooltip
-} from 'naive-ui';
-import CardType from '@/components/card-type';
-import Pen from './component/tools/pen';
-import AudioPay from './component/audio-pay';
-import TrainSettings from './model/train-settings';
-import { useRoute } from 'vue-router';
-import {
-  api_teacherChapterLessonCoursewareDetail,
-  courseScheduleUpdate,
-  lessonCoursewareDetail,
-  lessonPreTrainingPage,
-  queryCourseware
-} from '../prepare-lessons/api';
-import { vaildUrl } from '/src/utils/urlUtils';
-import TimerMeter from '/src/components/timerMeter';
-import Metronome from '/src/components/Metronome';
-import { iframeDislableKeyboard, px2vw } from '/src/utils';
-import PlaceholderTone from '/src/components/layout/modals/placeholderTone';
-import { state as globalState } from '/src/state';
-import Chapter from './model/chapter';
-import { useRouter } from 'vue-router';
-import { useUserStore } from '@/store/modules/users';
-import iconNote from './new-image/icon-note.png';
-import iconWhite from './new-image/icon-white.png';
-import timerMeterClose from './image/close.png';
-import rightIconEnd from './image/right_icon1.png';
-import rightIconArrange from './image/right_icon2.png';
-import rightIconPostil from './image/right_icon3.png';
-import rightIconWhiteboard from './image/right_icon4.png';
-import rightIconMetronome from './image/right_icon5.png';
-import rightIconTuner from './image/right_icon6.png';
-import rightIconTimer from './image/right_icon7.png';
-import rightIconCall from './image/right_icon10.png';
-import rightIconPackUp from './image/right_icon11.png';
-import leftIconPackUp from './image/right_icon8.png';
-import rightIconMusic from './image/right_icon9.png';
-import bottomIconSwitch from './image/bottom_icon1.png';
-import bottomIconResource from './image/bottom_icon2.png';
-import bottomIconPre from './image/bottom_icon3.png';
-import bottomIconNext from './image/bottom_icon4.png';
-import rightIconTool from './image/right_icon12.png';
-import rightHideIcon from './image/right_hide_icon.png';
-import leftHideIcon from './image/left_hide_icon.png';
-import SelectResources from '../prepare-lessons/model/select-resources';
-import { getStudentAfterWork, getStudentList } from '../studentList/api';
-import TheNoticeBar from '/src/components/TheNoticeBar';
-import ClassWork from './model/class-work';
-import SelectClass from './model/select-class';
-import SourceList from './model/source-list';
-import RhythmModal from './component/rhythm-modal';
-import InstruemntDetail from '/src/views/prepare-lessons/model/source-instrument/detail';
-import TheotyDetail from '/src/views/prepare-lessons/model/source-knowledge/detail';
-import MusicDetail from '/src/views/prepare-lessons/model/source-music/detail';
-import ListenModal from '/src/components/card-preview/listen-modal';
-import Train from '../prepare-lessons/components/lesson-main/train';
-import ResourceMain from '../prepare-lessons/components/resource-main';
-import { useResizeObserver } from '@vueuse/core';
-import { storage } from '/src/utils/storage';
-import { ACCESS_TOKEN_ADMIN } from '/src/store/mutation-types';
-import useDrag from '@/hooks/useDrag';
-import { getGuidanceShow } from '@/hooks/useDrag/useDragGuidance';
-import Dragbom from '@/hooks/useDrag/dragbom';
-import { api_cousseScheduleDetail } from '/src/api/user';
-
-export type ToolType = 'init' | 'pen' | 'whiteboard' | 'call';
-export type ToolItem = {
-  type: ToolType;
-  name: string;
-  icon: string;
-};
-
-export default defineComponent({
-  name: 'CoursewarePlay',
-  props: {
-    type: {
-      type: String,
-      default: ''
-    },
-    courseId: {
-      type: String,
-      default: ''
-    },
-    instrumentId: {
-      type: [String, Number],
-      default: ''
-    },
-    // 教材编号
-    lessonCourseId: {
-      type: [String, Number],
-      default: ''
-    },
-    detailId: {
-      type: String,
-      default: ''
-    },
-    // 班级编号
-    classGroupId: {
-      type: String,
-      default: ''
-    },
-    // 上课记录编号
-    classId: {
-      type: String,
-      defaault: ''
-    },
-    preStudentNum: {
-      type: [String, Number],
-      default: ''
-    }
-  },
-  emits: ['close'],
-  setup(props, { emit }) {
-    const message = useMessage();
-    const route = useRoute();
-    const router = useRouter();
-    const users = useUserStore();
-    /** 设置播放容器 16:9 */
-    const parentContainer = reactive({
-      width: '100vw'
-    });
-    // const NPopoverRef = ref();
-    // const setContainer = () => {
-    //   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;
-    //   } else {
-    //     parentContainer.width = width + 'px';
-    //   }
-    // };
-    const handleInit = (type = 0) => {
-      //设置容器16:9
-      // setContainer();
-    };
-    handleInit();
-    onUnmounted(() => {
-      handleInit(1);
-    });
-
-    const data = reactive({
-      type: 'class' as '' | 'preview' | 'class', // 预览类型
-      courseId: '' as any, // 课件编号
-      instrumentId: '' as any, // 声部编号
-      lessonCourseId: '' as any, // 教材编号
-      lessonCoursewareDetailId: '' as any, // 章节
-      lessonCoursewareSubjectList: [] as any, // 教材上的声部
-      detailId: '' as any, // 编号 - 课程编号
-      classGroupId: '' as any, // 上课时需要 班级编号
-      classId: '' as any, // 上课编号
-      preStudentNum: '' as any, // 班上学生
-      // detail: null,
-      knowledgePointList: [] as any,
-      itemList: [] as any,
-      // showHead: true,
-      // isCourse: false,
-      // isRecordPlay: false,
-      videoRefs: {} as any[],
-      audioRefs: {} as any[],
-      modelAttendStatus: false, // 布置作业提示弹窗
-      modalAttendMessage: '本节课未设置课后作业,是否继续?',
-      modelTrainStatus: false, // 训练设置
-      selectClassStatus: false, // 选择课件
-      homeworkStatus: true, // 布置作业完成时
-      removeVisiable: false,
-      nextEndShow: false,
-      removeTitle: '',
-      removeContent: '',
-      removeCourseStatus: false, // 是否布置作业
-
-      teacherChapterName: '',
-      lessonPreTrainingId: null,
-      selectResourceStatus: false,
-      videoState: 'init' as 'init' | 'play',
-      videoItemRef: null as any,
-      animationState: 'start' as 'start' | 'end'
-    });
-    const activeData = reactive({
-      isAutoPlay: false, // 是否自动播放
-      nowTime: 0,
-      model: true, // 遮罩
-      isAnimation: true, // 是否动画
-      // videoBtns: true, // 视频
-      // currentTime: 0,
-      // duration: 0,
-      timer: null as any,
-      item: null as any
-    });
-
-    // 键盘事件监听状态
-    const listenerKeyUpState = ref(false);
-
-    const getDetail = async () => {
-      try {
-        const res = await api_teacherChapterLessonCoursewareDetail(
-          data.courseId
-        );
-
-        data.teacherChapterName = res.data.name || '';
-        // 布置的作业编号
-        data.lessonPreTrainingId = res.data.lessonPreTrainingId;
-        // activeData.isAutoPlay = res.data.autoPlay || false; // 自动播放
-        const tempRows = res.data.chapterKnowledgeList || [];
-        const temp: any = [];
-        const allItem: any = [];
-        tempRows.forEach((row: any, index: number) => {
-          if (!Array.isArray(row.chapterKnowledgeMaterialList)) {
-            return;
-          }
-          const childList: any[] = [];
-          row.chapterKnowledgeMaterialList.forEach((child: any) => {
-            if (!child.removeFlag) {
-              childList.push({
-                id: child.id,
-                materialId: child.bizId,
-                coverImg: child.bizInfo.coverImg,
-                type: child.type,
-                title: child.bizInfo.name,
-                dataJson: child.dataJson,
-                isCollect: !!child.favoriteFlag,
-                isSelected: child.source === 'PLATFORM' ? true : false,
-                content: child.bizInfo.content,
-                parentIndex: index
-              });
-            }
-          });
-          temp.push({
-            title: row.name,
-            list: childList
-          });
-          allItem.push(...childList);
-        });
-
-        data.knowledgePointList = temp;
-        data.itemList = allItem?.map((m: any) => {
-          return {
-            ...m,
-            iframeRef: null,
-            videoEle: null,
-            audioEle: null,
-            autoPlay: res.data.autoPlay || false, //加载完成是否自动播放
-            isprepare: false, // 视频是否加载完成
-            isRender: false // 是否渲染了
-          };
-        });
-        setTimeout(() => {
-          data.animationState = 'end';
-        }, 500);
-      } catch (e) {
-        //
-        console.log(e);
-      }
-    };
-
-    const showModalBeat = ref(false);
-    const showModalTone = ref(false);
-    const showModalTime = ref(false);
-    // ifram事件处理
-    const iframeHandle = (ev: MessageEvent) => {
-      // console.log(ev.data?.api, ev.data, 'ev.data');
-      if (ev.data?.api === 'headerTogge') {
-        activeData.model =
-          ev.data.show || (ev.data.playState == 'play' ? false : true);
-      }
-
-      //
-      if (ev.data?.api === 'onAttendToggleMenu') {
-        activeData.model = !activeData.model;
-      }
-
-      if (ev.data?.api === 'api_fingerPreView') {
-        clearInterval(activeData.timer);
-        activeData.model = !ev.data.state;
-      }
-      //
-      if (ev.data?.api === 'documentBodyKeyup') {
-        if (ev.data?.code === 'ArrowUp') {
-          setModalOpen();
-          handlePreAndNext('up');
-        }
-
-        if (ev.data?.code === 'ArrowDown') {
-          setModalOpen();
-          handlePreAndNext('down');
-        }
-      }
-
-      // 点名返回
-      if (ev.data?.api === 'callBack') {
-        closeStudyTool();
-        studyData.callShow = false;
-      }
-
-      if (ev.data?.api === 'onLogin') {
-        const documentDom: any = document;
-        documentDom.exitFullscreen
-          ? documentDom.exitFullscreen()
-          : documentDom.mozCancelFullScreen
-          ? documentDom.mozCancelFullScreen()
-          : documentDom.webkitExitFullscreen
-          ? documentDom.webkitExitFullscreen()
-          : '';
-        users.logout();
-        router.replace('/login');
-      }
-    };
-
-    onMounted(async () => {
-      // initMoveable();
-      const query = route.query;
-      // console.log(query, props.preStudentNum, '学生人数');
-      // 先取参数,
-      data.type = props.type || (query.type as any);
-      data.courseId = props.courseId || query.courseId;
-      data.instrumentId = props.instrumentId || query.instrumentId;
-      data.detailId = props.detailId || query.detailId;
-      data.lessonCourseId = props.lessonCourseId || query.lessonCourseId;
-      data.classGroupId = props.classGroupId || query.classGroupId;
-      data.classId = props.classId || query.classId;
-      data.preStudentNum = props.preStudentNum || query.preStudentNum;
-      window.addEventListener('message', iframeHandle);
-
-      if (data.classId) {
-        const res = await api_cousseScheduleDetail(data.classId);
-        data.courseId = res.data.useChapterLessonCoursewareId;
-      }
-
-      getDetail();
-      getLessonCoursewareDetail();
-      if (data.type === 'preview') {
-        // 预览隐藏点名和布置作业
-        const hideListIds = [2, 10];
-        let index = rightList.length - 1;
-        while (index >= 0) {
-          if (hideListIds.includes(rightList[index].id)) {
-            console.log(index);
-            rightList.splice(index, 1);
-          }
-          index--;
-        }
-      }
-      rollCallStudentList();
-    });
-
-    // const onFullScreen = () => {
-    //   if (data.type === 'preview') {
-    //     const el: any = document.querySelector('#app');
-
-    //     if (el.mozRequestFullScreen) {
-    //       el.mozRequestFullScreen();
-    //     } else if (el.webkitRequestFullscreen) {
-    //       el.webkitRequestFullscreen();
-    //     } else if (el.requestFullScreen) {
-    //       el.requestFullscreen();
-    //     }
-    //   }
-    // };
-
-    const popupData = reactive({
-      open: false,
-      activeIndex: 0,
-      courseActiveIndex: 0, // 课件选择的第几个
-      toolOpen: false, // 工具弹窗控制
-      chapterOpen: false, // 切换章节
-      chapterDetails: [] as any,
-      courseId: null as any, // 章节编号
-      chapterLoading: false // 加载数据
-    });
-
-    watch(
-      () => [popupData.open, popupData.chapterOpen],
-      val => {
-        // 为了处理window电脑滑动时的问题 - pointr-events
-        setTimeout(() => {
-          const drawers = document.querySelectorAll('.n-drawer-container');
-          if (val[0] || val[1]) {
-            drawers?.forEach(drawer => {
-              drawer.classList.remove('n-drawer-container-relative');
-            });
-          } else {
-            drawers?.forEach(drawer => {
-              drawer.classList.add('n-drawer-container-relative');
-            });
-          }
-        }, 200);
-      }
-    );
-    const formatParentId = (id: any, list: any, ids = [] as any) => {
-      for (const item of list) {
-        if (item.knowledgeList && item.knowledgeList.length > 0) {
-          const cIds: any = formatParentId(id, item.knowledgeList, [
-            ...ids,
-            item.id
-          ]);
-          if (cIds.includes(id)) {
-            return cIds;
-          }
-        }
-        if (item.id === id) {
-          return [...ids, id];
-        }
-      }
-      return ids;
-    };
-
-    /** 获取章节 */
-    const getLessonCoursewareDetail = async () => {
-      try {
-        const res = await lessonCoursewareDetail({
-          id: data.lessonCourseId,
-          instrumentId: data.instrumentId
-        });
-
-        popupData.chapterDetails = res.data.lessonList || [];
-
-        const ids = formatParentId(data.detailId, popupData.chapterDetails);
-        data.lessonCoursewareDetailId = ids[0];
-        console.log(res.data, 'data');
-        data.lessonCoursewareSubjectList = res.data.instrumentList || [];
-      } catch {
-        //
-      }
-    };
-    /** 更新上课记录 */
-    const classCourseScheduleUpdate = async () => {
-      try {
-        if (!data.classId) return;
-        await courseScheduleUpdate({
-          lessonCoursewareKnowledgeDetailId: data.detailId,
-          id: data.classId
-        });
-      } catch {
-        //
-      }
-    };
-
-    const activeName = computed(() => {
-      let name = '';
-      popupData.chapterDetails.forEach((chapter: any) => {
-        if (chapter.id === data.lessonCoursewareDetailId) {
-          // name = chapter.name;
-          chapter.knowledgeList?.forEach((know: any) => {
-            if (know.id === data.detailId) {
-              name = know.name;
-            }
-          });
-        }
-      });
-      return name;
-    });
-
-    /**停止所有的播放 */
-    const handleStop = (isStop = true) => {
-      // console.log(isStop, 'stop');
-      for (let i = 0; i < data.itemList.length; i++) {
-        const activeItem = data.itemList[i];
-        if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
-          try {
-            if (isStop) {
-              activeItem.videoEle?.currentTime(0);
-            }
-            activeItem.videoEle?.pause();
-          } catch (e: any) {
-            // console.log(e, 'e');
-          }
-        }
-        if (activeItem.type === 'SONG' && activeItem.audioEle) {
-          activeItem.audioEle?.stop();
-        }
-        // console.log('🚀 ~ activeItem:', activeItem)
-        // 停止曲谱的播放
-        if (activeItem.type === 'MUSIC') {
-          activeItem.iframeRef?.contentWindow?.postMessage(
-            { api: 'setPlayState' },
-            '*'
-          );
-        }
-
-        if (
-          activeItem.type === 'INSTRUMENT' ||
-          activeItem.type === 'MUSICIAN' ||
-          activeItem.type === 'MUSIC_WIKI'
-        ) {
-          activeItem.iframeRef?.handleChangeAudio('pause');
-        }
-
-        // console.log(activeItem.type, 'activeItem.type');
-        if (activeItem.type === 'RHYTHM') {
-          activeItem.iframeRef?.contentWindow?.postMessage(
-            { api: 'setPlayState', data: false },
-            '*'
-          );
-        }
-
-        if (activeItem.type === 'LISTEN') {
-          activeItem.iframeRef?.contentWindow?.postMessage(
-            { api: 'setPlayState' },
-            '*'
-          );
-        }
-      }
-    };
-    // 切换素材
-    const toggleMaterial = (itemActive: any) => {
-      const index = data.itemList.findIndex((n: any) => n.id == itemActive);
-      if (index > -1) {
-        handleSwipeChange(index);
-      }
-    };
-    /** 延迟收起模态框 */
-    const setModelOpen = () => {
-      clearTimeout(activeData.timer);
-      message.destroyAll();
-      activeData.timer = setTimeout(() => {
-        activeData.model = false;
-        Object.values(data.videoRefs).map((n: any) =>
-          n?.toggleHideControl(false)
-        );
-
-        Object.values(data.audioRefs).map((n: any) =>
-          n?.toggleHideControl(false)
-        );
-      }, 4000);
-    };
-
-    /** 立即收起所有的模态框 */
-    const clearModel = () => {
-      clearTimeout(activeData.timer);
-      message.destroyAll();
-      activeData.model = false;
-      Object.values(data.videoRefs).map((n: any) =>
-        n?.toggleHideControl(false)
-      );
-      Object.values(data.audioRefs).map((n: any) =>
-        n?.toggleHideControl(false)
-      );
-    };
-    const toggleModel = (type = true) => {
-      activeData.model = type;
-      Object.values(data.videoRefs).map((n: any) => n?.toggleHideControl(type));
-      Object.values(data.audioRefs).map((n: any) => n?.toggleHideControl(type));
-    };
-
-    // 双击
-    const handleDbClick = (item: any) => {
-      if (item && item.type === 'VIDEO') {
-        const videoEle: HTMLVideoElement = item.videoEle;
-        if (videoEle) {
-          if (videoEle.paused) {
-            message.destroyAll();
-            videoEle.play();
-          } else {
-            message.warning('已暂停');
-            videoEle.pause();
-          }
-        }
-      }
-    };
-
-    // ppt iframe 点击事件
-    // const iframeClick = () => {
-    // 	if(document.all) {
-    // 		document.getElementById("iframe-ppt")?.attachEvent("click", () => {
-    //       activeData.model = !activeData.model
-    //     });
-    // 	} else {
-    // 		document.getElementById("iframe-ppt")?.contentWindow?.postMessage({
-    //       api: 'onAttendToggleMenu'
-    //     }, '*');
-    // 	}
-    // }
-
-    // 切换播放
-    // const togglePlay = (m: any, isPlay: boolean) => {
-    //   if (isPlay) {
-    //     m.videoEle?.play();
-    //   } else {
-    //     m.videoEle?.pause();
-    //   }
-    // };
-
-    // const showIndex = ref(-4);
-    const effectIndex = ref(3);
-    const effects = [
-      {
-        prev: {
-          transform: 'translate3d(0, 0, -800px) rotateX(180deg)'
-        },
-        next: {
-          transform: 'translate3d(0, 0, -800px) rotateX(-180deg)'
-        }
-      },
-      {
-        prev: {
-          transform: 'translate3d(-100%, 0, -800px)'
-        },
-        next: {
-          transform: 'translate3d(100%, 0, -800px)'
-        }
-      },
-      {
-        prev: {
-          transform: 'translate3d(-50%, 0, -800px) rotateY(80deg)'
-        },
-        next: {
-          transform: 'translate3d(50%, 0, -800px) rotateY(-80deg)'
-        }
-      },
-      {
-        prev: {
-          transform: 'translate3d(-100%, 0, -800px) rotateY(-120deg)'
-        },
-        next: {
-          transform: 'translate3d(100%, 0, -800px) rotateY(120deg)'
-        }
-      },
-      // 风车4
-      {
-        prev: {
-          transform: 'translate3d(-50%, 50%, -800px) rotateZ(-14deg)',
-          opacity: 0
-        },
-        next: {
-          transform: 'translate3d(50%, 50%, -800px) rotateZ(14deg)',
-          opacity: 0
-        }
-      },
-      // 翻页5
-      {
-        prev: {
-          transform: 'translateZ(-800px) rotate3d(0, -1, 0, 90deg)',
-          opacity: 0
-        },
-        next: {
-          transform: 'translateZ(-800px) rotate3d(0, 1, 0, 90deg)',
-          opacity: 0
-        },
-        current: { transitionDelay: '700ms' }
-      }
-    ];
-
-    const acitveTimer = ref();
-    // 轮播切换
-    const handleSwipeChange = (index: number) => {
-      // 如果是当前正在播放 或者是视频最后一个
-      if (popupData.activeIndex == index) return;
-      data.animationState = 'start';
-      data.videoState = 'init';
-      handleStop();
-      clearTimeout(acitveTimer.value);
-      activeData.model = true;
-      checkedAnimation(popupData.activeIndex, index);
-      popupData.activeIndex = index;
-
-      // 处理资源列表滚动
-      nextTick(() => {
-        scrollResourceSection();
-      });
-
-      acitveTimer.value = setTimeout(
-        () => {
-          const item = data.itemList[index];
-
-          if (item) {
-            if (item.type == 'MUSIC') {
-              activeData.model = true;
-            }
-            if (item.type === 'SONG') {
-              // 自动播放下一个音频
-              clearTimeout(activeData.timer);
-              message.destroyAll();
-              // item.autoPlay = false;
-              // nextTick(() => {
-              //   item.audioEle?.onPlay();
-              // });
-            }
-            if (item.type === 'VIDEO') {
-              // 自动播放下一个视频
-              clearTimeout(activeData.timer);
-              message.destroyAll();
-              nextTick(() => {
-                if (item.error) {
-                  // console.log(item, 'item error');
-                  item.videoEle?.src(item.content);
-                  item.error = false;
-                  //   item.videoEle?.onPlay();
-                }
-                data.animationState = 'end';
-              });
-            }
-            if (item.type === 'PPT') {
-              //
-            }
-          }
-        },
-        activeData.isAnimation ? 800 : 0
-      );
-    };
-
-    /** 是否有转场动画 */
-    const checkedAnimation = (index: number, nextIndex?: number) => {
-      const item = data.itemList[index];
-      const nextItem = data.itemList[nextIndex!];
-      if (nextItem) {
-        if (nextItem.knowledgePointId != item.knowledgePointId) {
-          activeData.isAnimation = true;
-          return;
-        }
-        const videoEle = item.videoEle;
-        const nextVideo = nextItem.videoEle;
-        if (videoEle && videoEle.duration < 8 && index < nextIndex!) {
-          activeData.isAnimation = false;
-        } else if (nextVideo && nextVideo.duration < 8 && index > nextIndex!) {
-          activeData.isAnimation = false;
-        } else {
-          activeData.isAnimation = true;
-        }
-      } else {
-        activeData.isAnimation = item?.adviseStudyTimeSecond < 8 ? false : true;
-      }
-    };
-
-    // 上一个知识点, 下一个知识点
-    const handlePreAndNext = async (type: string) => {
-      if (type === 'up') {
-        // 判断上面是否还有章节
-        if (popupData.activeIndex > 0) {
-          handleSwipeChange(popupData.activeIndex - 1);
-          return;
-        }
-
-        // 获取当前是哪个章节
-        let detailIndex = popupData.chapterDetails.findIndex(
-          (item: any) => item.id == data.lessonCoursewareDetailId
-        );
-        const detailItem =
-          popupData.chapterDetails[detailIndex]?.knowledgeList || [];
-        let lessonIndex = detailItem.findIndex(
-          (item: any) => item.id == data.detailId
-        );
-
-        let lessonStatus = false; // 当前章节上面是否有内容
-        let lessonCoursewareDetailId = '';
-        let coursewareDetailKnowledgeId = '';
-        while (lessonIndex >= 0) {
-          lessonIndex--;
-
-          if (lessonIndex >= 0) {
-            if (detailItem[lessonIndex].coursewareNum > 0) {
-              lessonStatus = true;
-              lessonCoursewareDetailId =
-                detailItem[lessonIndex].lessonCoursewareDetailId;
-              coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
-            }
-          }
-
-          if (lessonStatus) {
-            break;
-          }
-        }
-        // 判断当前章节下面课程是否有内容,否则往上一个章节走
-        if (lessonStatus) {
-          popupData.courseId = coursewareDetailKnowledgeId;
-          data.selectClassStatus = true;
-          return;
-        }
-
-        let prevLessonStatus = false;
-        while (detailIndex >= 0) {
-          detailIndex--;
-          const tempDetail =
-            popupData.chapterDetails[detailIndex]?.knowledgeList || [];
-          let tempLessonLength = tempDetail.length;
-          while (tempLessonLength > 0) {
-            if (tempDetail[tempLessonLength - 1].coursewareNum > 0) {
-              prevLessonStatus = true;
-              lessonCoursewareDetailId =
-                tempDetail[tempLessonLength - 1].lessonCoursewareDetailId;
-              coursewareDetailKnowledgeId = tempDetail[tempLessonLength - 1].id;
-            }
-            tempLessonLength--;
-            if (prevLessonStatus) {
-              break;
-            }
-          }
-
-          if (prevLessonStatus) {
-            break;
-          }
-        }
-
-        // 判断当前章节下面课程是否有内容,否则往上一个章节走
-        if (prevLessonStatus) {
-          popupData.courseId = coursewareDetailKnowledgeId;
-          data.selectClassStatus = true;
-          return;
-        }
-      } else {
-        if (popupData.activeIndex < data.itemList.length - 1) {
-          handleSwipeChange(popupData.activeIndex + 1);
-          return;
-        }
-        if (!isDownArrow.value) return;
-        data.nextEndShow = true;
-      }
-    };
-    // 当前课件结束之后选择下一个课件
-    function handleNextEnd() {
-      // 获取当前是哪个章节
-      let detailIndex = popupData.chapterDetails.findIndex(
-        (item: any) => item.id == data.lessonCoursewareDetailId
-      );
-      const detailItem =
-        popupData.chapterDetails[detailIndex]?.knowledgeList || [];
-      let lessonIndex = detailItem.findIndex(
-        (item: any) => item.id == data.detailId
-      );
-
-      let lessonStatus = false; // 当前章节下面是否有内容
-      let lessonCoursewareDetailId = '';
-      let coursewareDetailKnowledgeId = '';
-      while (lessonIndex < detailItem.length - 1) {
-        lessonIndex++;
-        if (lessonIndex >= 0) {
-          if (detailItem[lessonIndex].coursewareNum > 0) {
-            lessonStatus = true;
-            lessonCoursewareDetailId =
-              detailItem[lessonIndex].lessonCoursewareDetailId;
-            coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
-          }
-        }
-        if (lessonStatus) {
-          break;
-        }
-      }
-      // 判断当前章节下面课程是否有内容,否则往下一个章节走
-      if (lessonStatus) {
-        popupData.courseId = coursewareDetailKnowledgeId;
-        data.selectClassStatus = true;
-        return;
-      }
-
-      let nextLessonStatus = false;
-      while (detailIndex <= popupData.chapterDetails.length - 1) {
-        detailIndex++;
-        const tempDetail =
-          popupData.chapterDetails[detailIndex]?.knowledgeList || [];
-        let tempLessonLength = 0;
-        while (tempLessonLength <= tempDetail.length - 1) {
-          if (tempDetail[tempLessonLength].coursewareNum > 0) {
-            nextLessonStatus = true;
-            lessonCoursewareDetailId =
-              tempDetail[tempLessonLength].lessonCoursewareDetailId;
-            coursewareDetailKnowledgeId = tempDetail[tempLessonLength].id;
-          }
-          tempLessonLength++;
-          if (nextLessonStatus) {
-            break;
-          }
-        }
-
-        if (nextLessonStatus) {
-          break;
-        }
-      }
-
-      // 判断当前章节下面课程是否有内容,否则往上一个章节走
-      if (nextLessonStatus) {
-        popupData.courseId = coursewareDetailKnowledgeId;
-        data.selectClassStatus = true;
-        return;
-      }
-    }
-    /** 弹窗关闭 */
-    const handleClosePopup = () => {
-      const item = data.itemList[popupData.activeIndex];
-      if (item?.type == 'VIDEO' && !item.videoEle?.paused) {
-        setModelOpen();
-      }
-
-      if (item?.type == 'SONG' && !item.audioEle?.paused) {
-        setModelOpen();
-      }
-    };
-    document.body.addEventListener('keyup', (e: KeyboardEvent) => {
-      if (e.code === 'ArrowUp') {
-        // if (popupData.activeIndex === 0) return;
-        setModalOpen();
-        handlePreAndNext('up');
-      } else if (e.code === 'ArrowDown') {
-        // if (popupData.activeIndex === data.itemList.length - 1) return;
-        setModalOpen();
-        handlePreAndNext('down');
-      } else if (e.code === 'Space') {
-        // const activeItem = data.itemList[popupData.activeIndex];
-        // console.log(activeItem, activeItem.videoEle);
-        // // 暂停视频和曲谱的播放
-        // if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
-        //   activeItem.videoEle?.play();
-        // }
-        // if (activeItem.type === 'SONG' && activeItem.audioEle) {
-        //   activeItem.audioEle?.play();
-        // }
-        // if (activeItem.type === 'MUSIC') {
-        //   activeItem.iframeRef?.contentWindow?.postMessage(
-        //     { api: 'setPlayState' },
-        //     '*'
-        //   );
-        // }
-      }
-    });
-
-    // const toggleListenerKeyUp = (type: string) => {
-    //   if (type === 'remove') {
-    //     document.body.removeEventListener('keyup', () => {
-    //       listenerKeyUpState.value = false;
-    //     });
-    //   } else {
-    //     // 监听页面键盘事件 - 上下切换
-    //     document.body.addEventListener('keyup', (e: KeyboardEvent) => {
-    //       // console.log(e, 'e');
-    //       if (e.code === 'ArrowLeft') {
-    //         // if (popupData.activeIndex === 0) return;
-    //         setModalOpen();
-    //         handlePreAndNext('up');
-    //       } else if (e.code === 'ArrowRight') {
-    //         // if (popupData.activeIndex === data.itemList.length - 1) return;
-    //         setModalOpen();
-    //         handlePreAndNext('down');
-    //       }
-    //     });
-    //     listenerKeyUpState.value = true;
-    //   }
-    // };
-
-    // 监听切换到ppt课件时,手动移除键盘监听器
-    // watch(() => popupData.activeIndex, () => {
-    //   const activeItem = data.itemList[popupData.activeIndex];
-    //   if (activeItem?.type === 'PPT') {
-    //     toggleListenerKeyUp('remove')
-    //   } else {
-    //     !listenerKeyUpState.value && toggleListenerKeyUp('add')
-    //   }
-    // })
-    watch(
-      () => popupData.activeIndex,
-      () => {
-        const item = data.itemList[popupData.activeIndex];
-        popupData.courseActiveIndex = item.parentIndex;
-      }
-    );
-
-    const setModalOpen = (status = true) => {
-      clearTimeout(activeData.timer);
-      activeData.model = status;
-      Object.values(data.videoRefs).map((n: any) =>
-        n?.toggleHideControl(status)
-      );
-      Object.values(data.audioRefs).map((n: any) =>
-        n?.toggleHideControl(status)
-      );
-    };
-
-    /** 教学数据 */
-    const studyData = reactive({
-      type: '' as ToolType,
-      penShow: false,
-      whiteboardShow: false,
-      callShow: false,
-      callStudentList: [] as any // 学生列表
-    });
-
-    /** 打开教学工具 */
-    const openStudyTool = (item: ToolItem) => {
-      handleStop(false);
-      clearModel();
-      popupData.toolOpen = false;
-      studyData.type = item.type;
-
-      switch (item.type) {
-        case 'pen':
-          studyData.penShow = true;
-          break;
-        case 'whiteboard':
-          studyData.whiteboardShow = true;
-          break;
-        case 'call':
-          studyData.callShow = true;
-          break;
-      }
-    };
-
-    /** 关闭教学工具 */
-    const closeStudyTool = () => {
-      studyData.type = 'init';
-      toggleModel();
-      setModelOpen();
-    };
-
-    const startShowModal = (
-      val: 'setTimeIcon' | 'beatIcon' | 'toneIcon' | 'iconNote2'
-    ) => {
-      if (val == 'setTimeIcon') {
-        showModalTime.value = true;
-      }
-      if (val == 'beatIcon') {
-        showModalBeat.value = true;
-      }
-      if (val == 'toneIcon') {
-        showModalTone.value = true;
-      }
-    };
-
-    // 是否允许上一页
-    const isUpArrow = computed(() => {
-      /**
-       * 1,判断当前课程中是否处在第一个资源;
-       * 2,判断当前课程是否在当前章节的第一个;
-       * 3,判断当前章节,当前课程上面还没有其它课程,是否有资源;
-       * 4,判断当前章节上面还没有其它章节;
-       * 5,判断上面章节里面课程是否有资源;
-       */
-      if (popupData.activeIndex > 0) {
-        return true;
-      }
-
-      // 获取当前是哪个章节
-      let detailIndex = popupData.chapterDetails.findIndex(
-        (item: any) => item.id == data.lessonCoursewareDetailId
-      );
-      const detailItem =
-        popupData.chapterDetails[detailIndex]?.knowledgeList || [];
-      let lessonIndex = detailItem.findIndex(
-        (item: any) => item.id == data.detailId
-      );
-
-      // 说明已经是第一单元,第一课
-      if (detailIndex <= 0 && lessonIndex <= 0) {
-        return false;
-      }
-
-      let lessonStatus = false; // 当前章节上面是否有内容
-      while (lessonIndex >= 0) {
-        lessonIndex--;
-
-        if (lessonIndex >= 0) {
-          if (detailItem[lessonIndex].coursewareNum > 0) {
-            lessonStatus = true;
-          }
-        }
-      }
-      // 判断当前章节下面课程是否有内容,否则往上一个章节走
-      if (lessonStatus) {
-        return true;
-      }
-
-      // 已经是第一个章节了
-      if (detailIndex <= 0) {
-        return false;
-      }
-
-      let prevLessonStatus = false;
-      while (detailIndex >= 0) {
-        detailIndex--;
-        const tempDetail =
-          popupData.chapterDetails[detailIndex]?.knowledgeList || [];
-        let tempLessonLength = tempDetail.length;
-        while (tempLessonLength > 0) {
-          if (tempDetail[tempLessonLength - 1].coursewareNum > 0) {
-            prevLessonStatus = true;
-          }
-          tempLessonLength--;
-        }
-
-        if (prevLessonStatus) {
-          return true;
-        }
-      }
-
-      return false;
-    });
-
-    // 是否允许下一页
-    const isDownArrow = computed(() => {
-      if (popupData.activeIndex < data.itemList.length - 1) {
-        return true;
-      }
-
-      // 获取当前是哪个章节
-      let detailIndex = popupData.chapterDetails.findIndex(
-        (item: any) => item.id == data.lessonCoursewareDetailId
-      );
-
-      const detailItem =
-        popupData.chapterDetails[detailIndex]?.knowledgeList || [];
-      let lessonIndex = detailItem.findIndex(
-        (item: any) => item.id == data.detailId
-      );
-
-      // 说明已经是最后-单元,最后一课
-      if (
-        detailIndex >= popupData.chapterDetails.length - 1 &&
-        lessonIndex >= detailItem.length - 1
-      ) {
-        return false;
-      }
-
-      let lessonStatus = false; // 当前章节下面是否有内容
-      while (lessonIndex < detailItem.length - 1) {
-        lessonIndex++;
-        if (lessonIndex >= 0) {
-          if (detailItem[lessonIndex].coursewareNum > 0) {
-            lessonStatus = true;
-          }
-        }
-      }
-      // 判断当前章节下面课程是否有内容,否则往下一个章节走
-      if (lessonStatus) {
-        return true;
-      }
-
-      // 已经是最后一个章节了
-      if (detailIndex >= popupData.chapterDetails.length - 1) {
-        return false;
-      }
-
-      let nextLessonStatus = false;
-      while (detailIndex < popupData.chapterDetails.length - 1) {
-        detailIndex++;
-        const tempDetail =
-          popupData.chapterDetails[detailIndex]?.knowledgeList || [];
-        let tempLessonLength = 0;
-        while (tempLessonLength <= tempDetail.length - 1) {
-          if (tempDetail[tempLessonLength].coursewareNum > 0) {
-            nextLessonStatus = true;
-          }
-          tempLessonLength++;
-        }
-
-        if (nextLessonStatus) {
-          return true;
-        }
-      }
-
-      return false;
-    });
-    const activeVideoItem = computed(() => {
-      const item = data.itemList[popupData.activeIndex];
-      if (item && item.type && item.type.toLocaleUpperCase() === 'VIDEO') {
-        return item;
-      }
-      return {};
-    });
-
-    // 右侧菜单栏
-    const rightList = reactive([
-      {
-        name: '上一个',
-        icon: bottomIconPre,
-        id: 11
-      },
-      {
-        name: '下一个',
-        icon: bottomIconNext,
-        id: 12
-      },
-      {
-        name: '切换章节',
-        icon: bottomIconSwitch,
-        id: 13
-      },
-      {
-        name: '资源列表',
-        icon: bottomIconResource,
-        id: 14
-      },
-      {
-        name: '曲目资源',
-        icon: rightIconMusic,
-        id: 9
-      },
-      {
-        name: '点名',
-        icon: rightIconCall,
-        id: 10
-      },
-      {
-        name: '布置作业',
-        icon: rightIconArrange,
-        id: 2
-      },
-      {
-        name: '工具箱',
-        icon: rightIconTool,
-        id: 15
-      },
-      {
-        name: '结束课程',
-        name2: '结束预览',
-        icon: rightIconEnd,
-        id: 1
-      },
-      {
-        name: '收起',
-        icon: leftIconPackUp,
-        id: 8
-      }
-    ]);
-    const tooltipList = [
-      {
-        name: '节拍器',
-        icon: rightIconMetronome,
-        id: 5
-      },
-      {
-        name: '计时器',
-        icon: rightIconTimer,
-        id: 7
-      },
-      {
-        name: '批注',
-        icon: rightIconPostil,
-        id: 3
-      },
-      {
-        name: '白板',
-        icon: rightIconWhiteboard,
-        id: 4
-      }
-      // {
-      //   name: '调音器',
-      //   icon: rightIconTuner,
-      //   id: 6
-      // }
-    ];
-    // 默认收起菜单
-    const columnShow = ref(true);
-    // 菜单位置
-    const columnPos = ref<'left' | 'right'>('left');
-    watch(columnPos, () => {
-      for (let i = 0; i < data.itemList.length; i++) {
-        const activeItem = data.itemList[i];
-        if (['RHYTHM', 'MUSIC'].includes(activeItem.type)) {
-          activeItem.iframeRef?.contentWindow?.postMessage(
-            { api: 'imagePos', data: columnPos.value },
-            '*'
-          );
-        }
-      }
-    });
-    // 右边栏操作
-    const operateRightBtn = async (id: number) => {
-      if (![8, 11, 12, 13, 14].includes(id)) {
-        handleStop(false);
-      }
-
-      switch (id) {
-        case 1:
-          if (data.type === 'preview') {
-            data.removeVisiable = true;
-            data.removeTitle = '结束预览';
-            data.removeContent = '请确认是否结束预览?';
-          } else {
-            const res = await getStudentAfterWork({
-              courseScheduleId: data.classId,
-              page: 1,
-              rows: 99
-            });
-            if (res.data.rows && res.data.rows.length) {
-              data.removeContent = '请确认是否结束课程?';
-              data.removeCourseStatus = false;
-            } else {
-              data.removeContent = '本次课堂尚未布置作业,是否结束课程?';
-              data.removeCourseStatus = true;
-            }
-            data.removeVisiable = true;
-            data.removeTitle = '结束课程';
-          }
-          break;
-        case 2:
-          // 学生人数必须大于0,才可以布置作业
-          if (data.preStudentNum <= 0) return;
-          // const res = await lessonPreTrainingPage({
-          //   coursewareKnowledgeDetailId: data.detailId,
-          //   instrumentId: data.instrumentId,
-          //   page: 1,
-          //   rows: 99
-          // });
-          // if (res.data.rows && res.data.rows.length) {
-          //   data.modalAttendMessage = '本节课已设置课后作业,是否布置?';
-          // }
-          // data.modelAttendStatus = true;
-          const res = await getStudentAfterWork({
-            courseScheduleId: data.classId,
-            page: 1,
-            rows: 99
-          });
-          if (res.data.rows && res.data.rows.length) {
-            // data.modalAttendMessage = '请确认是否结束课程?';
-            data.modalAttendMessage = '本次课程已布置作业,是否继续?';
-            data.modelAttendStatus = true;
-          } else {
-            data.modelTrainStatus = true;
-            nextTick(() => {
-              getModalHeight();
-            });
-            data.modelAttendStatus = false;
-          }
-          break;
-        case 3:
-          openStudyTool({
-            type: 'pen',
-            icon: iconNote,
-            name: '批注'
-          });
-          break;
-        case 4:
-          openStudyTool({
-            type: 'whiteboard',
-            icon: iconWhite,
-            name: '白板'
-          });
-          break;
-        case 5:
-          startShowModal('beatIcon');
-          break;
-        case 6:
-          startShowModal('toneIcon');
-          break;
-        case 7:
-          startShowModal('setTimeIcon');
-          break;
-        case 8:
-          columnShow.value = false;
-          break;
-        case 9:
-          // 选择曲目时需要暂停所有播放
-          handleStop(false);
-          data.selectResourceStatus = true;
-          break;
-        case 10:
-          //  点名
-          // await rollCallStudentList();
-          if (studyData.callStudentList.length > 0) {
-            openStudyTool({
-              type: 'call',
-              icon: iconWhite,
-              name: '点名'
-            });
-            return;
-          }
-          break;
-        case 11:
-          if (!isUpArrow.value) return;
-          handlePreAndNext('up');
-          break;
-        case 12:
-          if (!isDownArrow.value) return;
-          handlePreAndNext('down');
-          break;
-        case 13:
-          popupData.chapterOpen = true;
-          break;
-        case 14:
-          popupData.open = true;
-          nextTick(() => {
-            scrollResourceSection();
-          });
-          break;
-        default:
-          break;
-      }
-    };
-
-    // 点名学生列表
-    const rollCallStudentList = async () => {
-      //
-      if (!data.classId) return;
-      try {
-        const res = await getStudentList({
-          classGroupId: data.classGroupId,
-          page: 1,
-          rows: 999
-        });
-        const result = res.data || {};
-        if (Array.isArray(result.rows) && result.rows.length > 0) {
-          const tempStudents: any = [];
-          result.rows.forEach((row: any) => {
-            tempStudents.push({
-              name: row.nickname,
-              img: row.avatar
-            });
-          });
-          studyData.callStudentList = [...tempStudents];
-        }
-      } catch {
-        //
-      }
-    };
-
-    // 滚动到某个元素的位置
-    const scrollResourceSection = () => {
-      const drawerCardItemRefs =
-        document.querySelectorAll('.drawerCardItemRef');
-      if (
-        popupData.activeIndex >= 0 &&
-        drawerCardItemRefs[popupData.activeIndex]
-      ) {
-        drawerCardItemRefs[popupData.activeIndex].scrollIntoView();
-      }
-    };
-
-    const getModalHeight = () => {
-      const dom: any = document.querySelector('#model-homework-height');
-      if (dom) {
-        useResizeObserver(dom as HTMLElement, (entries: any) => {
-          const entry = entries[0];
-          const { height } = entry.contentRect;
-          dom.style.setProperty('--window-page-lesson-height', height + 'px');
-        });
-      }
-    };
-
-    /* 弹窗加拖动 */
-    // 引导页
-    getGuidanceShow();
-    // 选择课件弹窗
-    const selCourBoxClass = 'selCourBoxClass_drag';
-    const selCourDragData = useDrag(
-      [`${selCourBoxClass}>.n-card-header`, `${selCourBoxClass} .bom_drag`],
-      selCourBoxClass,
-      toRef(data, 'selectClassStatus'),
-      users.info.id
-    );
-    // 选择资源弹窗
-    const selResourBoxClass = 'selResourBoxClass_drag';
-    const selResourDragData = useDrag(
-      [
-        `${selResourBoxClass} .select-resource>.n-tabs>.n-tabs-nav--top.n-tabs-nav`,
-        `${selResourBoxClass} .bom_drag`
-      ],
-      selResourBoxClass,
-      toRef(data, 'selectResourceStatus'),
-      users.info.id
-    );
-    // 结束预览  结束课程确认弹窗
-    const removeResourBoxClass = 'removeResourBoxClass_drag';
-    const removeResourDragData = useDrag(
-      [
-        `${removeResourBoxClass}>.n-card-header`,
-        `${removeResourBoxClass} .bom_drag`
-      ],
-      removeResourBoxClass,
-      toRef(data, 'removeVisiable'),
-      users.info.id
-    );
-    // 章节next弹窗
-    const nextEndBoxClass = 'nextEndBoxClass_drag';
-    const nextEndBoxDragData = useDrag(
-      [`${nextEndBoxClass}>.n-card-header`, `${nextEndBoxClass} .bom_drag`],
-      nextEndBoxClass,
-      toRef(data, 'nextEndShow'),
-      users.info.id
-    );
-    // 章节切换弹窗
-    const chapterConBoxClass = 'chapterConBoxClass_drag';
-    const chapterConBoxDragData = useDrag(
-      [
-        `${chapterConBoxClass}>.n-card-header`,
-        `${chapterConBoxClass} .bom_drag`
-      ],
-      chapterConBoxClass,
-      toRef(popupData, 'chapterOpen'),
-      users.info.id
-    );
-    // 资源列表弹窗
-    const resourcesConBoxClass = 'resourcesConBoxClass_drag';
-    const resourcesConDragData = useDrag(
-      [
-        `${resourcesConBoxClass}>.n-card-header`,
-        `${resourcesConBoxClass} .bom_drag`
-      ],
-      resourcesConBoxClass,
-      toRef(popupData, 'open'),
-      users.info.id
-    );
-    // 计时器
-    const timerMeterConBoxClass = 'timerMeterConBoxClass_drag';
-    const timerMeterConDragData = useDrag(
-      [
-        `${timerMeterConBoxClass} .timeBomCon .bom_drag`,
-        `${timerMeterConBoxClass} .topDragDom`
-      ],
-      timerMeterConBoxClass,
-      showModalTime,
-      users.info.id
-    );
-    // 节拍器
-    const metronomeConBoxClass = 'metronomeConBoxClass_drag';
-    const metronomeConBoxDragData = useDrag(
-      [
-        `${metronomeConBoxClass} .topDragDom`,
-        `${metronomeConBoxClass} .bom_drag`
-      ],
-      metronomeConBoxClass,
-      showModalBeat,
-      users.info.id
-    );
-    // 布置作业弹窗
-    const modelTrainStatusConBoxClass = 'modelTrainStatusConBoxClass_drag';
-    const modelTrainStatusConBoxDragData = useDrag(
-      [
-        `${modelTrainStatusConBoxClass}>.n-card-header`,
-        `${modelTrainStatusConBoxClass} .bom_drag`
-      ],
-      modelTrainStatusConBoxClass,
-      toRef(data, 'modelTrainStatus'),
-      users.info.id
-    );
-    // 布置作业课后作业
-    const modelTrainHomeWordConBoxClass =
-      'modelTrainHomeWordConBoxClassBoxClass_drag';
-    const modelTrainHomeWordConBoxDragData = useDrag(
-      [
-        `${modelTrainHomeWordConBoxClass}>.n-card-header`,
-        `${modelTrainHomeWordConBoxClass} .bom_drag`
-      ],
-      modelTrainHomeWordConBoxClass,
-      toRef(data, 'modelAttendStatus'),
-      users.info.id
-    );
-    return () => (
-      <div id="playContent" class={[styles.playContent, 'wrap']}>
-        <div
-          onClick={() => {
-            clearTimeout(activeData.timer);
-            activeData.model = !activeData.model;
-            Object.values(data.videoRefs).map((n: any) =>
-              n?.toggleHideControl(activeData.model)
-            );
-            Object.values(data.audioRefs).map((n: any) =>
-              n?.toggleHideControl(activeData.model)
-            );
-          }}>
-          <div
-            class={styles.coursewarePlay}
-            style={{ width: parentContainer.width }}>
-            {!popupData.chapterLoading ? (
-              <div class={styles.wraps}>
-                <div
-                  style={
-                    activeVideoItem.value.type &&
-                    data.animationState === 'end' &&
-                    data.videoState === 'play'
-                      ? {
-                          zIndex: 15,
-                          opacity: 1
-                        }
-                      : { opacity: 0, zIndex: -1 }
-                  }
-                  class={styles.itemDiv}>
-                  <VideoPlay
-                    imagePos={columnPos.value}
-                    ref={(el: any) => (data.videoItemRef = el)}
-                    item={activeVideoItem.value}
-                    showModel={activeData.model}
-                    onClose={setModelOpen}
-                    onLoadedmetadata={(videoItem: any) => {
-                      if (data.itemList[popupData.activeIndex]) {
-                        data.itemList[popupData.activeIndex].videoEle =
-                          videoItem;
-                      }
-                    }}
-                    onCanplay={() => {
-                      data.videoState = 'play';
-                      // activeVideoItem.value.videoEle = videoItem;
-                    }}
-                    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 isRender = Math.abs(popupData.activeIndex - mIndex) < 2;
-                  const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
-                  // if (isRender && m.type === 'PPT') {
-                  //   setTimeout(() => iframeClick() ,500)
-                  // }
-                  // if (isRender) {
-                  //   m.isRender = true;
-                  // }
-
-                  // const isRender =
-                  //   m.isRender || Math.abs(popupData.activeIndex - mIndex) < 2;
-                  // const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
-                  // if (isRender) {
-                  //   m.isRender = true;
-                  // }
-
-                  return isRender ? (
-                    <div
-                      key={'index' + mIndex}
-                      class={[
-                        styles.itemDiv,
-                        popupData.activeIndex === mIndex && styles.itemActive,
-                        activeData.isAnimation && styles.acitveAnimation,
-                        Math.abs(popupData.activeIndex - mIndex) < 2
-                          ? 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)
-                          );
-                          Object.values(data.audioRefs).map((n: any) =>
-                            n?.toggleHideControl(activeData.model)
-                          );
-                          if (activeData.model) {
-                            setModelOpen();
-                          }
-                        }, 300);
-                      }}>
-                      {m.type === 'VIDEO' ? (
-                        <>
-                          <img
-                            src={m.coverImg}
-                            onLoad={() => {
-                              m.isprepare = true;
-                            }}
-                          />
-                          {/* <VideoPlay
-                            ref={(v: any) => (data.videoRefs[mIndex] = v)}
-                            item={m}
-                            isEmtry={isEmtry}
-                            onLoadedmetadata={(videoItem: any) => {
-                              m.videoEle = videoItem;
-                              m.isprepare = true;
-                            }}
-                            onTogglePlay={(paused: boolean) => {
-                              m.autoPlay = false;
-                              if (paused || popupData.open) {
-                                clearTimeout(activeData.timer);
-                              } else {
-                                setModelOpen();
-                              }
-                            }}
-                            onReset={() => {
-                              if (!m.videoEle?.paused) {
-                                setModelOpen();
-                              }
-                            }}
-                            onError={() => {
-                              console.log('video error');
-                              m.error = true;
-                            }}
-                          /> */}
-                          <Transition name="van-fade">
-                            {
-                              <div class={styles.loadWrap}>
-                                <Vue3Lottie
-                                  animationData={playLoadData}></Vue3Lottie>
-                              </div>
-                            }
-                          </Transition>
-                        </>
-                      ) : m.type === 'IMG' ? (
-                        <img src={m.content} />
-                      ) : m.type === 'SONG' ? (
-                        <AudioPay
-                          imagePos={columnPos.value}
-                          item={m}
-                          activeStatus={popupData.activeIndex === mIndex}
-                          ref={(v: any) => (data.audioRefs[mIndex] = v)}
-                          onLoadedmetadata={(audioItem: any) => {
-                            m.audioEle = audioItem;
-                            m.isprepare = true;
-                          }}
-                          onTogglePlay={(paused: boolean) => {
-                            // m.autoPlay = false;
-                            if (paused || popupData.open) {
-                              clearTimeout(activeData.timer);
-                            } else {
-                              setModelOpen();
-                            }
-                          }}
-                          onEnded={() => {
-                            // const _index = popupData.activeIndex + 1;
-                            // if (_index < data.itemList.length) {
-                            //   handleSwipeChange(_index);
-                            // }
-                          }}
-                          onReset={() => {
-                            if (!m.audioEle?.paused) {
-                              setModelOpen();
-                            }
-                          }}
-                        />
-                      ) : // : m.type === 'PPT' ? <div class={styles.iframePpt}>
-                      //   <div class={styles.pptBox}></div>
-                      //   <iframe src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(m.content)}`} width='100%' height='100%' frameborder='1'></iframe>
-                      // </div>
-                      m.type === 'PPT' ? (
-                        <iframe
-                          src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
-                            m.content
-                          )}`}
-                          width="100%"
-                          height="100%"
-                          frameborder="1"></iframe>
-                      ) : m.type === 'RHYTHM' ? (
-                        <RhythmModal
-                          item={m}
-                          activeStatus={popupData.activeIndex === mIndex}
-                          imagePos={columnPos.value}
-                          onSetIframe={(el: any) => {
-                            m.iframeRef = el;
-                          }}
-                        />
-                      ) : m.type === 'LISTEN' ? (
-                        <ListenModal
-                          item={m}
-                          data-vid={m.id}
-                          activeStatus={popupData.activeIndex === mIndex}
-                          onSetIframe={(el: any) => {
-                            m.iframeRef = el;
-                          }}
-                        />
-                      ) : m.type === 'INSTRUMENT' || m.type === 'MUSICIAN' ? (
-                        <InstruemntDetail
-                          type="preview"
-                          id={m.content}
-                          contentType={m.type}
-                          activeStatus={popupData.activeIndex === mIndex}
-                          ref={(el: any) => (m.iframeRef = el)}
-                        />
-                      ) : m.type === 'MUSIC_WIKI' ? (
-                        <MusicDetail
-                          type="preview"
-                          id={m.content}
-                          contentType={m.type}
-                          activeStatus={popupData.activeIndex === mIndex}
-                          ref={(el: any) => (m.iframeRef = el)}
-                        />
-                      ) : m.type === 'THEORY' ? (
-                        <TheotyDetail
-                          type="preview"
-                          id={m.content}
-                          activeStatus={popupData.activeIndex === mIndex}
-                          ref={(el: any) => (m.iframeRef = el)}
-                        />
-                      ) : (
-                        <MusicScore
-                          activeModel={activeData.model}
-                          activeStatus={popupData.activeIndex === mIndex}
-                          data-vid={m.id}
-                          music={m}
-                          imagePos={columnPos.value}
-                          onSetIframe={(el: any) => {
-                            m.iframeRef = el;
-                          }}
-                        />
-                      )}
-                    </div>
-                  ) : null;
-                })}
-              </div>
-            ) : (
-              ''
-            )}
-          </div>
-        </div>
-
-        {/* 右边操作栏 */}
-        <div
-          class={[
-            styles.rightColumn,
-            columnShow.value && columnPos.value === 'right'
-              ? ''
-              : styles.rightColumnHide
-          ]}>
-          {rightList.map((item: any) => (
-            <div class={styles.columnItemBox}>
-              <div
-                class={[
-                  styles.columnItem,
-                  (item.id === 2 && data.preStudentNum <= 0) ||
-                  (item.id === 10 && studyData.callStudentList.length <= 0) ||
-                  (item.id === 11 && !isUpArrow.value) ||
-                  (item.id === 12 && !isDownArrow.value)
-                    ? styles.itemDisabled
-                    : ''
-                ]}
-                onClick={() => operateRightBtn(item.id)}>
-                <NTooltip
-                  showArrow={false}
-                  placement="left"
-                  class={[
-                    item.id === 15
-                      ? 'columnItemTooltip rightColumnItemTooltip'
-                      : ''
-                  ]}>
-                  {{
-                    trigger: () => (
-                      <img src={item.id === 8 ? rightIconPackUp : item.icon} />
-                    ),
-                    default: () =>
-                      item.id === 15 ? (
-                        <div class="tools">
-                          {tooltipList.map(i => (
-                            <div onClick={() => operateRightBtn(i.id)}>
-                              <img src={i.icon} />
-                              <div class="tit">{i.name}</div>
-                            </div>
-                          ))}
-                        </div>
-                      ) : item.id === 1 && data.type === 'preview' ? (
-                        item.name2
-                      ) : (
-                        item.name
-                      )
-                  }}
-                </NTooltip>
-              </div>
-            </div>
-          ))}
-        </div>
-        <NTooltip showArrow={false} placement="left">
-          {{
-            trigger: () => (
-              <div
-                class={[
-                  styles.rightHideIcon,
-                  !(columnShow.value && columnPos.value === 'right')
-                    ? styles.rightIconShow
-                    : ''
-                ]}
-                onClick={() => {
-                  columnPos.value = 'right';
-                  columnShow.value = true;
-                }}
-              />
-            ),
-            default: () => '按钮镜像'
-          }}
-        </NTooltip>
-        {/* 左边操作栏 */}
-        <div
-          class={[
-            styles.leftColumn,
-            columnShow.value && columnPos.value === 'left'
-              ? ''
-              : styles.leftColumnHide
-          ]}>
-          {rightList.map((item: any) => (
-            <div class={styles.columnItemBox}>
-              <div
-                class={[
-                  styles.columnItem,
-                  (item.id === 2 && data.preStudentNum <= 0) ||
-                  (item.id === 10 && studyData.callStudentList.length <= 0) ||
-                  (item.id === 11 && !isUpArrow.value) ||
-                  (item.id === 12 && !isDownArrow.value)
-                    ? styles.itemDisabled
-                    : ''
-                ]}
-                onClick={() => operateRightBtn(item.id)}>
-                <NTooltip
-                  showArrow={false}
-                  placement="right"
-                  class={[item.id === 15 ? 'columnItemTooltip' : '']}>
-                  {{
-                    trigger: () => <img src={item.icon} />,
-                    default: () =>
-                      item.id === 15 ? (
-                        <div class="tools">
-                          {tooltipList.map(i => (
-                            <div onClick={() => operateRightBtn(i.id)}>
-                              <img src={i.icon} />
-                              <div class="tit">{i.name}</div>
-                            </div>
-                          ))}
-                        </div>
-                      ) : item.id === 1 && data.type === 'preview' ? (
-                        item.name2
-                      ) : (
-                        item.name
-                      )
-                  }}
-                </NTooltip>
-              </div>
-            </div>
-          ))}
-        </div>
-        <NTooltip showArrow={false} placement="right">
-          {{
-            trigger: () => (
-              <div
-                class={[
-                  styles.leftHideIcon,
-                  !(columnShow.value && columnPos.value === 'left')
-                    ? styles.leftIconShow
-                    : ''
-                ]}
-                onClick={() => {
-                  columnPos.value = 'left';
-                  columnShow.value = true;
-                }}
-              />
-            ),
-            default: () => '按钮镜像'
-          }}
-        </NTooltip>
-
-        {/* 显示列表 */}
-        {/* <NDrawer
-          v-model:show={popupData.open}
-          class={[styles.drawerContainer, styles.drawerContainerSource]}
-          onAfterLeave={handleClosePopup}
-          showMask={false}
-          blockScroll={false}
-          trapFocus={false}>
-          <NDrawerContent closable>
-            {{
-              header: () => (
-                <TheNoticeBar text={activeName.value || '资源列表'} />
-              ),
-              default: () => (
-                <SourceList
-                  teacherChapterName={data.teacherChapterName}
-                  knowledgePointList={data.knowledgePointList}
-                  courseActiveIndex={popupData.courseActiveIndex}
-                  activeItem={data.itemList[popupData.activeIndex]}
-                  onConfirm={(item: any) => {
-                    popupData.open = false;
-                    toggleMaterial(item.id);
-                  }}
-                />
-              )
-            }}
-          </NDrawerContent>
-        </NDrawer> */}
-        <NModal
-          transformOrigin="center"
-          v-model:show={popupData.open}
-          preset="card"
-          class={[
-            'modalTitle background',
-            styles.drawerContainer,
-            resourcesConBoxClass
-          ]}
-          style={resourcesConDragData.styleDrag.value}
-          title={activeName.value || '资源列表'}>
-          <SourceList
-            teacherChapterName={data.teacherChapterName}
-            knowledgePointList={data.knowledgePointList}
-            courseActiveIndex={popupData.courseActiveIndex}
-            activeItem={data.itemList[popupData.activeIndex]}
-            onConfirm={(item: any) => {
-              popupData.open = false;
-              toggleMaterial(item.id);
-            }}
-          />
-          <Dragbom></Dragbom>
-        </NModal>
-        {/* 章节切换 */}
-        {/* <NDrawer
-          v-model:show={popupData.chapterOpen}
-          class={styles.drawerContainer}
-          onAfterLeave={handleClosePopup}
-          showMask={false}
-          maskClosable={data.selectClassStatus ? false : true}
-          blockScroll={false}
-          trapFocus={false}>
-          <NDrawerContent title="切换章节" closable>
-            <Chapter
-              treeList={popupData.chapterDetails}
-              itemActive={data.detailId as any}
-              onHandleSelect={async (val: any) => {
-                // itemActive: child.id,
-                // itemName: child.name
-                popupData.courseId = val.itemActive;
-                data.selectClassStatus = true;
-              }}
-            />
-          </NDrawerContent>
-        </NDrawer> */}
-        <NModal
-          transformOrigin="center"
-          v-model:show={popupData.chapterOpen}
-          preset="card"
-          class={[
-            'modalTitle background',
-            styles.drawerContainer,
-            chapterConBoxClass
-          ]}
-          style={chapterConBoxDragData.styleDrag.value}
-          title={'切换章节'}>
-          <Chapter
-            treeList={popupData.chapterDetails}
-            itemActive={data.detailId as any}
-            onHandleSelect={async (val: any) => {
-              // itemActive: child.id,
-              // itemName: child.name
-              popupData.courseId = val.itemActive;
-              data.selectClassStatus = true;
-            }}
-          />
-          <Dragbom></Dragbom>
-        </NModal>
-        {/* 批注 */}
-        {studyData.penShow && (
-          <Pen
-            isDrag={true}
-            show={studyData.type === 'pen'}
-            type={studyData.type}
-            close={() => closeStudyTool()}
-          />
-        )}
-
-        {studyData.whiteboardShow && (
-          <Pen
-            isDrag={true}
-            show={studyData.type === 'whiteboard'}
-            type={studyData.type}
-            close={() => closeStudyTool()}
-          />
-        )}
-
-        {studyData.callShow && (
-          <Pen
-            isDrag={true}
-            imagePos={columnPos.value}
-            callStudents={studyData.callStudentList}
-            show={studyData.type === 'call'}
-            type={studyData.type}
-            close={() => {
-              closeStudyTool();
-              studyData.callShow = false;
-            }}
-          />
-        )}
-
-        {/* 选择课件 */}
-        <NModal
-          transformOrigin="center"
-          v-model:show={data.selectClassStatus}
-          preset="card"
-          class={[
-            'modalTitle background',
-            // styles.attendClassModal,
-            styles.selectClassModal,
-            selCourBoxClass
-          ]}
-          style={selCourDragData.styleDrag.value}
-          title={'选择课件'}>
-          <SelectClass
-            classId={data.classId}
-            courseId={popupData.courseId}
-            instrumentId={data.instrumentId}
-            lessonCoursewareSubjectList={data.lessonCoursewareSubjectList}
-            onConfirm={async (val: any) => {
-              popupData.chapterLoading = true;
-              try {
-                data.selectClassStatus = false;
-                data.detailId = val.itemActive;
-                data.courseId = val.chapterId;
-                const ids = formatParentId(
-                  val.itemActive,
-                  popupData.chapterDetails
-                );
-                data.lessonCoursewareDetailId = ids[0];
-                // 更新上课记录 上课的时候才更新
-                if (data.type !== 'preview') {
-                  await classCourseScheduleUpdate();
-                }
-                await getDetail();
-                popupData.activeIndex = 0;
-                popupData.chapterOpen = false;
-              } catch {
-                //
-              }
-              popupData.chapterLoading = false;
-            }}
-          />
-          <Dragbom></Dragbom>
-        </NModal>
-
-        {/* 布置作业 */}
-        <NModal
-          transformOrigin="center"
-          v-model:show={data.modelAttendStatus}
-          preset="card"
-          title={'课后作业'}
-          style={modelTrainHomeWordConBoxDragData.styleDrag.value}
-          class={[
-            'modalTitle',
-            styles.removeVisiable,
-            modelTrainHomeWordConBoxClass
-          ]}>
-          <div class={styles.studentRemove}>
-            <p>{data.modalAttendMessage}</p>
-            <NSpace class={styles.btnGroupModal} justify="center">
-              <NButton
-                type="default"
-                round
-                onClick={() => {
-                  data.modelTrainStatus = true;
-                  nextTick(() => {
-                    getModalHeight();
-                  });
-                  data.modelAttendStatus = false;
-                }}>
-                布置作业
-              </NButton>
-              <NButton
-                type="primary"
-                round
-                onClick={() => {
-                  handleStop();
-                  data.modelAttendStatus = false;
-                }}>
-                取消
-              </NButton>
-            </NSpace>
-          </div>
-          <Dragbom></Dragbom>
-        </NModal>
-
-        {/* 训练设置 */}
-        {/* <NModal
-          transformOrigin="center"
-          v-model:show={data.modelTrainStatus}
-          preset="card"
-          class={[styles.attendClassModal, styles.trainClassModal]}
-          title={'作业设置'}>
-          <ClassWork
-            detailId={data.detailId}
-            instrumentId={data.instrumentId}
-            courseScheduleId={data.classId}
-            activeName={activeName.value}
-            classGroupId={data.classGroupId}
-            onClose={() => (data.modelTrainStatus = false)}
-          />
-        </NModal> */}
-        <NModal
-          v-model:show={data.modelTrainStatus}
-          preset="card"
-          class={[
-            'modalTitle background',
-            styles.workVisiable,
-            modelTrainStatusConBoxClass
-          ]}
-          style={modelTrainStatusConBoxDragData.styleDrag.value}
-          title={'布置作业'}>
-          <div id="model-homework-height" class={styles.workContainer}>
-            <div class={styles.workTrain}>
-              <Train
-                from={'class'}
-                cardType="homeworkRecord"
-                lessonPreTraining={{
-                  title: data.lessonPreTrainingId
-                    ? ''
-                    : data.teacherChapterName + '-课后作业',
-                  chapterId: data.courseId, // 课件编号
-                  id: data.lessonPreTrainingId // 作业编号
-                }}
-                coursewareKnowledgeDetailId={data.detailId}
-                classGroupId={data.classGroupId}
-                courseScheduleId={data.classId}
-                onChange={async (val: any) => {
-                  data.modelTrainStatus = val.status;
-                  // getCoursewareList();
-                  if (val.saveWork && data.classId) {
-                    data.lessonPreTrainingId = val.lessonPreTrainingId;
-                  }
-                }}
-              />
-            </div>
-            <div class={styles.resourceMain}>
-              <ResourceMain from={'class'} cardType="homerowk-record" />
-            </div>
-          </div>
-          <Dragbom></Dragbom>
-        </NModal>
-
-        {/* <NModal
-          transformOrigin="center"
-          class={['modalTitle background', metronomeConBoxClass]}
-          title={'节拍器'}
-          preset="card"
-          v-model:show={showModalBeat.value}
-          style={{
-            width: '687px',
-            ...metronomeConBoxDragData.styleDrag.value
-          }}>
-          <div class={styles.modeWrap}>
-            <iframe
-              src={`${vaildUrl()}/metronome/?id=${new Date().getTime()}`}
-              scrolling="no"
-              frameborder="0"
-              width="100%"
-              onLoad={(val: any) => {
-                iframeDislableKeyboard(val.target);
-              }}
-              height={'650px'}></iframe>
-            <Dragbom></Dragbom>
-          </div>
-        </NModal> */}
-        <Metronome
-          v-model={showModalBeat.value}
-          dragClass={metronomeConBoxClass}
-          dragStyle={metronomeConBoxDragData.styleDrag.value}></Metronome>
-        <NModal
-          transformOrigin="center"
-          class={['background']}
-          v-model:show={showModalTone.value}>
-          <div>
-            <PlaceholderTone
-              onClose={() => {
-                showModalTone.value = false;
-              }}></PlaceholderTone>
-          </div>
-        </NModal>
-        <NModal
-          v-model:show={showModalTime.value}
-          class={timerMeterConBoxClass}
-          style={timerMeterConDragData.styleDrag.value}>
-          <div>
-            <img
-              class={styles.timerMeterClose}
-              src={timerMeterClose}
-              onClick={() => {
-                showModalTime.value = false;
-              }}
-            />
-            <div class="topDragDom"></div>
-            <TimerMeter></TimerMeter>
-          </div>
-        </NModal>
-
-        <NModal
-          v-model:show={data.selectResourceStatus}
-          class={['modalTitle', styles.selectMusicModal, selResourBoxClass]}
-          style={selResourDragData.styleDrag.value}
-          preset="card"
-          title={'选择资源'}>
-          <SelectResources from="class" />
-          <Dragbom></Dragbom>
-        </NModal>
-        <NModal
-          transformOrigin="center"
-          v-model:show={data.removeVisiable}
-          preset="card"
-          class={['modalTitle', styles.removeVisiable, removeResourBoxClass]}
-          style={removeResourDragData.styleDrag.value}
-          title={data.removeTitle}>
-          <div class={styles.studentRemove}>
-            <p>{data.removeContent}</p>
-
-            <NSpace class={styles.btnGroupModal} justify="center">
-              <NButton
-                round
-                onClick={() => {
-                  if (data.removeCourseStatus) {
-                    if (globalState.application) {
-                      document.exitFullscreen
-                        ? document.exitFullscreen()
-                        : document.mozCancelFullScreen
-                        ? document.mozCancelFullScreen()
-                        : document.webkitExitFullscreen
-                        ? document.webkitExitFullscreen()
-                        : '';
-                      emit('close');
-                    } else {
-                      window.close();
-                    }
-                  } else {
-                    data.removeVisiable = false;
-                  }
-                }}>
-                {data.removeCourseStatus ? '结束课程' : '取消'}
-              </NButton>
-              <NButton
-                round
-                type="primary"
-                onClick={() => {
-                  //
-                  if (data.removeCourseStatus) {
-                    data.modelTrainStatus = true;
-                    data.removeVisiable = false;
-                    nextTick(() => {
-                      getModalHeight();
-                    });
-                  } else {
-                    if (globalState.application) {
-                      document.exitFullscreen
-                        ? document.exitFullscreen()
-                        : document.mozCancelFullScreen
-                        ? document.mozCancelFullScreen()
-                        : document.webkitExitFullscreen
-                        ? document.webkitExitFullscreen()
-                        : '';
-                      emit('close');
-                    } else {
-                      window.close();
-
-                      if (route.query.source === 'admin') {
-                        storage.remove(ACCESS_TOKEN_ADMIN);
-                        window.parent.postMessage(
-                          {
-                            api: 'iframe_exit'
-                          },
-                          '*'
-                        );
-                      }
-                    }
-                  }
-                }}>
-                {data.removeCourseStatus ? '布置作业' : '确定'}
-              </NButton>
-            </NSpace>
-            <Dragbom></Dragbom>
-          </div>
-        </NModal>
-        <NModal
-          style={nextEndBoxDragData.styleDrag.value}
-          z-index={3000}
-          transformOrigin="center"
-          v-model:show={data.nextEndShow}
-          preset="card"
-          title={'提示'}
-          class={['modalTitle', styles.removeVisiable, nextEndBoxClass]}>
-          <div class={styles.studentRemove}>
-            <p>当前课件已结束,是否进入下一章节</p>
-            <NSpace class={styles.btnGroupModal} justify="center">
-              <NButton
-                type="default"
-                round
-                onClick={() => {
-                  data.nextEndShow = false;
-                }}>
-                取消
-              </NButton>
-              <NButton
-                type="primary"
-                round
-                onClick={() => {
-                  data.nextEndShow = false;
-                  handleNextEnd();
-                }}>
-                确认
-              </NButton>
-            </NSpace>
-            <Dragbom></Dragbom>
-          </div>
-        </NModal>
-      </div>
-    );
-  }
-});
+import {
+  defineComponent,
+  onMounted,
+  reactive,
+  onUnmounted,
+  ref,
+  Transition,
+  computed,
+  nextTick,
+  watch,
+  toRef
+} from 'vue';
+import styles from './index.module.less';
+import 'plyr/dist/plyr.css';
+import MusicScore from './component/musicScore';
+// import iconChange from './image/icon-change.png';
+// import iconMenu from './image/icon-menu.png';
+// import iconUp from './image/icon-up.png';
+// import iconDown from './image/icon-down.png';
+// import iconNote from './image/icon-note.png';
+// import iconWhiteboard from './image/icon-whiteboard.png';
+// import iconAssignHomework from './image/icon-assignHomework.png';
+// import iconClose from './image/icon-close.png';
+// import iconOverPreivew from './image/icon-over-preview.png';
+import { Vue3Lottie } from 'vue3-lottie';
+import playLoadData from './datas/data.json';
+// import Moveable from 'moveable';
+import VideoPlay from './component/video-play';
+import {
+  useMessage,
+  NDrawer,
+  NDrawerContent,
+  NModal,
+  NSpace,
+  NButton,
+  NCollapse,
+  NCollapseItem,
+  NTooltip
+} from 'naive-ui';
+import CardType from '@/components/card-type';
+import Pen from './component/tools/pen';
+import AudioPay from './component/audio-pay';
+import TrainSettings from './model/train-settings';
+import { useRoute } from 'vue-router';
+import {
+  api_teacherChapterLessonCoursewareDetail,
+  courseScheduleUpdate,
+  lessonCoursewareDetail,
+  lessonPreTrainingPage,
+  queryCourseware
+} from '../prepare-lessons/api';
+import { vaildUrl } from '/src/utils/urlUtils';
+import TimerMeter from '/src/components/timerMeter';
+import Metronome from '/src/components/Metronome';
+import { iframeDislableKeyboard, px2vw } from '/src/utils';
+import PlaceholderTone from '/src/components/layout/modals/placeholderTone';
+import { state as globalState } from '/src/state';
+import Chapter from './model/chapter';
+import { useRouter } from 'vue-router';
+import { useUserStore } from '@/store/modules/users';
+import iconNote from './new-image/icon-note.png';
+import iconWhite from './new-image/icon-white.png';
+import timerMeterClose from './image/close.png';
+import rightIconEnd from './image/right_icon1.png';
+import rightIconArrange from './image/right_icon2.png';
+import rightIconPostil from './image/right_icon3.png';
+import rightIconWhiteboard from './image/right_icon4.png';
+import rightIconMetronome from './image/right_icon5.png';
+import rightIconTuner from './image/right_icon6.png';
+import rightIconTimer from './image/right_icon7.png';
+import rightIconCall from './image/right_icon10.png';
+import rightIconPackUp from './image/right_icon11.png';
+import leftIconPackUp from './image/right_icon8.png';
+import rightIconMusic from './image/right_icon9.png';
+import bottomIconSwitch from './image/bottom_icon1.png';
+import bottomIconResource from './image/bottom_icon2.png';
+import bottomIconPre from './image/bottom_icon3.png';
+import bottomIconNext from './image/bottom_icon4.png';
+import rightIconTool from './image/right_icon12.png';
+import rightHideIcon from './image/right_hide_icon.png';
+import leftHideIcon from './image/left_hide_icon.png';
+import SelectResources from '../prepare-lessons/model/select-resources';
+import { getStudentAfterWork, getStudentList } from '../studentList/api';
+import TheNoticeBar from '/src/components/TheNoticeBar';
+import ClassWork from './model/class-work';
+import SelectClass from './model/select-class';
+import SourceList from './model/source-list';
+import RhythmModal from './component/rhythm-modal';
+import InstruemntDetail from '/src/views/prepare-lessons/model/source-instrument/detail';
+import TheotyDetail from '/src/views/prepare-lessons/model/source-knowledge/detail';
+import MusicDetail from '/src/views/prepare-lessons/model/source-music/detail';
+import ListenModal from '/src/components/card-preview/listen-modal';
+import Train from '../prepare-lessons/components/lesson-main/train';
+import ResourceMain from '../prepare-lessons/components/resource-main';
+import { useResizeObserver } from '@vueuse/core';
+import { storage } from '/src/utils/storage';
+import { ACCESS_TOKEN_ADMIN } from '/src/store/mutation-types';
+import useDrag from '@/hooks/useDrag';
+import { getGuidanceShow } from '@/hooks/useDrag/useDragGuidance';
+import Dragbom from '@/hooks/useDrag/dragbom';
+import { api_cousseScheduleDetail } from '/src/api/user';
+
+export type ToolType = 'init' | 'pen' | 'whiteboard' | 'call';
+export type ToolItem = {
+  type: ToolType;
+  name: string;
+  icon: string;
+};
+
+export default defineComponent({
+  name: 'CoursewarePlay',
+  props: {
+    type: {
+      type: String,
+      default: ''
+    },
+    courseId: {
+      type: String,
+      default: ''
+    },
+    instrumentId: {
+      type: [String, Number],
+      default: ''
+    },
+    // 教材编号
+    lessonCourseId: {
+      type: [String, Number],
+      default: ''
+    },
+    detailId: {
+      type: String,
+      default: ''
+    },
+    // 班级编号
+    classGroupId: {
+      type: String,
+      default: ''
+    },
+    // 上课记录编号
+    classId: {
+      type: String,
+      defaault: ''
+    },
+    preStudentNum: {
+      type: [String, Number],
+      default: ''
+    }
+  },
+  emits: ['close'],
+  setup(props, { emit }) {
+    const message = useMessage();
+    const route = useRoute();
+    const router = useRouter();
+    const users = useUserStore();
+    /** 设置播放容器 16:9 */
+    const parentContainer = reactive({
+      width: '100vw'
+    });
+    // const NPopoverRef = ref();
+    // const setContainer = () => {
+    //   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;
+    //   } else {
+    //     parentContainer.width = width + 'px';
+    //   }
+    // };
+    const handleInit = (type = 0) => {
+      //设置容器16:9
+      // setContainer();
+    };
+    handleInit();
+    onUnmounted(() => {
+      handleInit(1);
+      window.removeEventListener('online', handleOnline);
+      window.removeEventListener('offline', handleOffline);
+    });
+
+    const data = reactive({
+      type: 'class' as '' | 'preview' | 'class', // 预览类型
+      courseId: '' as any, // 课件编号
+      instrumentId: '' as any, // 声部编号
+      lessonCourseId: '' as any, // 教材编号
+      lessonCoursewareDetailId: '' as any, // 章节
+      lessonCoursewareSubjectList: [] as any, // 教材上的声部
+      detailId: '' as any, // 编号 - 课程编号
+      classGroupId: '' as any, // 上课时需要 班级编号
+      classId: '' as any, // 上课编号
+      preStudentNum: '' as any, // 班上学生
+      // detail: null,
+      knowledgePointList: [] as any,
+      itemList: [] as any,
+      // showHead: true,
+      // isCourse: false,
+      // isRecordPlay: false,
+      videoRefs: {} as any[],
+      audioRefs: {} as any[],
+      modelAttendStatus: false, // 布置作业提示弹窗
+      modalAttendMessage: '本节课未设置课后作业,是否继续?',
+      modelTrainStatus: false, // 训练设置
+      selectClassStatus: false, // 选择课件
+      homeworkStatus: true, // 布置作业完成时
+      removeVisiable: false,
+      nextEndShow: false,
+      removeTitle: '',
+      removeContent: '',
+      removeCourseStatus: false, // 是否布置作业
+
+      teacherChapterName: '',
+      lessonPreTrainingId: null,
+      selectResourceStatus: false,
+      videoState: 'init' as 'init' | 'play',
+      videoItemRef: null as any,
+      animationState: 'start' as 'start' | 'end'
+    });
+    const activeData = reactive({
+      isAutoPlay: false, // 是否自动播放
+      nowTime: 0,
+      model: true, // 遮罩
+      isAnimation: true, // 是否动画
+      // videoBtns: true, // 视频
+      // currentTime: 0,
+      // duration: 0,
+      timer: null as any,
+      item: null as any
+    });
+
+    // 键盘事件监听状态
+    const listenerKeyUpState = ref(false);
+
+    const getDetail = async () => {
+      try {
+        const res = await api_teacherChapterLessonCoursewareDetail(
+          data.courseId
+        );
+
+        data.teacherChapterName = res.data.name || '';
+        // 布置的作业编号
+        data.lessonPreTrainingId = res.data.lessonPreTrainingId;
+        // activeData.isAutoPlay = res.data.autoPlay || false; // 自动播放
+        const tempRows = res.data.chapterKnowledgeList || [];
+        const temp: any = [];
+        const allItem: any = [];
+        tempRows.forEach((row: any, index: number) => {
+          if (!Array.isArray(row.chapterKnowledgeMaterialList)) {
+            return;
+          }
+          const childList: any[] = [];
+          row.chapterKnowledgeMaterialList.forEach((child: any) => {
+            if (!child.removeFlag) {
+              childList.push({
+                id: child.id,
+                materialId: child.bizId,
+                coverImg: child.bizInfo.coverImg,
+                type: child.type,
+                title: child.bizInfo.name,
+                dataJson: child.dataJson,
+                isCollect: !!child.favoriteFlag,
+                isSelected: child.source === 'PLATFORM' ? true : false,
+                content: child.bizInfo.content,
+                parentIndex: index
+              });
+            }
+          });
+          temp.push({
+            title: row.name,
+            list: childList
+          });
+          allItem.push(...childList);
+        });
+
+        data.knowledgePointList = temp;
+        data.itemList = allItem?.map((m: any) => {
+          return {
+            ...m,
+            iframeRef: null,
+            videoEle: null,
+            audioEle: null,
+            autoPlay: res.data.autoPlay || false, //加载完成是否自动播放
+            isprepare: false, // 视频是否加载完成
+            isRender: false // 是否渲染了
+          };
+        });
+        setTimeout(() => {
+          data.animationState = 'end';
+        }, 500);
+      } catch (e) {
+        //
+        console.log(e);
+      }
+    };
+
+    const showModalBeat = ref(false);
+    const showModalTone = ref(false);
+    const showModalTime = ref(false);
+    // ifram事件处理
+    const iframeHandle = (ev: MessageEvent) => {
+      // console.log(ev.data?.api, ev.data, 'ev.data');
+      if (ev.data?.api === 'headerTogge') {
+        activeData.model =
+          ev.data.show || (ev.data.playState == 'play' ? false : true);
+      }
+
+      //
+      if (ev.data?.api === 'onAttendToggleMenu') {
+        activeData.model = !activeData.model;
+      }
+
+      if (ev.data?.api === 'api_fingerPreView') {
+        clearInterval(activeData.timer);
+        activeData.model = !ev.data.state;
+      }
+      //
+      if (ev.data?.api === 'documentBodyKeyup') {
+        if (ev.data?.code === 'ArrowUp') {
+          setModalOpen();
+          handlePreAndNext('up');
+        }
+
+        if (ev.data?.code === 'ArrowDown') {
+          setModalOpen();
+          handlePreAndNext('down');
+        }
+      }
+
+      // 点名返回
+      if (ev.data?.api === 'callBack') {
+        closeStudyTool();
+        studyData.callShow = false;
+      }
+
+      if (ev.data?.api === 'onLogin') {
+        const documentDom: any = document;
+        documentDom.exitFullscreen
+          ? documentDom.exitFullscreen()
+          : documentDom.mozCancelFullScreen
+          ? documentDom.mozCancelFullScreen()
+          : documentDom.webkitExitFullscreen
+          ? documentDom.webkitExitFullscreen()
+          : '';
+        users.logout();
+        router.replace('/login');
+      }
+    };
+
+    onMounted(async () => {
+      // initMoveable();
+      const query = route.query;
+      // console.log(query, props.preStudentNum, '学生人数');
+      // 先取参数,
+      data.type = props.type || (query.type as any);
+      data.courseId = props.courseId || query.courseId;
+      data.instrumentId = props.instrumentId || query.instrumentId;
+      data.detailId = props.detailId || query.detailId;
+      data.lessonCourseId = props.lessonCourseId || query.lessonCourseId;
+      data.classGroupId = props.classGroupId || query.classGroupId;
+      data.classId = props.classId || query.classId;
+      data.preStudentNum = props.preStudentNum || query.preStudentNum;
+      window.addEventListener('message', iframeHandle);
+
+      if (data.classId) {
+        const res = await api_cousseScheduleDetail(data.classId);
+        data.courseId = res.data.useChapterLessonCoursewareId;
+      }
+
+      getDetail();
+      getLessonCoursewareDetail();
+      if (data.type === 'preview') {
+        // 预览隐藏点名和布置作业
+        const hideListIds = [2, 10];
+        let index = rightList.length - 1;
+        while (index >= 0) {
+          if (hideListIds.includes(rightList[index].id)) {
+            console.log(index);
+            rightList.splice(index, 1);
+          }
+          index--;
+        }
+      }
+      rollCallStudentList();
+
+      window.addEventListener('online', handleOnline);
+      window.addEventListener('offline', handleOffline);
+    });
+    const lineTimer = ref();
+    const handleOnline = () => {
+      clearTimeout(lineTimer.value);
+    };
+    const handleOffline = () => {
+      lineTimer.value = setTimeout(() => {
+        message.info('请检查网络', {
+          showIcon: false,
+          render: (props: any) => {
+            return (
+              <div style="background: rgba(0,0,0,.6); color: #fff; border-radius: 8px; padding: 6px 16px;">
+                {props.content}
+              </div>
+            );
+          }
+        });
+      }, 3000);
+    };
+
+    // const onFullScreen = () => {
+    //   if (data.type === 'preview') {
+    //     const el: any = document.querySelector('#app');
+
+    //     if (el.mozRequestFullScreen) {
+    //       el.mozRequestFullScreen();
+    //     } else if (el.webkitRequestFullscreen) {
+    //       el.webkitRequestFullscreen();
+    //     } else if (el.requestFullScreen) {
+    //       el.requestFullscreen();
+    //     }
+    //   }
+    // };
+
+    const popupData = reactive({
+      open: false,
+      activeIndex: 0,
+      courseActiveIndex: 0, // 课件选择的第几个
+      toolOpen: false, // 工具弹窗控制
+      chapterOpen: false, // 切换章节
+      chapterDetails: [] as any,
+      courseId: null as any, // 章节编号
+      chapterLoading: false // 加载数据
+    });
+
+    watch(
+      () => [popupData.open, popupData.chapterOpen],
+      val => {
+        // 为了处理window电脑滑动时的问题 - pointr-events
+        setTimeout(() => {
+          const drawers = document.querySelectorAll('.n-drawer-container');
+          if (val[0] || val[1]) {
+            drawers?.forEach(drawer => {
+              drawer.classList.remove('n-drawer-container-relative');
+            });
+          } else {
+            drawers?.forEach(drawer => {
+              drawer.classList.add('n-drawer-container-relative');
+            });
+          }
+        }, 200);
+      }
+    );
+    const formatParentId = (id: any, list: any, ids = [] as any) => {
+      for (const item of list) {
+        if (item.knowledgeList && item.knowledgeList.length > 0) {
+          const cIds: any = formatParentId(id, item.knowledgeList, [
+            ...ids,
+            item.id
+          ]);
+          if (cIds.includes(id)) {
+            return cIds;
+          }
+        }
+        if (item.id === id) {
+          return [...ids, id];
+        }
+      }
+      return ids;
+    };
+
+    /** 获取章节 */
+    const getLessonCoursewareDetail = async () => {
+      try {
+        const res = await lessonCoursewareDetail({
+          id: data.lessonCourseId,
+          instrumentId: data.instrumentId
+        });
+
+        popupData.chapterDetails = res.data.lessonList || [];
+
+        const ids = formatParentId(data.detailId, popupData.chapterDetails);
+        data.lessonCoursewareDetailId = ids[0];
+        console.log(res.data, 'data');
+        data.lessonCoursewareSubjectList = res.data.instrumentList || [];
+      } catch {
+        //
+      }
+    };
+    /** 更新上课记录 */
+    const classCourseScheduleUpdate = async () => {
+      try {
+        if (!data.classId) return;
+        await courseScheduleUpdate({
+          lessonCoursewareKnowledgeDetailId: data.detailId,
+          id: data.classId
+        });
+      } catch {
+        //
+      }
+    };
+
+    const activeName = computed(() => {
+      let name = '';
+      popupData.chapterDetails.forEach((chapter: any) => {
+        if (chapter.id === data.lessonCoursewareDetailId) {
+          // name = chapter.name;
+          chapter.knowledgeList?.forEach((know: any) => {
+            if (know.id === data.detailId) {
+              name = know.name;
+            }
+          });
+        }
+      });
+      return name;
+    });
+
+    /**停止所有的播放 */
+    const handleStop = (isStop = true) => {
+      // console.log(isStop, 'stop');
+      for (let i = 0; i < data.itemList.length; i++) {
+        const activeItem = data.itemList[i];
+        if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
+          try {
+            if (isStop) {
+              activeItem.videoEle?.currentTime(0);
+            }
+            activeItem.videoEle?.pause();
+          } catch (e: any) {
+            // console.log(e, 'e');
+          }
+        }
+        if (activeItem.type === 'SONG' && activeItem.audioEle) {
+          activeItem.audioEle?.stop();
+        }
+        // console.log('🚀 ~ activeItem:', activeItem)
+        // 停止曲谱的播放
+        if (activeItem.type === 'MUSIC') {
+          activeItem.iframeRef?.contentWindow?.postMessage(
+            { api: 'setPlayState' },
+            '*'
+          );
+        }
+
+        if (
+          activeItem.type === 'INSTRUMENT' ||
+          activeItem.type === 'MUSICIAN' ||
+          activeItem.type === 'MUSIC_WIKI'
+        ) {
+          activeItem.iframeRef?.handleChangeAudio('pause');
+        }
+
+        // console.log(activeItem.type, 'activeItem.type');
+        if (activeItem.type === 'RHYTHM') {
+          activeItem.iframeRef?.contentWindow?.postMessage(
+            { api: 'setPlayState', data: false },
+            '*'
+          );
+        }
+
+        if (activeItem.type === 'LISTEN') {
+          activeItem.iframeRef?.contentWindow?.postMessage(
+            { api: 'setPlayState' },
+            '*'
+          );
+        }
+      }
+    };
+    // 切换素材
+    const toggleMaterial = (itemActive: any) => {
+      const index = data.itemList.findIndex((n: any) => n.id == itemActive);
+      if (index > -1) {
+        handleSwipeChange(index);
+      }
+    };
+    /** 延迟收起模态框 */
+    const setModelOpen = () => {
+      clearTimeout(activeData.timer);
+      message.destroyAll();
+      activeData.timer = setTimeout(() => {
+        activeData.model = false;
+        Object.values(data.videoRefs).map((n: any) =>
+          n?.toggleHideControl(false)
+        );
+
+        Object.values(data.audioRefs).map((n: any) =>
+          n?.toggleHideControl(false)
+        );
+      }, 4000);
+    };
+
+    /** 立即收起所有的模态框 */
+    const clearModel = () => {
+      clearTimeout(activeData.timer);
+      message.destroyAll();
+      activeData.model = false;
+      Object.values(data.videoRefs).map((n: any) =>
+        n?.toggleHideControl(false)
+      );
+      Object.values(data.audioRefs).map((n: any) =>
+        n?.toggleHideControl(false)
+      );
+    };
+    const toggleModel = (type = true) => {
+      activeData.model = type;
+      Object.values(data.videoRefs).map((n: any) => n?.toggleHideControl(type));
+      Object.values(data.audioRefs).map((n: any) => n?.toggleHideControl(type));
+    };
+
+    // 双击
+    const handleDbClick = (item: any) => {
+      if (item && item.type === 'VIDEO') {
+        const videoEle: HTMLVideoElement = item.videoEle;
+        if (videoEle) {
+          if (videoEle.paused) {
+            message.destroyAll();
+            videoEle.play();
+          } else {
+            message.warning('已暂停');
+            videoEle.pause();
+          }
+        }
+      }
+    };
+
+    // ppt iframe 点击事件
+    // const iframeClick = () => {
+    // 	if(document.all) {
+    // 		document.getElementById("iframe-ppt")?.attachEvent("click", () => {
+    //       activeData.model = !activeData.model
+    //     });
+    // 	} else {
+    // 		document.getElementById("iframe-ppt")?.contentWindow?.postMessage({
+    //       api: 'onAttendToggleMenu'
+    //     }, '*');
+    // 	}
+    // }
+
+    // 切换播放
+    // const togglePlay = (m: any, isPlay: boolean) => {
+    //   if (isPlay) {
+    //     m.videoEle?.play();
+    //   } else {
+    //     m.videoEle?.pause();
+    //   }
+    // };
+
+    // const showIndex = ref(-4);
+    const effectIndex = ref(3);
+    const effects = [
+      {
+        prev: {
+          transform: 'translate3d(0, 0, -800px) rotateX(180deg)'
+        },
+        next: {
+          transform: 'translate3d(0, 0, -800px) rotateX(-180deg)'
+        }
+      },
+      {
+        prev: {
+          transform: 'translate3d(-100%, 0, -800px)'
+        },
+        next: {
+          transform: 'translate3d(100%, 0, -800px)'
+        }
+      },
+      {
+        prev: {
+          transform: 'translate3d(-50%, 0, -800px) rotateY(80deg)'
+        },
+        next: {
+          transform: 'translate3d(50%, 0, -800px) rotateY(-80deg)'
+        }
+      },
+      {
+        prev: {
+          transform: 'translate3d(-100%, 0, -800px) rotateY(-120deg)'
+        },
+        next: {
+          transform: 'translate3d(100%, 0, -800px) rotateY(120deg)'
+        }
+      },
+      // 风车4
+      {
+        prev: {
+          transform: 'translate3d(-50%, 50%, -800px) rotateZ(-14deg)',
+          opacity: 0
+        },
+        next: {
+          transform: 'translate3d(50%, 50%, -800px) rotateZ(14deg)',
+          opacity: 0
+        }
+      },
+      // 翻页5
+      {
+        prev: {
+          transform: 'translateZ(-800px) rotate3d(0, -1, 0, 90deg)',
+          opacity: 0
+        },
+        next: {
+          transform: 'translateZ(-800px) rotate3d(0, 1, 0, 90deg)',
+          opacity: 0
+        },
+        current: { transitionDelay: '700ms' }
+      }
+    ];
+
+    const acitveTimer = ref();
+    // 轮播切换
+    const handleSwipeChange = (index: number) => {
+      // 如果是当前正在播放 或者是视频最后一个
+      if (popupData.activeIndex == index) return;
+      data.animationState = 'start';
+      data.videoState = 'init';
+      handleStop();
+      clearTimeout(acitveTimer.value);
+      activeData.model = true;
+      checkedAnimation(popupData.activeIndex, index);
+      popupData.activeIndex = index;
+
+      // 处理资源列表滚动
+      nextTick(() => {
+        scrollResourceSection();
+      });
+
+      acitveTimer.value = setTimeout(
+        () => {
+          const item = data.itemList[index];
+
+          if (item) {
+            if (item.type == 'MUSIC') {
+              activeData.model = true;
+            }
+            if (item.type === 'SONG') {
+              // 自动播放下一个音频
+              clearTimeout(activeData.timer);
+              message.destroyAll();
+              // item.autoPlay = false;
+              // nextTick(() => {
+              //   item.audioEle?.onPlay();
+              // });
+            }
+            if (item.type === 'VIDEO') {
+              // 自动播放下一个视频
+              clearTimeout(activeData.timer);
+              message.destroyAll();
+              nextTick(() => {
+                if (item.error) {
+                  // console.log(item, 'item error');
+                  item.videoEle?.src(item.content);
+                  item.error = false;
+                  //   item.videoEle?.onPlay();
+                }
+                data.animationState = 'end';
+              });
+            }
+            if (item.type === 'PPT') {
+              //
+            }
+          }
+        },
+        activeData.isAnimation ? 800 : 0
+      );
+    };
+
+    /** 是否有转场动画 */
+    const checkedAnimation = (index: number, nextIndex?: number) => {
+      const item = data.itemList[index];
+      const nextItem = data.itemList[nextIndex!];
+      if (nextItem) {
+        if (nextItem.knowledgePointId != item.knowledgePointId) {
+          activeData.isAnimation = true;
+          return;
+        }
+        const videoEle = item.videoEle;
+        const nextVideo = nextItem.videoEle;
+        if (videoEle && videoEle.duration < 8 && index < nextIndex!) {
+          activeData.isAnimation = false;
+        } else if (nextVideo && nextVideo.duration < 8 && index > nextIndex!) {
+          activeData.isAnimation = false;
+        } else {
+          activeData.isAnimation = true;
+        }
+      } else {
+        activeData.isAnimation = item?.adviseStudyTimeSecond < 8 ? false : true;
+      }
+    };
+
+    // 上一个知识点, 下一个知识点
+    const handlePreAndNext = async (type: string) => {
+      if (type === 'up') {
+        // 判断上面是否还有章节
+        if (popupData.activeIndex > 0) {
+          handleSwipeChange(popupData.activeIndex - 1);
+          return;
+        }
+
+        // 获取当前是哪个章节
+        let detailIndex = popupData.chapterDetails.findIndex(
+          (item: any) => item.id == data.lessonCoursewareDetailId
+        );
+        const detailItem =
+          popupData.chapterDetails[detailIndex]?.knowledgeList || [];
+        let lessonIndex = detailItem.findIndex(
+          (item: any) => item.id == data.detailId
+        );
+
+        let lessonStatus = false; // 当前章节上面是否有内容
+        let lessonCoursewareDetailId = '';
+        let coursewareDetailKnowledgeId = '';
+        while (lessonIndex >= 0) {
+          lessonIndex--;
+
+          if (lessonIndex >= 0) {
+            if (detailItem[lessonIndex].coursewareNum > 0) {
+              lessonStatus = true;
+              lessonCoursewareDetailId =
+                detailItem[lessonIndex].lessonCoursewareDetailId;
+              coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
+            }
+          }
+
+          if (lessonStatus) {
+            break;
+          }
+        }
+        // 判断当前章节下面课程是否有内容,否则往上一个章节走
+        if (lessonStatus) {
+          popupData.courseId = coursewareDetailKnowledgeId;
+          data.selectClassStatus = true;
+          return;
+        }
+
+        let prevLessonStatus = false;
+        while (detailIndex >= 0) {
+          detailIndex--;
+          const tempDetail =
+            popupData.chapterDetails[detailIndex]?.knowledgeList || [];
+          let tempLessonLength = tempDetail.length;
+          while (tempLessonLength > 0) {
+            if (tempDetail[tempLessonLength - 1].coursewareNum > 0) {
+              prevLessonStatus = true;
+              lessonCoursewareDetailId =
+                tempDetail[tempLessonLength - 1].lessonCoursewareDetailId;
+              coursewareDetailKnowledgeId = tempDetail[tempLessonLength - 1].id;
+            }
+            tempLessonLength--;
+            if (prevLessonStatus) {
+              break;
+            }
+          }
+
+          if (prevLessonStatus) {
+            break;
+          }
+        }
+
+        // 判断当前章节下面课程是否有内容,否则往上一个章节走
+        if (prevLessonStatus) {
+          popupData.courseId = coursewareDetailKnowledgeId;
+          data.selectClassStatus = true;
+          return;
+        }
+      } else {
+        if (popupData.activeIndex < data.itemList.length - 1) {
+          handleSwipeChange(popupData.activeIndex + 1);
+          return;
+        }
+        if (!isDownArrow.value) return;
+        data.nextEndShow = true;
+      }
+    };
+    // 当前课件结束之后选择下一个课件
+    function handleNextEnd() {
+      // 获取当前是哪个章节
+      let detailIndex = popupData.chapterDetails.findIndex(
+        (item: any) => item.id == data.lessonCoursewareDetailId
+      );
+      const detailItem =
+        popupData.chapterDetails[detailIndex]?.knowledgeList || [];
+      let lessonIndex = detailItem.findIndex(
+        (item: any) => item.id == data.detailId
+      );
+
+      let lessonStatus = false; // 当前章节下面是否有内容
+      let lessonCoursewareDetailId = '';
+      let coursewareDetailKnowledgeId = '';
+      while (lessonIndex < detailItem.length - 1) {
+        lessonIndex++;
+        if (lessonIndex >= 0) {
+          if (detailItem[lessonIndex].coursewareNum > 0) {
+            lessonStatus = true;
+            lessonCoursewareDetailId =
+              detailItem[lessonIndex].lessonCoursewareDetailId;
+            coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
+          }
+        }
+        if (lessonStatus) {
+          break;
+        }
+      }
+      // 判断当前章节下面课程是否有内容,否则往下一个章节走
+      if (lessonStatus) {
+        popupData.courseId = coursewareDetailKnowledgeId;
+        data.selectClassStatus = true;
+        return;
+      }
+
+      let nextLessonStatus = false;
+      while (detailIndex <= popupData.chapterDetails.length - 1) {
+        detailIndex++;
+        const tempDetail =
+          popupData.chapterDetails[detailIndex]?.knowledgeList || [];
+        let tempLessonLength = 0;
+        while (tempLessonLength <= tempDetail.length - 1) {
+          if (tempDetail[tempLessonLength].coursewareNum > 0) {
+            nextLessonStatus = true;
+            lessonCoursewareDetailId =
+              tempDetail[tempLessonLength].lessonCoursewareDetailId;
+            coursewareDetailKnowledgeId = tempDetail[tempLessonLength].id;
+          }
+          tempLessonLength++;
+          if (nextLessonStatus) {
+            break;
+          }
+        }
+
+        if (nextLessonStatus) {
+          break;
+        }
+      }
+
+      // 判断当前章节下面课程是否有内容,否则往上一个章节走
+      if (nextLessonStatus) {
+        popupData.courseId = coursewareDetailKnowledgeId;
+        data.selectClassStatus = true;
+        return;
+      }
+    }
+    /** 弹窗关闭 */
+    const handleClosePopup = () => {
+      const item = data.itemList[popupData.activeIndex];
+      if (item?.type == 'VIDEO' && !item.videoEle?.paused) {
+        setModelOpen();
+      }
+
+      if (item?.type == 'SONG' && !item.audioEle?.paused) {
+        setModelOpen();
+      }
+    };
+    document.body.addEventListener('keyup', (e: KeyboardEvent) => {
+      if (e.code === 'ArrowUp') {
+        // if (popupData.activeIndex === 0) return;
+        setModalOpen();
+        handlePreAndNext('up');
+      } else if (e.code === 'ArrowDown') {
+        // if (popupData.activeIndex === data.itemList.length - 1) return;
+        setModalOpen();
+        handlePreAndNext('down');
+      } else if (e.code === 'Space') {
+        // const activeItem = data.itemList[popupData.activeIndex];
+        // console.log(activeItem, activeItem.videoEle);
+        // // 暂停视频和曲谱的播放
+        // if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
+        //   activeItem.videoEle?.play();
+        // }
+        // if (activeItem.type === 'SONG' && activeItem.audioEle) {
+        //   activeItem.audioEle?.play();
+        // }
+        // if (activeItem.type === 'MUSIC') {
+        //   activeItem.iframeRef?.contentWindow?.postMessage(
+        //     { api: 'setPlayState' },
+        //     '*'
+        //   );
+        // }
+      }
+    });
+
+    // const toggleListenerKeyUp = (type: string) => {
+    //   if (type === 'remove') {
+    //     document.body.removeEventListener('keyup', () => {
+    //       listenerKeyUpState.value = false;
+    //     });
+    //   } else {
+    //     // 监听页面键盘事件 - 上下切换
+    //     document.body.addEventListener('keyup', (e: KeyboardEvent) => {
+    //       // console.log(e, 'e');
+    //       if (e.code === 'ArrowLeft') {
+    //         // if (popupData.activeIndex === 0) return;
+    //         setModalOpen();
+    //         handlePreAndNext('up');
+    //       } else if (e.code === 'ArrowRight') {
+    //         // if (popupData.activeIndex === data.itemList.length - 1) return;
+    //         setModalOpen();
+    //         handlePreAndNext('down');
+    //       }
+    //     });
+    //     listenerKeyUpState.value = true;
+    //   }
+    // };
+
+    // 监听切换到ppt课件时,手动移除键盘监听器
+    // watch(() => popupData.activeIndex, () => {
+    //   const activeItem = data.itemList[popupData.activeIndex];
+    //   if (activeItem?.type === 'PPT') {
+    //     toggleListenerKeyUp('remove')
+    //   } else {
+    //     !listenerKeyUpState.value && toggleListenerKeyUp('add')
+    //   }
+    // })
+    watch(
+      () => popupData.activeIndex,
+      () => {
+        const item = data.itemList[popupData.activeIndex];
+        popupData.courseActiveIndex = item.parentIndex;
+      }
+    );
+
+    const setModalOpen = (status = true) => {
+      clearTimeout(activeData.timer);
+      activeData.model = status;
+      Object.values(data.videoRefs).map((n: any) =>
+        n?.toggleHideControl(status)
+      );
+      Object.values(data.audioRefs).map((n: any) =>
+        n?.toggleHideControl(status)
+      );
+    };
+
+    /** 教学数据 */
+    const studyData = reactive({
+      type: '' as ToolType,
+      penShow: false,
+      whiteboardShow: false,
+      callShow: false,
+      callStudentList: [] as any // 学生列表
+    });
+
+    /** 打开教学工具 */
+    const openStudyTool = (item: ToolItem) => {
+      handleStop(false);
+      clearModel();
+      popupData.toolOpen = false;
+      studyData.type = item.type;
+
+      switch (item.type) {
+        case 'pen':
+          studyData.penShow = true;
+          break;
+        case 'whiteboard':
+          studyData.whiteboardShow = true;
+          break;
+        case 'call':
+          studyData.callShow = true;
+          break;
+      }
+    };
+
+    /** 关闭教学工具 */
+    const closeStudyTool = () => {
+      studyData.type = 'init';
+      toggleModel();
+      setModelOpen();
+    };
+
+    const startShowModal = (
+      val: 'setTimeIcon' | 'beatIcon' | 'toneIcon' | 'iconNote2'
+    ) => {
+      if (val == 'setTimeIcon') {
+        showModalTime.value = true;
+      }
+      if (val == 'beatIcon') {
+        showModalBeat.value = true;
+      }
+      if (val == 'toneIcon') {
+        showModalTone.value = true;
+      }
+    };
+
+    // 是否允许上一页
+    const isUpArrow = computed(() => {
+      /**
+       * 1,判断当前课程中是否处在第一个资源;
+       * 2,判断当前课程是否在当前章节的第一个;
+       * 3,判断当前章节,当前课程上面还没有其它课程,是否有资源;
+       * 4,判断当前章节上面还没有其它章节;
+       * 5,判断上面章节里面课程是否有资源;
+       */
+      if (popupData.activeIndex > 0) {
+        return true;
+      }
+
+      // 获取当前是哪个章节
+      let detailIndex = popupData.chapterDetails.findIndex(
+        (item: any) => item.id == data.lessonCoursewareDetailId
+      );
+      const detailItem =
+        popupData.chapterDetails[detailIndex]?.knowledgeList || [];
+      let lessonIndex = detailItem.findIndex(
+        (item: any) => item.id == data.detailId
+      );
+
+      // 说明已经是第一单元,第一课
+      if (detailIndex <= 0 && lessonIndex <= 0) {
+        return false;
+      }
+
+      let lessonStatus = false; // 当前章节上面是否有内容
+      while (lessonIndex >= 0) {
+        lessonIndex--;
+
+        if (lessonIndex >= 0) {
+          if (detailItem[lessonIndex].coursewareNum > 0) {
+            lessonStatus = true;
+          }
+        }
+      }
+      // 判断当前章节下面课程是否有内容,否则往上一个章节走
+      if (lessonStatus) {
+        return true;
+      }
+
+      // 已经是第一个章节了
+      if (detailIndex <= 0) {
+        return false;
+      }
+
+      let prevLessonStatus = false;
+      while (detailIndex >= 0) {
+        detailIndex--;
+        const tempDetail =
+          popupData.chapterDetails[detailIndex]?.knowledgeList || [];
+        let tempLessonLength = tempDetail.length;
+        while (tempLessonLength > 0) {
+          if (tempDetail[tempLessonLength - 1].coursewareNum > 0) {
+            prevLessonStatus = true;
+          }
+          tempLessonLength--;
+        }
+
+        if (prevLessonStatus) {
+          return true;
+        }
+      }
+
+      return false;
+    });
+
+    // 是否允许下一页
+    const isDownArrow = computed(() => {
+      if (popupData.activeIndex < data.itemList.length - 1) {
+        return true;
+      }
+
+      // 获取当前是哪个章节
+      let detailIndex = popupData.chapterDetails.findIndex(
+        (item: any) => item.id == data.lessonCoursewareDetailId
+      );
+
+      const detailItem =
+        popupData.chapterDetails[detailIndex]?.knowledgeList || [];
+      let lessonIndex = detailItem.findIndex(
+        (item: any) => item.id == data.detailId
+      );
+
+      // 说明已经是最后-单元,最后一课
+      if (
+        detailIndex >= popupData.chapterDetails.length - 1 &&
+        lessonIndex >= detailItem.length - 1
+      ) {
+        return false;
+      }
+
+      let lessonStatus = false; // 当前章节下面是否有内容
+      while (lessonIndex < detailItem.length - 1) {
+        lessonIndex++;
+        if (lessonIndex >= 0) {
+          if (detailItem[lessonIndex].coursewareNum > 0) {
+            lessonStatus = true;
+          }
+        }
+      }
+      // 判断当前章节下面课程是否有内容,否则往下一个章节走
+      if (lessonStatus) {
+        return true;
+      }
+
+      // 已经是最后一个章节了
+      if (detailIndex >= popupData.chapterDetails.length - 1) {
+        return false;
+      }
+
+      let nextLessonStatus = false;
+      while (detailIndex < popupData.chapterDetails.length - 1) {
+        detailIndex++;
+        const tempDetail =
+          popupData.chapterDetails[detailIndex]?.knowledgeList || [];
+        let tempLessonLength = 0;
+        while (tempLessonLength <= tempDetail.length - 1) {
+          if (tempDetail[tempLessonLength].coursewareNum > 0) {
+            nextLessonStatus = true;
+          }
+          tempLessonLength++;
+        }
+
+        if (nextLessonStatus) {
+          return true;
+        }
+      }
+
+      return false;
+    });
+    const activeVideoItem = computed(() => {
+      const item = data.itemList[popupData.activeIndex];
+      if (item && item.type && item.type.toLocaleUpperCase() === 'VIDEO') {
+        return item;
+      }
+      return {};
+    });
+
+    // 右侧菜单栏
+    const rightList = reactive([
+      {
+        name: '上一个',
+        icon: bottomIconPre,
+        id: 11
+      },
+      {
+        name: '下一个',
+        icon: bottomIconNext,
+        id: 12
+      },
+      {
+        name: '切换章节',
+        icon: bottomIconSwitch,
+        id: 13
+      },
+      {
+        name: '资源列表',
+        icon: bottomIconResource,
+        id: 14
+      },
+      {
+        name: '曲目资源',
+        icon: rightIconMusic,
+        id: 9
+      },
+      {
+        name: '点名',
+        icon: rightIconCall,
+        id: 10
+      },
+      {
+        name: '布置作业',
+        icon: rightIconArrange,
+        id: 2
+      },
+      {
+        name: '工具箱',
+        icon: rightIconTool,
+        id: 15
+      },
+      {
+        name: '结束课程',
+        name2: '结束预览',
+        icon: rightIconEnd,
+        id: 1
+      },
+      {
+        name: '收起',
+        icon: leftIconPackUp,
+        id: 8
+      }
+    ]);
+    const tooltipList = [
+      {
+        name: '节拍器',
+        icon: rightIconMetronome,
+        id: 5
+      },
+      {
+        name: '计时器',
+        icon: rightIconTimer,
+        id: 7
+      },
+      {
+        name: '批注',
+        icon: rightIconPostil,
+        id: 3
+      },
+      {
+        name: '白板',
+        icon: rightIconWhiteboard,
+        id: 4
+      }
+      // {
+      //   name: '调音器',
+      //   icon: rightIconTuner,
+      //   id: 6
+      // }
+    ];
+    // 默认收起菜单
+    const columnShow = ref(true);
+    // 菜单位置
+    const columnPos = ref<'left' | 'right'>('left');
+    watch(columnPos, () => {
+      for (let i = 0; i < data.itemList.length; i++) {
+        const activeItem = data.itemList[i];
+        if (['RHYTHM', 'MUSIC'].includes(activeItem.type)) {
+          activeItem.iframeRef?.contentWindow?.postMessage(
+            { api: 'imagePos', data: columnPos.value },
+            '*'
+          );
+        }
+      }
+    });
+    // 右边栏操作
+    const operateRightBtn = async (id: number) => {
+      if (![8, 11, 12, 13, 14].includes(id)) {
+        handleStop(false);
+      }
+
+      switch (id) {
+        case 1:
+          if (data.type === 'preview') {
+            data.removeVisiable = true;
+            data.removeTitle = '结束预览';
+            data.removeContent = '请确认是否结束预览?';
+          } else {
+            const res = await getStudentAfterWork({
+              courseScheduleId: data.classId,
+              page: 1,
+              rows: 99
+            });
+            if (res.data.rows && res.data.rows.length) {
+              data.removeContent = '请确认是否结束课程?';
+              data.removeCourseStatus = false;
+            } else {
+              data.removeContent = '本次课堂尚未布置作业,是否结束课程?';
+              data.removeCourseStatus = true;
+            }
+            data.removeVisiable = true;
+            data.removeTitle = '结束课程';
+          }
+          break;
+        case 2:
+          // 学生人数必须大于0,才可以布置作业
+          if (data.preStudentNum <= 0) return;
+          // const res = await lessonPreTrainingPage({
+          //   coursewareKnowledgeDetailId: data.detailId,
+          //   instrumentId: data.instrumentId,
+          //   page: 1,
+          //   rows: 99
+          // });
+          // if (res.data.rows && res.data.rows.length) {
+          //   data.modalAttendMessage = '本节课已设置课后作业,是否布置?';
+          // }
+          // data.modelAttendStatus = true;
+          const res = await getStudentAfterWork({
+            courseScheduleId: data.classId,
+            page: 1,
+            rows: 99
+          });
+          if (res.data.rows && res.data.rows.length) {
+            // data.modalAttendMessage = '请确认是否结束课程?';
+            data.modalAttendMessage = '本次课程已布置作业,是否继续?';
+            data.modelAttendStatus = true;
+          } else {
+            data.modelTrainStatus = true;
+            nextTick(() => {
+              getModalHeight();
+            });
+            data.modelAttendStatus = false;
+          }
+          break;
+        case 3:
+          openStudyTool({
+            type: 'pen',
+            icon: iconNote,
+            name: '批注'
+          });
+          break;
+        case 4:
+          openStudyTool({
+            type: 'whiteboard',
+            icon: iconWhite,
+            name: '白板'
+          });
+          break;
+        case 5:
+          startShowModal('beatIcon');
+          break;
+        case 6:
+          startShowModal('toneIcon');
+          break;
+        case 7:
+          startShowModal('setTimeIcon');
+          break;
+        case 8:
+          columnShow.value = false;
+          break;
+        case 9:
+          // 选择曲目时需要暂停所有播放
+          handleStop(false);
+          data.selectResourceStatus = true;
+          break;
+        case 10:
+          //  点名
+          // await rollCallStudentList();
+          if (studyData.callStudentList.length > 0) {
+            openStudyTool({
+              type: 'call',
+              icon: iconWhite,
+              name: '点名'
+            });
+            return;
+          }
+          break;
+        case 11:
+          if (!isUpArrow.value) return;
+          handlePreAndNext('up');
+          break;
+        case 12:
+          if (!isDownArrow.value) return;
+          handlePreAndNext('down');
+          break;
+        case 13:
+          popupData.chapterOpen = true;
+          break;
+        case 14:
+          popupData.open = true;
+          nextTick(() => {
+            scrollResourceSection();
+          });
+          break;
+        default:
+          break;
+      }
+    };
+
+    // 点名学生列表
+    const rollCallStudentList = async () => {
+      //
+      if (!data.classId) return;
+      try {
+        const res = await getStudentList({
+          classGroupId: data.classGroupId,
+          page: 1,
+          rows: 999
+        });
+        const result = res.data || {};
+        if (Array.isArray(result.rows) && result.rows.length > 0) {
+          const tempStudents: any = [];
+          result.rows.forEach((row: any) => {
+            tempStudents.push({
+              name: row.nickname,
+              img: row.avatar
+            });
+          });
+          studyData.callStudentList = [...tempStudents];
+        }
+      } catch {
+        //
+      }
+    };
+
+    // 滚动到某个元素的位置
+    const scrollResourceSection = () => {
+      const drawerCardItemRefs =
+        document.querySelectorAll('.drawerCardItemRef');
+      if (
+        popupData.activeIndex >= 0 &&
+        drawerCardItemRefs[popupData.activeIndex]
+      ) {
+        drawerCardItemRefs[popupData.activeIndex].scrollIntoView();
+      }
+    };
+
+    const getModalHeight = () => {
+      const dom: any = document.querySelector('#model-homework-height');
+      if (dom) {
+        useResizeObserver(dom as HTMLElement, (entries: any) => {
+          const entry = entries[0];
+          const { height } = entry.contentRect;
+          dom.style.setProperty('--window-page-lesson-height', height + 'px');
+        });
+      }
+    };
+
+    /* 弹窗加拖动 */
+    // 引导页
+    getGuidanceShow();
+    // 选择课件弹窗
+    const selCourBoxClass = 'selCourBoxClass_drag';
+    const selCourDragData = useDrag(
+      [`${selCourBoxClass}>.n-card-header`, `${selCourBoxClass} .bom_drag`],
+      selCourBoxClass,
+      toRef(data, 'selectClassStatus'),
+      users.info.id
+    );
+    // 选择资源弹窗
+    const selResourBoxClass = 'selResourBoxClass_drag';
+    const selResourDragData = useDrag(
+      [
+        `${selResourBoxClass} .select-resource>.n-tabs>.n-tabs-nav--top.n-tabs-nav`,
+        `${selResourBoxClass} .bom_drag`
+      ],
+      selResourBoxClass,
+      toRef(data, 'selectResourceStatus'),
+      users.info.id
+    );
+    // 结束预览  结束课程确认弹窗
+    const removeResourBoxClass = 'removeResourBoxClass_drag';
+    const removeResourDragData = useDrag(
+      [
+        `${removeResourBoxClass}>.n-card-header`,
+        `${removeResourBoxClass} .bom_drag`
+      ],
+      removeResourBoxClass,
+      toRef(data, 'removeVisiable'),
+      users.info.id
+    );
+    // 章节next弹窗
+    const nextEndBoxClass = 'nextEndBoxClass_drag';
+    const nextEndBoxDragData = useDrag(
+      [`${nextEndBoxClass}>.n-card-header`, `${nextEndBoxClass} .bom_drag`],
+      nextEndBoxClass,
+      toRef(data, 'nextEndShow'),
+      users.info.id
+    );
+    // 章节切换弹窗
+    const chapterConBoxClass = 'chapterConBoxClass_drag';
+    const chapterConBoxDragData = useDrag(
+      [
+        `${chapterConBoxClass}>.n-card-header`,
+        `${chapterConBoxClass} .bom_drag`
+      ],
+      chapterConBoxClass,
+      toRef(popupData, 'chapterOpen'),
+      users.info.id
+    );
+    // 资源列表弹窗
+    const resourcesConBoxClass = 'resourcesConBoxClass_drag';
+    const resourcesConDragData = useDrag(
+      [
+        `${resourcesConBoxClass}>.n-card-header`,
+        `${resourcesConBoxClass} .bom_drag`
+      ],
+      resourcesConBoxClass,
+      toRef(popupData, 'open'),
+      users.info.id
+    );
+    // 计时器
+    const timerMeterConBoxClass = 'timerMeterConBoxClass_drag';
+    const timerMeterConDragData = useDrag(
+      [
+        `${timerMeterConBoxClass} .timeBomCon .bom_drag`,
+        `${timerMeterConBoxClass} .topDragDom`
+      ],
+      timerMeterConBoxClass,
+      showModalTime,
+      users.info.id
+    );
+    // 节拍器
+    const metronomeConBoxClass = 'metronomeConBoxClass_drag';
+    const metronomeConBoxDragData = useDrag(
+      [
+        `${metronomeConBoxClass} .topDragDom`,
+        `${metronomeConBoxClass} .bom_drag`
+      ],
+      metronomeConBoxClass,
+      showModalBeat,
+      users.info.id
+    );
+    // 布置作业弹窗
+    const modelTrainStatusConBoxClass = 'modelTrainStatusConBoxClass_drag';
+    const modelTrainStatusConBoxDragData = useDrag(
+      [
+        `${modelTrainStatusConBoxClass}>.n-card-header`,
+        `${modelTrainStatusConBoxClass} .bom_drag`
+      ],
+      modelTrainStatusConBoxClass,
+      toRef(data, 'modelTrainStatus'),
+      users.info.id
+    );
+    // 布置作业课后作业
+    const modelTrainHomeWordConBoxClass =
+      'modelTrainHomeWordConBoxClassBoxClass_drag';
+    const modelTrainHomeWordConBoxDragData = useDrag(
+      [
+        `${modelTrainHomeWordConBoxClass}>.n-card-header`,
+        `${modelTrainHomeWordConBoxClass} .bom_drag`
+      ],
+      modelTrainHomeWordConBoxClass,
+      toRef(data, 'modelAttendStatus'),
+      users.info.id
+    );
+    return () => (
+      <div id="playContent" class={[styles.playContent, 'wrap']}>
+        <div
+          onClick={() => {
+            clearTimeout(activeData.timer);
+            activeData.model = !activeData.model;
+            Object.values(data.videoRefs).map((n: any) =>
+              n?.toggleHideControl(activeData.model)
+            );
+            Object.values(data.audioRefs).map((n: any) =>
+              n?.toggleHideControl(activeData.model)
+            );
+          }}>
+          <div
+            class={styles.coursewarePlay}
+            style={{ width: parentContainer.width }}>
+            {!popupData.chapterLoading ? (
+              <div class={styles.wraps}>
+                <div
+                  style={
+                    activeVideoItem.value.type &&
+                    data.animationState === 'end' &&
+                    data.videoState === 'play'
+                      ? {
+                          zIndex: 15,
+                          opacity: 1
+                        }
+                      : { opacity: 0, zIndex: -1 }
+                  }
+                  class={styles.itemDiv}>
+                  <VideoPlay
+                    imagePos={columnPos.value}
+                    ref={(el: any) => (data.videoItemRef = el)}
+                    item={activeVideoItem.value}
+                    showModel={activeData.model}
+                    onClose={setModelOpen}
+                    onLoadedmetadata={(videoItem: any) => {
+                      if (data.itemList[popupData.activeIndex]) {
+                        data.itemList[popupData.activeIndex].videoEle =
+                          videoItem;
+                      }
+                    }}
+                    onCanplay={() => {
+                      data.videoState = 'play';
+                      // activeVideoItem.value.videoEle = videoItem;
+                    }}
+                    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 isRender = Math.abs(popupData.activeIndex - mIndex) < 2;
+                  const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
+                  // if (isRender && m.type === 'PPT') {
+                  //   setTimeout(() => iframeClick() ,500)
+                  // }
+                  // if (isRender) {
+                  //   m.isRender = true;
+                  // }
+
+                  // const isRender =
+                  //   m.isRender || Math.abs(popupData.activeIndex - mIndex) < 2;
+                  // const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
+                  // if (isRender) {
+                  //   m.isRender = true;
+                  // }
+
+                  return isRender ? (
+                    <div
+                      key={'index' + mIndex}
+                      class={[
+                        styles.itemDiv,
+                        popupData.activeIndex === mIndex && styles.itemActive,
+                        activeData.isAnimation && styles.acitveAnimation,
+                        Math.abs(popupData.activeIndex - mIndex) < 2
+                          ? 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)
+                          );
+                          Object.values(data.audioRefs).map((n: any) =>
+                            n?.toggleHideControl(activeData.model)
+                          );
+                          if (activeData.model) {
+                            setModelOpen();
+                          }
+                        }, 300);
+                      }}>
+                      {m.type === 'VIDEO' ? (
+                        <>
+                          <img
+                            src={m.coverImg}
+                            onLoad={() => {
+                              m.isprepare = true;
+                            }}
+                          />
+                          {/* <VideoPlay
+                            ref={(v: any) => (data.videoRefs[mIndex] = v)}
+                            item={m}
+                            isEmtry={isEmtry}
+                            onLoadedmetadata={(videoItem: any) => {
+                              m.videoEle = videoItem;
+                              m.isprepare = true;
+                            }}
+                            onTogglePlay={(paused: boolean) => {
+                              m.autoPlay = false;
+                              if (paused || popupData.open) {
+                                clearTimeout(activeData.timer);
+                              } else {
+                                setModelOpen();
+                              }
+                            }}
+                            onReset={() => {
+                              if (!m.videoEle?.paused) {
+                                setModelOpen();
+                              }
+                            }}
+                            onError={() => {
+                              console.log('video error');
+                              m.error = true;
+                            }}
+                          /> */}
+                          <Transition name="van-fade">
+                            {
+                              <div class={styles.loadWrap}>
+                                <Vue3Lottie
+                                  animationData={playLoadData}></Vue3Lottie>
+                              </div>
+                            }
+                          </Transition>
+                        </>
+                      ) : m.type === 'IMG' ? (
+                        <img src={m.content} />
+                      ) : m.type === 'SONG' ? (
+                        <AudioPay
+                          imagePos={columnPos.value}
+                          item={m}
+                          activeStatus={popupData.activeIndex === mIndex}
+                          ref={(v: any) => (data.audioRefs[mIndex] = v)}
+                          onLoadedmetadata={(audioItem: any) => {
+                            m.audioEle = audioItem;
+                            m.isprepare = true;
+                          }}
+                          onTogglePlay={(paused: boolean) => {
+                            // m.autoPlay = false;
+                            if (paused || popupData.open) {
+                              clearTimeout(activeData.timer);
+                            } else {
+                              setModelOpen();
+                            }
+                          }}
+                          onEnded={() => {
+                            // const _index = popupData.activeIndex + 1;
+                            // if (_index < data.itemList.length) {
+                            //   handleSwipeChange(_index);
+                            // }
+                          }}
+                          onReset={() => {
+                            if (!m.audioEle?.paused) {
+                              setModelOpen();
+                            }
+                          }}
+                        />
+                      ) : // : m.type === 'PPT' ? <div class={styles.iframePpt}>
+                      //   <div class={styles.pptBox}></div>
+                      //   <iframe src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(m.content)}`} width='100%' height='100%' frameborder='1'></iframe>
+                      // </div>
+                      m.type === 'PPT' ? (
+                        <iframe
+                          src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
+                            m.content
+                          )}`}
+                          width="100%"
+                          height="100%"
+                          frameborder="1"></iframe>
+                      ) : m.type === 'RHYTHM' ? (
+                        <RhythmModal
+                          item={m}
+                          activeStatus={popupData.activeIndex === mIndex}
+                          imagePos={columnPos.value}
+                          onSetIframe={(el: any) => {
+                            m.iframeRef = el;
+                          }}
+                        />
+                      ) : m.type === 'LISTEN' ? (
+                        <ListenModal
+                          item={m}
+                          data-vid={m.id}
+                          activeStatus={popupData.activeIndex === mIndex}
+                          onSetIframe={(el: any) => {
+                            m.iframeRef = el;
+                          }}
+                        />
+                      ) : m.type === 'INSTRUMENT' || m.type === 'MUSICIAN' ? (
+                        <InstruemntDetail
+                          type="preview"
+                          id={m.content}
+                          contentType={m.type}
+                          activeStatus={popupData.activeIndex === mIndex}
+                          ref={(el: any) => (m.iframeRef = el)}
+                        />
+                      ) : m.type === 'MUSIC_WIKI' ? (
+                        <MusicDetail
+                          type="preview"
+                          id={m.content}
+                          contentType={m.type}
+                          activeStatus={popupData.activeIndex === mIndex}
+                          ref={(el: any) => (m.iframeRef = el)}
+                        />
+                      ) : m.type === 'THEORY' ? (
+                        <TheotyDetail
+                          type="preview"
+                          id={m.content}
+                          activeStatus={popupData.activeIndex === mIndex}
+                          ref={(el: any) => (m.iframeRef = el)}
+                        />
+                      ) : (
+                        <MusicScore
+                          activeModel={activeData.model}
+                          activeStatus={popupData.activeIndex === mIndex}
+                          data-vid={m.id}
+                          music={m}
+                          imagePos={columnPos.value}
+                          onSetIframe={(el: any) => {
+                            m.iframeRef = el;
+                          }}
+                        />
+                      )}
+                    </div>
+                  ) : null;
+                })}
+              </div>
+            ) : (
+              ''
+            )}
+          </div>
+        </div>
+
+        {/* 右边操作栏 */}
+        <div
+          class={[
+            styles.rightColumn,
+            columnShow.value && columnPos.value === 'right'
+              ? ''
+              : styles.rightColumnHide
+          ]}>
+          {rightList.map((item: any) => (
+            <div class={styles.columnItemBox}>
+              <div
+                class={[
+                  styles.columnItem,
+                  (item.id === 2 && data.preStudentNum <= 0) ||
+                  (item.id === 10 && studyData.callStudentList.length <= 0) ||
+                  (item.id === 11 && !isUpArrow.value) ||
+                  (item.id === 12 && !isDownArrow.value)
+                    ? styles.itemDisabled
+                    : ''
+                ]}
+                onClick={() => operateRightBtn(item.id)}>
+                <NTooltip
+                  showArrow={false}
+                  placement="left"
+                  class={[
+                    item.id === 15
+                      ? 'columnItemTooltip rightColumnItemTooltip'
+                      : ''
+                  ]}>
+                  {{
+                    trigger: () => (
+                      <img src={item.id === 8 ? rightIconPackUp : item.icon} />
+                    ),
+                    default: () =>
+                      item.id === 15 ? (
+                        <div class="tools">
+                          {tooltipList.map(i => (
+                            <div onClick={() => operateRightBtn(i.id)}>
+                              <img src={i.icon} />
+                              <div class="tit">{i.name}</div>
+                            </div>
+                          ))}
+                        </div>
+                      ) : item.id === 1 && data.type === 'preview' ? (
+                        item.name2
+                      ) : (
+                        item.name
+                      )
+                  }}
+                </NTooltip>
+              </div>
+            </div>
+          ))}
+        </div>
+        <NTooltip showArrow={false} placement="left">
+          {{
+            trigger: () => (
+              <div
+                class={[
+                  styles.rightHideIcon,
+                  !(columnShow.value && columnPos.value === 'right')
+                    ? styles.rightIconShow
+                    : ''
+                ]}
+                onClick={() => {
+                  columnPos.value = 'right';
+                  columnShow.value = true;
+                }}
+              />
+            ),
+            default: () => '按钮镜像'
+          }}
+        </NTooltip>
+        {/* 左边操作栏 */}
+        <div
+          class={[
+            styles.leftColumn,
+            columnShow.value && columnPos.value === 'left'
+              ? ''
+              : styles.leftColumnHide
+          ]}>
+          {rightList.map((item: any) => (
+            <div class={styles.columnItemBox}>
+              <div
+                class={[
+                  styles.columnItem,
+                  (item.id === 2 && data.preStudentNum <= 0) ||
+                  (item.id === 10 && studyData.callStudentList.length <= 0) ||
+                  (item.id === 11 && !isUpArrow.value) ||
+                  (item.id === 12 && !isDownArrow.value)
+                    ? styles.itemDisabled
+                    : ''
+                ]}
+                onClick={() => operateRightBtn(item.id)}>
+                <NTooltip
+                  showArrow={false}
+                  placement="right"
+                  class={[item.id === 15 ? 'columnItemTooltip' : '']}>
+                  {{
+                    trigger: () => <img src={item.icon} />,
+                    default: () =>
+                      item.id === 15 ? (
+                        <div class="tools">
+                          {tooltipList.map(i => (
+                            <div onClick={() => operateRightBtn(i.id)}>
+                              <img src={i.icon} />
+                              <div class="tit">{i.name}</div>
+                            </div>
+                          ))}
+                        </div>
+                      ) : item.id === 1 && data.type === 'preview' ? (
+                        item.name2
+                      ) : (
+                        item.name
+                      )
+                  }}
+                </NTooltip>
+              </div>
+            </div>
+          ))}
+        </div>
+        <NTooltip showArrow={false} placement="right">
+          {{
+            trigger: () => (
+              <div
+                class={[
+                  styles.leftHideIcon,
+                  !(columnShow.value && columnPos.value === 'left')
+                    ? styles.leftIconShow
+                    : ''
+                ]}
+                onClick={() => {
+                  columnPos.value = 'left';
+                  columnShow.value = true;
+                }}
+              />
+            ),
+            default: () => '按钮镜像'
+          }}
+        </NTooltip>
+
+        {/* 显示列表 */}
+        {/* <NDrawer
+          v-model:show={popupData.open}
+          class={[styles.drawerContainer, styles.drawerContainerSource]}
+          onAfterLeave={handleClosePopup}
+          showMask={false}
+          blockScroll={false}
+          trapFocus={false}>
+          <NDrawerContent closable>
+            {{
+              header: () => (
+                <TheNoticeBar text={activeName.value || '资源列表'} />
+              ),
+              default: () => (
+                <SourceList
+                  teacherChapterName={data.teacherChapterName}
+                  knowledgePointList={data.knowledgePointList}
+                  courseActiveIndex={popupData.courseActiveIndex}
+                  activeItem={data.itemList[popupData.activeIndex]}
+                  onConfirm={(item: any) => {
+                    popupData.open = false;
+                    toggleMaterial(item.id);
+                  }}
+                />
+              )
+            }}
+          </NDrawerContent>
+        </NDrawer> */}
+        <NModal
+          transformOrigin="center"
+          v-model:show={popupData.open}
+          preset="card"
+          class={[
+            'modalTitle background',
+            styles.drawerContainer,
+            resourcesConBoxClass
+          ]}
+          style={resourcesConDragData.styleDrag.value}
+          title={activeName.value || '资源列表'}>
+          <SourceList
+            teacherChapterName={data.teacherChapterName}
+            knowledgePointList={data.knowledgePointList}
+            courseActiveIndex={popupData.courseActiveIndex}
+            activeItem={data.itemList[popupData.activeIndex]}
+            onConfirm={(item: any) => {
+              popupData.open = false;
+              toggleMaterial(item.id);
+            }}
+          />
+          <Dragbom></Dragbom>
+        </NModal>
+        {/* 章节切换 */}
+        {/* <NDrawer
+          v-model:show={popupData.chapterOpen}
+          class={styles.drawerContainer}
+          onAfterLeave={handleClosePopup}
+          showMask={false}
+          maskClosable={data.selectClassStatus ? false : true}
+          blockScroll={false}
+          trapFocus={false}>
+          <NDrawerContent title="切换章节" closable>
+            <Chapter
+              treeList={popupData.chapterDetails}
+              itemActive={data.detailId as any}
+              onHandleSelect={async (val: any) => {
+                // itemActive: child.id,
+                // itemName: child.name
+                popupData.courseId = val.itemActive;
+                data.selectClassStatus = true;
+              }}
+            />
+          </NDrawerContent>
+        </NDrawer> */}
+        <NModal
+          transformOrigin="center"
+          v-model:show={popupData.chapterOpen}
+          preset="card"
+          class={[
+            'modalTitle background',
+            styles.drawerContainer,
+            chapterConBoxClass
+          ]}
+          style={chapterConBoxDragData.styleDrag.value}
+          title={'切换章节'}>
+          <Chapter
+            treeList={popupData.chapterDetails}
+            itemActive={data.detailId as any}
+            onHandleSelect={async (val: any) => {
+              // itemActive: child.id,
+              // itemName: child.name
+              popupData.courseId = val.itemActive;
+              data.selectClassStatus = true;
+            }}
+          />
+          <Dragbom></Dragbom>
+        </NModal>
+        {/* 批注 */}
+        {studyData.penShow && (
+          <Pen
+            isDrag={true}
+            show={studyData.type === 'pen'}
+            type={studyData.type}
+            close={() => closeStudyTool()}
+          />
+        )}
+
+        {studyData.whiteboardShow && (
+          <Pen
+            isDrag={true}
+            show={studyData.type === 'whiteboard'}
+            type={studyData.type}
+            close={() => closeStudyTool()}
+          />
+        )}
+
+        {studyData.callShow && (
+          <Pen
+            isDrag={true}
+            imagePos={columnPos.value}
+            callStudents={studyData.callStudentList}
+            show={studyData.type === 'call'}
+            type={studyData.type}
+            close={() => {
+              closeStudyTool();
+              studyData.callShow = false;
+            }}
+          />
+        )}
+
+        {/* 选择课件 */}
+        <NModal
+          transformOrigin="center"
+          v-model:show={data.selectClassStatus}
+          preset="card"
+          class={[
+            'modalTitle background',
+            // styles.attendClassModal,
+            styles.selectClassModal,
+            selCourBoxClass
+          ]}
+          style={selCourDragData.styleDrag.value}
+          title={'选择课件'}>
+          <SelectClass
+            classId={data.classId}
+            courseId={popupData.courseId}
+            instrumentId={data.instrumentId}
+            lessonCoursewareSubjectList={data.lessonCoursewareSubjectList}
+            onConfirm={async (val: any) => {
+              popupData.chapterLoading = true;
+              try {
+                data.selectClassStatus = false;
+                data.detailId = val.itemActive;
+                data.courseId = val.chapterId;
+                const ids = formatParentId(
+                  val.itemActive,
+                  popupData.chapterDetails
+                );
+                data.lessonCoursewareDetailId = ids[0];
+                // 更新上课记录 上课的时候才更新
+                if (data.type !== 'preview') {
+                  await classCourseScheduleUpdate();
+                }
+                await getDetail();
+                popupData.activeIndex = 0;
+                popupData.chapterOpen = false;
+              } catch {
+                //
+              }
+              popupData.chapterLoading = false;
+            }}
+          />
+          <Dragbom></Dragbom>
+        </NModal>
+
+        {/* 布置作业 */}
+        <NModal
+          transformOrigin="center"
+          v-model:show={data.modelAttendStatus}
+          preset="card"
+          title={'课后作业'}
+          style={modelTrainHomeWordConBoxDragData.styleDrag.value}
+          class={[
+            'modalTitle',
+            styles.removeVisiable,
+            modelTrainHomeWordConBoxClass
+          ]}>
+          <div class={styles.studentRemove}>
+            <p>{data.modalAttendMessage}</p>
+            <NSpace class={styles.btnGroupModal} justify="center">
+              <NButton
+                type="default"
+                round
+                onClick={() => {
+                  data.modelTrainStatus = true;
+                  nextTick(() => {
+                    getModalHeight();
+                  });
+                  data.modelAttendStatus = false;
+                }}>
+                布置作业
+              </NButton>
+              <NButton
+                type="primary"
+                round
+                onClick={() => {
+                  handleStop();
+                  data.modelAttendStatus = false;
+                }}>
+                取消
+              </NButton>
+            </NSpace>
+          </div>
+          <Dragbom></Dragbom>
+        </NModal>
+
+        {/* 训练设置 */}
+        {/* <NModal
+          transformOrigin="center"
+          v-model:show={data.modelTrainStatus}
+          preset="card"
+          class={[styles.attendClassModal, styles.trainClassModal]}
+          title={'作业设置'}>
+          <ClassWork
+            detailId={data.detailId}
+            instrumentId={data.instrumentId}
+            courseScheduleId={data.classId}
+            activeName={activeName.value}
+            classGroupId={data.classGroupId}
+            onClose={() => (data.modelTrainStatus = false)}
+          />
+        </NModal> */}
+        <NModal
+          v-model:show={data.modelTrainStatus}
+          preset="card"
+          class={[
+            'modalTitle background',
+            styles.workVisiable,
+            modelTrainStatusConBoxClass
+          ]}
+          style={modelTrainStatusConBoxDragData.styleDrag.value}
+          title={'布置作业'}>
+          <div id="model-homework-height" class={styles.workContainer}>
+            <div class={styles.workTrain}>
+              <Train
+                from={'class'}
+                cardType="homeworkRecord"
+                lessonPreTraining={{
+                  title: data.lessonPreTrainingId
+                    ? ''
+                    : data.teacherChapterName + '-课后作业',
+                  chapterId: data.courseId, // 课件编号
+                  id: data.lessonPreTrainingId // 作业编号
+                }}
+                coursewareKnowledgeDetailId={data.detailId}
+                classGroupId={data.classGroupId}
+                courseScheduleId={data.classId}
+                onChange={async (val: any) => {
+                  data.modelTrainStatus = val.status;
+                  // getCoursewareList();
+                  if (val.saveWork && data.classId) {
+                    data.lessonPreTrainingId = val.lessonPreTrainingId;
+                  }
+                }}
+              />
+            </div>
+            <div class={styles.resourceMain}>
+              <ResourceMain from={'class'} cardType="homerowk-record" />
+            </div>
+          </div>
+          <Dragbom></Dragbom>
+        </NModal>
+
+        {/* <NModal
+          transformOrigin="center"
+          class={['modalTitle background', metronomeConBoxClass]}
+          title={'节拍器'}
+          preset="card"
+          v-model:show={showModalBeat.value}
+          style={{
+            width: '687px',
+            ...metronomeConBoxDragData.styleDrag.value
+          }}>
+          <div class={styles.modeWrap}>
+            <iframe
+              src={`${vaildUrl()}/metronome/?id=${new Date().getTime()}`}
+              scrolling="no"
+              frameborder="0"
+              width="100%"
+              onLoad={(val: any) => {
+                iframeDislableKeyboard(val.target);
+              }}
+              height={'650px'}></iframe>
+            <Dragbom></Dragbom>
+          </div>
+        </NModal> */}
+        <Metronome
+          v-model={showModalBeat.value}
+          dragClass={metronomeConBoxClass}
+          dragStyle={metronomeConBoxDragData.styleDrag.value}></Metronome>
+        <NModal
+          transformOrigin="center"
+          class={['background']}
+          v-model:show={showModalTone.value}>
+          <div>
+            <PlaceholderTone
+              onClose={() => {
+                showModalTone.value = false;
+              }}></PlaceholderTone>
+          </div>
+        </NModal>
+        <NModal
+          v-model:show={showModalTime.value}
+          class={timerMeterConBoxClass}
+          style={timerMeterConDragData.styleDrag.value}>
+          <div>
+            <img
+              class={styles.timerMeterClose}
+              src={timerMeterClose}
+              onClick={() => {
+                showModalTime.value = false;
+              }}
+            />
+            <div class="topDragDom"></div>
+            <TimerMeter></TimerMeter>
+          </div>
+        </NModal>
+
+        <NModal
+          v-model:show={data.selectResourceStatus}
+          class={['modalTitle', styles.selectMusicModal, selResourBoxClass]}
+          style={selResourDragData.styleDrag.value}
+          preset="card"
+          title={'选择资源'}>
+          <SelectResources from="class" />
+          <Dragbom></Dragbom>
+        </NModal>
+        <NModal
+          transformOrigin="center"
+          v-model:show={data.removeVisiable}
+          preset="card"
+          class={['modalTitle', styles.removeVisiable, removeResourBoxClass]}
+          style={removeResourDragData.styleDrag.value}
+          title={data.removeTitle}>
+          <div class={styles.studentRemove}>
+            <p>{data.removeContent}</p>
+
+            <NSpace class={styles.btnGroupModal} justify="center">
+              <NButton
+                round
+                onClick={() => {
+                  if (data.removeCourseStatus) {
+                    if (globalState.application) {
+                      document.exitFullscreen
+                        ? document.exitFullscreen()
+                        : document.mozCancelFullScreen
+                        ? document.mozCancelFullScreen()
+                        : document.webkitExitFullscreen
+                        ? document.webkitExitFullscreen()
+                        : '';
+                      emit('close');
+                    } else {
+                      window.close();
+                    }
+                  } else {
+                    data.removeVisiable = false;
+                  }
+                }}>
+                {data.removeCourseStatus ? '结束课程' : '取消'}
+              </NButton>
+              <NButton
+                round
+                type="primary"
+                onClick={() => {
+                  //
+                  if (data.removeCourseStatus) {
+                    data.modelTrainStatus = true;
+                    data.removeVisiable = false;
+                    nextTick(() => {
+                      getModalHeight();
+                    });
+                  } else {
+                    if (globalState.application) {
+                      document.exitFullscreen
+                        ? document.exitFullscreen()
+                        : document.mozCancelFullScreen
+                        ? document.mozCancelFullScreen()
+                        : document.webkitExitFullscreen
+                        ? document.webkitExitFullscreen()
+                        : '';
+                      emit('close');
+                    } else {
+                      window.close();
+
+                      if (route.query.source === 'admin') {
+                        storage.remove(ACCESS_TOKEN_ADMIN);
+                        window.parent.postMessage(
+                          {
+                            api: 'iframe_exit'
+                          },
+                          '*'
+                        );
+                      }
+                    }
+                  }
+                }}>
+                {data.removeCourseStatus ? '布置作业' : '确定'}
+              </NButton>
+            </NSpace>
+            <Dragbom></Dragbom>
+          </div>
+        </NModal>
+        <NModal
+          style={nextEndBoxDragData.styleDrag.value}
+          z-index={3000}
+          transformOrigin="center"
+          v-model:show={data.nextEndShow}
+          preset="card"
+          title={'提示'}
+          class={['modalTitle', styles.removeVisiable, nextEndBoxClass]}>
+          <div class={styles.studentRemove}>
+            <p>当前课件已结束,是否进入下一章节</p>
+            <NSpace class={styles.btnGroupModal} justify="center">
+              <NButton
+                type="default"
+                round
+                onClick={() => {
+                  data.nextEndShow = false;
+                }}>
+                取消
+              </NButton>
+              <NButton
+                type="primary"
+                round
+                onClick={() => {
+                  data.nextEndShow = false;
+                  handleNextEnd();
+                }}>
+                确认
+              </NButton>
+            </NSpace>
+            <Dragbom></Dragbom>
+          </div>
+        </NModal>
+      </div>
+    );
+  }
+});

+ 1 - 0
src/views/attend-class/model/train-type/index.tsx

@@ -417,6 +417,7 @@ export default defineComponent({
           from={props.from}
           v-model:show={previewShow.value}
           item={preivewItem.value}
+          isDownload={false}
         />
 
         <NModal

+ 1 - 0
src/views/prepare-lessons/components/lesson-main/courseware/addCourseware.tsx

@@ -950,6 +950,7 @@ export default defineComponent({
           }
           v-model:show={forms.show}
           item={forms.item}
+          isDownload={false}
         />
 
         <NModal

+ 5 - 1
src/views/prepare-lessons/components/resource-main/components/resource-item/index.tsx

@@ -269,7 +269,11 @@ export default defineComponent({
         </NScrollbar>
 
         {/* 弹窗查看 */}
-        <CardPreview v-model:show={state.show} item={state.item} />
+        <CardPreview
+          v-model:show={state.show}
+          item={state.item}
+          isDownload={false}
+        />
       </div>
     );
   }

+ 1 - 0
src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx

@@ -315,6 +315,7 @@ export default defineComponent({
           from={props.from}
           v-model:show={state.show}
           item={state.item}
+          isDownload={false}
         />
 
         <NModal

+ 1 - 0
src/views/prepare-lessons/model/select-music/select-item/index.tsx

@@ -222,6 +222,7 @@ export default defineComponent({
           from={props.from}
           v-model:show={state.show}
           item={state.item}
+          isDownload={false}
         />
       </div>
     );

+ 1 - 0
src/views/prepare-lessons/model/select-resources/select-item/index.tsx

@@ -311,6 +311,7 @@ export default defineComponent({
           v-model:show={state.show}
           from={props.from}
           item={state.item}
+          isDownload={false}
         />
       </div>
     );