index.tsx 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. import {
  2. closeToast,
  3. Icon,
  4. Loading,
  5. Popup,
  6. showDialog,
  7. showToast,
  8. Slider,
  9. Swipe,
  10. SwipeInstance,
  11. SwipeItem
  12. } from 'vant'
  13. import {
  14. defineComponent,
  15. onMounted,
  16. reactive,
  17. nextTick,
  18. onUnmounted,
  19. ref,
  20. watch,
  21. Transition,
  22. TransitionGroup,
  23. onBeforeUnmount
  24. } from 'vue'
  25. import iconBack from './image/back.svg'
  26. import styles from './index.module.less'
  27. import 'plyr/dist/plyr.css'
  28. import request from '@/helpers/request'
  29. import { state } from '@/state'
  30. import { useRoute, useRouter } from 'vue-router'
  31. import { listenerMessage, postMessage, promisefiyPostMessage } from '@/helpers/native-message'
  32. import MusicScore from './component/musicScore'
  33. import iconMenu from './image/icon-menu.svg'
  34. import iconDian from './image/icon-dian.svg'
  35. import iconTouping from './image/icon-touping.svg'
  36. import iconPoint from './image/icon-point.svg'
  37. import iconLoop from './image/icon-loop.svg'
  38. import iconLoopActive from './image/icon-loop-active.svg'
  39. import iconplay from './image/icon-play.svg'
  40. import iconpause from './image/icon-pause.svg'
  41. import iconUp from './image/icon-up.svg'
  42. import iconDown from './image/icon-down.svg'
  43. import Points from './component/points'
  44. import { browser, getSecondRPM } from '@/helpers/utils'
  45. import { Vue3Lottie } from 'vue3-lottie'
  46. import playLoadData from './datas/data.json'
  47. import { usePageVisibility, useRect } from '@vant/use'
  48. import PlayRecordTime from './playRecordTime'
  49. import VideoPlay from './component/video-play'
  50. import {
  51. Pagination,
  52. Navigation,
  53. Virtual,
  54. EffectFade,
  55. EffectFlip,
  56. EffectCreative,
  57. Lazy
  58. } from 'swiper'
  59. import { Swiper, SwiperSlide } from 'swiper/vue'
  60. import 'swiper/less'
  61. import 'swiper/less/effect-fade'
  62. import 'swiper/less/effect-flip'
  63. import 'swiper/less/effect-creative'
  64. import { handleCheckVip } from '../hook/useFee'
  65. import OGuide from '@/components/o-guide'
  66. export default defineComponent({
  67. name: 'CoursewarePlay',
  68. setup() {
  69. const pageVisibility = usePageVisibility()
  70. const isPlay = ref(false)
  71. /** 页面显示和隐藏 */
  72. watch(pageVisibility, (value) => {
  73. const activeItem = data.itemList[popupData.activeIndex]
  74. if (activeItem.type != 'VIDEO') return
  75. if (value == 'hidden') {
  76. isPlay.value = !activeItem.videoEle.paused
  77. togglePlay(activeItem, false)
  78. } else {
  79. // 页面显示,并且
  80. if (isPlay.value) togglePlay(activeItem, true)
  81. }
  82. })
  83. /** 设置播放容器 16:9 */
  84. const parentContainer = reactive({
  85. width: '100vw'
  86. })
  87. const setContainer = () => {
  88. let min = Math.min(screen.width, screen.height)
  89. let max = Math.max(screen.width, screen.height)
  90. let width = min * (16 / 9)
  91. if (width > max) {
  92. parentContainer.width = '100vw'
  93. return
  94. } else {
  95. parentContainer.width = width + 'px'
  96. }
  97. }
  98. const handleInit = (type = 0) => {
  99. //设置容器16:9
  100. setContainer()
  101. // 横屏
  102. postMessage(
  103. {
  104. api: 'setRequestedOrientation',
  105. content: {
  106. orientation: type
  107. }
  108. },
  109. () => {
  110. console.log(234)
  111. }
  112. )
  113. // 头,包括返回箭头
  114. postMessage({
  115. api: 'setTitleBarVisibility',
  116. content: {
  117. status: type
  118. }
  119. })
  120. // 安卓的状态栏
  121. postMessage({
  122. api: 'setStatusBarVisibility',
  123. content: {
  124. isVisibility: type
  125. }
  126. })
  127. // 进入页面设置常量
  128. postMessage({
  129. api: 'keepScreenLongLight',
  130. content: {
  131. isOpenLight: type ? true : false
  132. }
  133. })
  134. }
  135. handleInit()
  136. onUnmounted(() => {
  137. handleInit(1)
  138. window.removeEventListener('message', iframeHandle)
  139. })
  140. const route = useRoute()
  141. const router = useRouter()
  142. const headeRef = ref()
  143. const data = reactive({
  144. detail: null,
  145. knowledgePointList: [] as any,
  146. itemList: [] as any,
  147. showHead: true,
  148. isCourse: false,
  149. isRecordPlay: false,
  150. videoRefs: {}
  151. })
  152. const activeData = reactive({
  153. nowTime: 0,
  154. model: true, // 遮罩
  155. videoBtns: true, // 视频
  156. currentTime: 0,
  157. duration: 0,
  158. timer: null as any,
  159. item: null as any
  160. })
  161. // 获取缓存路径
  162. const getCacheFilePath = async (material: any) => {
  163. const res = await promisefiyPostMessage({
  164. api: 'getCourseFilePath',
  165. content: {
  166. url: material.content,
  167. localPath: '',
  168. materialId: material.materialId,
  169. updateTime: material.updateTime,
  170. type: material.type // SONG VIDEO IMAGE
  171. }
  172. })
  173. // console.log('缓存路径返回', res)
  174. return res
  175. }
  176. // 获取当前课程是否签退
  177. const getCourseSchedule = async () => {
  178. if (!route.query.courseId) return
  179. try {
  180. const res = await request.get(
  181. `${state.platformApi}/courseSchedule/detail/${route.query.courseId}`,
  182. {
  183. hideLoading: true
  184. }
  185. )
  186. if (res?.data) {
  187. data.isCourse =
  188. res.data.status === 'ING' && state.platformType == 'TEACHER' ? true : false
  189. // data.isRecordPlay = Date.now() > dayjs(res.data.startTime).valueOf()
  190. }
  191. } catch (e) {
  192. console.log(e)
  193. }
  194. }
  195. const getItemList = async () => {
  196. const list: any = []
  197. const browserInfo = browser()
  198. for (let i = 0; i < data.knowledgePointList.length; i++) {
  199. const item = data.knowledgePointList[i]
  200. const itemLength = item.materialList.length - 1
  201. for (let j = 0; j < item.materialList.length; j++) {
  202. const material = item.materialList[j]
  203. //请求本地缓存
  204. if (browserInfo.isApp && ['VIDEO', 'IMG'].includes(material.type)) {
  205. const localData = await getCacheFilePath(material)
  206. if (localData?.content?.localPath) {
  207. material.url = material.content
  208. material.content = localData.content.localPath
  209. }
  210. }
  211. list.push({
  212. ...material,
  213. iframeRef: null,
  214. videoEle: null,
  215. tabName: item.name,
  216. isAnimation: true, // 当前知识点
  217. autoPlay: false, //加载完成是否自动播放
  218. isprepare: false, // 视频是否加载完成
  219. isRender: false // 是否渲染了
  220. })
  221. }
  222. }
  223. let _firstIndex = list.findIndex((n: any) => n.materialId == route.query.kId)
  224. _firstIndex = _firstIndex > -1 ? _firstIndex : 0
  225. const item = list[_firstIndex]
  226. item.autoPlay = true
  227. popupData.activeIndex = _firstIndex
  228. popupData.tabName = item.tabName
  229. popupData.tabActive = item.knowledgePointId
  230. popupData.itemActive = item.id
  231. popupData.itemName = item.name
  232. nextTick(() => {
  233. data.itemList = list
  234. postMessage({
  235. api: 'courseLoading',
  236. content: {
  237. show: false,
  238. type: 'fullscreen'
  239. }
  240. })
  241. //检测是否录屏
  242. // handleLimitScreenRecord()
  243. })
  244. }
  245. const getDetail = async () => {
  246. try {
  247. const res: any = await request.get(
  248. state.platformApi + `/lessonCoursewareDetail/detail/${route.query.id}`,
  249. {
  250. hideLoading: true
  251. }
  252. )
  253. if (Array.isArray(res?.data)) {
  254. data.detail = res.data
  255. }
  256. if (Array.isArray(res?.data?.knowledgePointList)) {
  257. let index = 0
  258. data.knowledgePointList = res.data.knowledgePointList.map((n: any) => {
  259. if (Array.isArray(n.materialList)) {
  260. n.materialList = n.materialList.map((item: any) => {
  261. index++
  262. return {
  263. ...item,
  264. materialId: item.id,
  265. id: index + ''
  266. }
  267. })
  268. }
  269. return n
  270. })
  271. getItemList()
  272. }
  273. } catch (error) {}
  274. }
  275. // ifram事件处理
  276. const iframeHandle = (ev: MessageEvent) => {
  277. if (ev.data?.api === 'headerTogge') {
  278. activeData.model = ev.data.show || (ev.data.playState == 'play' ? false : true)
  279. }
  280. }
  281. //录屏时间触发
  282. const handleLimitScreenRecord = async () => {
  283. const result = await promisefiyPostMessage({
  284. api: 'getDeviceStatus',
  285. content: { type: 'video' }
  286. })
  287. const { status } = result?.content || {}
  288. if (status == '1') {
  289. data.itemList.forEach((item: any) => (item.autoPlay = false))
  290. handleStop()
  291. showDialog({
  292. title: '温馨提示',
  293. message: '课件内容请勿录屏',
  294. beforeClose: () => {
  295. return new Promise(async (resolve) => {
  296. const { content } =
  297. (await promisefiyPostMessage({
  298. api: 'getDeviceStatus',
  299. content: { type: 'video' }
  300. })) || {}
  301. if (content?.status == '1') {
  302. resolve(false)
  303. } else {
  304. const activeItem = data.itemList[popupData.activeIndex]
  305. togglePlay(activeItem, true)
  306. resolve(true)
  307. }
  308. })
  309. }
  310. })
  311. }
  312. }
  313. onMounted(() => {
  314. const hasVip = handleCheckVip()
  315. if (!hasVip) {
  316. nextTick(() => {
  317. postMessage({
  318. api: 'courseLoading',
  319. content: {
  320. show: false,
  321. type: 'fullscreen'
  322. }
  323. })
  324. })
  325. return
  326. }
  327. getDetail()
  328. getCourseSchedule()
  329. window.addEventListener('message', iframeHandle)
  330. //禁止录屏 ios
  331. // listenerMessage('setVideoPlayer', (result) => {
  332. // if (result?.content?.status == 'pause'){
  333. // handleLimitScreenRecord()
  334. // }
  335. // })
  336. // // 安卓
  337. // postMessage({
  338. // api: 'limitScreenRecord',
  339. // content: {
  340. // type: 1
  341. // }
  342. // })
  343. })
  344. // onBeforeUnmount(() => {
  345. // postMessage({
  346. // api: 'limitScreenRecord',
  347. // content: {
  348. // type: 0
  349. // }
  350. // })
  351. // })
  352. const playRef = ref()
  353. // 返回
  354. const goback = () => {
  355. try {
  356. playRef.value?.handleOut()
  357. } catch (error) {}
  358. postMessage({ api: 'goBack' })
  359. }
  360. const popupData = reactive({
  361. open: false,
  362. activeIndex: 0,
  363. tabActive: '',
  364. tabName: '',
  365. itemActive: '',
  366. itemName: '',
  367. guideOpen: false
  368. })
  369. /**停止所有的播放 */
  370. const handleStop = () => {
  371. for (let i = 0; i < data.itemList.length; i++) {
  372. const activeItem = data.itemList[i]
  373. if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
  374. activeItem.videoEle.stop()
  375. }
  376. // console.log('🚀 ~ activeItem:', activeItem)
  377. // 停止曲谱的播放
  378. if (activeItem.type === 'SONG') {
  379. activeItem.iframeRef?.contentWindow?.postMessage({ api: 'setPlayState' }, '*')
  380. }
  381. }
  382. }
  383. // 切换素材
  384. const toggleMaterial = (itemActive: any) => {
  385. const index = data.itemList.findIndex((n: any) => n.id == itemActive)
  386. if (index > -1) {
  387. handleSwipeChange(index)
  388. }
  389. }
  390. /** 延迟收起模态框 */
  391. const setModelOpen = () => {
  392. clearTimeout(activeData.timer)
  393. closeToast()
  394. activeData.timer = setTimeout(() => {
  395. activeData.model = false
  396. Object.values(data.videoRefs).map((n: any) => n.toggleHideControl(false))
  397. }, 4000)
  398. }
  399. // 去点名,签退
  400. const gotoRollCall = (pageTag: string) => {
  401. postMessage({
  402. api: 'open_app_page',
  403. content: {
  404. action: 'app',
  405. pageTag: pageTag,
  406. url: '',
  407. params: JSON.stringify({ courseId: route.query.courseId })
  408. }
  409. })
  410. }
  411. // 双击
  412. const handleDbClick = (item: any) => {
  413. if (item && item.type === 'VIDEO') {
  414. const videoEle: HTMLVideoElement = item.videoEle
  415. if (videoEle) {
  416. if (videoEle.paused) {
  417. closeToast()
  418. videoEle.play()
  419. } else {
  420. showToast('已暂停')
  421. videoEle.pause()
  422. }
  423. }
  424. }
  425. }
  426. // 切换播放
  427. const togglePlay = (m: any, isPlay: boolean) => {
  428. if (isPlay) {
  429. m.videoEle?.play()
  430. } else {
  431. m.videoEle?.pause()
  432. }
  433. }
  434. const showIndex = ref(-4)
  435. const effectIndex = ref(0)
  436. const effects = [
  437. {
  438. prev: {
  439. transform: 'translate3d(0, 0, -800px) rotateX(180deg)'
  440. },
  441. next: {
  442. transform: 'translate3d(0, 0, -800px) rotateX(-180deg)'
  443. }
  444. },
  445. {
  446. prev: {
  447. transform: 'translate3d(-100%, 0, -800px)'
  448. },
  449. next: {
  450. transform: 'translate3d(100%, 0, -800px)'
  451. }
  452. },
  453. {
  454. prev: {
  455. transform: 'translate3d(-50%, 0, -800px) rotateY(80deg)'
  456. },
  457. next: {
  458. transform: 'translate3d(50%, 0, -800px) rotateY(-80deg)'
  459. }
  460. },
  461. {
  462. prev: {
  463. transform: 'translate3d(-100%, 0, -800px) rotateY(-120deg)'
  464. },
  465. next: {
  466. transform: 'translate3d(100%, 0, -800px) rotateY(120deg)'
  467. }
  468. },
  469. //风车4
  470. {
  471. prev: {
  472. transform: 'translate3d(-50%, 50%, -800px) rotateZ(-14deg)',
  473. opacity: 0
  474. },
  475. next: {
  476. transform: 'translate3d(50%, 50%, -800px) rotateZ(14deg)',
  477. opacity: 0
  478. }
  479. },
  480. //翻页5
  481. {
  482. prev: {
  483. transform: 'translateZ(-800px) rotate3d(0, -1, 0, 90deg)',
  484. opacity: 0
  485. },
  486. next: {
  487. transform: 'translateZ(-800px) rotate3d(0, 1, 0, 90deg)',
  488. opacity: 0
  489. },
  490. current: { transitionDelay: '700ms' }
  491. }
  492. ]
  493. const acitveTimer = ref()
  494. // 轮播切换
  495. const handleSwipeChange = (index: number) => {
  496. if (popupData.activeIndex == index) return
  497. popupData.activeIndex = index
  498. handleStop()
  499. clearTimeout(acitveTimer.value)
  500. const item = data.itemList[index]
  501. if (item) {
  502. const preItem = data.itemList[index - 1]
  503. const nextItem = data.itemList[index + 1]
  504. preItem && (preItem.isAnimation = item.knowledgePointId != preItem.knowledgePointId)
  505. nextItem && (nextItem.isAnimation = item.knowledgePointId != nextItem.knowledgePointId)
  506. }
  507. acitveTimer.value = setTimeout(
  508. () => {
  509. if (item) {
  510. popupData.tabActive = item.knowledgePointId
  511. popupData.itemActive = item.id
  512. popupData.itemName = item.name
  513. popupData.tabName = item.tabName
  514. if (item.type == 'SONG') {
  515. activeData.model = true
  516. }
  517. if (item.type === 'VIDEO') {
  518. // 自动播放下一个视频
  519. clearTimeout(activeData.timer)
  520. closeToast()
  521. item.autoPlay = true
  522. nextTick(() => {
  523. item.videoEle?.play()
  524. })
  525. }
  526. }
  527. },
  528. item.isAnimation ? 800 : 0
  529. )
  530. }
  531. // 上一个知识点, 下一个知识点
  532. const handlePreAndNext = (type: string) => {
  533. if (type === 'up') {
  534. handleSwipeChange(popupData.activeIndex - 1)
  535. } else {
  536. handleSwipeChange(popupData.activeIndex + 1)
  537. }
  538. }
  539. return () => (
  540. <div class={styles.playContent}>
  541. <div
  542. onClick={() => {
  543. clearTimeout(activeData.timer)
  544. activeData.model = !activeData.model
  545. Object.values(data.videoRefs).map((n: any) => n.toggleHideControl(activeData.model))
  546. }}
  547. >
  548. <div
  549. class={styles.coursewarePlay}
  550. style={{ width: parentContainer.width }}
  551. onClick={(e: Event) => {
  552. e.stopPropagation()
  553. setModelOpen()
  554. }}
  555. >
  556. <div class={styles.wraps}>
  557. {data.itemList.map((m: any, mIndex: number) => {
  558. const isRender = m.isRender || Math.abs(popupData.activeIndex - mIndex) < 2
  559. if (isRender) {
  560. m.isRender = true
  561. }
  562. return isRender ? (
  563. <div
  564. key={'index' + mIndex}
  565. class={[styles.itemDiv, popupData.activeIndex === mIndex && styles.itemActive]}
  566. style={{
  567. ...(mIndex < popupData.activeIndex
  568. ? effects[effectIndex.value].prev
  569. : mIndex > popupData.activeIndex
  570. ? effects[effectIndex.value].next
  571. : effects[effectIndex.value].current),
  572. ...(m.isAnimation ? {} : { 'transition-duration': '0s' })
  573. }}
  574. onClick={(e: Event) => {
  575. e.stopPropagation()
  576. clearTimeout(activeData.timer)
  577. if (Date.now() - activeData.nowTime < 300) {
  578. handleDbClick(m)
  579. return
  580. }
  581. activeData.nowTime = Date.now()
  582. activeData.timer = setTimeout(() => {
  583. activeData.model = !activeData.model
  584. Object.values(data.videoRefs).map((n: any) =>
  585. n.toggleHideControl(activeData.model)
  586. )
  587. if (activeData.model) {
  588. setModelOpen()
  589. }
  590. }, 300)
  591. }}
  592. >
  593. {m.type === 'VIDEO' ? (
  594. <>
  595. <VideoPlay
  596. ref={(v: any) => (data.videoRefs[mIndex] = v)}
  597. item={m}
  598. onLoadedmetadata={(videoItem: any) => {
  599. m.videoEle = videoItem
  600. }}
  601. onTogglePlay={(paused: boolean) => {
  602. // console.log('播放切换', paused)
  603. // 首次播放完成
  604. if (!m.isprepare) {
  605. m.isprepare = true
  606. }
  607. m.autoPlay = false
  608. if (paused || popupData.open || popupData.guideOpen) {
  609. clearTimeout(activeData.timer)
  610. } else {
  611. setModelOpen()
  612. }
  613. }}
  614. onEnded={() => handleSwipeChange(popupData.activeIndex + 1)}
  615. onReset={() => {
  616. if (!m.videoEle?.paused) {
  617. setModelOpen()
  618. }
  619. }}
  620. />
  621. <Transition name="van-fade">
  622. {!m.isprepare && (
  623. <div class={styles.loadWrap}>
  624. <Vue3Lottie animationData={playLoadData}></Vue3Lottie>
  625. </div>
  626. )}
  627. </Transition>
  628. </>
  629. ) : m.type === 'IMG' ? (
  630. <img src={m.content} />
  631. ) : (
  632. <MusicScore
  633. activeModel={activeData.model}
  634. data-vid={m.id}
  635. music={m}
  636. onSetIframe={(el: any) => {
  637. m.iframeRef = el
  638. }}
  639. />
  640. )}
  641. </div>
  642. ) : null
  643. })}
  644. </div>
  645. <Transition name="right">
  646. {activeData.model && (
  647. <div
  648. class={styles.rightFixedBtns}
  649. onClick={(e: Event) => {
  650. e.stopPropagation()
  651. clearTimeout(activeData.timer)
  652. }}
  653. >
  654. <div class={styles.btnsWrap}>
  655. <div
  656. class={[styles.fullBtn, styles.point]}
  657. onClick={() => (popupData.open = true)}
  658. >
  659. <img src={iconMenu} />
  660. <span>知识点</span>
  661. </div>
  662. </div>
  663. <div class={[styles.btnsWrap, styles.btnsBottom]}>
  664. <div class={styles.fullBtn} onClick={() => (popupData.guideOpen = true)}>
  665. <img src={iconTouping} />
  666. <span>投屏</span>
  667. </div>
  668. {data.isCourse && (
  669. <>
  670. <div
  671. class={styles.fullBtn}
  672. onClick={() => gotoRollCall('student_roll_call')}
  673. >
  674. <img src={iconDian} />
  675. <span>点名</span>
  676. </div>
  677. <div class={styles.fullBtn} onClick={() => gotoRollCall('sign_out')}>
  678. <img src={iconPoint} />
  679. <span>签退</span>
  680. </div>
  681. </>
  682. )}
  683. </div>
  684. </div>
  685. )}
  686. </Transition>
  687. <Transition name="left">
  688. {activeData.model && (
  689. <div class={styles.leftFixedBtns} onClick={(e: Event) => e.stopPropagation()}>
  690. {popupData.activeIndex != 0 && (
  691. <div class={[styles.btnsWrap, styles.prePoint]}>
  692. <div class={styles.fullBtn} onClick={() => handlePreAndNext('up')}>
  693. <img src={iconUp} />
  694. <span style={{ textAlign: 'center' }}>上一个</span>
  695. </div>
  696. </div>
  697. )}
  698. {popupData.activeIndex != data.itemList.length - 1 && (
  699. <div class={styles.btnsWrap}>
  700. <div class={styles.fullBtn} onClick={() => handlePreAndNext('down')}>
  701. <span style={{ textAlign: 'center' }}>下一个</span>
  702. <img src={iconDown} />
  703. </div>
  704. </div>
  705. )}
  706. </div>
  707. )}
  708. </Transition>
  709. </div>
  710. </div>
  711. <div
  712. style={{ transform: activeData.model ? '' : 'translateY(-100%)' }}
  713. id="coursePlayHeader"
  714. class={styles.headerContainer}
  715. ref={headeRef}
  716. >
  717. <div class={styles.backBtn} onClick={() => goback()}>
  718. <Icon name={iconBack} />
  719. 返回
  720. </div>
  721. <div
  722. class={styles.menu}
  723. onClick={() => {
  724. // if (showIndex.value > 0) {
  725. // effectIndex.value =
  726. // effectIndex.value < effects.length - 1 ? effectIndex.value + 1 : 0
  727. // } else {
  728. // showIndex.value += 1
  729. // }
  730. // setModelOpen()
  731. }}
  732. >
  733. {popupData.tabName}
  734. </div>
  735. {data.isCourse && <PlayRecordTime ref={playRef} list={data.itemList} />}
  736. </div>
  737. <Popup
  738. class={styles.popup}
  739. overlayClass={styles.overlayClass}
  740. position="right"
  741. round
  742. v-model:show={popupData.open}
  743. onClose={() => {
  744. const item = data.itemList[popupData.activeIndex]
  745. if (item?.type == 'VIDEO' && !item.videoEle?.paused) {
  746. setModelOpen()
  747. }
  748. }}
  749. >
  750. <Points
  751. data={data.knowledgePointList}
  752. tabActive={popupData.tabActive}
  753. itemActive={popupData.itemActive}
  754. onHandleSelect={(res: any) => {
  755. popupData.open = false
  756. toggleMaterial(res.itemActive)
  757. }}
  758. />
  759. </Popup>
  760. <Popup
  761. class={styles.popup}
  762. overlayClass={styles.overlayClass}
  763. position="right"
  764. round
  765. v-model:show={popupData.guideOpen}
  766. onClose={() => {
  767. const item = data.itemList[popupData.activeIndex]
  768. if (item?.type == 'VIDEO' && !item.videoEle?.paused) {
  769. setModelOpen()
  770. }
  771. }}
  772. >
  773. <OGuide />
  774. </Popup>
  775. </div>
  776. )
  777. }
  778. })