index.tsx 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029
  1. import { closeToast, 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 VideoItem from './component/video-item';
  41. import Chapter from './component/chapter';
  42. import {
  43. api_classLessonCoursewareDetail,
  44. api_lessonCoursewareDetail
  45. } from '../courseware-list/api';
  46. import detail from '../information/help-center/detail';
  47. import { state } from '@/state';
  48. export default defineComponent({
  49. name: 'CoursewarePlay',
  50. setup() {
  51. const pageVisibility = usePageVisibility();
  52. const lastTimeKey = 'lastTime' + (state?.user?.data?.phone ?? '');
  53. /** 设置播放容器 16:9 */
  54. const parentContainer = reactive({
  55. width: '100vw'
  56. });
  57. const setContainer = () => {
  58. let min = Math.min(screen.width, screen.height);
  59. let max = Math.max(screen.width, screen.height);
  60. let width = min * (16 / 9);
  61. if (width > max) {
  62. parentContainer.width = '100vw';
  63. return;
  64. } else {
  65. parentContainer.width = width + 'px';
  66. }
  67. };
  68. const handleInit = (type = 0) => {
  69. //设置容器16:9
  70. setContainer();
  71. // 横屏
  72. // postMessage(
  73. // {
  74. // api: 'setRequestedOrientation',
  75. // content: {
  76. // orientation: type
  77. // }
  78. // },
  79. // () => {
  80. // console.log(234);
  81. // }
  82. // );
  83. // 头,包括返回箭头
  84. // postMessage({
  85. // api: 'setTitleBarVisibility',
  86. // content: {
  87. // status: type
  88. // }
  89. // })
  90. // 安卓的状态栏
  91. postMessage({
  92. api: 'setStatusBarVisibility',
  93. content: {
  94. isVisibility: type
  95. }
  96. });
  97. // 进入页面设置常量
  98. postMessage({
  99. api: 'keepScreenLongLight',
  100. content: {
  101. isOpenLight: type ? true : false
  102. }
  103. });
  104. };
  105. handleInit();
  106. onUnmounted(() => {
  107. handleInit(1);
  108. window.removeEventListener('message', iframeHandle);
  109. });
  110. const getCourseDetail = async () => {
  111. try {
  112. if (route.query.tab == 'course') {
  113. const res = await api_classLessonCoursewareDetail({
  114. id: activeData.courseId as any,
  115. subjectId: activeData.subjectId
  116. });
  117. if (res?.code == 200 && Array.isArray(res?.data?.lessonList)) {
  118. data.courseDetails = res.data.lessonList || [];
  119. console.log('🚀 ~ data.details course:', data.courseDetails);
  120. }
  121. } else {
  122. const res = await api_lessonCoursewareDetail({
  123. id: route.query.lessonCoursewareId as any,
  124. subjectId: activeData.subjectId
  125. });
  126. if (res?.code == 200 && Array.isArray(res?.data?.lessonList)) {
  127. data.courseDetails = res.data.lessonList || [];
  128. }
  129. }
  130. console.log(data.courseDetails, 'data.courseDetails');
  131. } catch {
  132. //
  133. }
  134. };
  135. const route = useRoute();
  136. const headeRef = ref();
  137. const loadingClass = ref(false); // 重新加载课件
  138. const data = reactive({
  139. knowledgePointList: [] as any,
  140. courseDetails: [] as any,
  141. itemList: [] as any,
  142. videoRefs: {} as any[],
  143. videoState: 'init' as 'init' | 'play',
  144. videoItemRef: null as any,
  145. animationState: 'start' as 'start' | 'end'
  146. });
  147. const activeData = reactive({
  148. isAutoPlay: true, // 是否自动播放
  149. subjectId: route.query.subjectId,
  150. lessonCoursewareId: route.query.lessonCoursewareId,
  151. lessonCoursewareDetailId: route.query.lessonCoursewareDetailId,
  152. coursewareDetailKnowledgeId: route.query.id,
  153. courseId: route.query.courseId, // 我的课程专用编号
  154. nowTime: 0,
  155. model: true, // 遮罩
  156. isAnimation: true, // 是否动画
  157. videoBtns: true, // 视频
  158. currentTime: 0,
  159. duration: 0,
  160. timer: null as any,
  161. item: null as any
  162. });
  163. const getDetail = async () => {
  164. let courseList: any[] = [];
  165. if (route.query.tab == 'course') {
  166. const res = await api_classLessonCoursewareQuery({
  167. coursewareDetailKnowledgeId: activeData.coursewareDetailKnowledgeId,
  168. subjectId: activeData.subjectId,
  169. page: 1,
  170. rows: -1
  171. });
  172. if (res?.code === 200 && Array.isArray(res.data.rows)) {
  173. const tempRows = res.data.rows || [];
  174. tempRows.forEach((item: any) => {
  175. courseList.push({
  176. content: item.content,
  177. coverImg: item.coverImg,
  178. id: item.id,
  179. materialId: item.materialId,
  180. name: item.materialName,
  181. relOrder: 0,
  182. sourceFrom: item.source,
  183. type: item.materialType
  184. });
  185. });
  186. }
  187. } else {
  188. const res = await api_lessonCoursewareKnowledgeDetailDetail({
  189. lessonCoursewareKnowledgeDetailId:
  190. activeData.coursewareDetailKnowledgeId,
  191. subjectId: activeData.subjectId
  192. });
  193. if (res?.code === 200 && Array.isArray(res.data)) {
  194. courseList = res.data || [];
  195. }
  196. }
  197. // 课程
  198. if (courseList.length > 0) {
  199. data.knowledgePointList = courseList.map((item: any) => {
  200. return {
  201. ...item,
  202. url:
  203. item.type === 'SONG'
  204. ? 'https://oss.dayaedu.com/ktqy/1698420034679a22d3f7a.png'
  205. : item.type === 'PPT' ? 'https://oss.dayaedu.com/ktqy/12/1701931810284.png'
  206. : item.coverImg
  207. };
  208. });
  209. }
  210. data.itemList = data.knowledgePointList.map((m: any, index: number) => {
  211. if (!popupData.itemActive) {
  212. popupData.itemActive = m.id;
  213. popupData.itemName = m.name;
  214. }
  215. return {
  216. ...m,
  217. iframeRef: null,
  218. videoEle: null,
  219. autoPlay: false, //加载完成是否自动播放
  220. isprepare: false, // 视频是否加载完成
  221. isRender: false // 是否渲染了
  222. };
  223. });
  224. setTimeout(() => {
  225. data.animationState = 'end';
  226. }, 500);
  227. };
  228. // ifram事件处理
  229. const iframeHandle = (ev: MessageEvent) => {
  230. if (ev.data?.api === 'headerTogge') {
  231. activeData.model =
  232. ev.data.show || (ev.data.playState == 'play' ? false : true);
  233. }
  234. if (ev.data?.api === 'api_fingerPreView') {
  235. clearInterval(activeData.timer);
  236. activeData.model = !ev.data.state;
  237. }
  238. };
  239. onMounted(() => {
  240. postMessage({
  241. api: 'courseLoading',
  242. content: {
  243. show: false,
  244. type: 'fullscreen'
  245. }
  246. });
  247. getDetail();
  248. getCourseDetail();
  249. window.addEventListener('message', iframeHandle);
  250. });
  251. const playRef = ref();
  252. // 返回
  253. const goback = () => {
  254. try {
  255. playRef.value?.handleOut();
  256. } catch (error) {}
  257. postMessage({ api: 'goBack' });
  258. // router.back()
  259. };
  260. const popupData = reactive({
  261. open: false,
  262. activeIndex: 0,
  263. itemActive: '',
  264. itemName: '',
  265. itemPointName: route.query.name as any,
  266. chapterOpen: false
  267. });
  268. // 切换素材
  269. const toggleMaterial = (itemActive: any) => {
  270. const index = data.itemList.findIndex((n: any) => n.id == itemActive);
  271. if (index > -1) {
  272. handleSwipeChange(index);
  273. }
  274. };
  275. /** 延迟收起模态框 */
  276. const setModelOpen = () => {
  277. clearTimeout(activeData.timer);
  278. closeToast();
  279. activeData.model = !activeData.model;
  280. activeData.timer = setTimeout(() => {
  281. activeData.model = false;
  282. }, 4000);
  283. };
  284. const setModelOpen1 = () => {
  285. clearTimeout(activeData.timer);
  286. closeToast();
  287. activeData.model = true;
  288. activeData.timer = setTimeout(() => {
  289. activeData.model = false;
  290. }, 4000);
  291. };
  292. // 双击
  293. const handleDbClick = (item: any) => {
  294. if (item && ['VIDEO'].includes(item.type)) {
  295. console.log('双击');
  296. }
  297. };
  298. const effectIndex = ref(3);
  299. const effects = [
  300. {
  301. prev: {
  302. transform: 'translate3d(0, 0, -800px) rotateX(180deg)'
  303. },
  304. next: {
  305. transform: 'translate3d(0, 0, -800px) rotateX(-180deg)'
  306. }
  307. },
  308. {
  309. prev: {
  310. transform: 'translate3d(-100%, 0, -800px)'
  311. },
  312. next: {
  313. transform: 'translate3d(100%, 0, -800px)'
  314. }
  315. },
  316. {
  317. prev: {
  318. transform: 'translate3d(-50%, 0, -800px) rotateY(80deg)'
  319. },
  320. next: {
  321. transform: 'translate3d(50%, 0, -800px) rotateY(-80deg)'
  322. }
  323. },
  324. {
  325. prev: {
  326. transform: 'translate3d(-100%, 0, -800px) rotateY(-120deg)',
  327. opacity: 0
  328. },
  329. next: {
  330. transform: 'translate3d(100%, 0, -800px) rotateY(120deg)',
  331. opacity: 0
  332. }
  333. },
  334. // 风车4
  335. {
  336. prev: {
  337. transform: 'translate3d(-50%, 50%, -800px) rotateZ(-14deg)',
  338. opacity: 0
  339. },
  340. next: {
  341. transform: 'translate3d(50%, 50%, -800px) rotateZ(14deg)',
  342. opacity: 0
  343. }
  344. },
  345. // 翻页5
  346. {
  347. prev: {
  348. transform: 'translateZ(-800px) rotate3d(0, -1, 0, 90deg)',
  349. opacity: 0
  350. },
  351. next: {
  352. transform: 'translateZ(-800px) rotate3d(0, 1, 0, 90deg)',
  353. opacity: 0
  354. },
  355. current: { transitionDelay: '700ms' }
  356. }
  357. ];
  358. const handleStop = () => {
  359. data.videoItemRef.pause();
  360. };
  361. const acitveTimer = ref();
  362. // 轮播切换
  363. const handleSwipeChange = (index: number) => {
  364. // 如果是当前正在播放 或者是视频最后一个
  365. if (popupData.activeIndex == index) return;
  366. data.animationState = 'start';
  367. data.videoState = 'init';
  368. handleStop();
  369. clearTimeout(acitveTimer.value);
  370. activeData.model = true;
  371. const item = data.itemList[index];
  372. popupData.activeIndex = index;
  373. popupData.itemActive = item.id;
  374. popupData.itemName = item.name;
  375. if (item.type == 'MUSIC') {
  376. activeData.model = true;
  377. } else if (item.type == 'VIDEO') {
  378. if (item.error) {
  379. data.videoRefs[index]?.onPlay();
  380. }
  381. setTimeout(() => {
  382. data.animationState = 'end';
  383. }, 800);
  384. }
  385. };
  386. // 上一个知识点, 下一个知识点
  387. const handlePreAndNext = async (type: string) => {
  388. if (type === 'up') {
  389. // 判断上面是否还有章节
  390. if (popupData.activeIndex > 0) {
  391. handleSwipeChange(popupData.activeIndex - 1);
  392. return;
  393. }
  394. // 获取当前是哪个章节
  395. let detailIndex = data.courseDetails.findIndex(
  396. (item: any) => item.id == activeData.lessonCoursewareDetailId
  397. );
  398. const detailItem = data.courseDetails[detailIndex]?.knowledgeList || [];
  399. let lessonIndex = detailItem.findIndex(
  400. (item: any) => item.id == activeData.coursewareDetailKnowledgeId
  401. );
  402. let lessonStatus = false; // 当前章节上面是否有内容
  403. let lessonCoursewareDetailId = '';
  404. let coursewareDetailKnowledgeId = '';
  405. let coursewareDetailKnowledgeName = '';
  406. while (lessonIndex >= 0) {
  407. lessonIndex--;
  408. if (lessonIndex >= 0) {
  409. if (detailItem[lessonIndex].containMaterial) {
  410. lessonStatus = true;
  411. lessonCoursewareDetailId =
  412. detailItem[lessonIndex].lessonCoursewareDetailId;
  413. coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
  414. coursewareDetailKnowledgeName = detailItem[lessonIndex].name;
  415. }
  416. }
  417. if (lessonStatus) {
  418. break;
  419. }
  420. }
  421. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  422. if (lessonStatus) {
  423. loadingClass.value = true;
  424. activeData.coursewareDetailKnowledgeId = coursewareDetailKnowledgeId;
  425. activeData.lessonCoursewareDetailId = lessonCoursewareDetailId;
  426. await getDetail();
  427. popupData.activeIndex = data.itemList.length - 1 || 0;
  428. popupData.itemActive =
  429. data.knowledgePointList[data.itemList.length - 1]?.id ||
  430. data.knowledgePointList[0]?.id;
  431. popupData.itemPointName = coursewareDetailKnowledgeName;
  432. popupData.itemName =
  433. data.knowledgePointList[data.itemList.length - 1]?.name ||
  434. data.knowledgePointList[0]?.name;
  435. localStorage.setItem(lastTimeKey, coursewareDetailKnowledgeId);
  436. popupData.chapterOpen = false;
  437. loadingClass.value = false;
  438. return;
  439. }
  440. let prevLessonStatus = false;
  441. while (detailIndex >= 0) {
  442. detailIndex--;
  443. const tempDetail =
  444. data.courseDetails[detailIndex]?.knowledgeList || [];
  445. let tempLessonLength = tempDetail.length;
  446. while (tempLessonLength > 0) {
  447. if (tempDetail[tempLessonLength - 1].containMaterial) {
  448. prevLessonStatus = true;
  449. lessonCoursewareDetailId =
  450. tempDetail[tempLessonLength - 1].lessonCoursewareDetailId;
  451. coursewareDetailKnowledgeId = tempDetail[tempLessonLength - 1].id;
  452. coursewareDetailKnowledgeName =
  453. tempDetail[tempLessonLength - 1].name;
  454. }
  455. tempLessonLength--;
  456. if (prevLessonStatus) {
  457. break;
  458. }
  459. }
  460. if (prevLessonStatus) {
  461. break;
  462. }
  463. }
  464. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  465. if (prevLessonStatus) {
  466. loadingClass.value = true;
  467. activeData.coursewareDetailKnowledgeId = coursewareDetailKnowledgeId;
  468. activeData.lessonCoursewareDetailId = lessonCoursewareDetailId;
  469. await getDetail();
  470. popupData.activeIndex = data.itemList.length - 1 || 0;
  471. popupData.itemActive =
  472. data.knowledgePointList[data.itemList.length - 1]?.id ||
  473. data.knowledgePointList[0]?.id;
  474. localStorage.setItem(lastTimeKey, coursewareDetailKnowledgeId);
  475. popupData.itemPointName = coursewareDetailKnowledgeName;
  476. popupData.itemName =
  477. data.knowledgePointList[data.itemList.length - 1]?.name ||
  478. data.knowledgePointList[0]?.name;
  479. popupData.chapterOpen = false;
  480. loadingClass.value = false;
  481. return;
  482. }
  483. } else {
  484. if (popupData.activeIndex < data.itemList.length - 1) {
  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. while (lessonIndex < detailItem.length - 1) {
  501. lessonIndex++;
  502. if (lessonIndex >= 0) {
  503. if (detailItem[lessonIndex].containMaterial) {
  504. lessonStatus = true;
  505. lessonCoursewareDetailId =
  506. detailItem[lessonIndex].lessonCoursewareDetailId;
  507. coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
  508. coursewareDetailKnowledgeName = detailItem[lessonIndex].name;
  509. }
  510. }
  511. if (lessonStatus) {
  512. break;
  513. }
  514. }
  515. // 判断当前章节下面课程是否有内容,否则往下一个章节走
  516. if (lessonStatus) {
  517. loadingClass.value = true;
  518. activeData.coursewareDetailKnowledgeId = coursewareDetailKnowledgeId;
  519. activeData.lessonCoursewareDetailId = lessonCoursewareDetailId;
  520. await getDetail();
  521. popupData.activeIndex = 0;
  522. popupData.itemActive = data.knowledgePointList[0].id;
  523. popupData.itemName = data.knowledgePointList[0].name;
  524. localStorage.setItem(lastTimeKey, coursewareDetailKnowledgeId);
  525. popupData.itemPointName = coursewareDetailKnowledgeName;
  526. popupData.chapterOpen = false;
  527. loadingClass.value = false;
  528. return;
  529. }
  530. let nextLessonStatus = false;
  531. while (detailIndex <= data.courseDetails.length - 1) {
  532. detailIndex++;
  533. const tempDetail =
  534. data.courseDetails[detailIndex]?.knowledgeList || [];
  535. let tempLessonLength = 0;
  536. while (tempLessonLength <= tempDetail.length - 1) {
  537. if (tempDetail[tempLessonLength].containMaterial) {
  538. nextLessonStatus = true;
  539. lessonCoursewareDetailId =
  540. tempDetail[tempLessonLength].lessonCoursewareDetailId;
  541. coursewareDetailKnowledgeId = tempDetail[tempLessonLength].id;
  542. coursewareDetailKnowledgeName = tempDetail[tempLessonLength].name;
  543. }
  544. tempLessonLength++;
  545. if (nextLessonStatus) {
  546. break;
  547. }
  548. }
  549. if (nextLessonStatus) {
  550. break;
  551. }
  552. }
  553. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  554. if (nextLessonStatus) {
  555. loadingClass.value = true;
  556. activeData.coursewareDetailKnowledgeId = coursewareDetailKnowledgeId;
  557. activeData.lessonCoursewareDetailId = lessonCoursewareDetailId;
  558. await getDetail();
  559. popupData.activeIndex = 0;
  560. popupData.itemActive = data.knowledgePointList[0].id;
  561. localStorage.setItem(lastTimeKey, coursewareDetailKnowledgeId);
  562. popupData.itemName = data.knowledgePointList[0].name;
  563. popupData.itemPointName = coursewareDetailKnowledgeName;
  564. popupData.chapterOpen = false;
  565. loadingClass.value = false;
  566. return;
  567. }
  568. }
  569. };
  570. /** 弹窗关闭 */
  571. const handleClosePopup = () => {
  572. // setModelOpen();
  573. };
  574. // popupData.activeIndex == 0 && styles.btnsDisabled
  575. // popupData.activeIndex == data.itemList.length - 1
  576. // 是否允许上一页
  577. const isUpArrow = computed(() => {
  578. /**
  579. * 1,判断当前课程中是否处在第一个资源;
  580. * 2,判断当前课程是否在当前章节的第一个;
  581. * 3,判断当前章节,当前课程上面还没有其它课程,是否有资源;
  582. * 4,判断当前章节上面还没有其它章节;
  583. * 5,判断上面章节里面课程是否有资源;
  584. */
  585. if (popupData.activeIndex > 0) {
  586. return true;
  587. }
  588. // 获取当前是哪个章节
  589. let detailIndex = data.courseDetails.findIndex(
  590. (item: any) => item.id == activeData.lessonCoursewareDetailId
  591. );
  592. const detailItem = data.courseDetails[detailIndex]?.knowledgeList || [];
  593. let lessonIndex = detailItem.findIndex(
  594. (item: any) => item.id == activeData.coursewareDetailKnowledgeId
  595. );
  596. // 说明已经是第一单元,第一课
  597. if (detailIndex <= 0 && lessonIndex <= 0) {
  598. return false;
  599. }
  600. let lessonStatus = false; // 当前章节上面是否有内容
  601. while (lessonIndex >= 0) {
  602. lessonIndex--;
  603. if (lessonIndex >= 0) {
  604. if (detailItem[lessonIndex].containMaterial) {
  605. lessonStatus = true;
  606. }
  607. }
  608. }
  609. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  610. if (lessonStatus) {
  611. return true;
  612. }
  613. // 已经是第一个章节了
  614. if (detailIndex <= 0) {
  615. return false;
  616. }
  617. let prevLessonStatus = false;
  618. while (detailIndex >= 0) {
  619. detailIndex--;
  620. const tempDetail = data.courseDetails[detailIndex]?.knowledgeList || [];
  621. let tempLessonLength = tempDetail.length;
  622. while (tempLessonLength > 0) {
  623. if (tempDetail[tempLessonLength - 1].containMaterial) {
  624. prevLessonStatus = true;
  625. }
  626. tempLessonLength--;
  627. }
  628. if (prevLessonStatus) {
  629. return true;
  630. }
  631. }
  632. return false;
  633. });
  634. // 是否允许下一页
  635. const isDownArrow = computed(() => {
  636. if (popupData.activeIndex < data.itemList.length - 1) {
  637. return true;
  638. }
  639. // 获取当前是哪个章节
  640. let detailIndex = data.courseDetails.findIndex(
  641. (item: any) => item.id == activeData.lessonCoursewareDetailId
  642. );
  643. const detailItem = data.courseDetails[detailIndex]?.knowledgeList || [];
  644. let lessonIndex = detailItem.findIndex(
  645. (item: any) => item.id == activeData.coursewareDetailKnowledgeId
  646. );
  647. // 说明已经是最后-单元,最后一课
  648. if (
  649. detailIndex >= data.courseDetails.length - 1 &&
  650. lessonIndex >= detailItem.length - 1
  651. ) {
  652. return false;
  653. }
  654. let lessonStatus = false; // 当前章节下面是否有内容
  655. while (lessonIndex < detailItem.length - 1) {
  656. lessonIndex++;
  657. if (lessonIndex >= 0) {
  658. if (detailItem[lessonIndex].containMaterial) {
  659. lessonStatus = true;
  660. }
  661. }
  662. }
  663. // 判断当前章节下面课程是否有内容,否则往下一个章节走
  664. if (lessonStatus) {
  665. return true;
  666. }
  667. // 已经是最后一个章节了
  668. if (detailIndex >= data.courseDetails.length - 1) {
  669. return false;
  670. }
  671. let nextLessonStatus = false;
  672. while (detailIndex < data.courseDetails.length - 1) {
  673. detailIndex++;
  674. const tempDetail = data.courseDetails[detailIndex]?.knowledgeList || [];
  675. let tempLessonLength = 0;
  676. while (tempLessonLength <= tempDetail.length - 1) {
  677. if (tempDetail[tempLessonLength].containMaterial) {
  678. nextLessonStatus = true;
  679. }
  680. tempLessonLength++;
  681. }
  682. if (nextLessonStatus) {
  683. return true;
  684. }
  685. }
  686. return false;
  687. });
  688. const activeVideoItem = computed(() => {
  689. const item = data.itemList[popupData.activeIndex];
  690. if (item && item.type && item.type.toLocaleUpperCase() === 'VIDEO') {
  691. return item;
  692. }
  693. return {};
  694. });
  695. return () => (
  696. <div id="playContent" class={styles.playContent}>
  697. <div
  698. class={styles.coursewarePlay}
  699. style={{ width: parentContainer.width }}
  700. onClick={() => setModelOpen()}>
  701. {!loadingClass.value && (
  702. <div class={styles.wraps}>
  703. <div
  704. style={
  705. activeVideoItem.value.type &&
  706. data.animationState === 'end' &&
  707. data.videoState === 'play'
  708. ? {
  709. zIndex: 15,
  710. opacity: 1
  711. }
  712. : { opacity: 0, zIndex: -1 }
  713. }
  714. class={styles.itemDiv}>
  715. <VideoItem
  716. ref={(el: any) => (data.videoItemRef = el)}
  717. item={activeVideoItem.value}
  718. showModel={activeData.model}
  719. onClose={setModelOpen1}
  720. onCanplay={() => {
  721. data.videoState = 'play';
  722. }}
  723. onPause={() => {
  724. clearTimeout(activeData.timer);
  725. activeData.model = true;
  726. }}
  727. onEnded={() => {
  728. // const _index = popupData.activeIndex + 1
  729. // if (_index < data.itemList.length) {
  730. // handleSwipeChange(_index);
  731. // }
  732. }}
  733. />
  734. </div>
  735. {data.itemList.map((m: any, mIndex: number) => {
  736. const isRender =
  737. m.isRender || Math.abs(popupData.activeIndex - mIndex) < 2;
  738. const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
  739. if (isRender) {
  740. m.isRender = true;
  741. }
  742. return isRender ? (
  743. <div
  744. key={'index' + mIndex}
  745. class={[
  746. styles.itemDiv,
  747. popupData.activeIndex === mIndex && styles.itemActive,
  748. activeData.isAnimation && styles.acitveAnimation,
  749. Math.abs(popupData.activeIndex - mIndex) < 2
  750. ? styles.show
  751. : styles.hide
  752. ]}
  753. style={
  754. mIndex < popupData.activeIndex
  755. ? effects[effectIndex.value].prev
  756. : mIndex > popupData.activeIndex
  757. ? effects[effectIndex.value].next
  758. : {}
  759. }
  760. onClick={(e: Event) => {
  761. if (Date.now() - activeData.nowTime < 300) {
  762. handleDbClick(m);
  763. return;
  764. }
  765. activeData.nowTime = Date.now();
  766. }}>
  767. {m.type === 'IMG' && <img src={m.content} />}
  768. {m.type === 'PPT' && <iframe src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(m.content)}`} width='100%' height='100%' frameborder='1'></iframe>}
  769. {m.type === 'VIDEO' && (
  770. <img
  771. src={m.coverImg}
  772. onLoad={() => {
  773. m.isprepare = true;
  774. }}
  775. />
  776. )}
  777. {/* {m.type === 'VIDEO' && (
  778. <VideoItem
  779. ref={(v: any) => (data.videoRefs[mIndex] = v)}
  780. item={m}
  781. show={popupData.activeIndex === mIndex}
  782. pageVisibility={pageVisibility.value}
  783. showModel={activeData.model}
  784. isEmtry={isEmtry}
  785. onLoadedmetadata={() => {
  786. m.isprepare = true;
  787. m.error = false;
  788. }}
  789. onEnded={() => {
  790. const _index = popupData.activeIndex + 1;
  791. if (_index < data.itemList.length) {
  792. handleSwipeChange(_index);
  793. }
  794. }}
  795. onReset={() => {
  796. m.error = false;
  797. }}
  798. onError={() => {
  799. m.isprepare = true;
  800. m.error = true;
  801. }}
  802. />
  803. )} */}
  804. {m.type === 'SONG' && (
  805. <AudioItem
  806. item={m}
  807. show={popupData.activeIndex === mIndex}
  808. pageVisibility={pageVisibility.value}
  809. showModel={activeData.model}
  810. isEmtry={isEmtry}
  811. onEnded={() => {
  812. const _index = popupData.activeIndex + 1;
  813. if (_index < data.itemList.length) {
  814. handleSwipeChange(_index);
  815. }
  816. }}
  817. onClose={() => {
  818. clearTimeout(activeData.timer);
  819. activeData.timer = setTimeout(() => {
  820. activeData.model = false;
  821. }, 4000);
  822. }}
  823. />
  824. )}
  825. {m.type === 'MUSIC' && (
  826. <MusicScore
  827. pageVisibility={pageVisibility.value}
  828. show={popupData.activeIndex === mIndex}
  829. activeModel={activeData.model}
  830. data-vid={m.id}
  831. music={m}
  832. />
  833. )}
  834. {m.type === 'VIDEO' && (
  835. <Transition name="van-fade">
  836. {/* {!m.isprepare && (
  837. <div class={styles.loadWrap}>
  838. <Vue3Lottie
  839. style={{ width: '100%', height: '100%' }}
  840. animationData={playLoadData}></Vue3Lottie>
  841. </div>
  842. )} */}
  843. {data.videoState !== 'play' && (
  844. <div class={styles.loadWrap}>
  845. <Vue3Lottie
  846. style={{ width: '100%', height: '100%' }}
  847. animationData={playLoadData}></Vue3Lottie>
  848. </div>
  849. )}
  850. </Transition>
  851. )}
  852. </div>
  853. ) : (
  854. <div
  855. key={'index' + mIndex}
  856. class={[
  857. styles.itemDiv,
  858. popupData.activeIndex === mIndex && styles.itemActive,
  859. activeData.isAnimation && styles.acitveAnimation,
  860. Math.abs(popupData.activeIndex - mIndex) < 2
  861. ? styles.show
  862. : styles.hide
  863. ]}
  864. style={
  865. mIndex < popupData.activeIndex
  866. ? effects[effectIndex.value].prev
  867. : mIndex > popupData.activeIndex
  868. ? effects[effectIndex.value].next
  869. : {}
  870. }></div>
  871. );
  872. })}
  873. </div>
  874. )}
  875. <Transition name="right">
  876. {activeData.model && (
  877. <div
  878. class={styles.rightFixedBtns}
  879. onClick={(e: Event) => {
  880. e.stopPropagation();
  881. clearTimeout(activeData.timer);
  882. }}>
  883. <div
  884. class={[styles.fullBtn, styles.point]}
  885. onClick={() => (popupData.chapterOpen = true)}>
  886. <img src={iconChange} />
  887. <span>切换</span>
  888. </div>
  889. <div
  890. class={[styles.fullBtn, styles.point]}
  891. onClick={() => (popupData.open = true)}>
  892. <img src={iconMenu} />
  893. <span>课件</span>
  894. </div>
  895. <div
  896. class={[
  897. styles.fullBtn,
  898. // popupData.activeIndex == 0 && styles.btnsDisabled
  899. !isUpArrow.value && styles.btnsDisabled
  900. ]}
  901. onClick={() => handlePreAndNext('up')}>
  902. <img src={iconUp} />
  903. <span style={{ textAlign: 'center' }}>上一个</span>
  904. </div>
  905. <div
  906. class={[
  907. styles.fullBtn,
  908. !isDownArrow.value && styles.btnsDisabled
  909. ]}
  910. onClick={() => handlePreAndNext('down')}>
  911. <span style={{ textAlign: 'center' }}>下一个</span>
  912. <img src={iconDown} />
  913. </div>
  914. </div>
  915. )}
  916. </Transition>
  917. </div>
  918. <div
  919. style={{ transform: activeData.model ? '' : 'translateY(-100%)' }}
  920. class={styles.headerContainer}
  921. ref={headeRef}>
  922. <div class={styles.backBtn} onClick={() => goback()}>
  923. <Icon name={iconBack} />
  924. 返回
  925. </div>
  926. <div class={styles.menu}>{popupData.itemName}</div>
  927. </div>
  928. {/* 课件列表 */}
  929. <Popup
  930. class={styles.popup}
  931. style={{ background: 'rgba(0,0,0, 0.75)' }}
  932. overlayClass={styles.overlayClass}
  933. position="right"
  934. round
  935. v-model:show={popupData.open}
  936. onClose={handleClosePopup}>
  937. <Points
  938. data={data.knowledgePointList}
  939. itemActive={popupData.itemActive}
  940. itemName={popupData.itemPointName}
  941. onHandleSelect={(res: any) => {
  942. popupData.open = false;
  943. toggleMaterial(res.itemActive);
  944. }}
  945. />
  946. </Popup>
  947. {/* 知识点列表 */}
  948. <Popup
  949. class={styles.popup}
  950. style={{ background: 'rgba(0,0,0, 0.75)' }}
  951. overlayClass={styles.overlayClass}
  952. position="right"
  953. round
  954. v-model:show={popupData.chapterOpen}
  955. onClose={handleClosePopup}>
  956. <Chapter
  957. detail={data.courseDetails}
  958. itemActive={activeData.coursewareDetailKnowledgeId as any}
  959. active={activeData.lessonCoursewareDetailId as any}
  960. onHandleSelect={async (item: any) => {
  961. loadingClass.value = true;
  962. activeData.coursewareDetailKnowledgeId = item.itemActive;
  963. activeData.lessonCoursewareDetailId = item.tabActive;
  964. await getDetail();
  965. popupData.activeIndex = 0;
  966. popupData.itemActive = data.knowledgePointList[0].id;
  967. popupData.itemName = data.knowledgePointList[0].name;
  968. popupData.itemPointName = item.itemName;
  969. popupData.chapterOpen = false;
  970. loadingClass.value = false;
  971. }}
  972. />
  973. </Popup>
  974. </div>
  975. );
  976. }
  977. });