|
@@ -42,7 +42,9 @@ import iconDown from './image/icon-down.svg'
|
|
|
import iconVideobg from './image/icon-videobg.png'
|
|
|
import Points from './component/points'
|
|
|
import { browser, getSecondRPM } from '@/helpers/utils'
|
|
|
-import { useRect } from '@vant/use'
|
|
|
+import VideoPlay from './component/video-play'
|
|
|
+import { Vue3Lottie } from 'vue3-lottie'
|
|
|
+import playLoadData from './datas/data.json'
|
|
|
|
|
|
export default defineComponent({
|
|
|
name: 'CoursewarePlay',
|
|
@@ -75,6 +77,18 @@ export default defineComponent({
|
|
|
handleInit(1)
|
|
|
window.removeEventListener('message', iframeHandle)
|
|
|
})
|
|
|
+ /** 设置播放容器 16:9 */
|
|
|
+ const parentContainer = reactive({
|
|
|
+ width: '100vw'
|
|
|
+ })
|
|
|
+ const setContainer = () => {
|
|
|
+ let width = screen.height * (16 / 9)
|
|
|
+ if (width > screen.width) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ parentContainer.width = width + 'px'
|
|
|
+ }
|
|
|
+ setContainer()
|
|
|
|
|
|
const route = useRoute()
|
|
|
const headeRef = ref()
|
|
@@ -226,6 +240,7 @@ export default defineComponent({
|
|
|
getDetail()
|
|
|
getCourseSchedule()
|
|
|
window.addEventListener('message', iframeHandle)
|
|
|
+ window.addEventListener('resize', setContainer)
|
|
|
})
|
|
|
// 返回
|
|
|
const goback = () => {
|
|
@@ -378,276 +393,283 @@ export default defineComponent({
|
|
|
}
|
|
|
|
|
|
return () => (
|
|
|
- <div class={styles.coursewarePlay}>
|
|
|
- <Swipe
|
|
|
- style={{ height: '100vh' }}
|
|
|
- ref={swipeRef}
|
|
|
- showIndicators={false}
|
|
|
- loop={false}
|
|
|
- vertical
|
|
|
- lazyRender={true}
|
|
|
- touchable={false}
|
|
|
- initialSwipe={popupData.firstIndex}
|
|
|
- onChange={handleSwipeChange}
|
|
|
- >
|
|
|
- {data.itemList.map((m: any, mIndex: number) => {
|
|
|
- return (
|
|
|
- <SwipeItem class={styles.swipeItem}>
|
|
|
- <>
|
|
|
- <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
|
|
|
- playsinline="false"
|
|
|
- muted={m.muted}
|
|
|
- preload="auto"
|
|
|
- class="player"
|
|
|
- poster={iconVideobg}
|
|
|
- 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).toFixed(1)
|
|
|
- )
|
|
|
- }}
|
|
|
- onPlay={() => {
|
|
|
- // 播放
|
|
|
- m.paused = false
|
|
|
- console.log('播放')
|
|
|
- setModelOpen()
|
|
|
- m.muted = false
|
|
|
- }}
|
|
|
- onPause={() => {
|
|
|
- //暂停
|
|
|
- clearTimeout(activeData.timer)
|
|
|
- m.paused = true
|
|
|
- }}
|
|
|
- onEnded={() => handleEnded(m)}
|
|
|
- >
|
|
|
- <source src={m.content} type="video/mp4" />
|
|
|
- </video>
|
|
|
- <Transition name="bottom">
|
|
|
- {activeData.model && (
|
|
|
- <div class={[styles.bottomFixedContainer]}>
|
|
|
- <div class={styles.time}>
|
|
|
- <span>{getSecondRPM(m.currentTime)}</span>
|
|
|
- <span>{getSecondRPM(m.duration)}</span>
|
|
|
- </div>
|
|
|
- <div class={styles.slider}>
|
|
|
- <Slider
|
|
|
- onClick={() => {
|
|
|
- setModelOpen()
|
|
|
- }}
|
|
|
- style={{ display: m.isprepare ? 'block' : 'none' }}
|
|
|
- buttonSize={16}
|
|
|
- step={0.1}
|
|
|
- modelValue={m.progress}
|
|
|
- onUpdate:modelValue={(val: any) => {
|
|
|
- m.progress = val
|
|
|
- handleChangeSlider(m)
|
|
|
- }}
|
|
|
- onDragStart={(e: Event) => {
|
|
|
- // 开始拖动,暂停播放
|
|
|
- console.log('开始拖动')
|
|
|
- // 如果拖动之前,视频是播放状态,拖动完毕后继续播放
|
|
|
- if (!m.paused) {
|
|
|
- m.isDrage = true
|
|
|
- }
|
|
|
- handlePaused(e, m)
|
|
|
- }}
|
|
|
- onDragEnd={(e: Event) => {
|
|
|
- console.log('结束拖动')
|
|
|
- if (m.isDrage) {
|
|
|
- m.isDrage = false
|
|
|
- handlePlay(e, m)
|
|
|
- }
|
|
|
- }}
|
|
|
- min={0}
|
|
|
- max={100}
|
|
|
- />
|
|
|
- </div>
|
|
|
+ <div class={styles.playContent}>
|
|
|
+ <div class={styles.coursewarePlay} style={{ ...parentContainer }}>
|
|
|
+ <Swipe
|
|
|
+ style={{ height: '100%' }}
|
|
|
+ ref={swipeRef}
|
|
|
+ showIndicators={false}
|
|
|
+ loop={false}
|
|
|
+ vertical
|
|
|
+ lazyRender={true}
|
|
|
+ touchable={false}
|
|
|
+ initialSwipe={popupData.firstIndex}
|
|
|
+ onChange={handleSwipeChange}
|
|
|
+ >
|
|
|
+ {data.itemList.map((m: any, mIndex: number) => {
|
|
|
+ return (
|
|
|
+ <SwipeItem class={styles.swipeItem}>
|
|
|
+ <>
|
|
|
+ <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
|
|
|
+ playsinline="false"
|
|
|
+ muted={m.muted}
|
|
|
+ preload="auto"
|
|
|
+ class="player"
|
|
|
+ poster={iconVideobg}
|
|
|
+ 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).toFixed(1)
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ onPlay={() => {
|
|
|
+ // 播放
|
|
|
+ m.paused = false
|
|
|
+ console.log('播放')
|
|
|
+ setModelOpen()
|
|
|
+ m.muted = 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>
|
|
|
+ )}
|
|
|
+ <Transition name="bottom">
|
|
|
+ {activeData.model && (
|
|
|
+ <div class={[styles.bottomFixedContainer]}>
|
|
|
+ <div class={styles.time}>
|
|
|
+ <span>{getSecondRPM(m.currentTime)}</span>
|
|
|
+ <span>{getSecondRPM(m.duration)}</span>
|
|
|
+ </div>
|
|
|
+ <div class={styles.slider}>
|
|
|
+ <Slider
|
|
|
+ onClick={() => {
|
|
|
+ setModelOpen()
|
|
|
+ }}
|
|
|
+ style={{ display: m.isprepare ? 'block' : 'none' }}
|
|
|
+ buttonSize={16}
|
|
|
+ step={0.1}
|
|
|
+ modelValue={m.progress}
|
|
|
+ onUpdate:modelValue={(val: any) => {
|
|
|
+ m.progress = val
|
|
|
+ handleChangeSlider(m)
|
|
|
+ }}
|
|
|
+ onDragStart={(e: Event) => {
|
|
|
+ // 开始拖动,暂停播放
|
|
|
+ console.log('开始拖动')
|
|
|
+ // 如果拖动之前,视频是播放状态,拖动完毕后继续播放
|
|
|
+ if (!m.paused) {
|
|
|
+ m.isDrage = true
|
|
|
+ }
|
|
|
+ handlePaused(e, m)
|
|
|
+ }}
|
|
|
+ onDragEnd={(e: Event) => {
|
|
|
+ console.log('结束拖动')
|
|
|
+ if (m.isDrage) {
|
|
|
+ m.isDrage = false
|
|
|
+ handlePlay(e, m)
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ min={0}
|
|
|
+ max={100}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
|
|
|
- <div class={styles.actions}>
|
|
|
- <div class={styles.actionBtn}>
|
|
|
- {m.isprepare ? (
|
|
|
- <>
|
|
|
- {m.paused ? (
|
|
|
- <img
|
|
|
- src={iconplay}
|
|
|
- onClick={(e: Event) => handlePlay(e, m)}
|
|
|
- />
|
|
|
- ) : (
|
|
|
- <img
|
|
|
- src={iconpause}
|
|
|
- onClick={(e: Event) => handlePaused(e, m)}
|
|
|
- />
|
|
|
- )}
|
|
|
- </>
|
|
|
- ) : (
|
|
|
- <Loading color="#fff" />
|
|
|
- )}
|
|
|
+ <div class={styles.actions}>
|
|
|
+ <div class={styles.actionBtn}>
|
|
|
+ {m.isprepare ? (
|
|
|
+ <>
|
|
|
+ {m.paused ? (
|
|
|
+ <img
|
|
|
+ src={iconplay}
|
|
|
+ onClick={(e: Event) => handlePlay(e, m)}
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <img
|
|
|
+ src={iconpause}
|
|
|
+ onClick={(e: Event) => handlePaused(e, m)}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </>
|
|
|
+ ) : (
|
|
|
+ <Loading color="#fff" />
|
|
|
+ )}
|
|
|
|
|
|
- {m.loop ? (
|
|
|
- <img
|
|
|
- src={iconLoopActive}
|
|
|
- onClick={(e: Event) => {
|
|
|
- e.stopPropagation()
|
|
|
- m.loop = false
|
|
|
- }}
|
|
|
- />
|
|
|
- ) : (
|
|
|
- <img
|
|
|
- src={iconLoop}
|
|
|
- onClick={(e: Event) => {
|
|
|
- e.stopPropagation()
|
|
|
- m.loop = true
|
|
|
- }}
|
|
|
- />
|
|
|
- )}
|
|
|
+ {m.loop ? (
|
|
|
+ <img
|
|
|
+ src={iconLoopActive}
|
|
|
+ onClick={(e: Event) => {
|
|
|
+ e.stopPropagation()
|
|
|
+ m.loop = false
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <img
|
|
|
+ src={iconLoop}
|
|
|
+ onClick={(e: Event) => {
|
|
|
+ e.stopPropagation()
|
|
|
+ m.loop = true
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <div>{m.name}</div>
|
|
|
</div>
|
|
|
- <div>{m.name}</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </Transition>
|
|
|
- </>
|
|
|
- ) : m.type === 'IMG' ? (
|
|
|
- <img src={m.content} />
|
|
|
- ) : (
|
|
|
- <MusicScore
|
|
|
- data-vid={m.id}
|
|
|
- music={m}
|
|
|
- onSetIframe={(el: any) => {
|
|
|
- m.iframeRef = el
|
|
|
- }}
|
|
|
- />
|
|
|
- )}
|
|
|
- </div>
|
|
|
- </>
|
|
|
- </SwipeItem>
|
|
|
- )
|
|
|
- })}
|
|
|
- </Swipe>
|
|
|
+ )}
|
|
|
+ </Transition>
|
|
|
+ </>
|
|
|
+ ) : m.type === 'IMG' ? (
|
|
|
+ <img src={m.content} />
|
|
|
+ ) : (
|
|
|
+ <MusicScore
|
|
|
+ data-vid={m.id}
|
|
|
+ music={m}
|
|
|
+ onSetIframe={(el: any) => {
|
|
|
+ m.iframeRef = el
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ </SwipeItem>
|
|
|
+ )
|
|
|
+ })}
|
|
|
+ </Swipe>
|
|
|
|
|
|
- <Transition name="top">
|
|
|
- {activeData.model && (
|
|
|
- <div class={styles.headerContainer} ref={headeRef}>
|
|
|
- <div class={styles.backBtn} onClick={() => goback()}>
|
|
|
- <Icon name={iconBack} />
|
|
|
- 返回
|
|
|
+ <Transition name="top">
|
|
|
+ {activeData.model && (
|
|
|
+ <div class={styles.headerContainer} ref={headeRef}>
|
|
|
+ <div class={styles.backBtn} onClick={() => goback()}>
|
|
|
+ <Icon name={iconBack} />
|
|
|
+ 返回
|
|
|
+ </div>
|
|
|
+ <div class={styles.menu}>{popupData.tabName}</div>
|
|
|
</div>
|
|
|
- <div class={styles.menu}>{popupData.tabName}</div>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </Transition>
|
|
|
+ )}
|
|
|
+ </Transition>
|
|
|
|
|
|
- <Transition name="right">
|
|
|
- {activeData.model && (
|
|
|
- <div class={styles.rightFixedBtns}>
|
|
|
- <div
|
|
|
- class={styles.fullBtn}
|
|
|
- onClick={() => {
|
|
|
- clearTimeout(activeData.timer)
|
|
|
- popupData.open = true
|
|
|
- }}
|
|
|
- >
|
|
|
- <img src={iconMenu} />
|
|
|
- <span>知识点</span>
|
|
|
+ <Transition name="right">
|
|
|
+ {activeData.model && (
|
|
|
+ <div class={styles.rightFixedBtns}>
|
|
|
+ <div
|
|
|
+ class={styles.fullBtn}
|
|
|
+ onClick={() => {
|
|
|
+ clearTimeout(activeData.timer)
|
|
|
+ popupData.open = true
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <img src={iconMenu} />
|
|
|
+ <span>知识点</span>
|
|
|
+ </div>
|
|
|
+ {data.isCourse && (
|
|
|
+ <>
|
|
|
+ <div
|
|
|
+ class={[styles.fullBtn, styles.point]}
|
|
|
+ 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>
|
|
|
- {data.isCourse && (
|
|
|
- <>
|
|
|
+ )}
|
|
|
+ </Transition>
|
|
|
+
|
|
|
+ <Transition name="left">
|
|
|
+ {activeData.model && (
|
|
|
+ <div class={styles.leftFixedBtns}>
|
|
|
+ {popupData.activeIndex != 0 && (
|
|
|
<div
|
|
|
- class={[styles.fullBtn, styles.point]}
|
|
|
- onClick={() => gotoRollCall('student_roll_call')}
|
|
|
+ class={[styles.fullBtn, styles.prePoint]}
|
|
|
+ onClick={() => handlePreAndNext('up')}
|
|
|
>
|
|
|
- <img src={iconDian} />
|
|
|
- <span>点名</span>
|
|
|
+ <img src={iconUp} />
|
|
|
+ <span style={{ textAlign: 'center' }}>上一个</span>
|
|
|
</div>
|
|
|
- <div class={styles.fullBtn} onClick={() => gotoRollCall('sign_out')}>
|
|
|
- <img src={iconPoint} />
|
|
|
- <span>签退</span>
|
|
|
+ )}
|
|
|
+ {popupData.activeIndex != data.itemList.length - 1 && (
|
|
|
+ <div class={styles.fullBtn} onClick={() => handlePreAndNext('down')}>
|
|
|
+ <span style={{ textAlign: 'center' }}>下一个</span>
|
|
|
+ <img src={iconDown} />
|
|
|
</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>
|
|
|
+ )}
|
|
|
+ </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
|
|
|
+ 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()
|
|
|
+ }
|
|
|
}}
|
|
|
- />
|
|
|
- </Popup>
|
|
|
+ >
|
|
|
+ <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>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
)
|
|
|
}
|