index.tsx 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405
  1. import { closeToast, Icon, Popup, showDialog, showToast } from 'vant';
  2. import {
  3. defineComponent,
  4. onMounted,
  5. reactive,
  6. onUnmounted,
  7. ref,
  8. watch,
  9. Transition,
  10. computed
  11. } from 'vue';
  12. import iconBack from './image/back.svg';
  13. import styles from './index.module.less';
  14. import 'plyr/dist/plyr.css';
  15. import { useRoute } from 'vue-router';
  16. import {
  17. listenerMessage,
  18. postMessage,
  19. promisefiyPostMessage
  20. } from '@/helpers/native-message';
  21. import MusicScore from './component/musicScore';
  22. import iconMenu from './image/icon-menu.svg';
  23. import iconChange from './image/icon-change.svg';
  24. // import iconDian from './image/icon-dian.svg';
  25. // import iconPoint from './image/icon-point.svg';
  26. import iconUp from './image/icon-up.svg';
  27. import iconDown from './image/icon-down.svg';
  28. import selectIcon from './image/select_btn.png';
  29. import memberLockBg from './image/member_lock_bg.png';
  30. import Points from './component/points';
  31. import { Vue3Lottie } from 'vue3-lottie';
  32. import playLoadData from './datas/data.json';
  33. import { usePageVisibility } from '@vant/use';
  34. import AudioItem from './component/audio-item';
  35. import {
  36. api_lessonCoursewareStudentDetail,
  37. api_lessonCoursewareDetail
  38. } from '../courseware-list/api';
  39. import VideoItem from './component/video-item';
  40. import Chapter from './component/chapter';
  41. import { setLogin, state } from '@/state';
  42. import Theory from './component/theory';
  43. import InstrumentInfo from './component/instrument-info';
  44. import TempoItem from './component/tempo-item';
  45. import ListenItem from './component/listen-item';
  46. import request from '@/helpers/request';
  47. import PptList from './component/PptList';
  48. import SelectCoursewareTip from './select-courseware-tip';
  49. export default defineComponent({
  50. name: 'CoursewarePlay',
  51. setup() {
  52. const pageVisibility = usePageVisibility();
  53. // const lastTimeKey = 'lastTime' + (state?.user?.data?.phone ?? '');
  54. const showSelectCourseware = ref(false);
  55. const showMember = ref(false);
  56. const debounceSkip = ref(false);
  57. /** 设置播放容器 16:9 */
  58. const parentContainer = reactive({
  59. width: '100vw'
  60. });
  61. const setContainer = () => {
  62. let min = Math.min(screen.width, screen.height);
  63. let max = Math.max(screen.width, screen.height);
  64. let width = min * (16 / 9);
  65. if (width > max) {
  66. parentContainer.width = '100vw';
  67. return;
  68. } else {
  69. parentContainer.width = width + 'px';
  70. }
  71. };
  72. const handleInit = (type = 0) => {
  73. //设置容器16:9
  74. setContainer();
  75. // 横屏
  76. // postMessage(
  77. // {
  78. // api: 'setRequestedOrientation',
  79. // content: {
  80. // orientation: type
  81. // }
  82. // },
  83. // () => {
  84. // console.log(234);
  85. // }
  86. // );
  87. // 头,包括返回箭头
  88. // postMessage({
  89. // api: 'setTitleBarVisibility',
  90. // content: {
  91. // status: type
  92. // }
  93. // })
  94. // 安卓的状态栏
  95. postMessage({
  96. api: 'setStatusBarVisibility',
  97. content: {
  98. isVisibility: type
  99. }
  100. });
  101. // 进入页面设置常量
  102. postMessage({
  103. api: 'keepScreenLongLight',
  104. content: {
  105. isOpenLight: type ? true : false
  106. }
  107. });
  108. };
  109. handleInit();
  110. onUnmounted(() => {
  111. handleInit(1);
  112. window.removeEventListener('message', iframeHandle);
  113. window.removeEventListener('online', handleOnline);
  114. window.removeEventListener('offline', handleOffline);
  115. });
  116. const getCourseDetail = async () => {
  117. try {
  118. const res = await api_lessonCoursewareDetail({
  119. id: activeData.lessonCoursewareId as any
  120. });
  121. if (res?.code == 200 && Array.isArray(res?.data?.lessonList)) {
  122. data.courseDetails = res.data.lessonList || [];
  123. }
  124. } catch {
  125. //
  126. }
  127. };
  128. const route = useRoute();
  129. const headeRef = ref();
  130. const loadingClass = ref(false); // 重新加载课件
  131. const data = reactive({
  132. disableScreenRecordingFlag: '0',
  133. allList: [] as any, // 所选章节下的所有课件列表
  134. id: route.query.id as string, // 章节ID
  135. kjId: route.query.id as string, // 所选的课件id
  136. currentCourse: {} as any, // 当前选中的课件
  137. zsdId: '' as string, // 知识点id
  138. courseDetails: [] as any,
  139. itemList: [] as any,
  140. videoRefs: {} as any[],
  141. videoState: 'init' as 'init' | 'play',
  142. videoItemRef: null as any,
  143. animationState: 'start' as 'start' | 'end',
  144. coursewareType: 'CLASSIC' as 'CLASSIC' | 'PPT' // 课件类型
  145. });
  146. // 上一下,下一个切换知识点或单元时临时存的数据
  147. const changeLessonCourseWare = ref<any>({});
  148. const pptData = reactive({
  149. pptEl: null as any,
  150. slidesLen: 1,
  151. activeLen: 0,
  152. disable: false,
  153. isAnimationed: true
  154. });
  155. const activeData = reactive({
  156. isAutoPlay: true, // 是否自动播放
  157. lessonCoursewareId: route.query.lessonCoursewareId,
  158. lessonCoursewareDetailId: null, // 单元编号
  159. // coursewareDetailKnowledgeId: route.query.coursewareDetailKnowledgeId,
  160. nowTime: 0,
  161. model: true, // 遮罩
  162. isAnimation: true, // 是否动画
  163. timer: null as any,
  164. item: null as any
  165. });
  166. // 切换单元临时数据
  167. const temporaryData = reactive({
  168. dyId: '', // 单元id
  169. zjId: '' // 章节id
  170. });
  171. const getDetail = async () => {
  172. // data.allList = [];
  173. const res = await api_lessonCoursewareStudentDetail({
  174. lessonCoursewareKnowledgeDetailId: data.id // 章节id
  175. });
  176. if (res?.code !== 200) return;
  177. // data.allList = [res.data];
  178. const currentCourse = res?.data;
  179. const knowledgeList = currentCourse?.knowledgeList || [];
  180. const courseList: any = [];
  181. knowledgeList?.forEach((item: any) => {
  182. if (Array.isArray(item.materialInfo)) {
  183. item.materialInfo?.forEach((n: any) => {
  184. n.audioPlayTypesArray = n.audioPlayTypes
  185. ? n.audioPlayTypes.split(',')
  186. : [];
  187. n.url =
  188. n.coverImg || n.type === 'SONG'
  189. ? 'https://oss.dayaedu.com/ktqy/1698420034679a22d3f7a.png'
  190. : n.type === 'PPT'
  191. ? 'https://oss.dayaedu.com/ktqy/12/1701931810284.png'
  192. : n.coverImg;
  193. n.zsdId = item.id; // 知识点id
  194. n.kjId = currentCourse.id; // 课件id
  195. n.bizId = n.bizId;
  196. courseList.push(n);
  197. });
  198. }
  199. });
  200. data.zsdId = knowledgeList?.[0].id;
  201. activeData.lessonCoursewareDetailId =
  202. currentCourse.lessonCoursewareDetailId;
  203. data.currentCourse = {
  204. ...currentCourse,
  205. knowledgeList
  206. };
  207. popupData.itemPointName = currentCourse.name || '';
  208. // 当前的资源id
  209. let resourceId: any = null;
  210. // 课程
  211. if (courseList.length > 0) {
  212. resourceId = courseList[0].id;
  213. data.coursewareType =
  214. courseList[0].type === 'PPT_JSON' ? 'PPT' : 'CLASSIC';
  215. }
  216. data.itemList = courseList.map((m: any) => {
  217. if (!popupData.itemActive) {
  218. popupData.itemActive = m.id;
  219. popupData.itemName = m.name;
  220. }
  221. return {
  222. ...m,
  223. iframeRef: null,
  224. videoEle: null,
  225. audioEle: null,
  226. domEle: null,
  227. autoPlay:
  228. showMember.value || m.paymentType === 'FREE'
  229. ? data.currentCourse.autoPlay || false
  230. : false, //加载完成是否自动播放
  231. isprepare: false, // 视频是否加载完成
  232. isRender: false // 是否渲染了
  233. };
  234. });
  235. const resourceIndex = data.itemList.findIndex(
  236. (resource: any) => resource.id === resourceId
  237. );
  238. setTimeout(() => {
  239. handleSwipeChange(resourceIndex);
  240. }, 0);
  241. //检测是否录屏
  242. if (data.disableScreenRecordingFlag === '1') {
  243. handleLimitScreenRecord();
  244. }
  245. setTimeout(() => {
  246. data.animationState = 'end';
  247. }, 500);
  248. };
  249. // ifram事件处理
  250. const iframeHandle = (ev: MessageEvent) => {
  251. if (ev.data?.api === 'headerTogge') {
  252. activeData.model =
  253. ev.data.show || (ev.data.playState == 'play' ? false : true);
  254. }
  255. if (ev.data?.api === 'api_fingerPreView') {
  256. clearInterval(activeData.timer);
  257. activeData.model = !ev.data.state;
  258. }
  259. if (ev.data?.api === 'clickTempo' || ev.data?.api === 'clickViewFigner') {
  260. setModelOpen();
  261. }
  262. };
  263. let timers: any = null;
  264. const checkVideoPlay = (activeItem: any) => {
  265. const activeVideoRef =
  266. activeItem.type === 'VIDEO' ? activeItem.videoEle : activeItem.audioEle;
  267. if (activeVideoRef) {
  268. timers = setInterval(() => {
  269. const paused =
  270. activeItem.type === 'VIDEO'
  271. ? activeVideoRef.paused()
  272. : activeVideoRef.paused;
  273. if (!paused) {
  274. activeVideoRef.pause();
  275. clearInterval(timers);
  276. }
  277. activeVideoRef.pause();
  278. }, 100);
  279. }
  280. setTimeout(() => {
  281. clearInterval(timers);
  282. }, 3000);
  283. };
  284. //录屏时间触发
  285. const handleLimitScreenRecord = async () => {
  286. const result = await promisefiyPostMessage({
  287. api: 'getDeviceStatus',
  288. content: { type: 'video' }
  289. });
  290. const { status } = result?.content || {};
  291. if (status == '1') {
  292. data.itemList.forEach((item: any) => (item.autoPlay = false));
  293. handleStop();
  294. const activeItem = data.itemList[popupData.activeIndex];
  295. if (activeItem.type === 'VIDEO' || activeItem.type === 'SONG') {
  296. // 处理事件 - 事件事件后加载的
  297. checkVideoPlay(activeItem);
  298. }
  299. showDialog({
  300. title: '温馨提示',
  301. message: '课件内容请勿录屏',
  302. beforeClose: () => {
  303. return new Promise(resolve => {
  304. promisefiyPostMessage({
  305. api: 'getDeviceStatus',
  306. content: { type: 'video' }
  307. }).then((res: any) => {
  308. const content = res.content;
  309. if (content?.status == '1') {
  310. resolve(false);
  311. } else {
  312. data.itemList.forEach((item: any) => (item.autoPlay = true));
  313. resolve(true);
  314. }
  315. });
  316. });
  317. }
  318. });
  319. }
  320. };
  321. // 获取支付渠道
  322. const sysParamConfig = async () => {
  323. try {
  324. const res = await request.get(
  325. '/edu-app/sysParamConfig/queryByParamName',
  326. {
  327. params: {
  328. paramName: 'disable_screen_recording_flag'
  329. }
  330. }
  331. );
  332. data.disableScreenRecordingFlag = res.data.paramValue || '';
  333. } catch {
  334. //
  335. }
  336. };
  337. onMounted(async () => {
  338. await sysParamConfig();
  339. if (state?.user?.data.vipMember) {
  340. showMember.value = true;
  341. } else {
  342. showMember.value = false;
  343. }
  344. postMessage({
  345. api: 'courseLoading',
  346. content: {
  347. show: false,
  348. type: 'fullscreen'
  349. }
  350. });
  351. getDetail();
  352. getCourseDetail();
  353. window.addEventListener('message', iframeHandle);
  354. if (data.disableScreenRecordingFlag === '1') {
  355. //禁止录屏 ios
  356. listenerMessage('setVideoPlayer', result => {
  357. if (result?.content?.status == 'pause') {
  358. handleLimitScreenRecord();
  359. }
  360. });
  361. // 安卓
  362. postMessage({
  363. api: 'limitScreenRecord',
  364. content: {
  365. type: 1
  366. }
  367. });
  368. }
  369. window.addEventListener('online', handleOnline);
  370. window.addEventListener('offline', handleOffline);
  371. });
  372. const lineTimer = ref();
  373. const handleOnline = () => {
  374. clearTimeout(lineTimer.value);
  375. };
  376. const handleOffline = () => {
  377. showToast('请检查网络');
  378. };
  379. const playRef = ref();
  380. // 返回
  381. const goback = () => {
  382. try {
  383. playRef.value?.handleOut();
  384. } catch (error) {}
  385. postMessage({ api: 'goBack' });
  386. // router.back()
  387. };
  388. const popupData = reactive({
  389. open: false,
  390. activeIndex: 0,
  391. itemActive: '',
  392. itemName: '',
  393. itemPointName: '' as any,
  394. chapterOpen: false
  395. });
  396. // 切换素材
  397. const toggleMaterial = (itemActive: any) => {
  398. // 如果切换了知识点或者切换了课件,需要加载新的
  399. const index = data.itemList.findIndex((n: any) => n.id == itemActive);
  400. if (index > -1) {
  401. handleSwipeChange(index);
  402. }
  403. };
  404. /** 延迟收起模态框 */
  405. const setModelOpen = () => {
  406. clearTimeout(activeData.timer);
  407. closeToast();
  408. activeData.model = !activeData.model;
  409. activeData.timer = setTimeout(() => {
  410. activeData.model = false;
  411. }, 4000);
  412. };
  413. const setModelOpen1 = () => {
  414. clearTimeout(activeData.timer);
  415. closeToast();
  416. activeData.model = true;
  417. activeData.timer = setTimeout(() => {
  418. activeData.model = false;
  419. }, 4000);
  420. };
  421. // 双击
  422. const handleDbClick = (item: any) => {
  423. if (item && ['VIDEO'].includes(item.type)) {
  424. console.log('双击');
  425. }
  426. };
  427. const effectIndex = ref(3);
  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. opacity: 0
  457. },
  458. next: {
  459. transform: 'translate3d(100%, 0, -800px) rotateY(120deg)',
  460. opacity: 0
  461. }
  462. },
  463. // 风车4
  464. {
  465. prev: {
  466. transform: 'translate3d(-50%, 50%, -800px) rotateZ(-14deg)',
  467. opacity: 0
  468. },
  469. next: {
  470. transform: 'translate3d(50%, 50%, -800px) rotateZ(14deg)',
  471. opacity: 0
  472. }
  473. },
  474. // 翻页5
  475. {
  476. prev: {
  477. transform: 'translateZ(-800px) rotate3d(0, -1, 0, 90deg)',
  478. opacity: 0
  479. },
  480. next: {
  481. transform: 'translateZ(-800px) rotate3d(0, 1, 0, 90deg)',
  482. opacity: 0
  483. },
  484. current: { transitionDelay: '700ms' }
  485. }
  486. ];
  487. const handleStop = () => {
  488. // data.videoItemRef?.pause();
  489. for (let i = 0; i < data.itemList.length; i++) {
  490. const activeItem = data.itemList[i];
  491. if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
  492. activeItem.videoEle?.pause();
  493. }
  494. if (activeItem.type === 'SONG' && activeItem.audioEle) {
  495. activeItem.audioEle?.pause();
  496. }
  497. // 停止曲谱的播放
  498. if (activeItem.type === 'MUSIC') {
  499. activeItem.iframeRef?.contentWindow?.postMessage(
  500. { api: 'setPlayState' },
  501. '*'
  502. );
  503. }
  504. if (
  505. activeItem.type === 'INSTRUMENT' ||
  506. activeItem.type === 'MUSICIAN' ||
  507. activeItem.type === 'MUSIC_WIKI'
  508. ) {
  509. activeItem.domEle?.onChangeAudioStop();
  510. }
  511. // console.log(activeItem.type, 'activeItem.type');
  512. if (activeItem.type === 'RHYTHM') {
  513. activeItem.iframeRef?.contentWindow?.postMessage(
  514. { api: 'setPlayState', data: false },
  515. '*'
  516. );
  517. }
  518. if (activeItem.type === 'LISTEN') {
  519. activeItem.iframeRef?.contentWindow?.postMessage(
  520. { api: 'setPlayState' },
  521. '*'
  522. );
  523. }
  524. }
  525. };
  526. const acitveTimer = ref();
  527. // 轮播切换
  528. const handleSwipeChange = (index: number) => {
  529. // 如果是当前正在播放 或者是视频最后一个
  530. if (popupData.activeIndex == index) return;
  531. data.animationState = 'start';
  532. data.videoState = 'init';
  533. handleStop();
  534. clearTimeout(acitveTimer.value);
  535. activeData.model = true;
  536. const item = data.itemList[index];
  537. data.kjId = item.kjId;
  538. data.zsdId = item.zsdId;
  539. popupData.activeIndex = index;
  540. popupData.itemActive = item.id;
  541. popupData.itemName = item.name;
  542. if (item.type == 'MUSIC') {
  543. activeData.model = true;
  544. } else if (item.type == 'VIDEO') {
  545. if (item.error) {
  546. data.videoRefs[index]?.onPlay();
  547. }
  548. setTimeout(() => {
  549. data.animationState = 'end';
  550. }, 800);
  551. }
  552. };
  553. // 上一个知识点, 下一个知识点
  554. const handlePreAndNext = async (type: string) => {
  555. // 这里新加了ppt 逻辑所以需要
  556. if (data.coursewareType === 'PPT') {
  557. if (type === 'up' && pptData.activeLen > 0) {
  558. pptData.pptEl?.handleChangeSlide('prev');
  559. return;
  560. } else if (
  561. type === 'down' &&
  562. (pptData.activeLen < pptData.slidesLen - 1 || !pptData.isAnimationed)
  563. ) {
  564. pptData.pptEl?.handleChangeSlide('next');
  565. return;
  566. }
  567. }
  568. // 层级关系:单元〉章节〉知识点〉课件资源
  569. if (type === 'up') {
  570. // 判断上面是否还有章节
  571. if (popupData.activeIndex > 0) {
  572. handleSwipeChange(popupData.activeIndex - 1);
  573. return;
  574. }
  575. // 获取当前是哪个章节
  576. let detailIndex = data.courseDetails.findIndex(
  577. (item: any) => item.id == activeData.lessonCoursewareDetailId
  578. );
  579. const detailItem = data.courseDetails[detailIndex]?.knowledgeList || [];
  580. let lessonIndex = detailItem.findIndex(
  581. (item: any) => item.id == data.id
  582. );
  583. let lessonStatus = false; // 当前章节上面是否有内容
  584. let lessonCoursewareDetailId = '';
  585. let coursewareDetailKnowledgeId = '';
  586. let coursewareDetailKnowledgeName = '';
  587. let coursewareItem = {} as any;
  588. while (lessonIndex >= 0) {
  589. lessonIndex--;
  590. if (lessonIndex >= 0) {
  591. if (detailItem[lessonIndex].containMaterial) {
  592. lessonStatus = true;
  593. lessonCoursewareDetailId =
  594. detailItem[lessonIndex].lessonCoursewareDetailId;
  595. coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
  596. coursewareDetailKnowledgeName = detailItem[lessonIndex].name;
  597. coursewareItem = detailItem[lessonIndex];
  598. }
  599. }
  600. if (lessonStatus) {
  601. break;
  602. }
  603. }
  604. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  605. if (lessonStatus) {
  606. temporaryData.zjId = coursewareDetailKnowledgeId;
  607. temporaryData.dyId = lessonCoursewareDetailId;
  608. changeCourseWareItem(
  609. {
  610. ...coursewareItem,
  611. itemActive: coursewareDetailKnowledgeId
  612. },
  613. type
  614. );
  615. return;
  616. }
  617. let prevLessonStatus = false;
  618. while (detailIndex >= 0) {
  619. detailIndex--;
  620. const tempDetail =
  621. data.courseDetails[detailIndex]?.knowledgeList || [];
  622. let tempLessonLength = tempDetail.length;
  623. while (tempLessonLength > 0) {
  624. if (tempDetail[tempLessonLength - 1].containMaterial) {
  625. prevLessonStatus = true;
  626. lessonCoursewareDetailId =
  627. tempDetail[tempLessonLength - 1].lessonCoursewareDetailId;
  628. coursewareDetailKnowledgeId = tempDetail[tempLessonLength - 1].id;
  629. coursewareDetailKnowledgeName =
  630. tempDetail[tempLessonLength - 1].name;
  631. coursewareItem = tempDetail[tempLessonLength - 1];
  632. }
  633. tempLessonLength--;
  634. if (prevLessonStatus) {
  635. break;
  636. }
  637. }
  638. if (prevLessonStatus) {
  639. break;
  640. }
  641. }
  642. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  643. if (prevLessonStatus) {
  644. temporaryData.zjId = coursewareDetailKnowledgeId;
  645. temporaryData.dyId = lessonCoursewareDetailId;
  646. // activeData.coursewareDetailKnowledgeId = coursewareDetailKnowledgeId;
  647. changeCourseWareItem(
  648. {
  649. ...coursewareItem,
  650. itemActive: coursewareDetailKnowledgeId
  651. },
  652. type
  653. );
  654. return;
  655. }
  656. } else {
  657. if (popupData.activeIndex < data.itemList.length - 1) {
  658. handleSwipeChange(popupData.activeIndex + 1);
  659. return;
  660. }
  661. // 获取当前是哪个单元
  662. let detailIndex = data.courseDetails.findIndex(
  663. (item: any) => item.id == activeData.lessonCoursewareDetailId
  664. );
  665. // 当前章节列表
  666. const detailItem = data.courseDetails[detailIndex]?.knowledgeList || [];
  667. // 获取当前是哪个章节
  668. let lessonIndex = detailItem.findIndex(
  669. (item: any) => item.id == data.id
  670. );
  671. let lessonStatus = false; // 当前章节下面是否有内容
  672. let lessonCoursewareDetailId = '';
  673. let coursewareDetailKnowledgeId = '';
  674. let coursewareDetailKnowledgeName = '';
  675. let coursewareItem = {} as any;
  676. while (lessonIndex < detailItem.length - 1) {
  677. lessonIndex++;
  678. if (lessonIndex >= 0) {
  679. if (detailItem[lessonIndex].containMaterial) {
  680. lessonStatus = true;
  681. lessonCoursewareDetailId =
  682. detailItem[lessonIndex].lessonCoursewareDetailId;
  683. coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
  684. coursewareDetailKnowledgeName = detailItem[lessonIndex].name;
  685. coursewareItem = detailItem[lessonIndex];
  686. }
  687. }
  688. if (lessonStatus) {
  689. break;
  690. }
  691. }
  692. // 判断当前章节下面课程是否有内容,否则往下一个章节走
  693. if (lessonStatus) {
  694. temporaryData.zjId = coursewareDetailKnowledgeId;
  695. temporaryData.dyId = lessonCoursewareDetailId;
  696. changeCourseWareItem(
  697. {
  698. ...coursewareItem,
  699. itemActive: coursewareDetailKnowledgeId
  700. },
  701. type
  702. );
  703. return;
  704. }
  705. let nextLessonStatus = false;
  706. while (detailIndex <= data.courseDetails.length - 1) {
  707. detailIndex++;
  708. const tempDetail =
  709. data.courseDetails[detailIndex]?.knowledgeList || [];
  710. let tempLessonLength = 0;
  711. while (tempLessonLength <= tempDetail.length - 1) {
  712. if (tempDetail[tempLessonLength].containMaterial) {
  713. nextLessonStatus = true;
  714. lessonCoursewareDetailId =
  715. tempDetail[tempLessonLength].lessonCoursewareDetailId;
  716. coursewareDetailKnowledgeId = tempDetail[tempLessonLength].id;
  717. coursewareDetailKnowledgeName = tempDetail[tempLessonLength].name;
  718. coursewareItem = tempDetail[tempLessonLength];
  719. }
  720. tempLessonLength++;
  721. if (nextLessonStatus) {
  722. break;
  723. }
  724. }
  725. if (nextLessonStatus) {
  726. break;
  727. }
  728. }
  729. // 判断当前章节下面课程是否有内容,否则往下一个单元走
  730. if (nextLessonStatus) {
  731. temporaryData.zjId = coursewareDetailKnowledgeId;
  732. temporaryData.dyId = lessonCoursewareDetailId;
  733. changeCourseWareItem(
  734. {
  735. ...coursewareItem,
  736. itemActive: coursewareDetailKnowledgeId
  737. },
  738. type
  739. );
  740. return;
  741. }
  742. }
  743. };
  744. /** 弹窗关闭 */
  745. const handleClosePopup = () => {
  746. // setModelOpen();
  747. };
  748. // 是否允许上一页
  749. const isUpArrow = computed(() => {
  750. if (data.coursewareType === 'PPT') {
  751. if (pptData.disable) return false;
  752. if (pptData.activeLen > 0) {
  753. return true;
  754. }
  755. }
  756. /**
  757. * 1,判断当前课程中是否处在第一个资源;
  758. * 2,判断当前课程是否在当前章节的第一个;
  759. * 3,判断当前章节,当前课程上面还没有其它课程,是否有资源;
  760. * 4,判断当前章节上面还没有其它章节;
  761. * 5,判断上面章节里面课程是否有资源;
  762. */
  763. if (popupData.activeIndex > 0) {
  764. return true;
  765. }
  766. // 获取当前是哪个章节
  767. let detailIndex = data.courseDetails.findIndex(
  768. (item: any) => item.id == activeData.lessonCoursewareDetailId
  769. );
  770. const detailItem = data.courseDetails[detailIndex]?.knowledgeList || [];
  771. let lessonIndex = detailItem.findIndex((item: any) => item.id == data.id);
  772. // 说明已经是第一单元,第一课
  773. if (detailIndex <= 0 && lessonIndex <= 0) {
  774. return false;
  775. }
  776. let lessonStatus = false; // 当前章节上面是否有内容
  777. while (lessonIndex >= 0) {
  778. lessonIndex--;
  779. if (lessonIndex >= 0) {
  780. if (detailItem[lessonIndex].containMaterial) {
  781. lessonStatus = true;
  782. }
  783. }
  784. }
  785. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  786. if (lessonStatus) {
  787. return true;
  788. }
  789. // 已经是第一个章节了
  790. if (detailIndex <= 0) {
  791. return false;
  792. }
  793. let prevLessonStatus = false;
  794. while (detailIndex >= 0) {
  795. detailIndex--;
  796. const tempDetail = data.courseDetails[detailIndex]?.knowledgeList || [];
  797. let tempLessonLength = tempDetail.length;
  798. while (tempLessonLength > 0) {
  799. if (tempDetail[tempLessonLength - 1].containMaterial) {
  800. prevLessonStatus = true;
  801. }
  802. tempLessonLength--;
  803. }
  804. if (prevLessonStatus) {
  805. return true;
  806. }
  807. }
  808. return false;
  809. });
  810. // 是否允许下一页
  811. const isDownArrow = computed(() => {
  812. if (data.coursewareType === 'PPT') {
  813. if (pptData.disable) return false;
  814. if (
  815. pptData.activeLen < pptData.slidesLen - 1 ||
  816. !pptData.isAnimationed
  817. ) {
  818. return true;
  819. }
  820. }
  821. if (popupData.activeIndex < data.itemList.length - 1) {
  822. return true;
  823. }
  824. // 获取当前是哪个章节
  825. let detailIndex = data.courseDetails.findIndex(
  826. (item: any) => item.id == activeData.lessonCoursewareDetailId
  827. );
  828. const detailItem = data.courseDetails[detailIndex]?.knowledgeList || [];
  829. let lessonIndex = detailItem.findIndex((item: any) => item.id == data.id);
  830. // 说明已经是最后-单元,最后一课
  831. if (
  832. detailIndex >= data.courseDetails.length - 1 &&
  833. lessonIndex >= detailItem.length - 1
  834. ) {
  835. return false;
  836. }
  837. let lessonStatus = false; // 当前章节下面是否有内容
  838. while (lessonIndex < detailItem.length - 1) {
  839. lessonIndex++;
  840. if (lessonIndex >= 0) {
  841. if (detailItem[lessonIndex].containMaterial) {
  842. lessonStatus = true;
  843. }
  844. }
  845. }
  846. // 判断当前章节下面课程是否有内容,否则往下一个章节走
  847. if (lessonStatus) {
  848. return true;
  849. }
  850. // 已经是最后一个章节了
  851. if (detailIndex >= data.courseDetails.length - 1) {
  852. return false;
  853. }
  854. let nextLessonStatus = false;
  855. while (detailIndex < data.courseDetails.length - 1) {
  856. detailIndex++;
  857. const tempDetail = data.courseDetails[detailIndex]?.knowledgeList || [];
  858. let tempLessonLength = 0;
  859. while (tempLessonLength <= tempDetail.length - 1) {
  860. if (tempDetail[tempLessonLength].containMaterial) {
  861. nextLessonStatus = true;
  862. }
  863. tempLessonLength++;
  864. }
  865. if (nextLessonStatus) {
  866. return true;
  867. }
  868. }
  869. return false;
  870. });
  871. const activeVideoItem = computed(() => {
  872. const item = data.itemList[popupData.activeIndex];
  873. if (item && item.type && item.type.toLocaleUpperCase() === 'VIDEO') {
  874. return item;
  875. }
  876. return {};
  877. });
  878. // 加载新的章节里的课件
  879. const loadNewCourseware = async (item: any) => {
  880. if (item.id === data.kjId) {
  881. showSelectCourseware.value = false;
  882. return;
  883. }
  884. if (debounceSkip.value) return;
  885. debounceSkip.value = true;
  886. data.itemList = [];
  887. loadingClass.value = true;
  888. // activeData.lessonCoursewareDetailId = item.id;
  889. popupData.chapterOpen = false;
  890. showSelectCourseware.value = false;
  891. data.kjId = item.id;
  892. data.id = item.id;
  893. popupData.itemActive = '';
  894. popupData.itemName = '';
  895. await getDetail();
  896. popupData.activeIndex = 0;
  897. loadingClass.value = false;
  898. debounceSkip.value = false;
  899. };
  900. // 通过章节id,检测此章节有几个课件
  901. const changeCourseWareItem = (item: any, direction: string) => {
  902. if (item.coursewareNum) {
  903. changeLessonCourseWare.value = {
  904. ...item,
  905. tipMessage:
  906. direction === 'up'
  907. ? '当前是课件第一个资源,是否切换上一个课件?'
  908. : '当前是课件最后一个资源,是否切换下一个课件?'
  909. };
  910. showSelectCourseware.value = true;
  911. }
  912. };
  913. const gotoMember = () => {
  914. postMessage({
  915. api: 'openWebView',
  916. content: {
  917. url: `${
  918. location.origin.includes('localhost')
  919. ? 'https://test.lexiaoya.cn'
  920. : location.origin
  921. }/classroom-app/#/member-center`,
  922. orientation: 1
  923. }
  924. });
  925. };
  926. watch(
  927. () => pageVisibility.value,
  928. async (val: any) => {
  929. // 为了处理从没有会员,到购买会员返回时状态变化
  930. if (val === 'visible') {
  931. const userCash = await request.get('/edu-app/user/getUserInfo', {
  932. initRequest: true // 初始化接口
  933. });
  934. setLogin(userCash.data);
  935. if (state?.user?.data.vipMember) {
  936. showMember.value = true;
  937. } else {
  938. showMember.value = false;
  939. }
  940. }
  941. }
  942. );
  943. return () => (
  944. <div id="playContent" class={styles.playContent}>
  945. <div
  946. class={styles.coursewarePlay}
  947. style={{ width: parentContainer.width }}
  948. onClick={() => setModelOpen()}>
  949. {!loadingClass.value && (
  950. <div class={styles.wraps}>
  951. <div
  952. style={
  953. activeVideoItem.value.type &&
  954. data.animationState === 'end' &&
  955. data.videoState === 'play'
  956. ? {
  957. zIndex: 15,
  958. opacity: 1
  959. }
  960. : { opacity: 0, zIndex: -1 }
  961. }
  962. class={styles.itemDiv}>
  963. {activeVideoItem.value.paymentType !== 'FREE' &&
  964. !showMember.value && (
  965. <div class={styles.memberLock}>
  966. <img src={memberLockBg} class={styles.memberLockBg} />
  967. <img
  968. src={selectIcon}
  969. class={styles.selectIcon}
  970. onClick={gotoMember}
  971. />
  972. </div>
  973. )}
  974. <VideoItem
  975. ref={(el: any) => (data.videoItemRef = el)}
  976. item={activeVideoItem.value}
  977. showModel={activeData.model}
  978. onLoadedmetadata={(videoItem: any) => {
  979. if (data.itemList[popupData.activeIndex]) {
  980. data.itemList[popupData.activeIndex].videoEle = videoItem;
  981. }
  982. }}
  983. onClose={setModelOpen1}
  984. onCanplay={() => {
  985. data.videoState = 'play';
  986. }}
  987. onPause={() => {
  988. clearTimeout(activeData.timer);
  989. activeData.model = true;
  990. }}
  991. onEnded={() => {
  992. // const _index = popupData.activeIndex + 1
  993. // if (_index < data.itemList.length) {
  994. // handleSwipeChange(_index);
  995. // }
  996. }}
  997. />
  998. </div>
  999. {data.itemList.map((m: any, mIndex: number) => {
  1000. const isRender =
  1001. m.isRender || Math.abs(popupData.activeIndex - mIndex) < 2;
  1002. const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
  1003. if (isRender) {
  1004. m.isRender = true;
  1005. }
  1006. return isRender ? (
  1007. <div
  1008. key={'index' + mIndex}
  1009. class={[
  1010. styles.itemDiv,
  1011. popupData.activeIndex === mIndex && styles.itemActive,
  1012. activeData.isAnimation && styles.acitveAnimation,
  1013. Math.abs(popupData.activeIndex - mIndex) < 2
  1014. ? styles.show
  1015. : styles.hide
  1016. ]}
  1017. style={
  1018. mIndex < popupData.activeIndex
  1019. ? effects[effectIndex.value].prev
  1020. : mIndex > popupData.activeIndex
  1021. ? effects[effectIndex.value].next
  1022. : {}
  1023. }
  1024. onClick={(e: Event) => {
  1025. if (Date.now() - activeData.nowTime < 300) {
  1026. handleDbClick(m);
  1027. return;
  1028. }
  1029. activeData.nowTime = Date.now();
  1030. }}>
  1031. {m.paymentType !== 'FREE' && !showMember.value && (
  1032. <div class={styles.memberLock}>
  1033. <img src={memberLockBg} class={styles.memberLockBg} />
  1034. <img
  1035. src={selectIcon}
  1036. class={styles.selectIcon}
  1037. onClick={gotoMember}
  1038. />
  1039. </div>
  1040. )}
  1041. {m.type === 'IMG' && <img src={m.content} />}
  1042. {m.type === 'PPT' && (
  1043. <iframe
  1044. src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
  1045. m.content
  1046. )}`}
  1047. width="100%"
  1048. height="100%"
  1049. frameborder="1"></iframe>
  1050. )}
  1051. {m.type === 'VIDEO' && (
  1052. <img
  1053. src={m.coverImg}
  1054. onLoad={() => {
  1055. m.isprepare = true;
  1056. }}
  1057. />
  1058. )}
  1059. {m.type === 'SONG' && (
  1060. <AudioItem
  1061. item={m}
  1062. show={popupData.activeIndex === mIndex}
  1063. pageVisibility={pageVisibility.value}
  1064. showModel={activeData.model}
  1065. isEmtry={isEmtry}
  1066. onLoadedmetadata={(audioEle: any) => {
  1067. console.log(audioEle, 'audioEle', m);
  1068. m.audioEle = audioEle;
  1069. }}
  1070. onEnded={() => {
  1071. const _index = popupData.activeIndex + 1;
  1072. if (_index < data.itemList.length) {
  1073. handleSwipeChange(_index);
  1074. }
  1075. }}
  1076. onClose={() => {
  1077. clearTimeout(activeData.timer);
  1078. activeData.timer = setTimeout(() => {
  1079. activeData.model = false;
  1080. }, 4000);
  1081. }}
  1082. />
  1083. )}
  1084. {m.type === 'MUSIC' && (
  1085. <MusicScore
  1086. pageVisibility={pageVisibility.value}
  1087. show={popupData.activeIndex === mIndex}
  1088. activeModel={activeData.model}
  1089. data-vid={m.id}
  1090. music={m}
  1091. onSetIframe={(el: any) => {
  1092. m.iframeRef = el;
  1093. }}
  1094. />
  1095. )}
  1096. {m.type === 'VIDEO' && (
  1097. <Transition name="van-fade">
  1098. {data.videoState !== 'play' && (
  1099. <div class={styles.loadWrap}>
  1100. <Vue3Lottie
  1101. style={{ width: '100%', height: '100%' }}
  1102. animationData={playLoadData}></Vue3Lottie>
  1103. </div>
  1104. )}
  1105. </Transition>
  1106. )}
  1107. {/* 新增:RHYTHM:节奏练习,THEORY:乐理知识,MUSIC_WIKI:名曲鉴赏 INSTRUMENT:乐器 MUSICIAN:音乐家 资源类型 */}
  1108. {m.type === 'RHYTHM' && (
  1109. <TempoItem
  1110. key={mIndex}
  1111. class={styles.tempoPracticeGroup}
  1112. dataJson={m.dataJson}
  1113. show={popupData.activeIndex === mIndex}
  1114. pageVisibility={pageVisibility.value}
  1115. onSetIframe={(el: any) => {
  1116. m.iframeRef = el;
  1117. }}
  1118. />
  1119. )}
  1120. {m.type === 'THEORY' && <Theory id={m.bizId} />}
  1121. {/* {popupData.activeIndex === mIndex && ( */}
  1122. {m.type === 'MUSIC_WIKI' && (
  1123. <InstrumentInfo
  1124. type={'wiki'}
  1125. id={m.bizId}
  1126. show={popupData.activeIndex === mIndex}
  1127. ref={el => (m.domEle = el)}
  1128. />
  1129. )}
  1130. {m.type === 'INSTRUMENT' && (
  1131. <InstrumentInfo
  1132. type={'instrument'}
  1133. id={m.bizId}
  1134. show={popupData.activeIndex === mIndex}
  1135. ref={el => (m.domEle = el)}
  1136. />
  1137. )}
  1138. {m.type === 'MUSICIAN' && (
  1139. <InstrumentInfo
  1140. type={'musician'}
  1141. id={m.bizId}
  1142. show={popupData.activeIndex === mIndex}
  1143. ref={el => (m.domEle = el)}
  1144. />
  1145. )}
  1146. {m.type === 'LISTEN' && (
  1147. <ListenItem
  1148. pageVisibility={pageVisibility.value}
  1149. show={popupData.activeIndex === mIndex}
  1150. activeModel={activeData.model}
  1151. data-vid={m.id}
  1152. item={m}
  1153. onSetIframe={(el: any) => {
  1154. m.iframeRef = el;
  1155. }}
  1156. />
  1157. )}
  1158. {m.type === 'PPT_JSON' && (
  1159. <PptList
  1160. ref={el => {
  1161. pptData.pptEl = el;
  1162. }}
  1163. onInit={() => {
  1164. pptData.disable = true;
  1165. }}
  1166. onInitPPT={({ slidesLen, isAnimationed }) => {
  1167. pptData.disable = false;
  1168. pptData.slidesLen = slidesLen;
  1169. pptData.isAnimationed = isAnimationed;
  1170. pptData.activeLen = 0;
  1171. }}
  1172. onChangeSlideIndex={({ slideIndex, isAnimationed }) => {
  1173. pptData.activeLen = slideIndex;
  1174. pptData.isAnimationed = isAnimationed;
  1175. }}
  1176. pptData={m}
  1177. fromType={
  1178. route.query.tab == 'course' ? 'CLASS' : 'PLATFORM'
  1179. }></PptList>
  1180. )}
  1181. </div>
  1182. ) : (
  1183. <div
  1184. key={'index' + mIndex}
  1185. class={[
  1186. styles.itemDiv,
  1187. popupData.activeIndex === mIndex && styles.itemActive,
  1188. activeData.isAnimation && styles.acitveAnimation,
  1189. Math.abs(popupData.activeIndex - mIndex) < 2
  1190. ? styles.show
  1191. : styles.hide
  1192. ]}
  1193. style={
  1194. mIndex < popupData.activeIndex
  1195. ? effects[effectIndex.value].prev
  1196. : mIndex > popupData.activeIndex
  1197. ? effects[effectIndex.value].next
  1198. : {}
  1199. }></div>
  1200. );
  1201. })}
  1202. </div>
  1203. )}
  1204. <Transition name="right">
  1205. {activeData.model && (
  1206. <div
  1207. class={styles.rightFixedBtns}
  1208. onClick={(e: Event) => {
  1209. e.stopPropagation();
  1210. clearTimeout(activeData.timer);
  1211. }}>
  1212. <div
  1213. class={[styles.fullBtn, styles.point]}
  1214. onClick={() => (popupData.chapterOpen = true)}>
  1215. <img src={iconChange} />
  1216. <span>切换</span>
  1217. </div>
  1218. <div
  1219. class={[
  1220. styles.fullBtn,
  1221. styles.point,
  1222. data.coursewareType === 'PPT' && data.allList.length <= 1
  1223. ? styles.hideBtn
  1224. : ''
  1225. ]}
  1226. onClick={() => {
  1227. // 现在 如果当前是ppt,并且有多个课件的时候,就直接出现选择课件
  1228. // if (
  1229. // data.coursewareType === 'PPT' &&
  1230. // data.allList.length > 1
  1231. // ) {
  1232. // checkCourseware({}, 'same');
  1233. // } else {
  1234. popupData.open = true;
  1235. // }
  1236. }}>
  1237. <img src={iconMenu} />
  1238. <span>课件</span>
  1239. </div>
  1240. <div
  1241. class={[
  1242. styles.fullBtn,
  1243. !isUpArrow.value && styles.btnsDisabled
  1244. ]}
  1245. onClick={() => handlePreAndNext('up')}>
  1246. <img src={iconUp} />
  1247. <span style={{ textAlign: 'center' }}>上一个</span>
  1248. </div>
  1249. <div
  1250. class={[
  1251. styles.fullBtn,
  1252. !isDownArrow.value && styles.btnsDisabled
  1253. ]}
  1254. onClick={() => handlePreAndNext('down')}>
  1255. <span style={{ textAlign: 'center' }}>下一个</span>
  1256. <img src={iconDown} />
  1257. </div>
  1258. </div>
  1259. )}
  1260. </Transition>
  1261. </div>
  1262. <div
  1263. style={{ transform: activeData.model ? '' : 'translateY(-100%)' }}
  1264. class={styles.headerContainer}
  1265. ref={headeRef}>
  1266. <div class={styles.backBtn} onClick={() => goback()}>
  1267. <Icon name={iconBack} />
  1268. 返回
  1269. </div>
  1270. <div class={styles.menu}>{popupData.itemName}</div>
  1271. </div>
  1272. {/* 课件列表 */}
  1273. <Popup
  1274. class={styles.popup}
  1275. style={{ background: 'rgba(0,0,0, 0.75)' }}
  1276. overlayClass={styles.overlayClass}
  1277. position="right"
  1278. round
  1279. v-model:show={popupData.open}
  1280. onClose={handleClosePopup}>
  1281. <Points
  1282. // allList={data.allList}
  1283. currentCourse={data.currentCourse}
  1284. itemActive={popupData.itemActive}
  1285. itemName={popupData.itemPointName}
  1286. kjId={data.kjId}
  1287. zsdId={data.zsdId}
  1288. popShow={popupData.open}
  1289. onHandleSelect={(res: any) => {
  1290. popupData.open = false;
  1291. toggleMaterial(res.itemActive);
  1292. }}
  1293. />
  1294. </Popup>
  1295. {/* 知识点列表 */}
  1296. <Popup
  1297. class={styles.popup}
  1298. style={{ background: 'rgba(0,0,0, 0.75)' }}
  1299. overlayClass={styles.overlayClass}
  1300. position="right"
  1301. round
  1302. v-model:show={popupData.chapterOpen}
  1303. onClose={handleClosePopup}>
  1304. <Chapter
  1305. detail={data.courseDetails}
  1306. itemActive={data.id as any}
  1307. active={activeData.lessonCoursewareDetailId as any}
  1308. popShow={popupData.chapterOpen}
  1309. onHandleSelect={async (item: any) => {
  1310. temporaryData.dyId = item.tabActive;
  1311. temporaryData.zjId = item.itemActive;
  1312. popupData.itemPointName = item.itemName;
  1313. if (item.coursewareNum) {
  1314. loadNewCourseware({
  1315. id: item.itemActive
  1316. });
  1317. }
  1318. }}
  1319. />
  1320. </Popup>
  1321. {showSelectCourseware.value && (
  1322. <SelectCoursewareTip
  1323. message={changeLessonCourseWare.value.tipMessage}
  1324. onClose={() => (showSelectCourseware.value = false)}
  1325. onConfirm={() => loadNewCourseware(changeLessonCourseWare.value)}
  1326. />
  1327. )}
  1328. </div>
  1329. );
  1330. }
  1331. });