index.tsx 43 KB

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