Browse Source

测试课件播放

liushengqiang 2 years ago
parent
commit
17a0713335
2 changed files with 879 additions and 0 deletions
  1. 8 0
      src/router/routes-common.ts
  2. 871 0
      src/views/coursewarePlay/play.tsx

+ 8 - 0
src/router/routes-common.ts

@@ -28,6 +28,14 @@ export const router: RouteRecordRaw[] = [
     }
   },
   {
+    path: '/play',
+    name: 'play',
+    component: () => import('@/views/coursewarePlay/play'),
+    meta: {
+      title: '课程播放'
+    }
+  },
+  {
     path: '/exerciseAfterClass',
     name: 'exerciseAfterClass',
     component: () => import('@/views/exercise-after-class/index'),

+ 871 - 0
src/views/coursewarePlay/play.tsx

@@ -0,0 +1,871 @@
+import {
+  closeToast,
+  Icon,
+  Loading,
+  Popup,
+  showToast,
+  Slider,
+  Swipe,
+  SwipeInstance,
+  SwipeItem
+} from 'vant'
+import {
+  defineComponent,
+  onMounted,
+  reactive,
+  nextTick,
+  onUnmounted,
+  ref,
+  watch,
+  Transition,
+  TransitionGroup
+} from 'vue'
+import iconBack from './image/back.svg'
+import styles from './index.module.less'
+import 'plyr/dist/plyr.css'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { useRoute, useRouter } from 'vue-router'
+import { listenerMessage, postMessage, promisefiyPostMessage } from '@/helpers/native-message'
+import MusicScore from './component/musicScore'
+import iconMenu from './image/icon-menu.svg'
+import iconDian from './image/icon-dian.svg'
+import iconTouping from './image/icon-touping.svg'
+import iconPoint from './image/icon-point.svg'
+import iconLoop from './image/icon-loop.svg'
+import iconLoopActive from './image/icon-loop-active.svg'
+import iconplay from './image/icon-play.svg'
+import iconpause from './image/icon-pause.svg'
+import iconUp from './image/icon-up.svg'
+import iconDown from './image/icon-down.svg'
+import Points from './component/points'
+import { browser, getSecondRPM } from '@/helpers/utils'
+import { Vue3Lottie } from 'vue3-lottie'
+import playLoadData from './datas/data.json'
+import { usePageVisibility, useRect } from '@vant/use'
+import PlayRecordTime from './playRecordTime'
+import dayjs from 'dayjs'
+
+import {
+  Pagination,
+  Navigation,
+  Virtual,
+  EffectFade,
+  EffectFlip,
+  EffectCreative,
+  Lazy
+} from 'swiper'
+import { Swiper, SwiperSlide } from 'swiper/vue'
+import 'swiper/less'
+import 'swiper/less/effect-fade'
+import 'swiper/less/effect-flip'
+import 'swiper/less/effect-creative'
+import { handleCheckVip } from '../hook/useFee'
+import OGuide from '@/components/o-guide'
+
+export default defineComponent({
+  name: 'CoursewarePlay',
+  setup() {
+    const pageVisibility = usePageVisibility()
+    const isPlay = ref(false)
+    /** 页面显示和隐藏 */
+    watch(pageVisibility, (value) => {
+      const activeItem = data.itemList[popupData.activeIndex]
+      if (activeItem.type != 'VIDEO') return
+      if (value == 'hidden') {
+        isPlay.value = !activeItem.paused
+        handlePaused(activeItem)
+      } else {
+        // 页面显示,并且
+        if (isPlay.value) handlePlay(activeItem)
+      }
+    })
+    /** 设置播放容器 16:9 */
+    const parentContainer = reactive({
+      width: '100vw'
+    })
+    const setContainer = () => {
+      let min = Math.min(screen.width, screen.height)
+      let max = Math.max(screen.width, screen.height)
+      let width = min * (16 / 9)
+      if (width > max) {
+        parentContainer.width = '100vw'
+        return
+      } else {
+        parentContainer.width = width + 'px'
+      }
+    }
+    const handleInit = (type = 0) => {
+      // postMessage({
+      //   api: 'courseLoading',
+      //   content: {
+      //     show: true,
+      //     type: 'fullscreen'
+      //   }
+      // })
+      //设置容器16:9
+      setContainer()
+      // 横屏
+      postMessage({
+        api: 'setRequestedOrientation',
+        content: {
+          orientation: type
+        }
+      })
+      // 头,包括返回箭头
+      postMessage({
+        api: 'setTitleBarVisibility',
+        content: {
+          status: type
+        }
+      })
+      // 安卓的状态栏
+      postMessage({
+        api: 'setStatusBarVisibility',
+        content: {
+          isVisibility: type
+        }
+      })
+      // 进入页面设置常量
+      postMessage({
+        api: 'keepScreenLongLight',
+        content: {
+          isOpenLight: type ? true : false
+        }
+      })
+    }
+    handleInit()
+    onUnmounted(() => {
+      handleInit(1)
+      window.removeEventListener('message', iframeHandle)
+    })
+
+    const route = useRoute()
+    const router = useRouter()
+    const headeRef = ref()
+    const data = reactive({
+      detail: null,
+      knowledgePointList: [] as any,
+      itemList: [] as any,
+      showHead: true,
+      isCourse: false,
+      isRecordPlay: false
+    })
+    const activeData = reactive({
+      nowTime: 0,
+      model: true, // 遮罩
+      videoBtns: true, // 视频
+      currentTime: 0,
+      duration: 0,
+      timer: null as any,
+      item: null as any
+    })
+    // 获取缓存路径
+    const getCacheFilePath = async (material: any) => {
+      const res = await promisefiyPostMessage({
+        api: 'getCourseFilePath',
+        content: {
+          url: material.content,
+          localPath: '',
+          materialId: material.materialId,
+          updateTime: material.updateTime,
+          type: material.type // SONG VIDEO IMAGE
+        }
+      })
+      // console.log('缓存路径返回', res)
+      return res
+    }
+    // 获取当前课程是否签退
+    const getCourseSchedule = async () => {
+      if (!route.query.courseId) return
+      try {
+        const res = await request.get(
+          `${state.platformApi}/courseSchedule/detail/${route.query.courseId}`,
+          {
+            hideLoading: true
+          }
+        )
+        if (res?.data) {
+          data.isCourse =
+            res.data.status === 'ING' && state.platformType == 'TEACHER' ? true : false
+          // data.isRecordPlay = Date.now() > dayjs(res.data.startTime).valueOf()
+        }
+      } catch (e) {
+        console.log(e)
+      }
+    }
+    const getItemList = async () => {
+      const list: any = []
+      const browserInfo = browser()
+      for (let i = 0; i < data.knowledgePointList.length; i++) {
+        const item = data.knowledgePointList[i]
+        const itemLength = item.materialList.length - 1
+        for (let j = 0; j < item.materialList.length; j++) {
+          const material = item.materialList[j]
+          //请求本地缓存
+          if (browserInfo.isApp && ['VIDEO', 'IMG'].includes(material.type)) {
+            const localData = await getCacheFilePath(material)
+            if (localData?.content?.localPath) {
+              material.url = material.content
+              material.content = localData.content.localPath
+              // console.log("🚀 ~ material", material)
+            }
+          }
+
+          let videoItem = {}
+          if (material.type === 'VIDEO') {
+            videoItem = {
+              currentTime: 0,
+              duration: 0,
+              progress: 0,
+              paused: true,
+              loop: false,
+              videoEle: null,
+              timer: null,
+              playModel: false,
+              isprepare: false,
+              isDrage: false,
+              muted: true // 是否静音
+            }
+          }
+          list.push({
+            ...material,
+            ...videoItem,
+            iframeRef: null,
+            tabName: item.name,
+            isLast: j === itemLength, // 当前知识点
+            autoPlay: false, //加载完成是否自动播放
+            display: false
+          })
+        }
+      }
+
+      let item: any = null
+      if (route.query.kId) {
+        item = list.find((n: any) => n.materialId == route.query.kId)
+        const _firstIndex = list.findIndex((n: any) => n.materialId == route.query.kId)
+        console.log('🚀 ~ item:', _firstIndex, list)
+        popupData.firstIndex = _firstIndex > -1 ? _firstIndex : 0
+      }
+      item = item ? item : list[0] || {}
+      if (item) {
+        popupData.tabName = item.tabName
+        popupData.tabActive = item.knowledgePointId
+        popupData.itemActive = item.id
+        popupData.itemName = item.name
+        popupData.activeIndex = popupData.firstIndex
+        console.log("🚀 ~ popupData.activeIndex:", popupData.activeIndex)
+        item.autoPlay = true
+        item.muted = true
+        item.display = true
+      }
+      // console.log('🚀 ~ list', list)
+      nextTick(() => {
+        data.itemList = list
+        postMessage({
+          api: 'courseLoading',
+          content: {
+            show: false,
+            type: 'fullscreen'
+          }
+        })
+      })
+      // setTimeout(() => {
+      // }, 300)
+    }
+    const getDetail = async () => {
+      try {
+        const res: any = await request.get(
+          state.platformApi + `/lessonCoursewareDetail/detail/${route.query.id}`,
+          {
+            hideLoading: true
+          }
+        )
+        if (Array.isArray(res?.data)) {
+          data.detail = res.data
+        }
+        if (Array.isArray(res?.data?.knowledgePointList)) {
+          let index = 0
+          data.knowledgePointList = res.data.knowledgePointList.map((n: any) => {
+            if (Array.isArray(n.materialList)) {
+              n.materialList = n.materialList.map((item: any) => {
+                index++
+                return {
+                  ...item,
+                  materialId: item.id,
+                  id: index + ''
+                }
+              })
+            }
+            return n
+          })
+          getItemList()
+        }
+      } catch (error) {}
+    }
+    // ifram事件处理
+    const iframeHandle = (ev: MessageEvent) => {
+      if (ev.data?.api === 'headerTogge') {
+        // console.log("🚀 ~ ev.data", ev.data)
+        activeData.model = ev.data.show || (ev.data.playState == 'play' ? true : false)
+      }
+    }
+
+    const swiperDom = ref()
+    onMounted(() => {
+      const hasVip = handleCheckVip()
+      if (!hasVip) {
+        nextTick(() => {
+          postMessage({
+            api: 'courseLoading',
+            content: {
+              show: false,
+              type: 'fullscreen'
+            }
+          })
+        })
+        return
+      }
+      getDetail()
+      getCourseSchedule()
+      window.addEventListener('message', iframeHandle)
+    })
+    const playRef = ref()
+    // 返回
+    const goback = () => {
+      try {
+        playRef.value?.handleOut()
+      } catch (error) {}
+      if (route.query.source == 'my-course') {
+        router.back()
+        return
+      }
+      postMessage({ api: 'goBack' })
+    }
+
+    const swipeRef = ref<SwipeInstance>()
+    const popupData = reactive({
+      firstIndex: 0,
+      open: false,
+      activeIndex: 0,
+      tabActive: '',
+      tabName: '',
+      itemActive: '',
+      itemName: '',
+      guideOpen: false
+    })
+
+    /**停止所有的播放 */
+    const handleStop = () => {
+      const activeItem = data.itemList[popupData.activeIndex]
+      for (let i = 0; i < data.itemList.length; i++) {
+        const item = data.itemList[i]
+        // 停止视频播放
+        if (item.type === 'VIDEO') {
+          // console.log("🚀 ~ item", item)
+          if (item?.id != activeItem.id) {
+            item.currentTime = 0
+            item.progress = 0
+            if (item.videoEle) {
+              item.videoEle.currentTime = 0
+              item.videoEle.pause()
+            }
+          }
+        }
+        // 停止曲谱的播放
+        if (item.type === 'SONG') {
+          item.iframeRef?.contentWindow?.postMessage({ api: 'setPlayState' }, '*')
+          item.display = false
+        }
+      }
+    }
+    // 切换素材
+    const toggleMaterial = () => {
+      const index = data.itemList.findIndex((n: any) => n.id == popupData.itemActive)
+      if (index > -1) {
+        // swipeRef.value?.swipeTo(index, {
+        //   immediate: true
+        // })
+        swiperDom.value?.slideTo(index, 1000)
+      }
+    }
+    /** 延迟收起模态框 */
+    const setModelOpen = () => {
+      clearTimeout(activeData.timer)
+      closeToast()
+      activeData.timer = setTimeout(() => {
+        activeData.model = false
+      }, 4000)
+    }
+    // 轮播切换
+    const handleSwipeChange = (val: any) => {
+      console.log('轮播切换')
+      popupData.activeIndex = val
+      const item = data.itemList[val]
+      handleStop()
+      if (item) {
+        popupData.tabActive = item.knowledgePointId
+        popupData.itemActive = item.id
+        popupData.itemName = item.name
+        popupData.tabName = item.tabName
+        if (item.type == 'SONG') {
+          activeData.model = true
+          item.display = true
+        }
+        if (item.type === 'VIDEO') {
+          // console.log("🚀 ~ item", item)
+          // 自动播放下一个视频
+          clearTimeout(activeData.timer)
+          closeToast()
+          item.currentTime = 0
+          item.videoEle && (item.videoEle.currentTime = 0)
+          nextTick(() => {
+            item.autoPlay = true
+            item.videoEle?.play()
+          })
+        }
+      }
+    }
+
+    // 去点名,签退
+    const gotoRollCall = (pageTag: string) => {
+      postMessage({
+        api: 'open_app_page',
+        content: {
+          action: 'app',
+          pageTag: pageTag,
+          url: '',
+          params: JSON.stringify({ courseId: route.query.courseId })
+        }
+      })
+    }
+
+    // 双击
+    const handleDbClick = (item: any) => {
+      // console.log(item)
+      if (item && item.type === 'VIDEO') {
+        const videoEle: HTMLVideoElement = item.videoEle
+        if (videoEle) {
+          if (videoEle.paused) {
+            closeToast()
+            videoEle.play()
+          } else {
+            showToast('已暂停')
+            videoEle.pause()
+          }
+        }
+      }
+    }
+
+    // 暂停播放
+    const handlePaused = (m: any) => {
+      m.videoEle?.pause()
+      m.paused = true
+    }
+    // 开始播放
+    const handlePlay = (m: any) => {
+      closeToast()
+      m.videoEle?.play()
+    }
+
+    // 调整播放进度
+    const handleChangeSlider = (m: any) => {
+      if (m?.videoEle) {
+        // console.log('进度条', m.progress)
+        m.currentTime = m.duration * (m.progress / 100)
+        m.videoEle.currentTime = m.currentTime
+      }
+    }
+
+    //当前视频播放完
+    const handleEnded = (m: any) => {
+      // console.log(m)
+      if (popupData.activeIndex != data.itemList.length - 1) {
+        swiperDom.value.slideNext(800)
+      }
+    }
+
+    const effects = [
+      {
+        prev: {
+          shadow: true,
+          translate: [0, 0, -400]
+        },
+        next: {
+          translate: ['100%', 0, 0]
+        }
+      },
+      {
+        prev: {
+          shadow: true,
+          translate: ['-120%', 0, -500]
+        },
+        next: {
+          shadow: true,
+          translate: ['120%', 0, -500]
+        }
+      },
+      {
+        prev: {
+          shadow: true,
+          translate: ['-20%', 0, -1]
+        },
+        next: {
+          translate: ['100%', 0, 0]
+        }
+      },
+      {
+        prev: {
+          shadow: true,
+          translate: [0, 0, -800],
+          rotate: [-180, 0, 0]
+        },
+        next: {
+          shadow: true,
+          translate: [0, 0, -800],
+          rotate: [180, 0, 0]
+        }
+      },
+      {
+        prev: {
+          shadow: true,
+          translate: ['-125%', 0, -800],
+          rotate: [0, 0, -90]
+        },
+        next: {
+          shadow: true,
+          translate: ['125%', 0, -800],
+          rotate: [0, 0, 90]
+        }
+      },
+      {
+        prev: {
+          shadow: true,
+          origin: 'right center',
+          translate: ['5%', 0, -200],
+          rotate: [0, -100, 0]
+        },
+        next: {
+          origin: 'left center',
+          translate: ['-5%', 0, -200],
+          rotate: [0, 100, 0]
+        }
+      },
+      {
+        prev: {
+          scale: 0.3,
+          opacity: 0.4
+        },
+        next: {
+          opacity: 0.4,
+          scale: 0.3
+        }
+      }
+    ]
+    const swiperType = ref(effects[3])
+    // 上一个知识点, 下一个知识点
+    const handlePreAndNext = (type: string) => {
+      setTimeout(() => {
+        if (type === 'up') {
+          swiperDom.value.slidePrev(800)
+        } else {
+          swiperDom.value.slideNext(800)
+        }
+      }, 400)
+    }
+
+    return () => (
+      <div class={styles.playContent}>
+        <div class={styles.coursewarePlay} style={{ width: parentContainer.width }}>
+          {data.itemList.length && (
+            <Swiper
+              style={{ height: '100%' }}
+              class={styles.swiperContainer}
+              effect="creative"
+              modules={[Pagination, Navigation, EffectCreative]}
+              creativeEffect={swiperType.value}
+              direction="vertical"
+              navigation
+              allowTouchMove={false}
+              onSwiper={(swiper: any) => {
+                swiperDom.value = swiper
+              }}
+              onSlideChange={(swiper: any) => {
+                handleSwipeChange(swiper.activeIndex)
+              }}
+              initialSlide={popupData.activeIndex}
+            >
+              {data.itemList.map((m: any, mIndex: number) => {
+                return (
+                  <SwiperSlide class={styles.swipeItem} key={'index' + mIndex}>
+                    <div
+                      class={[styles.itemDiv]}
+                      onClick={() => {
+                        clearTimeout(activeData.timer)
+                        if (Date.now() - activeData.nowTime < 300) {
+                          handleDbClick(m)
+                          return
+                        }
+                        activeData.nowTime = Date.now()
+                        activeData.timer = setTimeout(() => {
+                          activeData.model = !activeData.model
+                          setModelOpen()
+                        }, 300)
+                      }}
+                    >
+                      {m.type === 'VIDEO' ? (
+                        <>
+                          <video
+                            class={['player']}
+                            playsinline="false"
+                            muted={m.muted}
+                            preload="auto"
+                            data-vid={m.id}
+                            src={m.content}
+                            loop={m.loop}
+                            autoplay={m.autoPlay}
+                            onLoadedmetadata={(e: Event) => {
+                              const videoEle = e.target as unknown as HTMLVideoElement
+                              m.currentTime = videoEle.currentTime
+                              m.duration = videoEle.duration
+                              m.videoEle = videoEle
+                              m.isprepare = true
+                            }}
+                            onTimeupdate={(e: Event) => {
+                              if (!m.isprepare) return
+                              const videoEle = e.target as unknown as HTMLVideoElement
+                              m.currentTime = videoEle.currentTime
+                              m.progress = Number((videoEle.currentTime / m.duration) * 100)
+                            }}
+                            onPlay={() => {
+                              // 播放
+                              m.paused = false
+                              console.log('播放')
+                              setModelOpen()
+                              // 第一次播放
+                              if (m.muted) {
+                                m.muted = false
+                                m.autoPlay = false
+                              }
+                            }}
+                            onPause={() => {
+                              //暂停
+                              clearTimeout(activeData.timer)
+                              m.paused = true
+                            }}
+                            onEnded={() => handleEnded(m)}
+                          >
+                            <source src={m.content} type="video/mp4" />
+                          </video>
+                          {m.muted && (
+                            <div class={styles.loadWrap}>
+                              <Vue3Lottie animationData={playLoadData}></Vue3Lottie>
+                            </div>
+                          )}
+                          <div
+                            style={{ transform: activeData.model ? '' : 'translateY(100%)' }}
+                            class={styles.bottomFixedContainer}
+                            onClick={(e: Event) => {
+                              e.stopPropagation()
+                              setModelOpen()
+                            }}
+                          >
+                            <div style={{ opacity: m.isprepare ? '1' : '0' }}>
+                              <div class={styles.time}>
+                                <span>{getSecondRPM(m.currentTime)}</span>
+                                <span>{getSecondRPM(m.duration)}</span>
+                              </div>
+                              <div class={styles.slider}>
+                                <Slider
+                                  onClick={() => setModelOpen()}
+                                  buttonSize={16}
+                                  step={1}
+                                  modelValue={m.progress}
+                                  onUpdate:modelValue={(val: any) => {
+                                    console.log('val', val)
+                                    m.progress = val
+                                    handleChangeSlider(m)
+                                  }}
+                                  onDragStart={(e: Event) => {
+                                    // 开始拖动,暂停播放
+                                    console.log('开始拖动')
+                                    // 如果拖动之前,视频是播放状态,拖动完毕后继续播放
+                                    if (!m.paused) {
+                                      m.isDrage = true
+                                    }
+                                    handlePaused(m)
+                                  }}
+                                  onDragEnd={(e: Event) => {
+                                    console.log('结束拖动')
+                                    if (m.isDrage) {
+                                      m.isDrage = false
+                                      handlePlay(m)
+                                    }
+                                  }}
+                                  min={0}
+                                  max={100}
+                                />
+                              </div>
+                            </div>
+
+                            <div class={styles.actions}>
+                              <div class={styles.actionBtn}>
+                                {m.isprepare ? (
+                                  <>
+                                    {m.paused ? (
+                                      <img src={iconplay} onClick={(e: Event) => handlePlay(m)} />
+                                    ) : (
+                                      <img
+                                        src={iconpause}
+                                        onClick={(e: Event) => handlePaused(m)}
+                                      />
+                                    )}
+                                  </>
+                                ) : (
+                                  <Loading color="#fff" />
+                                )}
+
+                                {m.loop ? (
+                                  <img
+                                    src={iconLoopActive}
+                                    onClick={(e: Event) => (m.loop = false)}
+                                  />
+                                ) : (
+                                  <img src={iconLoop} onClick={(e: Event) => (m.loop = true)} />
+                                )}
+                              </div>
+                              <div>{m.name}</div>
+                            </div>
+                          </div>
+                        </>
+                      ) : m.type === 'IMG' ? (
+                        <img src={m.content} />
+                      ) : (
+                        <MusicScore
+                          data-vid={m.id}
+                          music={m}
+                          onSetIframe={(el: any) => {
+                            m.iframeRef = el
+                          }}
+                        />
+                      )}
+                    </div>
+                  </SwiperSlide>
+                )
+              })}
+            </Swiper>
+          )}
+
+          <div
+            style={{ transform: activeData.model ? '' : 'translateY(-100%)' }}
+            id="coursePlayHeader"
+            class={styles.headerContainer}
+            ref={headeRef}
+          >
+            <div class={styles.backBtn} onClick={() => goback()}>
+              <Icon name={iconBack} />
+              返回
+            </div>
+            <div class={styles.menu}>{popupData.tabName}</div>
+            {data.isCourse && <PlayRecordTime ref={playRef} list={data.itemList} />}
+          </div>
+          <Transition name="right">
+            {activeData.model && (
+              <div class={styles.rightFixedBtns}>
+                <div class={styles.btnsWrap}>
+                  <div
+                    class={[styles.fullBtn, styles.point]}
+                    onClick={() => {
+                      clearTimeout(activeData.timer)
+                      popupData.open = true
+                    }}
+                  >
+                    <img src={iconMenu} />
+                    <span>知识点</span>
+                  </div>
+                </div>
+
+                <div class={[styles.btnsWrap, styles.btnsBottom]}>
+                  <div class={styles.fullBtn} onClick={() => (popupData.guideOpen = true)}>
+                    <img src={iconTouping} />
+                    <span>投屏</span>
+                  </div>
+                  {data.isCourse && (
+                    <>
+                      <div class={styles.fullBtn} onClick={() => gotoRollCall('student_roll_call')}>
+                        <img src={iconDian} />
+                        <span>点名</span>
+                      </div>
+                      <div class={styles.fullBtn} onClick={() => gotoRollCall('sign_out')}>
+                        <img src={iconPoint} />
+                        <span>签退</span>
+                      </div>
+                    </>
+                  )}
+                </div>
+              </div>
+            )}
+          </Transition>
+          <Transition name="left">
+            {activeData.model && (
+              <div class={styles.leftFixedBtns}>
+                {popupData.activeIndex != 0 && (
+                  <div
+                    class={[styles.fullBtn, styles.prePoint]}
+                    onClick={() => handlePreAndNext('up')}
+                  >
+                    <img src={iconUp} />
+                    <span style={{ textAlign: 'center' }}>上一个</span>
+                  </div>
+                )}
+                {popupData.activeIndex != data.itemList.length - 1 && (
+                  <div class={[styles.fullBtn]} onClick={() => handlePreAndNext('down')}>
+                    <span style={{ textAlign: 'center' }}>下一个</span>
+                    <img src={iconDown} />
+                  </div>
+                )}
+              </div>
+            )}
+          </Transition>
+          <Popup
+            class={styles.popup}
+            overlayClass={styles.overlayClass}
+            position="right"
+            round
+            v-model:show={popupData.open}
+            onClose={() => {
+              const item = data.itemList[popupData.activeIndex]
+              if (item?.type == 'VIDEO') {
+                setModelOpen()
+              }
+            }}
+          >
+            <Points
+              data={data.knowledgePointList}
+              tabActive={popupData.tabActive}
+              itemActive={popupData.itemActive}
+              onHandleSelect={(res: any) => {
+                // console.log(res)
+                popupData.tabActive = res.tabActive
+                popupData.itemActive = res.itemActive
+                popupData.tabName = res.tabName
+                popupData.open = false
+                toggleMaterial()
+              }}
+            />
+          </Popup>
+
+          <Popup
+            class={styles.popup}
+            overlayClass={styles.overlayClass}
+            position="right"
+            round
+            v-model:show={popupData.guideOpen}
+          >
+            <OGuide />
+          </Popup>
+        </div>
+      </div>
+    )
+  }
+})