index.tsx 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008
  1. import { closeToast, Icon, Popup, showDialog, showToast } from "vant"
  2. import { defineComponent, onMounted, reactive, nextTick, onUnmounted, ref, watch, Transition, computed } from "vue"
  3. import iconBack from "./image/back.svg"
  4. import styles from "./index.module.scss"
  5. import "plyr/dist/plyr.css"
  6. import "vant/lib/index.css"
  7. // import request from "@/helpers/request"
  8. // import { state } from "@/state"
  9. const state = {
  10. platformApi: "/api",
  11. platformType: "TEACHER"
  12. }
  13. import { useRoute, useRouter } from "vue-router"
  14. import { postMessage, promisefiyPostMessage } from "./helpers/native-message"
  15. import MusicScore from "./component/musicScore"
  16. import iconDian from "./image/icon-dian.svg"
  17. import iconPoint from "./image/icon-point.svg"
  18. import icons from "./image/icons.json"
  19. const { iconUp, iconDown, iconPen, iconTouping } = icons
  20. import Points from "./component/points"
  21. import { browser } from "./helpers/utils"
  22. import { Vue3Lottie } from "vue3-lottie"
  23. import playLoadData from "./datas/data.json"
  24. import { usePageVisibility } from "@vant/use"
  25. import PlayRecordTime from "./playRecordTime"
  26. //import { handleCheckVip } from "../hook/useFee"
  27. function handleCheckVip() {
  28. return true
  29. }
  30. //import OGuide from "./component/o-guide"
  31. import Tool, { ToolItem, ToolType } from "./component/tool"
  32. import Pen from "./component/tools/pen"
  33. import VideoItem from "./component/video-item"
  34. import { getLessonCourseDetail_gym, getLessonCoursewareDetail_gyt } from "@/api/cloudTextbooks.api"
  35. import { httpAjaxErrMsg } from "@/plugin/httpAjax"
  36. import userStore from "@/store/modules/user"
  37. export default defineComponent({
  38. name: "CoursewarePlay",
  39. setup() {
  40. const pageVisibility = usePageVisibility()
  41. const userStoreHook = userStore()
  42. const router = useRouter()
  43. /** 页面显示和隐藏 */
  44. watch(
  45. () => pageVisibility.value,
  46. value => {
  47. if (value == "hidden") {
  48. handleStop()
  49. }
  50. }
  51. )
  52. /** 设置播放容器 16:9 */
  53. const parentContainer = reactive({
  54. width: "100vw"
  55. })
  56. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  57. const setContainer = () => {
  58. const min = Math.min(screen.width, screen.height)
  59. const max = Math.max(screen.width, screen.height)
  60. const width = min * (16 / 9)
  61. if (width > max) {
  62. parentContainer.width = "100vw"
  63. return
  64. } else {
  65. parentContainer.width = width + "px"
  66. }
  67. }
  68. const handleInit = (type = 0) => {
  69. //设置容器16:9
  70. //setContainer()
  71. // 横屏
  72. postMessage(
  73. {
  74. api: "setRequestedOrientation",
  75. content: {
  76. orientation: type
  77. }
  78. },
  79. () => {
  80. console.log(234)
  81. }
  82. )
  83. // 头,包括返回箭头
  84. // postMessage({
  85. // api: 'setTitleBarVisibility',
  86. // content: {
  87. // status: type
  88. // }
  89. // })
  90. // 安卓的状态栏
  91. postMessage({
  92. api: "setStatusBarVisibility",
  93. content: {
  94. isVisibility: type
  95. }
  96. })
  97. // 进入页面设置常量
  98. postMessage({
  99. api: "keepScreenLongLight",
  100. content: {
  101. isOpenLight: type ? true : false
  102. }
  103. })
  104. }
  105. handleInit()
  106. onUnmounted(() => {
  107. handleInit(1)
  108. window.removeEventListener("message", iframeHandle)
  109. })
  110. const route = useRoute()
  111. const headeRef = ref()
  112. const data = reactive({
  113. detail: null as any,
  114. knowledgePointList: [] as any,
  115. itemList: [] as any,
  116. showHead: true,
  117. isCourse: false,
  118. isRecordPlay: false,
  119. videoRefs: {},
  120. videoState: "init" as "init" | "play",
  121. videoItemRef: null as any,
  122. animationState: "start" as "start" | "end"
  123. })
  124. const activeData = reactive({
  125. isAutoPlay: true, // 是否自动播放
  126. nowTime: 0,
  127. model: true, // 遮罩
  128. isAnimation: true, // 是否动画
  129. videoBtns: true, // 视频
  130. currentTime: 0,
  131. duration: 0,
  132. timer: null as any,
  133. item: null as any
  134. })
  135. // 获取缓存路径
  136. const getCacheFilePath = async (material: any) => {
  137. const res = await promisefiyPostMessage({
  138. api: "getCourseFilePath",
  139. content: {
  140. url: material.content,
  141. localPath: "",
  142. materialId: material.materialId,
  143. updateTime: material.updateTime,
  144. type: material.typeCode // SONG VIDEO IMAGE
  145. }
  146. })
  147. // console.log('缓存路径返回', res)
  148. return res
  149. }
  150. // 获取当前课程是否签退
  151. // const getCourseSchedule = async () => {
  152. // if (!route.query.courseId) return;
  153. // try {
  154. // const res = await request.get(
  155. // `${state.platformApi}/courseSchedule/detail/${route.query.courseId}`,
  156. // {
  157. // hideLoading: true
  158. // }
  159. // );
  160. // if (res?.data) {
  161. // data.isCourse =
  162. // res.data.status === 'ING' && state.platformType == 'TEACHER'
  163. // ? true
  164. // : false;
  165. // // data.isRecordPlay = Date.now() > dayjs(res.data.startTime).valueOf()
  166. // }
  167. // } catch (e) {
  168. // console.log(e);
  169. // }
  170. // };
  171. const getTempList = async (materialList: any, name: any) => {
  172. const list: any = []
  173. const browserInfo = browser()
  174. for (let j = 0; j < materialList.length; j++) {
  175. const material = materialList[j]
  176. //请求本地缓存
  177. if (browserInfo.isApp && ["VIDEO", "IMG"].includes(material.typeCode)) {
  178. const localData = await getCacheFilePath(material)
  179. if (localData?.content?.localPath) {
  180. material.url = material.content
  181. material.content = localData.content.localPath
  182. }
  183. }
  184. list.push({
  185. ...material,
  186. iframeRef: null,
  187. videoEle: null,
  188. tabName: name,
  189. autoPlay: false, //加载完成是否自动播放
  190. isprepare: false, // 视频是否加载完成
  191. isRender: false // 是否渲染了
  192. })
  193. }
  194. return list
  195. }
  196. const getItemList = async () => {
  197. const list: any = []
  198. for (let i = 0; i < data.knowledgePointList.length; i++) {
  199. const item = data.knowledgePointList[i]
  200. if (item.materialList && item.materialList.length > 0) {
  201. const tempList = await getTempList(item.materialList, item.name)
  202. list.push(...tempList)
  203. }
  204. // 第二层级
  205. if (item.children && item.children.length > 0) {
  206. const childrenList = item.children || []
  207. for (let j = 0; j < childrenList.length; j++) {
  208. const childItem = childrenList[j]
  209. const tempList = await getTempList(childItem.materialList, childItem.name)
  210. list.push(...tempList)
  211. }
  212. }
  213. }
  214. // console.log(list, 'list')
  215. let _firstIndex = list.findIndex((n: any) => n.knowledgePointMaterialRelationId == route.query.kId || n.materialId == route.query.kId)
  216. _firstIndex = _firstIndex > -1 ? _firstIndex : 0
  217. const item = list[_firstIndex]
  218. // console.log(_firstIndex, '_firstIndex', route.query.kId, 'route.query.kId', item)
  219. // 是否自动播放
  220. if (activeData.isAutoPlay) {
  221. item.autoPlay = true
  222. }
  223. popupData.activeIndex = _firstIndex
  224. popupData.playIndex = _firstIndex
  225. popupData.tabName = item.tabName
  226. popupData.tabActive = item.knowledgePointId
  227. popupData.itemActive = item.id
  228. popupData.itemName = item.name
  229. nextTick(() => {
  230. data.itemList = list
  231. checkedAnimation(popupData.activeIndex)
  232. postMessage({
  233. api: "courseLoading",
  234. content: {
  235. show: false,
  236. type: "fullscreen"
  237. }
  238. })
  239. setTimeout(() => {
  240. data.animationState = "end"
  241. }, 500)
  242. })
  243. }
  244. const getDetail = async () => {
  245. try {
  246. //const res: any = await request.get(state.platformApi + `/lessonCourseware/getLessonCourseDetail/${route.query.id}`)
  247. const res: any = await httpAjaxErrMsg(
  248. userStoreHook.roles === "GYM" ? getLessonCourseDetail_gym : getLessonCoursewareDetail_gyt,
  249. route.params.id as string
  250. )
  251. data.detail = res.data
  252. if (res?.data?.lockFlag) {
  253. postMessage({
  254. api: "courseLoading",
  255. content: {
  256. show: false,
  257. type: "fullscreen"
  258. }
  259. })
  260. showDialog({
  261. title: "温馨提示",
  262. message: "课件已锁定"
  263. }).then(() => {
  264. goback()
  265. })
  266. return
  267. }
  268. if (Array.isArray(res?.data?.knowledgePointList)) {
  269. let index = 0
  270. data.knowledgePointList = res.data.knowledgePointList.map((n: any) => {
  271. if (Array.isArray(n.materialList)) {
  272. n.materialList = n.materialList.map((item: any) => {
  273. index++
  274. return {
  275. ...item,
  276. knowledgePointId: [item.knowledgePointId],
  277. materialId: item.id,
  278. id: index + ""
  279. }
  280. })
  281. }
  282. if (Array.isArray(n.children)) {
  283. n.children = n.children.map((cn: any) => {
  284. cn.materialList = cn.materialList.map((item: any) => {
  285. index++
  286. return {
  287. ...item,
  288. knowledgePointId: [n.id, item.knowledgePointId],
  289. materialId: item.id,
  290. id: index + ""
  291. }
  292. })
  293. return cn
  294. })
  295. }
  296. return n
  297. })
  298. getItemList()
  299. }
  300. } catch (error) {
  301. console.log(error)
  302. }
  303. }
  304. // ifram事件处理
  305. const iframeHandle = (ev: MessageEvent) => {
  306. if (ev.data?.api === "headerTogge") {
  307. activeData.model = ev.data.show || (ev.data.playState == "play" ? false : true)
  308. }
  309. }
  310. onMounted(async () => {
  311. await getDetail()
  312. const hasFree = String(data.detail?.accessScope) === "0"
  313. if (!hasFree) {
  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. }
  328. // getCourseSchedule();
  329. window.addEventListener("message", iframeHandle)
  330. })
  331. const playRef = ref()
  332. // 返回
  333. const goback = () => {
  334. try {
  335. playRef.value?.handleOut()
  336. } catch (error) {
  337. console.log(error)
  338. }
  339. postMessage({ api: "goBack" })
  340. router.back()
  341. }
  342. const popupData = reactive({
  343. open: false,
  344. activeIndex: 0,
  345. playIndex: 0,
  346. tabActive: "",
  347. tabName: "",
  348. itemActive: "",
  349. itemName: "",
  350. guideOpen: false,
  351. toolOpen: false // 工具弹窗控制
  352. })
  353. const stopVideo = (el: HTMLVideoElement) => {
  354. return new Promise(resolve => {
  355. if (el.paused) return resolve(true)
  356. el.onpause = () => {
  357. console.log("暂停")
  358. resolve(true)
  359. }
  360. el.pause()
  361. })
  362. }
  363. /**停止所有的播放 */
  364. const handleStop = async () => {
  365. const videos = document.querySelectorAll("video")
  366. for (let i = 0; i < videos.length; i++) {
  367. const videoEle = videos[i] as HTMLVideoElement
  368. await stopVideo(videoEle)
  369. }
  370. console.log("视频暂停完成")
  371. data.itemList.forEach((item: any) => {
  372. if (item.typeCode === "SONG") {
  373. item.iframeRef?.contentWindow?.postMessage({ api: "setPlayState" }, "*")
  374. }
  375. })
  376. }
  377. // 切换素材
  378. const toggleMaterial = (itemActive: any) => {
  379. const index = data.itemList.findIndex((n: any) => n.id == itemActive)
  380. if (index > -1) {
  381. handleSwipeChange(index)
  382. }
  383. }
  384. /** 延迟收起模态框 */
  385. const setModelOpen = () => {
  386. clearTimeout(activeData.timer)
  387. closeToast()
  388. activeData.timer = setTimeout(() => {
  389. activeData.model = false
  390. }, 4000)
  391. }
  392. /** 立即收起所有的模态框 */
  393. const clearModel = () => {
  394. clearTimeout(activeData.timer)
  395. closeToast()
  396. activeData.model = false
  397. }
  398. const toggleModel = (type = true) => {
  399. activeData.model = type
  400. }
  401. // 去点名,签退
  402. const gotoRollCall = (pageTag: string) => {
  403. postMessage({
  404. api: "open_app_page",
  405. content: {
  406. action: "app",
  407. pageTag: pageTag,
  408. url: "",
  409. params: JSON.stringify({ courseId: route.query.courseId })
  410. }
  411. })
  412. }
  413. // 双击
  414. const handleDbClick = () => {
  415. if (activeVideoItem.value.typeCode === "VIDEO") {
  416. const activeVideoRef = data.videoItemRef?.getVideoRef()
  417. if (activeVideoRef) {
  418. if (activeVideoRef.paused) {
  419. activeVideoRef.play()
  420. } else {
  421. activeVideoRef.pause()
  422. showToast("已暂停")
  423. }
  424. }
  425. }
  426. }
  427. const effectIndex = ref(0)
  428. const effects = [
  429. {
  430. prev: {
  431. transform: "translate3d(0, 0, -800px) rotateX(180deg)"
  432. },
  433. next: {
  434. transform: "translate3d(0, 0, -800px) rotateX(-180deg)"
  435. }
  436. },
  437. {
  438. prev: {
  439. transform: "translate3d(-100%, 0, -800px)"
  440. },
  441. next: {
  442. transform: "translate3d(100%, 0, -800px)"
  443. }
  444. },
  445. {
  446. prev: {
  447. transform: "translate3d(-50%, 0, -800px) rotateY(80deg)"
  448. },
  449. next: {
  450. transform: "translate3d(50%, 0, -800px) rotateY(-80deg)"
  451. }
  452. },
  453. {
  454. prev: {
  455. transform: "translate3d(-100%, 0, -800px) rotateY(-120deg)"
  456. },
  457. next: {
  458. transform: "translate3d(100%, 0, -800px) rotateY(120deg)"
  459. }
  460. },
  461. // 风车4
  462. {
  463. prev: {
  464. transform: "translate3d(-50%, 50%, -800px) rotateZ(-14deg)",
  465. opacity: 0
  466. },
  467. next: {
  468. transform: "translate3d(50%, 50%, -800px) rotateZ(14deg)",
  469. opacity: 0
  470. }
  471. },
  472. // 翻页5
  473. {
  474. prev: {
  475. transform: "translateZ(-800px) rotate3d(0, -1, 0, 90deg)",
  476. opacity: 0
  477. },
  478. next: {
  479. transform: "translateZ(-800px) rotate3d(0, 1, 0, 90deg)",
  480. opacity: 0
  481. },
  482. current: { transitionDelay: "700ms" }
  483. }
  484. ]
  485. const acitveTimer = ref()
  486. // 轮播切换
  487. const handleSwipeChange = async (index: number) => {
  488. // 如果是当前正在播放 或者是视频最后一个
  489. if (popupData.activeIndex == index) return
  490. await handleStop()
  491. data.animationState = "start"
  492. data.videoState = "init"
  493. clearTimeout(acitveTimer.value)
  494. checkedAnimation(popupData.activeIndex, index)
  495. nextTick(() => {
  496. popupData.activeIndex = index
  497. acitveTimer.value = setTimeout(
  498. () => {
  499. popupData.playIndex = index
  500. const item = data.itemList[index]
  501. if (item) {
  502. popupData.tabActive = item.knowledgePointId
  503. popupData.itemActive = item.id
  504. popupData.itemName = item.name
  505. popupData.tabName = item.tabName
  506. if (item.typeCode == "SONG") {
  507. activeData.model = true
  508. }
  509. }
  510. requestAnimationFrame(() => {
  511. const _effectIndex = effectIndex.value + 1
  512. effectIndex.value = _effectIndex >= effects.length - 1 ? 0 : _effectIndex
  513. if (item && item.typeCode === "VIDEO") {
  514. // 自动播放下一个视频
  515. clearTimeout(activeData.timer)
  516. closeToast()
  517. item.autoPlay = true
  518. data.animationState = "end"
  519. }
  520. })
  521. },
  522. activeData.isAnimation ? 850 : 0
  523. )
  524. })
  525. }
  526. /** 是否有转场动画 */
  527. const checkedAnimation = (index: number, nextIndex?: number) => {
  528. nextIndex = nextIndex ? nextIndex : index + 1
  529. const item = data.itemList[index]
  530. const nextItem = data.itemList[nextIndex]
  531. if (nextItem) {
  532. if (nextItem.knowledgePointId != item.knowledgePointId) {
  533. activeData.isAnimation = true
  534. return
  535. }
  536. const videoEle = item.videoEle
  537. const nextVideo = nextItem.videoEle
  538. if (videoEle && videoEle.duration < 8 && index < nextIndex) {
  539. activeData.isAnimation = false
  540. } else if (nextVideo && nextVideo.duration < 8 && index > nextIndex) {
  541. activeData.isAnimation = false
  542. } else {
  543. activeData.isAnimation = true
  544. }
  545. } else {
  546. activeData.isAnimation = item?.adviseStudyTimeSecond < 8 ? false : true
  547. }
  548. }
  549. // 上一个知识点, 下一个知识点
  550. const handlePreAndNext = (type: string) => {
  551. if (type === "up") {
  552. handleSwipeChange(popupData.activeIndex - 1)
  553. } else {
  554. handleSwipeChange(popupData.activeIndex + 1)
  555. }
  556. }
  557. /** 弹窗关闭 */
  558. const handleClosePopup = () => {
  559. const item = data.itemList[popupData.activeIndex]
  560. if (item?.typeCode == "VIDEO" && !item.videoEle?.paused) {
  561. setModelOpen()
  562. }
  563. }
  564. /** 教学数据 */
  565. const studyData = reactive({
  566. type: "" as ToolType,
  567. penShow: false
  568. })
  569. const isWhite = ref(false)
  570. /** 打开教学工具 */
  571. const openStudyTool = (item: ToolItem, isw?: boolean) => {
  572. const activeItem = data.itemList[popupData.activeIndex]
  573. // 暂停视频和曲谱的播放
  574. if (activeItem.typeCode === "VIDEO" && data.videoItemRef?.getVideoItem()) {
  575. data.videoItemRef?.getVideoItem().pause()
  576. }
  577. if (activeItem.typeCode === "SONG") {
  578. activeItem.iframeRef?.contentWindow?.postMessage({ api: "setPlayState" }, "*")
  579. }
  580. clearModel()
  581. popupData.toolOpen = false
  582. studyData.type = item.type
  583. isw && (isWhite.value = true)
  584. switch (item.type) {
  585. case "pen":
  586. studyData.penShow = true
  587. break
  588. }
  589. }
  590. /** 关闭教学工具 */
  591. const closeStudyTool = () => {
  592. isWhite.value = false
  593. studyData.type = "init"
  594. toggleModel()
  595. }
  596. const activeVideoItem = computed(() => {
  597. console.log(data.itemList, " data.itemList")
  598. const item = data.itemList[popupData.activeIndex]
  599. if (item && item.typeCode && item.typeCode.toLocaleUpperCase() === "VIDEO") {
  600. return item
  601. }
  602. return {}
  603. })
  604. let closeModelTimer: any = null
  605. return () => (
  606. <div id="playContent" class={styles.playContent}>
  607. <div
  608. class={styles.coursewarePlay}
  609. style={{ width: parentContainer.width }}
  610. onClick={() => {
  611. clearTimeout(closeModelTimer)
  612. clearTimeout(activeData.timer)
  613. closeToast()
  614. if (Date.now() - activeData.nowTime < 300) {
  615. handleDbClick()
  616. return
  617. }
  618. activeData.nowTime = Date.now()
  619. closeModelTimer = setTimeout(() => {
  620. activeData.model = !activeData.model
  621. }, 300)
  622. }}
  623. >
  624. <div class={styles.wraps}>
  625. <div
  626. style={
  627. activeVideoItem.value.typeCode && data.animationState === "end" && data.videoState === "play"
  628. ? {
  629. zIndex: 15,
  630. opacity: 1
  631. }
  632. : { opacity: 0, zIndex: -1 }
  633. }
  634. class={styles.itemDiv}
  635. >
  636. <VideoItem
  637. ref={(el: any) => (data.videoItemRef = el)}
  638. item={activeVideoItem.value}
  639. activeModel={activeData.model}
  640. onClose={setModelOpen}
  641. onPlay={() => {
  642. data.videoState = "play"
  643. }}
  644. onPause={() => {
  645. clearTimeout(activeData.timer)
  646. activeData.model = true
  647. }}
  648. onEnded={() => {
  649. const _index = popupData.activeIndex + 1
  650. if (_index < data.itemList.length) {
  651. handleSwipeChange(_index)
  652. }
  653. }}
  654. />
  655. </div>
  656. {data.itemList.map((m: any, mIndex: number) => {
  657. const isRenderItem = Math.abs(popupData.activeIndex - mIndex) < 2
  658. const isRender = Math.abs(popupData.playIndex - mIndex) < 2
  659. // 判断是否是当前选中的元素
  660. const activeEle = popupData.playIndex === mIndex ? true : false
  661. return isRenderItem ? (
  662. <div
  663. key={"index" + mIndex}
  664. data-id={"data" + mIndex}
  665. class={[
  666. styles.itemDiv,
  667. activeEle && styles.itemActive,
  668. activeData.isAnimation && styles.acitveAnimation,
  669. isRenderItem ? styles.show : styles.hide
  670. ]}
  671. style={
  672. mIndex < popupData.activeIndex
  673. ? effects[effectIndex.value].prev
  674. : mIndex > popupData.activeIndex
  675. ? effects[effectIndex.value].next
  676. : {}
  677. }
  678. >
  679. {/* {m.type === 'VIDEO' && (
  680. <>
  681. <VideoPlay
  682. ref={(v: any) => (data.videoRefs[mIndex] = v)}
  683. item={m}
  684. isActive={activeEle}
  685. isEmtry={isEmtry}
  686. onPrepare={(val) => {
  687. m.isprepare = val
  688. }}
  689. onLoadedmetadata={(videoItem: any) => {
  690. m.videoEle = videoItem
  691. }}
  692. onTogglePlay={(paused: boolean) => {
  693. // console.log('播放切换', paused)
  694. if (!m.isprepare) {
  695. m.isprepare = true
  696. }
  697. m.autoPlay = false
  698. if (paused || popupData.open || popupData.guideOpen) {
  699. clearTimeout(activeData.timer)
  700. } else {
  701. setModelOpen()
  702. }
  703. }}
  704. onEnded={() => {
  705. const _index = popupData.activeIndex + 1
  706. if (_index < data.itemList.length) {
  707. handleSwipeChange(_index)
  708. }
  709. }}
  710. onReset={() => {
  711. if (!m.videoEle?.paused) {
  712. setModelOpen()
  713. }
  714. }}
  715. />
  716. <Transition name="van-fade">
  717. {!m.isprepare && (
  718. <div class={styles.loadWrap}>
  719. <Vue3Lottie animationData={playLoadData}></Vue3Lottie>
  720. </div>
  721. )}
  722. </Transition>
  723. </>
  724. )} */}
  725. <Transition name="van-fade">
  726. {m.typeCode === "VIDEO" && data.animationState !== "end" && data.videoState != "play" && (
  727. <div class={styles.loadWrap}>
  728. <Vue3Lottie animationData={playLoadData}></Vue3Lottie>
  729. </div>
  730. )}
  731. </Transition>
  732. {isRender && m.typeCode === "IMG" && <img src={m.content} />}
  733. {isRender && m.typeCode === "SONG" && (
  734. <MusicScore
  735. activeModel={activeData.model}
  736. data-vid={m.id}
  737. music={m}
  738. onSetIframe={(el: any) => {
  739. m.iframeRef = el
  740. }}
  741. />
  742. )}
  743. </div>
  744. ) : (
  745. ""
  746. )
  747. })}
  748. </div>
  749. <Transition name="right">
  750. {activeData.model && (
  751. <div
  752. class={styles.rightFixedBtns}
  753. onClick={(e: Event) => {
  754. e.stopPropagation()
  755. clearTimeout(activeData.timer)
  756. }}
  757. >
  758. <div class={styles.btnsWrap}>
  759. <div
  760. class={[styles.fullBtn, styles.point]}
  761. onClick={() =>
  762. openStudyTool(
  763. {
  764. type: "pen",
  765. icon: iconPen,
  766. name: "白板"
  767. },
  768. true
  769. )
  770. }
  771. >
  772. <img src={require("./image/bb.png")} />
  773. <span>白板</span>
  774. </div>
  775. </div>
  776. <div class={styles.btnsWrap}>
  777. <div
  778. class={[styles.fullBtn, styles.point]}
  779. onClick={() =>
  780. openStudyTool({
  781. type: "pen",
  782. icon: iconPen,
  783. name: "批注"
  784. })
  785. }
  786. >
  787. <img src={require("./image/pz.png")} />
  788. <span>批注</span>
  789. </div>
  790. </div>
  791. <div class={styles.btnsWrap}>
  792. <div class={[styles.fullBtn, styles.point]} onClick={() => (console.log(popupData.open), (popupData.open = true))}>
  793. <img src={require("./image/zzd.png")} />
  794. <span>知识点</span>
  795. </div>
  796. </div>
  797. <div class={styles.btnsWrap}>
  798. <div
  799. class={[styles.fullBtn, styles.point]}
  800. onClick={() => {
  801. goback()
  802. }}
  803. >
  804. <img src={require("./image/js.png")} />
  805. <span>结束</span>
  806. </div>
  807. </div>
  808. <div class={[styles.btnsWrap, styles.btnsBottom]}>
  809. {/* <div class={styles.fullBtn} onClick={() => (popupData.guideOpen = true)}>
  810. <img src={iconTouping} />
  811. <span>投屏</span>
  812. </div> */}
  813. {data.isCourse && (
  814. <>
  815. <div class={styles.fullBtn} onClick={() => gotoRollCall("student_roll_call")}>
  816. <img src={iconDian} />
  817. <span>点名</span>
  818. </div>
  819. <div class={styles.fullBtn} onClick={() => gotoRollCall("sign_out")}>
  820. <img src={iconPoint} />
  821. <span>签退</span>
  822. </div>
  823. </>
  824. )}
  825. </div>
  826. </div>
  827. )}
  828. </Transition>
  829. <Transition name="left">
  830. {activeData.model && (
  831. <div class={styles.leftFixedBtns} onClick={(e: Event) => e.stopPropagation()}>
  832. {popupData.activeIndex != 0 && (
  833. <div class={[styles.btnsWrap, styles.prePoint]}>
  834. <div
  835. class={styles.fullBtn}
  836. onClick={() => {
  837. // useThrottleFn(() => {
  838. // handlePreAndNext('up')
  839. // }, 300)
  840. // onChangeSwiper('up')
  841. handlePreAndNext("up")
  842. }}
  843. >
  844. <img src={iconUp} />
  845. <span style={{ textAlign: "center" }}>上一个</span>
  846. </div>
  847. </div>
  848. )}
  849. {popupData.activeIndex != data.itemList.length - 1 && (
  850. <div class={styles.btnsWrap}>
  851. <div
  852. class={styles.fullBtn}
  853. onClick={() => {
  854. // console.log('click down')
  855. // useThrottleFn(() => {
  856. // console.log('click down pass')
  857. // handlePreAndNext('down')
  858. // }, 300)
  859. // onChangeSwiper('down')
  860. handlePreAndNext("down")
  861. }}
  862. >
  863. <span style={{ textAlign: "center" }}>下一个</span>
  864. <img src={iconDown} />
  865. </div>
  866. </div>
  867. )}
  868. </div>
  869. )}
  870. </Transition>
  871. </div>
  872. <div
  873. style={{ transform: activeData.model ? "" : "translateY(-100%)" }}
  874. id="coursePlayHeader"
  875. class={styles.headerContainer}
  876. ref={headeRef}
  877. >
  878. <div class={styles.backBtn} onClick={() => goback()}>
  879. <Icon name={iconBack} />
  880. 返回
  881. </div>
  882. {data.isCourse && <PlayRecordTime ref={playRef} list={data.knowledgePointList} />}
  883. <div
  884. class={styles.menu}
  885. onClick={() => {
  886. const _effectIndex = effectIndex.value + 1
  887. effectIndex.value = _effectIndex >= effects.length - 1 ? 0 : _effectIndex
  888. setModelOpen()
  889. }}
  890. >
  891. {popupData.tabName}
  892. </div>
  893. {/* 移除标注 */}
  894. {state.platformType == "TEACHER1" && (
  895. <div
  896. class={styles.headRight}
  897. onClick={(e: Event) => {
  898. e.stopPropagation()
  899. clearTimeout(activeData.timer)
  900. }}
  901. >
  902. <div class={styles.rightBtn} onClick={() => (popupData.guideOpen = true)}>
  903. <img src={iconTouping} />
  904. </div>
  905. <div
  906. class={styles.rightBtn}
  907. onClick={() => {
  908. openStudyTool(
  909. {
  910. type: "pen",
  911. icon: iconPen,
  912. name: "批注"
  913. },
  914. true
  915. )
  916. }}
  917. >
  918. <img src={iconPen} />
  919. </div>
  920. {/* <div class={styles.rightBtn} onClick={() => (popupData.toolOpen = true)}>
  921. <img src={iconMore} />
  922. </div> */}
  923. </div>
  924. )}
  925. </div>
  926. {/* 更多弹窗 */}
  927. <Popup
  928. class={styles.popupMore}
  929. overlayClass={styles.overlayClass}
  930. position="right"
  931. round
  932. v-model:show={popupData.toolOpen}
  933. onClose={handleClosePopup}
  934. >
  935. <Tool onHandleTool={openStudyTool} />
  936. </Popup>
  937. <Popup
  938. class={styles.popup}
  939. style={{ background: "rgba(0,0,0, 0.75)" }}
  940. overlayClass={styles.overlayClass}
  941. position="right"
  942. round
  943. v-model:show={popupData.open}
  944. onClose={handleClosePopup}
  945. >
  946. <Points
  947. data={data.knowledgePointList}
  948. tabActive={popupData.tabActive}
  949. itemActive={popupData.itemActive}
  950. onHandleSelect={(res: any) => {
  951. // onChangeSwiper('change', res.itemActive)
  952. popupData.open = false
  953. toggleMaterial(res.itemActive)
  954. }}
  955. />
  956. </Popup>
  957. <Popup
  958. class={styles.popup}
  959. overlayClass={styles.overlayClass}
  960. position="right"
  961. round
  962. v-model:show={popupData.guideOpen}
  963. onClose={handleClosePopup}
  964. >
  965. {/* <OGuide /> */}
  966. </Popup>
  967. {studyData.penShow && <Pen isWhite={isWhite.value} show={studyData.type === "pen"} close={() => closeStudyTool()} />}
  968. </div>
  969. )
  970. }
  971. })