index.tsx 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458
  1. import { Toast, Icon, Popup, Dialog } from 'vant'
  2. import {
  3. defineComponent,
  4. onMounted,
  5. reactive,
  6. nextTick,
  7. onUnmounted,
  8. ref,
  9. watch,
  10. Transition,
  11. computed,
  12. onBeforeUnmount,
  13. shallowRef
  14. } from 'vue'
  15. import iconBack from './image/back.svg'
  16. import styles from './index.module.less'
  17. import 'plyr/dist/plyr.css'
  18. import request from '@/helpers/request'
  19. import { state } from '@/state'
  20. import { useRoute, useRouter } from 'vue-router'
  21. import {
  22. postMessage,
  23. promisefiyPostMessage,
  24. listenerMessage
  25. } from '@/helpers/native-message'
  26. import MusicScore from './component/musicScore'
  27. // import iconDian from './image/icon-dian.svg'
  28. // import iconPoint from './image/icon-point.svg'
  29. import { state as baseState } from '@/state'
  30. import {
  31. iconUp,
  32. iconDown,
  33. // iconPen,
  34. iconTouping,
  35. iconCourseType,
  36. iconSearch,
  37. iconMenu
  38. } from './image/icons.json'
  39. import Points from './component/points'
  40. import { browser, getHttpOrigin } from '@/helpers/utils'
  41. import { Vue3Lottie } from 'vue3-lottie'
  42. import playLoadData from './datas/data.json'
  43. import { usePageVisibility } from '@vant/use'
  44. // import PlayRecordTime from './playRecordTime'
  45. // import { handleCheckVip } from '../hook/useFee'
  46. import OGuide from './component/o-guide'
  47. import Tool, { ToolItem, ToolType } from './component/tool'
  48. // import Pen from './component/tools/pen'
  49. // import VideoItem from './component/video-item';
  50. import VideoPlay from './component/video-play'
  51. import { musicBuy } from '../music'
  52. import { isPlay, penShow, toolOpen, whitePenShow } from '@/components/globalTools/globalTools'
  53. import CoursewareTips from './component/courseware-tips'
  54. import GlobalTools from '@/components/globalTools'
  55. import CoursewareType from './component/courseware-type'
  56. import { useNetwork } from '@vueuse/core'
  57. import PointsSearch from './component/points-search'
  58. export default defineComponent({
  59. name: 'CoursewarePlay',
  60. setup() {
  61. const browserInfo = browser()
  62. const router = useRouter()
  63. const apiSuffix = ref(
  64. baseState.platformType === 'STUDENT' ? '/api-student' : '/api-teacher'
  65. )
  66. const pageVisibility = usePageVisibility()
  67. const { isOnline } = useNetwork()
  68. /** 页面显示和隐藏 */
  69. watch(
  70. () => pageVisibility.value,
  71. value => {
  72. if (value == 'hidden') {
  73. handleStop()
  74. }
  75. }
  76. )
  77. /** 设置播放容器 16:9 */
  78. const parentContainer = reactive({
  79. width: '100vw'
  80. })
  81. const handleInit = (type = 0) => {
  82. //设置容器16:9
  83. // setContainer()
  84. // 横屏
  85. postMessage(
  86. {
  87. api: 'setRequestedOrientation',
  88. content: {
  89. orientation: type
  90. }
  91. },
  92. () => {
  93. // console.log(234);
  94. }
  95. )
  96. // 安卓的状态栏
  97. postMessage({
  98. api: 'setStatusBarVisibility',
  99. content: {
  100. isVisibility: type
  101. }
  102. })
  103. // 进入页面设置常量
  104. postMessage({
  105. api: 'keepScreenLongLight',
  106. content: {
  107. isOpenLight: type ? true : false
  108. }
  109. })
  110. }
  111. handleInit()
  112. onUnmounted(() => {
  113. handleInit(1)
  114. window.removeEventListener('message', iframeHandle)
  115. })
  116. const route = useRoute()
  117. const headeRef = ref()
  118. const detailTempSearchList = shallowRef<any[]>()
  119. const detailList = shallowRef<any[]>()// 搜索来的所有数据
  120. const data = reactive({
  121. source: route.query.source as any, // 来源 search 搜索
  122. searchLoading: false, // 搜索加载状态
  123. search: route.query.search as any, // 默认的搜索条件 -
  124. searchTemp: route.query.search as any, // 默认的搜索条件 -
  125. isSearch: route.query.source === "search" ? true : false, // 是否搜索
  126. currentId: route.query.id as any,
  127. lessonId: null as any,
  128. detail: null as any,
  129. knowledgePointList: [] as any,
  130. itemList: [] as any,
  131. showHead: true,
  132. isCourse: false,
  133. isRecordPlay: false,
  134. videoRefs: {},
  135. refLevelList: [] as any,
  136. videoState: 'init' as 'init' | 'play',
  137. videoItemRef: null as any,
  138. animationState: 'start' as 'start' | 'end',
  139. disableScreenRecordingFlag: '0' // disable recording
  140. })
  141. const activeData = reactive({
  142. isAutoPlay: true, // 是否自动播放
  143. nowTime: 0,
  144. model: true, // 遮罩
  145. isAnimation: true, // 是否动画
  146. videoBtns: true, // 视频
  147. currentTime: 0,
  148. duration: 0,
  149. timer: null as any,
  150. item: null as any
  151. })
  152. // 获取缓存路径
  153. const getCacheFilePath = async (material: any) => {
  154. const res = await promisefiyPostMessage({
  155. api: 'getCourseFilePath',
  156. content: {
  157. url: material.content,
  158. localPath: '',
  159. materialId: material.materialId,
  160. updateTime: material.updateTime,
  161. type: material.typeCode // SONG VIDEO IMAGE
  162. }
  163. })
  164. // console.log('缓存路径返回', res)
  165. return res
  166. }
  167. const getTempList = async (materialList: any, name: any) => {
  168. const list: any = []
  169. // const browserInfo = browser()
  170. for (let j = 0; j < materialList.length; j++) {
  171. const material = materialList[j]
  172. //请求本地缓存
  173. // if (browserInfo.isApp && ['VIDEO', 'IMG'].includes(material.typeCode)) {
  174. // const localData: any = await getCacheFilePath(material)
  175. // if (localData?.content?.localPath) {
  176. // material.url = material.content
  177. // material.content = localData.content.localPath
  178. // } else {
  179. // material.url = material.content + '?t=' + +new Date()
  180. // material.content = material.content + '?t=' + +new Date()
  181. // }
  182. // }
  183. material.isReadCatch = false // 是否读取缓存
  184. material.iframeRef = null
  185. material.videoEle = null
  186. material.tabName = name
  187. material.autoPlay = false //加载完成是否自动播放
  188. material.isprepare = false // 视频是否加载完成
  189. material.isRender = false // 是否渲染了
  190. list.push(material)
  191. // list.push({
  192. // ...material,
  193. // iframeRef: null,
  194. // videoEle: null,
  195. // tabName: name,
  196. // autoPlay: false, //加载完成是否自动播放
  197. // isprepare: false, // 视频是否加载完成
  198. // isRender: false // 是否渲染了
  199. // })
  200. }
  201. return list
  202. }
  203. const getItemList = async () => {
  204. const list: any = []
  205. for (let i = 0; i < data.knowledgePointList.length; i++) {
  206. const item = data.knowledgePointList[i]
  207. if (item.materialList && item.materialList.length > 0) {
  208. const tempList = await getTempList(item.materialList, item.name)
  209. list.push(...tempList)
  210. }
  211. // 第二层级
  212. if (item.children && item.children.length > 0) {
  213. const childrenList = item.children || []
  214. for (let j = 0; j < childrenList.length; j++) {
  215. const childItem = childrenList[j]
  216. const tempList = await getTempList(
  217. childItem.materialList,
  218. childItem.name
  219. )
  220. list.push(...tempList)
  221. }
  222. }
  223. }
  224. // console.log(list, 'list')
  225. let _firstIndex = list.findIndex(
  226. (n: any) =>
  227. n.knowledgePointMaterialRelationId == route.query.kId ||
  228. n.materialId == route.query.kId
  229. )
  230. _firstIndex = _firstIndex > -1 ? _firstIndex : 0
  231. const item = list[_firstIndex]
  232. // 是否自动播放
  233. if (activeData.isAutoPlay) {
  234. item.autoPlay = true
  235. }
  236. popupData.activeIndex = _firstIndex
  237. popupData.playIndex = _firstIndex
  238. popupData.tabName = item.tabName
  239. popupData.tabActive = item.knowledgePointId
  240. popupData.itemActive = item.id
  241. popupData.itemName = item.name
  242. nextTick(() => {
  243. data.itemList = list
  244. getCurrentItemCatch(popupData.activeIndex) // 获取当前元素的缓存
  245. checkedAnimation(popupData.activeIndex)
  246. postMessage({
  247. api: 'courseLoading',
  248. content: {
  249. show: false,
  250. type: 'fullscreen'
  251. }
  252. })
  253. if (data.disableScreenRecordingFlag === '1') {
  254. // 检测是否录屏
  255. handleLimitScreenRecord()
  256. }
  257. setTimeout(() => {
  258. data.animationState = 'end'
  259. }, 500)
  260. })
  261. }
  262. /** 获取当前元素的缓存 */
  263. const getCurrentItemCatch = async (index: number) => {
  264. const item = data.itemList[index]
  265. if (browserInfo.isApp && ['VIDEO', 'IMG'].includes(item.typeCode) && !item.isReadCatch) {
  266. const localData: any = await getCacheFilePath(item)
  267. item.isReadCatch = true
  268. if (localData?.content?.localPath) {
  269. item.url = item.content
  270. item.content = localData.content.localPath
  271. } else {
  272. item.url = item.content + '?t=' + +new Date()
  273. item.content = item.content + '?t=' + +new Date()
  274. }
  275. console.log('加载了缓存')
  276. }
  277. }
  278. const getDetail = async (id?: any) => {
  279. try {
  280. const res: any = await request.get(
  281. apiSuffix.value +
  282. `/tenantAlbumMusic/getLessonCourseDetail/${id || route.query.id}`,
  283. {
  284. hideLoading: true
  285. }
  286. )
  287. const result = res.data || {}
  288. result.lessonTargetDesc = result.lessonTargetDesc ? result.lessonTargetDesc.replace(/\n/g, "<br />") : ""
  289. data.detail = result;
  290. data.lessonId = result.lessonCoursewareId
  291. if (res?.data?.lockFlag) {
  292. postMessage({
  293. api: 'courseLoading',
  294. content: {
  295. show: false,
  296. type: 'fullscreen'
  297. }
  298. })
  299. Dialog.alert({
  300. title: '温馨提示',
  301. message: '课件已锁定'
  302. }).then(() => {
  303. goback()
  304. })
  305. return
  306. }
  307. if (Array.isArray(res?.data?.knowledgePointList)) {
  308. let index = 0
  309. data.knowledgePointList = res.data.knowledgePointList.map(
  310. (n: any) => {
  311. if (Array.isArray(n.materialList)) {
  312. n.materialList = n.materialList.map((item: any) => {
  313. index++
  314. const materialRefs = item.materialRefs
  315. ? item.materialRefs
  316. : []
  317. const materialMusicId =
  318. materialRefs.length > 0 ? materialRefs[0].resourceId : null
  319. return {
  320. ...item,
  321. materialMusicId,
  322. content: item.content,
  323. knowledgePointId: [item.knowledgePointId],
  324. materialId: item.id,
  325. id: index + '',
  326. typeCode: item.typeCode || item.type
  327. }
  328. })
  329. }
  330. if (Array.isArray(n.children)) {
  331. n.children = n.children.map((cn: any) => {
  332. cn.materialList = cn.materialList.map((item: any) => {
  333. index++
  334. const materialRefs = item.materialRefs
  335. ? item.materialRefs
  336. : []
  337. const materialMusicId =
  338. materialRefs.length > 0
  339. ? materialRefs[0].resourceId
  340. : null
  341. return {
  342. ...item,
  343. materialMusicId,
  344. content: item.content,
  345. knowledgePointId: [n.id, item.knowledgePointId],
  346. materialId: item.id,
  347. id: index + '',
  348. typeCode: item.typeCode || item.type
  349. }
  350. })
  351. return cn
  352. })
  353. }
  354. return n
  355. }
  356. )
  357. getItemList()
  358. }
  359. return true
  360. } catch (error) {
  361. console.log(error)
  362. }
  363. }
  364. const getSearchItemList = async (knowledgePointList: any[]) => {
  365. const list: any = [];
  366. for (let i = 0; i < knowledgePointList.length; i++) {
  367. const item = knowledgePointList[i];
  368. if (item.materialList && item.materialList.length > 0) {
  369. const tempList = await getTempList(item.materialList, item.name);
  370. list.push(...tempList);
  371. }
  372. // 第二层级
  373. if (item.children && item.children.length > 0) {
  374. const childrenList = item.children || [];
  375. for (let j = 0; j < childrenList.length; j++) {
  376. const childItem = childrenList[j];
  377. const tempList = await getTempList(
  378. childItem.materialList,
  379. childItem.name
  380. );
  381. list.push(...tempList);
  382. }
  383. }
  384. }
  385. return list
  386. };
  387. /** 从搜索页面来的 */
  388. const getSearchDetail = async (params: { type?: string, id?: any, search?: string }) => {
  389. try {
  390. const res = await request.get(
  391. state.platformApi +
  392. `/tenantAlbumMusic/getLessonCoursewareCourseList/${params.id || route.query.lessonId}`,
  393. {
  394. hideLoading: true,
  395. params: {
  396. detailFlag: "1",
  397. search: params.search
  398. }
  399. }
  400. );
  401. const result = res.data || []
  402. const allList: any[] = []
  403. for(let i = 0; i < result.length; i++) {
  404. const itemResult = result[i];
  405. itemResult.name = itemResult.coursewareDetailName;
  406. itemResult.id = itemResult.coursewareDetailId;
  407. itemResult.lessonTargetDesc = itemResult.lessonTargetDesc ? itemResult.lessonTargetDesc.replace(/\n/g, "<br />") : ""
  408. if (Array.isArray(itemResult?.knowledgePointList)) {
  409. let index = 0;
  410. itemResult.children = itemResult.knowledgePointList.map(
  411. (n: any) => {
  412. if (Array.isArray(n.materialList)) {
  413. n.materialList = n.materialList.map((item: any) => {
  414. index++;
  415. const materialRefs = item.materialRefs
  416. ? item.materialRefs
  417. : [];
  418. const materialMusicId =
  419. materialRefs.length > 0
  420. ? materialRefs[0].resourceIdStr
  421. : null;
  422. return {
  423. ...item,
  424. materialMusicId,
  425. content: item.content,
  426. coursewareDetailId: itemResult.coursewareDetailId,
  427. knowledgePointId: [itemResult.coursewareDetailId, item.knowledgePointId],
  428. materialId: item.id,
  429. id: (i * 1000 + '') + index + ''
  430. };
  431. });
  432. }
  433. if (Array.isArray(n.children)) {
  434. n.children = n.children.map((cn: any) => {
  435. cn.materialList = cn.materialList.map((item: any) => {
  436. index++;
  437. const materialRefs = item.materialRefs
  438. ? item.materialRefs
  439. : [];
  440. const materialMusicId =
  441. materialRefs.length > 0
  442. ? materialRefs[0].resourceIdStr
  443. : null;
  444. return {
  445. ...item,
  446. materialMusicId,
  447. coursewareDetailId: itemResult.coursewareDetailId,
  448. content: item.content,
  449. knowledgePointId: [itemResult.coursewareDetailId, n.id, item.knowledgePointId],
  450. materialId: item.id,
  451. id: (i * 1000 + '') + index + ''
  452. };
  453. });
  454. return cn;
  455. });
  456. }
  457. return n;
  458. }
  459. );
  460. itemResult.knowledgePointList = null // 去掉不要的
  461. itemResult.list = await getSearchItemList(itemResult.children);
  462. allList.push(...itemResult.list)
  463. }
  464. }
  465. if(data.source !== 'search') {
  466. if(params.type === "pointSearch") {
  467. detailTempSearchList.value = result
  468. popupData.tempTabActive = allList.length > 0 ? allList[0].knowledgePointId : []
  469. popupData.tempItemActive = "-1"
  470. data.searchTemp = params.search
  471. return
  472. }
  473. detailList.value = result
  474. detailTempSearchList.value = result
  475. return
  476. }
  477. if(params.type === 'pointSearch') {
  478. detailTempSearchList.value = result
  479. // 初始化选中的数据 临时
  480. popupData.tempTabActive = allList.length > 0 ? allList[0].knowledgePointId : []
  481. popupData.tempItemActive = "-1"
  482. data.searchTemp = params.search
  483. return
  484. }
  485. detailList.value = result
  486. detailTempSearchList.value = result
  487. if(!params.type) {
  488. let _firstIndex = allList.findIndex(
  489. (n: any) =>
  490. n.knowledgePointMaterialRelationId == route.query.kId ||
  491. n.materialId == route.query.kId
  492. );
  493. _firstIndex = _firstIndex > -1 ? _firstIndex : 0;
  494. const item = allList[_firstIndex];
  495. // console.log(item, 'item')
  496. // console.log(_firstIndex, '_firstIndex', route.query.kId, 'route.query.kId', item)
  497. // 是否自动播放
  498. if (activeData.isAutoPlay) {
  499. item.autoPlay = true;
  500. }
  501. popupData.activeIndex = _firstIndex;
  502. popupData.playIndex = _firstIndex;
  503. popupData.tabName = item.tabName;
  504. popupData.tabActive = item.knowledgePointId;
  505. popupData.itemActive = item.id;
  506. popupData.itemName = item.name;
  507. data.detail = detailList.value?.find((child: any) => child.coursewareDetailId === item.coursewareDetailId)
  508. }
  509. nextTick(() => {
  510. data.itemList = allList;
  511. getCurrentItemCatch(popupData.activeIndex) // 获取当前元素的缓存
  512. checkedAnimation(popupData.activeIndex);
  513. postMessage({
  514. api: 'courseLoading',
  515. content: {
  516. show: false,
  517. type: 'fullscreen'
  518. }
  519. });
  520. if (data.disableScreenRecordingFlag === '1') {
  521. // 检测是否录屏
  522. handleLimitScreenRecord();
  523. }
  524. setTimeout(() => {
  525. data.animationState = 'end';
  526. }, 500);
  527. });
  528. return true
  529. } catch (error) {
  530. console.log(error);
  531. }
  532. }
  533. const onTitleTip = (type: "phaseGoals" | "checkItem", text: string) => {
  534. handleStop()
  535. popupData.pointOpen = true
  536. popupData.pointContent = text
  537. if(type === "checkItem") {
  538. popupData.pointTitle = '检查事项'
  539. } else if(type === "phaseGoals") {
  540. popupData.pointTitle = '阶段目标'
  541. }
  542. }
  543. // ifram事件处理
  544. const iframeHandle = (ev: MessageEvent) => {
  545. console.log('headerTogge', ev)
  546. if (ev.data?.api === 'headerTogge') {
  547. activeData.model =
  548. ev.data.show || (ev.data.playState == 'play' ? false : true)
  549. }
  550. }
  551. //录屏时间触发
  552. const handleLimitScreenRecord = async () => {
  553. const result = await promisefiyPostMessage({
  554. api: 'getDeviceStatus',
  555. content: { type: 'video' }
  556. })
  557. const { status } = result?.content || {}
  558. if (status == '1') {
  559. data.itemList.forEach((item: any) => (item.autoPlay = false))
  560. handleStop()
  561. // 处理事件 - 事件事件后加载的
  562. checkVideoPlay()
  563. Dialog.alert({
  564. title: '温馨提示',
  565. message: '课件内容请勿录屏',
  566. beforeClose: () => {
  567. return new Promise(resolve => {
  568. promisefiyPostMessage({
  569. api: 'getDeviceStatus',
  570. content: { type: 'video' }
  571. }).then((res: any) => {
  572. const content = res.content
  573. if (content?.status == '1') {
  574. const activeItem = data.itemList[popupData.activeIndex]
  575. togglePlay(activeItem, false)
  576. resolve(false)
  577. } else {
  578. const activeItem = data.itemList[popupData.activeIndex]
  579. togglePlay(activeItem, true)
  580. resolve(true)
  581. }
  582. })
  583. })
  584. }
  585. })
  586. }
  587. }
  588. // 切换播放
  589. const togglePlay = (m: any, isPlay: boolean) => {
  590. if (isPlay) {
  591. m.videoEle?.play()
  592. } else {
  593. m.videoEle?.pause()
  594. }
  595. }
  596. let timers: any = null
  597. const checkVideoPlay = () => {
  598. const activeVideoRef = data.videoItemRef?.getPlyrRef()
  599. if (activeVideoRef) {
  600. timers = setInterval(() => {
  601. if (!activeVideoRef.paused()) {
  602. activeVideoRef.pause()
  603. clearInterval(timers)
  604. }
  605. activeVideoRef.pause()
  606. }, 100)
  607. }
  608. setTimeout(() => {
  609. clearInterval(timers)
  610. }, 3000)
  611. }
  612. // 获取支付渠道
  613. const sysParamConfig = async () => {
  614. try {
  615. const res = await request.get(
  616. apiSuffix.value + '/sysConfig/queryByParamName',
  617. {
  618. params: {
  619. paramName: 'disable_screen_recording_flag'
  620. }
  621. }
  622. )
  623. data.disableScreenRecordingFlag = res.data.paramValue || ''
  624. } catch {
  625. //
  626. }
  627. }
  628. const getRefLevel = async (id?: any) => {
  629. try {
  630. const res = await request.post(apiSuffix.value + '/tenantAlbumMusic/refLevel', {
  631. data: {
  632. lessonCoursewareDetailId: id || route.query.id
  633. }
  634. })
  635. data.refLevelList = res.data || []
  636. return true
  637. } catch {
  638. //
  639. }
  640. }
  641. onMounted(async () => {
  642. await sysParamConfig()
  643. if(data.source === 'search') {
  644. await getSearchDetail({search: data.search})
  645. } else {
  646. // 只有老师端才有课程类型
  647. if(baseState.platformType === "TEACHER") {
  648. await getRefLevel()
  649. }
  650. await getDetail()
  651. data.lessonId && await getSearchDetail({search: data.search, id: data.lessonId})
  652. }
  653. // getCourseSchedule();
  654. window.addEventListener('message', iframeHandle)
  655. if (data.disableScreenRecordingFlag === '1') {
  656. // 禁止录屏 ios
  657. listenerMessage('setVideoPlayer', result => {
  658. if (result?.content?.status == 'pause') {
  659. handleLimitScreenRecord()
  660. }
  661. })
  662. // 禁止录屏 安卓
  663. postMessage({
  664. api: 'limitScreenRecord',
  665. content: {
  666. type: 1
  667. }
  668. })
  669. }
  670. })
  671. onBeforeUnmount(() => {
  672. if (data.disableScreenRecordingFlag === '1') {
  673. // 取消 禁止录屏
  674. postMessage({
  675. api: 'limitScreenRecord',
  676. content: {
  677. type: 0
  678. }
  679. })
  680. }
  681. })
  682. const playRef = ref()
  683. // 返回
  684. const goback = () => {
  685. try {
  686. playRef.value?.handleOut()
  687. } catch (error) {
  688. console.log(error)
  689. }
  690. postMessage({ api: 'back' })
  691. }
  692. const popupData = reactive({
  693. pointOpen: false,
  694. pointContent: "",
  695. pointTitle: "",
  696. coursewareOpen: false,
  697. open: false,
  698. activeIndex: 0,
  699. playIndex: 0,
  700. tempTabActive: '', // 临时选中
  701. tempItemActive: "", // 临时编号
  702. tabActive: '',
  703. tabName: '',
  704. itemActive: '',
  705. itemName: '',
  706. guideOpen: false,
  707. toolOpen: false // 工具弹窗控制
  708. })
  709. const stopVideo = (el: HTMLVideoElement) => {
  710. return new Promise(resolve => {
  711. if (el.paused) return resolve(true)
  712. el.onpause = () => {
  713. console.log('暂停')
  714. resolve(true)
  715. }
  716. el.pause()
  717. })
  718. }
  719. /**停止所有的播放 */
  720. const handleStop = async () => {
  721. const videos = document.querySelectorAll('video')
  722. for (let i = 0; i < videos.length; i++) {
  723. const videoEle = videos[i] as HTMLVideoElement
  724. await stopVideo(videoEle)
  725. }
  726. console.log('视频暂停完成')
  727. data.itemList.forEach((item: any) => {
  728. if (item.typeCode === 'SONG') {
  729. item.iframeRef?.contentWindow?.postMessage(
  730. { api: 'setPlayState' },
  731. '*'
  732. )
  733. }
  734. })
  735. }
  736. // 切换素材
  737. const toggleMaterial = (itemActive: any) => {
  738. const index = data.itemList.findIndex((n: any) => n.id == itemActive)
  739. if (index > -1) {
  740. handleSwipeChange(index)
  741. }
  742. }
  743. /** 延迟收起模态框 */
  744. const setModelOpen = () => {
  745. clearTimeout(activeData.timer)
  746. Toast.clear()
  747. activeData.timer = setTimeout(() => {
  748. activeData.model = false
  749. }, 4000)
  750. }
  751. /** 立即收起所有的模态框 */
  752. const clearModel = () => {
  753. clearTimeout(activeData.timer)
  754. Toast.clear()
  755. activeData.model = false
  756. }
  757. // 双击
  758. const handleDbClick = () => {
  759. if (activeVideoItem.value.typeCode === 'VIDEO') {
  760. const activeVideoRef = data.videoItemRef?.getPlyrRef()
  761. if (activeVideoRef) {
  762. if (activeVideoRef.paused()) {
  763. activeVideoRef.play()
  764. } else {
  765. activeVideoRef.pause()
  766. Toast('已暂停')
  767. }
  768. }
  769. }
  770. }
  771. const effectIndex = ref(0)
  772. const effects = [
  773. {
  774. prev: {
  775. transform: 'translate3d(0, 0, -800px) rotateX(180deg)'
  776. },
  777. next: {
  778. transform: 'translate3d(0, 0, -800px) rotateX(-180deg)'
  779. }
  780. },
  781. {
  782. prev: {
  783. transform: 'translate3d(-100%, 0, -800px)'
  784. },
  785. next: {
  786. transform: 'translate3d(100%, 0, -800px)'
  787. }
  788. },
  789. {
  790. prev: {
  791. transform: 'translate3d(-50%, 0, -800px) rotateY(80deg)'
  792. },
  793. next: {
  794. transform: 'translate3d(50%, 0, -800px) rotateY(-80deg)'
  795. }
  796. },
  797. {
  798. prev: {
  799. transform: 'translate3d(-100%, 0, -800px) rotateY(-120deg)'
  800. },
  801. next: {
  802. transform: 'translate3d(100%, 0, -800px) rotateY(120deg)'
  803. }
  804. },
  805. // 风车4
  806. {
  807. prev: {
  808. transform: 'translate3d(-50%, 50%, -800px) rotateZ(-14deg)',
  809. opacity: 0
  810. },
  811. next: {
  812. transform: 'translate3d(50%, 50%, -800px) rotateZ(14deg)',
  813. opacity: 0
  814. }
  815. },
  816. // 翻页5
  817. {
  818. prev: {
  819. transform: 'translateZ(-800px) rotate3d(0, -1, 0, 90deg)',
  820. opacity: 0
  821. },
  822. next: {
  823. transform: 'translateZ(-800px) rotate3d(0, 1, 0, 90deg)',
  824. opacity: 0
  825. },
  826. current: { transitionDelay: '700ms' }
  827. }
  828. ]
  829. const acitveTimer = ref()
  830. // 轮播切换
  831. const handleSwipeChange = async (index: number) => {
  832. if(data.source === 'search') {
  833. const item = data.itemList[index];
  834. data.detail = detailList.value?.find((child: any) => child.coursewareDetailId === item.coursewareDetailId)
  835. popupData.tabActive = item.knowledgePointId;
  836. popupData.itemActive = item.id;
  837. popupData.itemName = item.name;
  838. popupData.tabName = item.tabName;
  839. if (item.typeCode == 'SONG') {
  840. activeData.model = true;
  841. }
  842. }
  843. // 如果是当前正在播放 或者是视频最后一个
  844. if (popupData.activeIndex == index) return
  845. await handleStop()
  846. data.animationState = 'start'
  847. data.videoState = 'init'
  848. clearTimeout(acitveTimer.value)
  849. checkedAnimation(popupData.activeIndex, index)
  850. nextTick(() => {
  851. popupData.activeIndex = index
  852. getCurrentItemCatch(index) // 获取当前元素的缓存
  853. acitveTimer.value = setTimeout(
  854. () => {
  855. popupData.playIndex = index
  856. const item = data.itemList[index]
  857. if (item) {
  858. popupData.tabActive = item.knowledgePointId
  859. popupData.itemActive = item.id
  860. popupData.itemName = item.name
  861. popupData.tabName = item.tabName
  862. if (item.typeCode == 'SONG') {
  863. activeData.model = true
  864. }
  865. }
  866. requestAnimationFrame(() => {
  867. const _effectIndex = effectIndex.value + 1
  868. effectIndex.value =
  869. _effectIndex >= effects.length - 1 ? 0 : _effectIndex
  870. if (item && item.typeCode === 'VIDEO') {
  871. // 自动播放下一个视频
  872. clearTimeout(activeData.timer)
  873. Toast.clear()
  874. item.autoPlay = true
  875. data.animationState = 'end'
  876. }
  877. })
  878. },
  879. activeData.isAnimation ? 850 : 0
  880. )
  881. })
  882. }
  883. /** 是否有转场动画 */
  884. const checkedAnimation = (index: number, nextIndex?: number) => {
  885. nextIndex = nextIndex ? nextIndex : index + 1
  886. const item = data.itemList[index]
  887. const nextItem = data.itemList[nextIndex]
  888. if (nextItem) {
  889. if (nextItem.knowledgePointId != item.knowledgePointId) {
  890. activeData.isAnimation = true
  891. return
  892. }
  893. const videoEle = item.videoEle
  894. const nextVideo = nextItem.videoEle
  895. if (videoEle && videoEle.duration < 8 && index < nextIndex) {
  896. activeData.isAnimation = false
  897. } else if (nextVideo && nextVideo.duration < 8 && index > nextIndex) {
  898. activeData.isAnimation = false
  899. } else {
  900. activeData.isAnimation = true
  901. }
  902. } else {
  903. activeData.isAnimation = item?.adviseStudyTimeSecond < 8 ? false : true
  904. }
  905. }
  906. // 上一个知识点, 下一个知识点
  907. const handlePreAndNext = (type: string) => {
  908. if (type === 'up') {
  909. handleSwipeChange(popupData.activeIndex - 1)
  910. } else {
  911. handleSwipeChange(popupData.activeIndex + 1)
  912. }
  913. }
  914. /** 弹窗关闭 */
  915. const handleClosePopup = () => {
  916. const item = data.itemList[popupData.activeIndex]
  917. if (item?.typeCode == 'VIDEO' && !item.videoEle?.paused) {
  918. setModelOpen()
  919. }
  920. }
  921. /** 教学数据 */
  922. const studyData = reactive({
  923. type: '' as ToolType,
  924. penShow: false
  925. })
  926. /** 打开教学工具 */
  927. const openStudyTool = (item: ToolItem) => {
  928. const activeItem = data.itemList[popupData.activeIndex]
  929. // 暂停视频和曲谱的播放
  930. if (activeItem.typeCode === 'VIDEO' && activeItem.videoEle) {
  931. activeItem.videoEle.pause()
  932. }
  933. if (activeItem.typeCode === 'SONG') {
  934. activeItem.iframeRef?.contentWindow?.postMessage(
  935. { api: 'setPlayState' },
  936. '*'
  937. )
  938. }
  939. clearModel()
  940. popupData.toolOpen = false
  941. studyData.type = item.type
  942. switch (item.type) {
  943. case 'pen':
  944. studyData.penShow = true
  945. break
  946. }
  947. }
  948. // 白板的批注打开时暂停播放
  949. watch(
  950. () => [whitePenShow.value, penShow.value],
  951. () => {
  952. if (whitePenShow.value || penShow.value) {
  953. handleStop()
  954. }
  955. }
  956. )
  957. // 是否收起
  958. watch(
  959. () => activeData.model,
  960. () => {
  961. if (activeData.model) {
  962. isPlay.value = false
  963. } else {
  964. isPlay.value = true
  965. toolOpen.value = false
  966. }
  967. }
  968. )
  969. const activeVideoItem = computed(() => {
  970. const item = data.itemList[popupData.activeIndex]
  971. if (
  972. item &&
  973. item.typeCode &&
  974. item.typeCode.toLocaleUpperCase() === 'VIDEO'
  975. ) {
  976. return item
  977. }
  978. return {}
  979. })
  980. let closeModelTimer: any = null
  981. return () => (
  982. <div id="playContent" class={styles.playContent}>
  983. <div
  984. class={styles.coursewarePlay}
  985. style={{ width: parentContainer.width }}
  986. onClick={() => {
  987. clearTimeout(closeModelTimer)
  988. clearTimeout(activeData.timer)
  989. Toast.clear()
  990. if (Date.now() - activeData.nowTime < 300) {
  991. handleDbClick()
  992. return
  993. }
  994. activeData.nowTime = Date.now()
  995. closeModelTimer = setTimeout(() => {
  996. activeData.model = !activeData.model
  997. }, 300)
  998. }}
  999. >
  1000. <div class={styles.wraps}>
  1001. <div
  1002. style={
  1003. activeVideoItem.value.typeCode &&
  1004. data.animationState === 'end' &&
  1005. data.videoState === 'play'
  1006. ? {
  1007. zIndex: 15,
  1008. opacity: 1
  1009. }
  1010. : { opacity: 0, zIndex: -1, pointerEvents: "none" }
  1011. }
  1012. class={styles.itemDiv}
  1013. >
  1014. <VideoPlay
  1015. ref={(el: any) => (data.videoItemRef = el)}
  1016. item={activeVideoItem.value}
  1017. activeModel={activeData.model}
  1018. onPlay={() => {
  1019. data.videoState = 'play'
  1020. data.animationState = 'end'
  1021. if(whitePenShow.value || penShow.value || popupData.coursewareOpen || popupData.open || popupData.guideOpen || popupData.pointOpen) {
  1022. handleStop()
  1023. }
  1024. }}
  1025. onLoadedmetadata={(videoItem: any) => {
  1026. data.videoState = 'play'
  1027. activeVideoItem.value.videoEle = videoItem
  1028. if (!activeVideoItem.value.isprepare) {
  1029. activeVideoItem.value.isprepare = true
  1030. }
  1031. }}
  1032. onPause={() => {
  1033. clearTimeout(activeData.timer)
  1034. // activeData.model = true
  1035. }}
  1036. onEnded={async () => {
  1037. const _index = popupData.activeIndex + 1
  1038. if (_index < data.itemList.length) {
  1039. handleSwipeChange(_index)
  1040. }
  1041. }}
  1042. onError={() => {
  1043. // 视屏异常
  1044. activeVideoItem.value.error = true
  1045. }}
  1046. />
  1047. </div>
  1048. {data.itemList.map((m: any, mIndex: number) => {
  1049. const isRenderItem = Math.abs(popupData.activeIndex - mIndex) < 2
  1050. const isRender = Math.abs(popupData.playIndex - mIndex) < 2
  1051. // 判断是否是当前选中的元素
  1052. const activeEle = popupData.playIndex === mIndex ? true : false
  1053. return isRenderItem ? (
  1054. <div
  1055. key={'index' + mIndex}
  1056. data-id={'data' + mIndex}
  1057. class={[
  1058. styles.itemDiv,
  1059. activeEle && styles.itemActive,
  1060. activeData.isAnimation && styles.acitveAnimation,
  1061. isRenderItem ? styles.show : styles.hide
  1062. ]}
  1063. style={
  1064. mIndex < popupData.activeIndex
  1065. ? effects[effectIndex.value].prev
  1066. : mIndex > popupData.activeIndex
  1067. ? effects[effectIndex.value].next
  1068. : {}
  1069. }
  1070. >
  1071. <Transition name="van-fade">
  1072. {m.typeCode === 'VIDEO' &&
  1073. data.animationState !== 'end' &&
  1074. data.videoState != 'play' ? (
  1075. <div class={styles.loadWrap}>
  1076. <Vue3Lottie animationData={playLoadData}></Vue3Lottie>
  1077. </div>
  1078. ) : ''}
  1079. </Transition>
  1080. {isRender && m.typeCode === 'IMG' && (
  1081. <div>
  1082. <img src={m.content} />
  1083. {m.materialMusicId && (
  1084. <div
  1085. class={[
  1086. styles.goPractice,
  1087. activeData.model ? '' : styles.hide
  1088. ]}
  1089. onClick={(e: any) => {
  1090. // 去云练习完整版
  1091. e.stopPropagation()
  1092. musicBuy({ id: m.materialMusicId }, () => {}, {
  1093. setting: JSON.stringify({
  1094. feeType: 'FREE'
  1095. })
  1096. })
  1097. }}
  1098. ></div>
  1099. )}
  1100. </div>
  1101. )}
  1102. {isRender && m.typeCode === 'SONG' && (
  1103. <MusicScore
  1104. activeModel={activeData.model}
  1105. data-vid={m.id}
  1106. music={m}
  1107. onSetIframe={(el: any) => {
  1108. m.iframeRef = el
  1109. }}
  1110. />
  1111. )}
  1112. </div>
  1113. ) : (
  1114. ''
  1115. )
  1116. })}
  1117. </div>
  1118. <Transition name="left">
  1119. {activeData.model && (
  1120. <div
  1121. class={styles.leftFixedBtns}
  1122. onClick={(e: Event) => e.stopPropagation()}
  1123. >
  1124. <div class={[styles.btnsWrap, styles.prePoint]}>
  1125. {/* {data.source === 'search' ?
  1126. <div class={styles.fullBtn} onClick={() => {
  1127. handleStop()
  1128. detailTempSearchList.value = detailList.value
  1129. popupData.tempItemActive = ""
  1130. popupData.tempTabActive = ""
  1131. data.searchTemp = ""
  1132. // data.searchTemp = JSON.parse(JSON.stringify(data.search))
  1133. popupData.open = true
  1134. }}>
  1135. <img src={iconSearch} />
  1136. </div> : <>
  1137. {baseState.platformType === "TEACHER" && <div class={styles.fullBtn} onClick={() => {
  1138. handleStop()
  1139. popupData.coursewareOpen = true
  1140. }}>
  1141. <img src={iconCourseType} />
  1142. </div>}
  1143. <div class={styles.fullBtn} onClick={() => {
  1144. handleStop()
  1145. popupData.open = true
  1146. }}>
  1147. <img src={iconMenu} />
  1148. </div>
  1149. </>} */}
  1150. {baseState.platformType === "TEACHER" && data.source !== 'search' && <div class={styles.fullBtn} onClick={() => {
  1151. handleStop()
  1152. popupData.coursewareOpen = true
  1153. }}>
  1154. <img src={iconCourseType} />
  1155. </div>}
  1156. <div class={styles.fullBtn} onClick={() => {
  1157. handleStop()
  1158. detailTempSearchList.value = detailList.value
  1159. data.isSearch = true
  1160. popupData.tempItemActive = ""
  1161. popupData.tempTabActive = ""
  1162. data.searchTemp = ""
  1163. // data.searchTemp = JSON.parse(JSON.stringify(data.search))
  1164. popupData.open = true
  1165. }}>
  1166. <img src={iconSearch} />
  1167. </div>
  1168. {data.source !== 'search' && <div class={styles.fullBtn} onClick={() => {
  1169. handleStop()
  1170. data.isSearch = false
  1171. popupData.open = true
  1172. }}>
  1173. <img src={iconMenu} />
  1174. </div>}
  1175. <div
  1176. class={[styles.fullBtn, !(popupData.activeIndex != 0) && styles.disabled]}
  1177. onClick={() => {
  1178. if(popupData.activeIndex != 0) handlePreAndNext('up')
  1179. }}
  1180. >
  1181. <img src={iconUp} />
  1182. {/* <span style={{ textAlign: 'center' }}>上一个</span> */}
  1183. </div>
  1184. <div
  1185. class={[styles.fullBtn, !(popupData.activeIndex != data.itemList.length - 1) && styles.disabled]}
  1186. onClick={() => {
  1187. if(popupData.activeIndex != data.itemList.length - 1) handlePreAndNext('down')
  1188. }}
  1189. >
  1190. {/* <span style={{ textAlign: 'center' }}>下一个</span> */}
  1191. <img src={iconDown} />
  1192. </div>
  1193. </div>
  1194. </div>
  1195. )}
  1196. </Transition>
  1197. </div>
  1198. <div
  1199. style={{ transform: activeData.model ? '' : 'translateY(-100%)' }}
  1200. id="coursePlayHeader"
  1201. class={styles.headerContainer}
  1202. ref={headeRef}
  1203. >
  1204. <div class={styles.backBtn}>
  1205. <Icon name={iconBack} onClick={() => {
  1206. goback()
  1207. }} />
  1208. <div class={styles.titleSection}>
  1209. <div class={styles.title}>{popupData.tabName}</div>
  1210. <div class={styles.titleContent}>
  1211. <p>{data.itemList[popupData.activeIndex]?.name}</p>
  1212. {data.detail?.lessonTargetDesc ? <span onClick={() => onTitleTip('phaseGoals', data.detail?.lessonTargetDesc)}>阶段目标</span>: ""}
  1213. {data.itemList[popupData.activeIndex]?.checkItem ? <span onClick={() => onTitleTip('checkItem', data.itemList[popupData.activeIndex]?.checkItem)}>检查事项</span> : ""}
  1214. </div>
  1215. </div>
  1216. </div>
  1217. {state.platformType === 'TEACHER' && (
  1218. <div
  1219. class={styles.headRight}
  1220. onClick={(e: Event) => {
  1221. e.stopPropagation()
  1222. clearTimeout(activeData.timer)
  1223. }}
  1224. >
  1225. <div class={styles.rightBtn} onClick={() => {
  1226. handleStop()
  1227. popupData.guideOpen = true
  1228. }}>
  1229. <img src={iconTouping} />
  1230. </div>
  1231. </div>
  1232. )}
  1233. </div>
  1234. {/* 更多弹窗 */}
  1235. <Popup
  1236. class={styles.popupMore}
  1237. overlayClass={styles.overlayClass}
  1238. position="right"
  1239. round
  1240. v-model:show={popupData.toolOpen}
  1241. onClose={handleClosePopup}
  1242. >
  1243. <Tool onHandleTool={openStudyTool} />
  1244. </Popup>
  1245. <Popup
  1246. class={[styles.popup, styles.popupCoursewarePlay]}
  1247. overlayClass={styles.overlayClass}
  1248. position="right"
  1249. round
  1250. v-model:show={popupData.open}
  1251. onClose={handleClosePopup}
  1252. >
  1253. {data.isSearch ?
  1254. <PointsSearch
  1255. data={detailTempSearchList.value}
  1256. search={data.searchTemp || data.search}
  1257. loading={data.searchLoading}
  1258. tabActive={popupData.tempTabActive || popupData.tabActive}
  1259. itemActive={popupData.tempItemActive || popupData.itemActive}
  1260. open={popupData.open}
  1261. onHandleSelect={(res: any) => {
  1262. if(data.source !== "search") {
  1263. if (browser().isApp) {
  1264. postMessage({
  1265. api: 'openWebView',
  1266. content: {
  1267. url: `${getHttpOrigin()}${location.pathname}#/coursewarePlay?lessonId=${data.lessonId}&source=search&kId=${res.materialId}&search=${encodeURIComponent(data.searchTemp ? JSON.parse(JSON.stringify(data.searchTemp)) : '')}`,
  1268. orientation: 0,
  1269. c_orientation: 0,
  1270. isHideTitle: true,
  1271. statusBarTextColor: false,
  1272. isOpenLight: true,
  1273. showLoadingAnim: true
  1274. }
  1275. });
  1276. return
  1277. } else {
  1278. router.push({
  1279. path: '/coursewarePlay',
  1280. query: {
  1281. lessonId: data.lessonId,
  1282. kId: res.materialId,
  1283. search: data.searchTemp ? JSON.parse(JSON.stringify(data.searchTemp)) : '',
  1284. source: 'search'
  1285. }
  1286. }).then(() => {
  1287. window.location.reload()
  1288. });
  1289. // Toast('请使用App打开')
  1290. return
  1291. }
  1292. }
  1293. popupData.open = false;
  1294. if(res.isSearch) {
  1295. detailList.value = detailTempSearchList.value
  1296. const tempList: any[] = []
  1297. detailTempSearchList.value?.forEach((item: any) => {
  1298. if(Array.isArray(item.list)) {
  1299. tempList.push(...item.list)
  1300. }
  1301. })
  1302. data.itemList = tempList || []
  1303. data.search = data.searchTemp ? JSON.parse(JSON.stringify(data.searchTemp)) : ''
  1304. }
  1305. toggleMaterial(res.itemActive);
  1306. }}
  1307. onHandleSearch={async (val: any) => {
  1308. data.searchLoading = true
  1309. detailTempSearchList.value = []
  1310. if(data.source === 'search') {
  1311. await getSearchDetail({
  1312. type: 'pointSearch',
  1313. search: val.search
  1314. })
  1315. } else {
  1316. await getSearchDetail({
  1317. type: 'pointSearch',
  1318. search: val.search,
  1319. id: data.lessonId
  1320. })
  1321. }
  1322. data.searchTemp = val.search;
  1323. data.searchLoading = false
  1324. }} /> :
  1325. <Points
  1326. data={data.knowledgePointList}
  1327. tabActive={popupData.tabActive}
  1328. itemActive={popupData.itemActive}
  1329. onHandleSelect={(res: any) => {
  1330. // onChangeSwiper('change', res.itemActive)
  1331. popupData.open = false
  1332. toggleMaterial(res.itemActive)
  1333. }}
  1334. />}
  1335. </Popup>
  1336. <Popup
  1337. class={[styles.popup, styles.popupCoursewarePlay]}
  1338. overlayClass={styles.overlayClass}
  1339. position="right"
  1340. round
  1341. v-model:show={popupData.coursewareOpen}
  1342. onClose={handleClosePopup}>
  1343. {/* 课件类型 */}
  1344. <CoursewareType list={data.refLevelList} onConfirm={async (item: any) => {
  1345. // 判断是否为当前课程类型
  1346. if(data.currentId === item.id) {
  1347. return
  1348. }
  1349. data.currentId = item.id;
  1350. const n = await getDetail(item.id);
  1351. const s = await getRefLevel(item.id);
  1352. data.isSearch = false
  1353. if(n && s) {
  1354. popupData.coursewareOpen = false;
  1355. popupData.activeIndex = 0;
  1356. getCurrentItemCatch(popupData.activeIndex) // 获取当前元素的缓存
  1357. nextTick(() => {
  1358. popupData.open = true
  1359. })
  1360. } else {
  1361. if(!isOnline.value) {
  1362. Toast('网络异常')
  1363. }
  1364. }
  1365. data.lessonId && await getSearchDetail({search: data.search, id: data.lessonId})
  1366. }} />
  1367. </Popup>
  1368. <Popup
  1369. class={[styles.popup, styles.popupCoursewarePlay]}
  1370. overlayClass={styles.overlayClass}
  1371. position="right"
  1372. round
  1373. v-model:show={popupData.guideOpen}
  1374. onClose={handleClosePopup}
  1375. >
  1376. <OGuide />
  1377. </Popup>
  1378. <Popup
  1379. class={[styles.popup, styles.popupPoint]}
  1380. round
  1381. style={{ background: 'transparent !important' }}
  1382. v-model:show={popupData.pointOpen}
  1383. onClose={handleClosePopup}>
  1384. <CoursewareTips onClose={() => {
  1385. popupData.pointOpen = false
  1386. }} content={popupData.pointContent} titleName={popupData.pointTitle} />
  1387. </Popup>
  1388. <GlobalTools />
  1389. </div>
  1390. )
  1391. }
  1392. })