index.tsx 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595
  1. import {
  2. defineComponent,
  3. onMounted,
  4. reactive,
  5. onUnmounted,
  6. ref,
  7. Transition,
  8. computed,
  9. nextTick
  10. } from 'vue';
  11. import styles from './index.module.less';
  12. import 'plyr/dist/plyr.css';
  13. import MusicScore from './component/musicScore';
  14. // import iconChange from './image/icon-change.png';
  15. // import iconMenu from './image/icon-menu.png';
  16. // import iconUp from './image/icon-up.png';
  17. // import iconDown from './image/icon-down.png';
  18. // import iconNote from './image/icon-note.png';
  19. // import iconWhiteboard from './image/icon-whiteboard.png';
  20. // import iconAssignHomework from './image/icon-assignHomework.png';
  21. // import iconClose from './image/icon-close.png';
  22. // import iconOverPreivew from './image/icon-over-preview.png';
  23. import { Vue3Lottie } from 'vue3-lottie';
  24. import playLoadData from './datas/data.json';
  25. // import Moveable from 'moveable';
  26. import VideoPlay from './component/video-play';
  27. import {
  28. useMessage,
  29. NDrawer,
  30. NDrawerContent,
  31. NModal,
  32. NSpace,
  33. NButton
  34. // NTooltip,
  35. // NPopover,
  36. // NImage
  37. } from 'naive-ui';
  38. import CardType from '@/components/card-type';
  39. import Pen from './component/tools/pen';
  40. import AudioPay from './component/audio-pay';
  41. import TrainSettings from './model/train-settings';
  42. import { useRoute } from 'vue-router';
  43. import {
  44. courseScheduleUpdate,
  45. lessonCoursewareDetail,
  46. lessonPreTrainingPage,
  47. queryCourseware
  48. } from '../prepare-lessons/api';
  49. // import Attentguide from '@/custom-plugins/guide-page/attent-guide';
  50. import { vaildUrl } from '/src/utils/urlUtils';
  51. import TimerMeter from '/src/components/timerMeter';
  52. // import toneImage from '/src/components/layout/images/toneImage.png';
  53. // import toolbox from '/src/components/layout/images/toolbox.png';
  54. // import setTimeIcon from '/src/components/layout/images/setTimeIcon.png';
  55. // import beatIcon from '/src/components/layout/images/beatIcon.png';
  56. // import toneIcon from '/src/components/layout/images/toneIcon.png';
  57. import { px2vw } from '/src/utils';
  58. import PlaceholderTone from '/src/components/layout/modals/placeholderTone';
  59. import { state as globalState } from '/src/state';
  60. import Chapter from './model/chapter';
  61. import { useRouter } from 'vue-router';
  62. import { useUserStore } from '@/store/modules/users';
  63. import iconBeatIcon from './new-image/icon-beatIcon.png';
  64. import iconChange from './new-image/icon-change.png';
  65. import iconDown from './new-image/icon-down.png';
  66. import iconMenu from './new-image/icon-menu.png';
  67. import iconNote from './new-image/icon-note.png';
  68. import iconOverClass from './new-image/icon-overclass.png';
  69. import iconSetTime from './new-image/icon-setTime.png';
  70. import iconToneIcon from './new-image/icon-toneIcon.png';
  71. import iconUp from './new-image/icon-up.png';
  72. import iconWhite from './new-image/icon-white.png';
  73. import iconWork from './new-image/icon-work.png';
  74. export type ToolType = 'init' | 'pen' | 'whiteboard';
  75. export type ToolItem = {
  76. type: ToolType;
  77. name: string;
  78. icon: string;
  79. };
  80. export default defineComponent({
  81. name: 'CoursewarePlay',
  82. props: {
  83. type: {
  84. type: String,
  85. default: ''
  86. },
  87. subjectId: {
  88. type: [String, Number],
  89. default: ''
  90. },
  91. // 教材编号
  92. lessonCourseId: {
  93. type: [String, Number],
  94. default: ''
  95. },
  96. detailId: {
  97. type: String,
  98. default: ''
  99. },
  100. // 班级编号
  101. classGroupId: {
  102. type: String,
  103. default: ''
  104. },
  105. // 上课记录编号
  106. classId: {
  107. type: String,
  108. defaault: ''
  109. },
  110. preStudentNum: {
  111. type: [String, Number],
  112. default: ''
  113. }
  114. },
  115. emits: ['close'],
  116. setup(props, { emit }) {
  117. const message = useMessage();
  118. const route = useRoute();
  119. const router = useRouter();
  120. const users = useUserStore();
  121. /** 设置播放容器 16:9 */
  122. const parentContainer = reactive({
  123. width: '100vw'
  124. });
  125. // const NPopoverRef = ref();
  126. // const setContainer = () => {
  127. // const min = Math.min(screen.width, screen.height);
  128. // const max = Math.max(screen.width, screen.height);
  129. // const width = min * (16 / 9);
  130. // if (width > max) {
  131. // parentContainer.width = '100vw';
  132. // return;
  133. // } else {
  134. // parentContainer.width = width + 'px';
  135. // }
  136. // };
  137. const handleInit = (type = 0) => {
  138. //设置容器16:9
  139. // setContainer();
  140. };
  141. handleInit();
  142. onUnmounted(() => {
  143. handleInit(1);
  144. });
  145. const data = reactive({
  146. type: 'class' as '' | 'preview' | 'class', // 预览类型
  147. subjectId: '' as any, // 声部编号
  148. lessonCourseId: '' as any, // 教材编号
  149. lessonCoursewareDetailId: '' as any, // 章节
  150. detailId: '' as any, // 编号 - 课程编号
  151. classGroupId: '' as any, // 上课时需要 班级编号
  152. classId: '' as any, // 上课编号
  153. preStudentNum: '' as any, // 班上学生
  154. // detail: null,
  155. knowledgePointList: [] as any,
  156. itemList: [] as any,
  157. // showHead: true,
  158. // isCourse: false,
  159. // isRecordPlay: false,
  160. videoRefs: {} as any[],
  161. audioRefs: {} as any[],
  162. modelAttendStatus: false, // 布置作业提示弹窗
  163. modalAttendMessage: '本节课未设置课后作业,是否继续?',
  164. modelTrainStatus: false, // 训练设置
  165. homeworkStatus: true, // 布置作业完成时
  166. removeVisiable: false,
  167. removeTitle: '',
  168. removeContent: '',
  169. videoState: 'init' as 'init' | 'play',
  170. videoItemRef: null as any,
  171. animationState: 'start' as 'start' | 'end'
  172. });
  173. const activeData = reactive({
  174. // isAutoPlay: false, // 是否自动播放
  175. nowTime: 0,
  176. model: true, // 遮罩
  177. isAnimation: true, // 是否动画
  178. // videoBtns: true, // 视频
  179. // currentTime: 0,
  180. // duration: 0,
  181. timer: null as any,
  182. item: null as any
  183. });
  184. const getDetail = async () => {
  185. try {
  186. const res = await queryCourseware({
  187. coursewareDetailKnowledgeId: data.detailId,
  188. subjectId: data.subjectId,
  189. pag: 1,
  190. rows: 99
  191. });
  192. const tempRows = res.data.rows || [];
  193. const temp: any = [];
  194. tempRows.forEach((row: any) => {
  195. if (!row.removeFlag) {
  196. temp.push({
  197. id: row.id,
  198. materialId: row.materialId,
  199. coverImg: row.coverImg,
  200. type: row.materialType,
  201. title: row.materialName,
  202. isCollect: !!row.favoriteFlag,
  203. isSelected: row.source === 'PLATFORM' ? true : false,
  204. content: row.content
  205. });
  206. }
  207. });
  208. data.knowledgePointList = temp;
  209. data.itemList = data.knowledgePointList.map((m: any) => {
  210. return {
  211. ...m,
  212. iframeRef: null,
  213. videoEle: null,
  214. audioEle: null,
  215. autoPlay: false, //加载完成是否自动播放
  216. isprepare: false, // 视频是否加载完成
  217. isRender: false // 是否渲染了
  218. };
  219. });
  220. setTimeout(() => {
  221. data.animationState = 'end'
  222. }, 500)
  223. } catch {
  224. //
  225. }
  226. };
  227. const showModalBeat = ref(false);
  228. const showModalTone = ref(false);
  229. const showModalTime = ref(false);
  230. // ifram事件处理
  231. const iframeHandle = (ev: MessageEvent) => {
  232. console.log(ev.data?.api, ev.data, 'ev.data');
  233. if (ev.data?.api === 'headerTogge') {
  234. activeData.model =
  235. ev.data.show || (ev.data.playState == 'play' ? false : true);
  236. }
  237. //
  238. if (ev.data?.api === 'onAttendToggleMenu') {
  239. activeData.model = !activeData.model;
  240. }
  241. if (ev.data?.api === 'api_fingerPreView') {
  242. clearInterval(activeData.timer);
  243. activeData.model = !ev.data.state;
  244. }
  245. //
  246. if (ev.data?.api === 'documentBodyKeyup') {
  247. if (ev.data?.code === 'ArrowLeft') {
  248. setModalOpen();
  249. handlePreAndNext('up');
  250. }
  251. if (ev.data?.code === 'ArrowRight') {
  252. setModalOpen();
  253. handlePreAndNext('down');
  254. }
  255. }
  256. if (ev.data?.api === 'onLogin') {
  257. const documentDom: any = document;
  258. documentDom.exitFullscreen
  259. ? documentDom.exitFullscreen()
  260. : documentDom.mozCancelFullScreen
  261. ? documentDom.mozCancelFullScreen()
  262. : documentDom.webkitExitFullscreen
  263. ? documentDom.webkitExitFullscreen()
  264. : '';
  265. users.logout();
  266. router.replace('/login');
  267. }
  268. };
  269. onMounted(() => {
  270. // initMoveable();
  271. const query = route.query;
  272. console.log(query, props.preStudentNum, '学生人数');
  273. // 先取参数,
  274. data.type = props.type || (query.type as any);
  275. data.subjectId = props.subjectId || query.subjectId;
  276. data.detailId = props.detailId || query.detailId;
  277. data.lessonCourseId = props.lessonCourseId || query.lessonCourseId;
  278. data.classGroupId = props.classGroupId || query.classGroupId;
  279. data.classId = props.classId || query.classId;
  280. data.preStudentNum = props.preStudentNum || query.preStudentNum;
  281. window.addEventListener('message', iframeHandle);
  282. getDetail();
  283. getLessonCoursewareDetail();
  284. });
  285. const onFullScreen = () => {
  286. if (data.type === 'preview') {
  287. const el: any = document.querySelector('#app');
  288. if (el.mozRequestFullScreen) {
  289. el.mozRequestFullScreen();
  290. } else if (el.webkitRequestFullscreen) {
  291. el.webkitRequestFullscreen();
  292. } else if (el.requestFullScreen) {
  293. el.requestFullscreen();
  294. }
  295. }
  296. };
  297. const popupData = reactive({
  298. open: false,
  299. activeIndex: 0,
  300. toolOpen: false, // 工具弹窗控制
  301. chapterOpen: false, // 切换章节
  302. chapterDetails: [] as any,
  303. chapterLoading: false // 加载数据
  304. });
  305. const formatParentId = (id: any, list: any, ids = [] as any) => {
  306. for (const item of list) {
  307. if (item.knowledgeList && item.knowledgeList.length > 0) {
  308. const cIds: any = formatParentId(id, item.knowledgeList, [
  309. ...ids,
  310. item.id
  311. ]);
  312. if (cIds.includes(id)) {
  313. return cIds;
  314. }
  315. }
  316. if (item.id === id) {
  317. return [...ids, id];
  318. }
  319. }
  320. return ids;
  321. };
  322. /** 获取章节 */
  323. const getLessonCoursewareDetail = async () => {
  324. try {
  325. const res = await lessonCoursewareDetail({
  326. id: data.lessonCourseId,
  327. subjectId: data.subjectId
  328. });
  329. popupData.chapterDetails = res.data.lessonList || [];
  330. const ids = formatParentId(data.detailId, popupData.chapterDetails);
  331. data.lessonCoursewareDetailId = ids[0];
  332. } catch {
  333. //
  334. }
  335. };
  336. /** 更新上课记录 */
  337. const classCourseScheduleUpdate = async () => {
  338. try {
  339. if (!data.classId) return;
  340. await courseScheduleUpdate({
  341. lessonCoursewareKnowledgeDetailId: data.detailId,
  342. id: data.classId
  343. });
  344. } catch {
  345. //
  346. }
  347. };
  348. const activeName = computed(() => {
  349. let name = '';
  350. // data.knowledgePointList.forEach((item: any, index: number) => {
  351. // if (popupData.activeIndex === index) {
  352. // name = item.title;
  353. // }
  354. // });
  355. popupData.chapterDetails.forEach((chapter: any) => {
  356. if (chapter.id === data.lessonCoursewareDetailId) {
  357. name = chapter.name;
  358. chapter.knowledgeList?.forEach((know: any) => {
  359. if (know.id === data.detailId) {
  360. name += ' - ' + know.name;
  361. }
  362. });
  363. }
  364. });
  365. return name;
  366. });
  367. /**停止所有的播放 */
  368. const handleStop = () => {
  369. for (let i = 0; i < data.itemList.length; i++) {
  370. const activeItem = data.itemList[i];
  371. if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
  372. // console.log(activeItem.videoEle, ' activeItem.videoEle');
  373. try {
  374. activeItem.videoEle?.currentTime(0);
  375. activeItem.videoEle?.pause();
  376. } catch (e: any) {
  377. // console.log(e, 'e');
  378. }
  379. }
  380. if (activeItem.type === 'SONG' && activeItem.audioEle) {
  381. activeItem.audioEle?.stop();
  382. }
  383. // console.log('🚀 ~ activeItem:', activeItem)
  384. // 停止曲谱的播放
  385. if (activeItem.type === 'MUSIC') {
  386. activeItem.iframeRef?.contentWindow?.postMessage(
  387. { api: 'setPlayState' },
  388. '*'
  389. );
  390. }
  391. }
  392. };
  393. // 切换素材
  394. const toggleMaterial = (itemActive: any) => {
  395. const index = data.itemList.findIndex((n: any) => n.id == itemActive);
  396. if (index > -1) {
  397. handleSwipeChange(index);
  398. }
  399. };
  400. /** 延迟收起模态框 */
  401. const setModelOpen = () => {
  402. clearTimeout(activeData.timer);
  403. message.destroyAll();
  404. activeData.timer = setTimeout(() => {
  405. activeData.model = false;
  406. Object.values(data.videoRefs).map((n: any) =>
  407. n?.toggleHideControl(false)
  408. );
  409. Object.values(data.audioRefs).map((n: any) =>
  410. n?.toggleHideControl(false)
  411. );
  412. }, 4000);
  413. };
  414. /** 立即收起所有的模态框 */
  415. const clearModel = () => {
  416. clearTimeout(activeData.timer);
  417. message.destroyAll();
  418. activeData.model = false;
  419. Object.values(data.videoRefs).map((n: any) =>
  420. n?.toggleHideControl(false)
  421. );
  422. Object.values(data.audioRefs).map((n: any) =>
  423. n?.toggleHideControl(false)
  424. );
  425. };
  426. const toggleModel = (type = true) => {
  427. activeData.model = type;
  428. Object.values(data.videoRefs).map((n: any) => n?.toggleHideControl(type));
  429. Object.values(data.audioRefs).map((n: any) => n?.toggleHideControl(type));
  430. };
  431. // 双击
  432. const handleDbClick = (item: any) => {
  433. if (item && item.type === 'VIDEO') {
  434. const videoEle: HTMLVideoElement = item.videoEle;
  435. if (videoEle) {
  436. if (videoEle.paused) {
  437. message.destroyAll();
  438. videoEle.play();
  439. } else {
  440. message.warning('已暂停');
  441. videoEle.pause();
  442. }
  443. }
  444. }
  445. };
  446. // 切换播放
  447. // const togglePlay = (m: any, isPlay: boolean) => {
  448. // if (isPlay) {
  449. // m.videoEle?.play();
  450. // } else {
  451. // m.videoEle?.pause();
  452. // }
  453. // };
  454. // const showIndex = ref(-4);
  455. const effectIndex = ref(3);
  456. const effects = [
  457. {
  458. prev: {
  459. transform: 'translate3d(0, 0, -800px) rotateX(180deg)'
  460. },
  461. next: {
  462. transform: 'translate3d(0, 0, -800px) rotateX(-180deg)'
  463. }
  464. },
  465. {
  466. prev: {
  467. transform: 'translate3d(-100%, 0, -800px)'
  468. },
  469. next: {
  470. transform: 'translate3d(100%, 0, -800px)'
  471. }
  472. },
  473. {
  474. prev: {
  475. transform: 'translate3d(-50%, 0, -800px) rotateY(80deg)'
  476. },
  477. next: {
  478. transform: 'translate3d(50%, 0, -800px) rotateY(-80deg)'
  479. }
  480. },
  481. {
  482. prev: {
  483. transform: 'translate3d(-100%, 0, -800px) rotateY(-120deg)'
  484. },
  485. next: {
  486. transform: 'translate3d(100%, 0, -800px) rotateY(120deg)'
  487. }
  488. },
  489. // 风车4
  490. {
  491. prev: {
  492. transform: 'translate3d(-50%, 50%, -800px) rotateZ(-14deg)',
  493. opacity: 0
  494. },
  495. next: {
  496. transform: 'translate3d(50%, 50%, -800px) rotateZ(14deg)',
  497. opacity: 0
  498. }
  499. },
  500. // 翻页5
  501. {
  502. prev: {
  503. transform: 'translateZ(-800px) rotate3d(0, -1, 0, 90deg)',
  504. opacity: 0
  505. },
  506. next: {
  507. transform: 'translateZ(-800px) rotate3d(0, 1, 0, 90deg)',
  508. opacity: 0
  509. },
  510. current: { transitionDelay: '700ms' }
  511. }
  512. ];
  513. const acitveTimer = ref();
  514. // 轮播切换
  515. const handleSwipeChange = (index: number) => {
  516. // 如果是当前正在播放 或者是视频最后一个
  517. if (popupData.activeIndex == index) return;
  518. data.animationState = 'start'
  519. data.videoState = 'init'
  520. handleStop();
  521. clearTimeout(acitveTimer.value);
  522. activeData.model = true;
  523. checkedAnimation(popupData.activeIndex, index);
  524. popupData.activeIndex = index;
  525. acitveTimer.value = setTimeout(
  526. () => {
  527. const item = data.itemList[index];
  528. if (item) {
  529. if (item.type == 'MUSIC') {
  530. activeData.model = true;
  531. }
  532. if (item.type === 'SONG') {
  533. // 自动播放下一个音频
  534. clearTimeout(activeData.timer);
  535. message.destroyAll();
  536. // item.autoPlay = false;
  537. // nextTick(() => {
  538. // item.audioEle?.onPlay();
  539. // });
  540. }
  541. if (item.type === 'VIDEO') {
  542. // 自动播放下一个视频
  543. clearTimeout(activeData.timer);
  544. message.destroyAll();
  545. nextTick(() => {
  546. if (item.error) {
  547. // console.log(item, 'item error');
  548. item.videoEle?.src(item.content);
  549. item.error = false;
  550. // item.videoEle?.onPlay();
  551. }
  552. data.animationState = 'end'
  553. });
  554. }
  555. }
  556. },
  557. activeData.isAnimation ? 800 : 0
  558. );
  559. };
  560. /** 是否有转场动画 */
  561. const checkedAnimation = (index: number, nextIndex?: number) => {
  562. const item = data.itemList[index];
  563. const nextItem = data.itemList[nextIndex!];
  564. if (nextItem) {
  565. if (nextItem.knowledgePointId != item.knowledgePointId) {
  566. activeData.isAnimation = true;
  567. return;
  568. }
  569. const videoEle = item.videoEle;
  570. const nextVideo = nextItem.videoEle;
  571. if (videoEle && videoEle.duration < 8 && index < nextIndex!) {
  572. activeData.isAnimation = false;
  573. } else if (nextVideo && nextVideo.duration < 8 && index > nextIndex!) {
  574. activeData.isAnimation = false;
  575. } else {
  576. activeData.isAnimation = true;
  577. }
  578. } else {
  579. activeData.isAnimation = item?.adviseStudyTimeSecond < 8 ? false : true;
  580. }
  581. };
  582. // 上一个知识点, 下一个知识点
  583. const handlePreAndNext = async (type: string) => {
  584. // if (type === 'up') {
  585. // handleSwipeChange(popupData.activeIndex - 1);
  586. // } else {
  587. // handleSwipeChange(popupData.activeIndex + 1);
  588. // }
  589. if (type === 'up') {
  590. // 判断上面是否还有章节
  591. if (popupData.activeIndex > 0) {
  592. handleSwipeChange(popupData.activeIndex - 1);
  593. return;
  594. }
  595. // 获取当前是哪个章节
  596. let detailIndex = popupData.chapterDetails.findIndex(
  597. (item: any) => item.id == data.lessonCoursewareDetailId
  598. );
  599. const detailItem =
  600. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  601. let lessonIndex = detailItem.findIndex(
  602. (item: any) => item.id == data.detailId
  603. );
  604. let lessonStatus = false; // 当前章节上面是否有内容
  605. let lessonCoursewareDetailId = '';
  606. let coursewareDetailKnowledgeId = '';
  607. while (lessonIndex >= 0) {
  608. lessonIndex--;
  609. if (lessonIndex >= 0) {
  610. if (detailItem[lessonIndex].containMaterial) {
  611. lessonStatus = true;
  612. lessonCoursewareDetailId =
  613. detailItem[lessonIndex].lessonCoursewareDetailId;
  614. coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
  615. }
  616. }
  617. if (lessonStatus) {
  618. break;
  619. }
  620. }
  621. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  622. if (lessonStatus) {
  623. popupData.chapterLoading = true;
  624. data.detailId = coursewareDetailKnowledgeId;
  625. data.lessonCoursewareDetailId = lessonCoursewareDetailId;
  626. // 更新上课记录 上课的时候才更新
  627. if (data.type !== 'preview') {
  628. await classCourseScheduleUpdate();
  629. }
  630. await getDetail();
  631. popupData.activeIndex = data.itemList.length - 1 || 0;
  632. popupData.chapterOpen = false;
  633. popupData.chapterLoading = false;
  634. return;
  635. }
  636. let prevLessonStatus = false;
  637. while (detailIndex >= 0) {
  638. detailIndex--;
  639. const tempDetail =
  640. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  641. let tempLessonLength = tempDetail.length;
  642. while (tempLessonLength > 0) {
  643. if (tempDetail[tempLessonLength - 1].containMaterial) {
  644. prevLessonStatus = true;
  645. lessonCoursewareDetailId =
  646. tempDetail[tempLessonLength - 1].lessonCoursewareDetailId;
  647. coursewareDetailKnowledgeId = tempDetail[tempLessonLength - 1].id;
  648. }
  649. tempLessonLength--;
  650. if (prevLessonStatus) {
  651. break;
  652. }
  653. }
  654. if (prevLessonStatus) {
  655. break;
  656. }
  657. }
  658. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  659. if (prevLessonStatus) {
  660. popupData.chapterLoading = true;
  661. data.detailId = coursewareDetailKnowledgeId;
  662. data.lessonCoursewareDetailId = lessonCoursewareDetailId;
  663. await getDetail();
  664. popupData.activeIndex = data.itemList.length - 1 || 0;
  665. popupData.chapterLoading = false;
  666. return;
  667. }
  668. } else {
  669. if (popupData.activeIndex < data.itemList.length - 1) {
  670. handleSwipeChange(popupData.activeIndex + 1);
  671. return;
  672. }
  673. // 获取当前是哪个章节
  674. let detailIndex = popupData.chapterDetails.findIndex(
  675. (item: any) => item.id == data.lessonCoursewareDetailId
  676. );
  677. const detailItem =
  678. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  679. let lessonIndex = detailItem.findIndex(
  680. (item: any) => item.id == data.detailId
  681. );
  682. let lessonStatus = false; // 当前章节下面是否有内容
  683. let lessonCoursewareDetailId = '';
  684. let coursewareDetailKnowledgeId = '';
  685. while (lessonIndex < detailItem.length - 1) {
  686. lessonIndex++;
  687. if (lessonIndex >= 0) {
  688. if (detailItem[lessonIndex].containMaterial) {
  689. lessonStatus = true;
  690. lessonCoursewareDetailId =
  691. detailItem[lessonIndex].lessonCoursewareDetailId;
  692. coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
  693. }
  694. }
  695. if (lessonStatus) {
  696. break;
  697. }
  698. }
  699. // 判断当前章节下面课程是否有内容,否则往下一个章节走
  700. if (lessonStatus) {
  701. popupData.chapterLoading = true;
  702. data.detailId = coursewareDetailKnowledgeId;
  703. data.lessonCoursewareDetailId = lessonCoursewareDetailId;
  704. // 更新上课记录 上课的时候才更新
  705. if (data.type !== 'preview') {
  706. await classCourseScheduleUpdate();
  707. }
  708. await getDetail();
  709. popupData.activeIndex = 0;
  710. popupData.chapterOpen = false;
  711. popupData.chapterLoading = false;
  712. return;
  713. }
  714. let nextLessonStatus = false;
  715. while (detailIndex <= popupData.chapterDetails.length - 1) {
  716. detailIndex++;
  717. const tempDetail =
  718. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  719. let tempLessonLength = 0;
  720. while (tempLessonLength <= tempDetail.length - 1) {
  721. if (tempDetail[tempLessonLength].containMaterial) {
  722. nextLessonStatus = true;
  723. lessonCoursewareDetailId =
  724. tempDetail[tempLessonLength].lessonCoursewareDetailId;
  725. coursewareDetailKnowledgeId = tempDetail[tempLessonLength].id;
  726. }
  727. tempLessonLength++;
  728. if (nextLessonStatus) {
  729. break;
  730. }
  731. }
  732. if (nextLessonStatus) {
  733. break;
  734. }
  735. }
  736. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  737. if (nextLessonStatus) {
  738. popupData.chapterLoading = true;
  739. data.detailId = coursewareDetailKnowledgeId;
  740. data.lessonCoursewareDetailId = lessonCoursewareDetailId;
  741. // 更新上课记录 上课的时候才更新
  742. if (data.type !== 'preview') {
  743. await classCourseScheduleUpdate();
  744. }
  745. await getDetail();
  746. popupData.activeIndex = 0;
  747. popupData.chapterOpen = false;
  748. popupData.chapterLoading = false;
  749. return;
  750. }
  751. }
  752. };
  753. /** 弹窗关闭 */
  754. const handleClosePopup = () => {
  755. const item = data.itemList[popupData.activeIndex];
  756. if (item?.type == 'VIDEO' && !item.videoEle?.paused) {
  757. setModelOpen();
  758. }
  759. if (item?.type == 'SONG' && !item.audioEle?.paused) {
  760. setModelOpen();
  761. }
  762. };
  763. // 监听页面键盘事件 - 上下切换
  764. document.body.addEventListener('keyup', (e: KeyboardEvent) => {
  765. // console.log(e, 'e');
  766. if (e.code === 'ArrowLeft') {
  767. // if (popupData.activeIndex === 0) return;
  768. setModalOpen();
  769. handlePreAndNext('up');
  770. } else if (e.code === 'ArrowRight') {
  771. // if (popupData.activeIndex === data.itemList.length - 1) return;
  772. setModalOpen();
  773. handlePreAndNext('down');
  774. }
  775. });
  776. const setModalOpen = (status = true) => {
  777. clearTimeout(activeData.timer);
  778. activeData.model = status;
  779. Object.values(data.videoRefs).map((n: any) =>
  780. n?.toggleHideControl(status)
  781. );
  782. Object.values(data.audioRefs).map((n: any) =>
  783. n?.toggleHideControl(status)
  784. );
  785. };
  786. /** 教学数据 */
  787. const studyData = reactive({
  788. type: '' as ToolType,
  789. penShow: false,
  790. whiteboardShow: false
  791. });
  792. /** 打开教学工具 */
  793. const openStudyTool = (item: ToolItem) => {
  794. const activeItem = data.itemList[popupData.activeIndex];
  795. // 暂停视频和曲谱的播放
  796. if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
  797. activeItem.videoEle?.pause();
  798. }
  799. if (activeItem.type === 'SONG' && activeItem.audioEle) {
  800. activeItem.audioEle?.stop();
  801. }
  802. if (activeItem.type === 'MUSIC') {
  803. activeItem.iframeRef?.contentWindow?.postMessage(
  804. { api: 'setPlayState' },
  805. '*'
  806. );
  807. }
  808. clearModel();
  809. popupData.toolOpen = false;
  810. studyData.type = item.type;
  811. switch (item.type) {
  812. case 'pen':
  813. studyData.penShow = true;
  814. break;
  815. case 'whiteboard':
  816. studyData.whiteboardShow = true;
  817. }
  818. };
  819. /** 关闭教学工具 */
  820. const closeStudyTool = () => {
  821. studyData.type = 'init';
  822. toggleModel();
  823. };
  824. const startShowModal = (
  825. val: 'setTimeIcon' | 'beatIcon' | 'toneIcon' | 'iconNote2'
  826. ) => {
  827. if (val == 'setTimeIcon') {
  828. showModalTime.value = true;
  829. }
  830. if (val == 'beatIcon') {
  831. showModalBeat.value = true;
  832. }
  833. if (val == 'toneIcon') {
  834. showModalTone.value = true;
  835. }
  836. // if (val == 'iconNote2') {
  837. // if (NPopoverRef.value) {
  838. // NPopoverRef.value.setShow(false);
  839. // }
  840. // eventGlobal.emit('teacher-guideInfo-attend-class', 'attend-class');
  841. // }
  842. };
  843. // 是否允许上一页
  844. const isUpArrow = computed(() => {
  845. /**
  846. * 1,判断当前课程中是否处在第一个资源;
  847. * 2,判断当前课程是否在当前章节的第一个;
  848. * 3,判断当前章节,当前课程上面还没有其它课程,是否有资源;
  849. * 4,判断当前章节上面还没有其它章节;
  850. * 5,判断上面章节里面课程是否有资源;
  851. */
  852. if (popupData.activeIndex > 0) {
  853. return true;
  854. }
  855. // 获取当前是哪个章节
  856. let detailIndex = popupData.chapterDetails.findIndex(
  857. (item: any) => item.id == data.lessonCoursewareDetailId
  858. );
  859. const detailItem =
  860. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  861. let lessonIndex = detailItem.findIndex(
  862. (item: any) => item.id == data.detailId
  863. );
  864. // 说明已经是第一单元,第一课
  865. if (detailIndex <= 0 && lessonIndex <= 0) {
  866. return false;
  867. }
  868. let lessonStatus = false; // 当前章节上面是否有内容
  869. while (lessonIndex >= 0) {
  870. lessonIndex--;
  871. if (lessonIndex >= 0) {
  872. if (detailItem[lessonIndex].containMaterial) {
  873. lessonStatus = true;
  874. }
  875. }
  876. }
  877. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  878. if (lessonStatus) {
  879. return true;
  880. }
  881. // 已经是第一个章节了
  882. if (detailIndex <= 0) {
  883. return false;
  884. }
  885. let prevLessonStatus = false;
  886. while (detailIndex >= 0) {
  887. detailIndex--;
  888. const tempDetail =
  889. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  890. let tempLessonLength = tempDetail.length;
  891. while (tempLessonLength > 0) {
  892. if (tempDetail[tempLessonLength - 1].containMaterial) {
  893. prevLessonStatus = true;
  894. }
  895. tempLessonLength--;
  896. }
  897. if (prevLessonStatus) {
  898. return true;
  899. }
  900. }
  901. return false;
  902. });
  903. // 是否允许下一页
  904. const isDownArrow = computed(() => {
  905. if (popupData.activeIndex < data.itemList.length - 1) {
  906. return true;
  907. }
  908. // 获取当前是哪个章节
  909. let detailIndex = popupData.chapterDetails.findIndex(
  910. (item: any) => item.id == data.lessonCoursewareDetailId
  911. );
  912. const detailItem =
  913. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  914. let lessonIndex = detailItem.findIndex(
  915. (item: any) => item.id == data.detailId
  916. );
  917. // 说明已经是最后-单元,最后一课
  918. if (
  919. detailIndex >= popupData.chapterDetails.length - 1 &&
  920. lessonIndex >= detailItem.length - 1
  921. ) {
  922. return false;
  923. }
  924. let lessonStatus = false; // 当前章节下面是否有内容
  925. while (lessonIndex < detailItem.length - 1) {
  926. lessonIndex++;
  927. if (lessonIndex >= 0) {
  928. if (detailItem[lessonIndex].containMaterial) {
  929. lessonStatus = true;
  930. }
  931. }
  932. }
  933. // 判断当前章节下面课程是否有内容,否则往下一个章节走
  934. if (lessonStatus) {
  935. return true;
  936. }
  937. // 已经是最后一个章节了
  938. if (detailIndex >= popupData.chapterDetails.length - 1) {
  939. return false;
  940. }
  941. let nextLessonStatus = false;
  942. while (detailIndex < popupData.chapterDetails.length - 1) {
  943. detailIndex++;
  944. const tempDetail =
  945. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  946. let tempLessonLength = 0;
  947. while (tempLessonLength <= tempDetail.length - 1) {
  948. if (tempDetail[tempLessonLength].containMaterial) {
  949. nextLessonStatus = true;
  950. }
  951. tempLessonLength++;
  952. }
  953. if (nextLessonStatus) {
  954. return true;
  955. }
  956. }
  957. return false;
  958. });
  959. const activeVideoItem = computed(() => {
  960. const item = data.itemList[popupData.activeIndex]
  961. if (item && item.type && item.type.toLocaleUpperCase() === 'VIDEO') {
  962. return item
  963. }
  964. return {}
  965. })
  966. return () => (
  967. <div id="playContent" class={[styles.playContent, 'wrap']}>
  968. <div
  969. onClick={() => {
  970. clearTimeout(activeData.timer);
  971. activeData.model = !activeData.model;
  972. Object.values(data.videoRefs).map((n: any) =>
  973. n?.toggleHideControl(activeData.model)
  974. );
  975. Object.values(data.audioRefs).map((n: any) =>
  976. n?.toggleHideControl(activeData.model)
  977. );
  978. }}>
  979. <div
  980. class={styles.coursewarePlay}
  981. style={{ width: parentContainer.width }}>
  982. {!popupData.chapterLoading ? (
  983. <div class={styles.wraps}>
  984. <div
  985. style={
  986. activeVideoItem.value.type &&
  987. data.animationState === 'end' &&
  988. data.videoState === 'play'
  989. ? {
  990. zIndex: 15,
  991. opacity: 1
  992. }
  993. : { opacity: 0, zIndex: -1 }
  994. }
  995. class={styles.itemDiv}
  996. >
  997. <VideoPlay
  998. ref={(el: any) => data.videoItemRef = el}
  999. item={activeVideoItem.value}
  1000. showModel={activeData.model}
  1001. onClose={setModelOpen}
  1002. onCanplay={() => {
  1003. data.videoState = 'play'
  1004. }}
  1005. onPause={() => {
  1006. clearTimeout(activeData.timer)
  1007. activeData.model = true
  1008. }}
  1009. onEnded={() => {
  1010. const _index = popupData.activeIndex + 1
  1011. if (_index < data.itemList.length) {
  1012. handleSwipeChange(_index);
  1013. }
  1014. }}
  1015. />
  1016. </div>
  1017. {data.itemList.map((m: any, mIndex: number) => {
  1018. const isRender = Math.abs(popupData.activeIndex - mIndex) < 2;
  1019. const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
  1020. // if (isRender) {
  1021. // m.isRender = true;
  1022. // }
  1023. // console.log(isRender, 'isRender', mIndex);
  1024. return isRender ? (
  1025. <div
  1026. key={'index' + mIndex}
  1027. class={[
  1028. styles.itemDiv,
  1029. popupData.activeIndex === mIndex && styles.itemActive,
  1030. activeData.isAnimation && styles.acitveAnimation,
  1031. Math.abs(popupData.activeIndex - mIndex) < 2
  1032. ? styles.show
  1033. : styles.hide
  1034. ]}
  1035. style={
  1036. mIndex < popupData.activeIndex
  1037. ? effects[effectIndex.value].prev
  1038. : mIndex > popupData.activeIndex
  1039. ? effects[effectIndex.value].next
  1040. : {}
  1041. }
  1042. onClick={(e: Event) => {
  1043. e.stopPropagation();
  1044. clearTimeout(activeData.timer);
  1045. if (Date.now() - activeData.nowTime < 300) {
  1046. handleDbClick(m);
  1047. return;
  1048. }
  1049. activeData.nowTime = Date.now();
  1050. activeData.timer = setTimeout(() => {
  1051. activeData.model = !activeData.model;
  1052. Object.values(data.videoRefs).map((n: any) =>
  1053. n?.toggleHideControl(activeData.model)
  1054. );
  1055. Object.values(data.audioRefs).map((n: any) =>
  1056. n?.toggleHideControl(activeData.model)
  1057. );
  1058. if (activeData.model) {
  1059. setModelOpen();
  1060. }
  1061. }, 300);
  1062. }}>
  1063. {m.type === 'VIDEO' ? (
  1064. <>
  1065. <img src={m.coverImg} onLoad={() => {
  1066. m.isprepare = true;
  1067. }} />
  1068. {/* <VideoPlay
  1069. ref={(v: any) => (data.videoRefs[mIndex] = v)}
  1070. item={m}
  1071. isEmtry={isEmtry}
  1072. onLoadedmetadata={(videoItem: any) => {
  1073. m.videoEle = videoItem;
  1074. m.isprepare = true;
  1075. }}
  1076. onTogglePlay={(paused: boolean) => {
  1077. m.autoPlay = false;
  1078. if (paused || popupData.open) {
  1079. clearTimeout(activeData.timer);
  1080. } else {
  1081. setModelOpen();
  1082. }
  1083. }}
  1084. onReset={() => {
  1085. if (!m.videoEle?.paused) {
  1086. setModelOpen();
  1087. }
  1088. }}
  1089. onError={() => {
  1090. console.log('video error');
  1091. m.error = true;
  1092. }}
  1093. /> */}
  1094. <Transition name="van-fade">
  1095. { (
  1096. <div class={styles.loadWrap}>
  1097. <Vue3Lottie
  1098. animationData={playLoadData}></Vue3Lottie>
  1099. </div>
  1100. )}
  1101. </Transition>
  1102. </>
  1103. ) : m.type === 'IMG' ? (
  1104. <img src={m.content} />
  1105. ) : m.type === 'SONG' ? (
  1106. <AudioPay
  1107. item={m}
  1108. ref={(v: any) => (data.audioRefs[mIndex] = v)}
  1109. onLoadedmetadata={(audioItem: any) => {
  1110. m.audioEle = audioItem;
  1111. m.isprepare = true;
  1112. }}
  1113. onTogglePlay={(paused: boolean) => {
  1114. m.autoPlay = false;
  1115. if (paused || popupData.open) {
  1116. clearTimeout(activeData.timer);
  1117. } else {
  1118. setModelOpen();
  1119. }
  1120. }}
  1121. onEnded={() => {
  1122. const _index = popupData.activeIndex + 1;
  1123. if (_index < data.itemList.length) {
  1124. handleSwipeChange(_index);
  1125. }
  1126. }}
  1127. onReset={() => {
  1128. if (!m.audioEle?.paused) {
  1129. setModelOpen();
  1130. }
  1131. }}
  1132. />
  1133. ) : (
  1134. <MusicScore
  1135. activeModel={activeData.model}
  1136. activeStatus={popupData.activeIndex === mIndex}
  1137. data-vid={m.id}
  1138. music={m}
  1139. onSetIframe={(el: any) => {
  1140. m.iframeRef = el;
  1141. }}
  1142. />
  1143. )}
  1144. </div>
  1145. ) : null;
  1146. })}
  1147. </div>
  1148. ) : (
  1149. ''
  1150. )}
  1151. </div>
  1152. </div>
  1153. {/* 头部样式 */}
  1154. <div
  1155. style={{ transform: activeData.model ? '' : 'translateY(-100%)' }}
  1156. class={styles.headerContainer}>
  1157. <div class={styles.menu}>{activeName.value}</div>
  1158. </div>
  1159. {/* 布置作业按钮 */}
  1160. <div
  1161. onClick={(e: any) => {
  1162. e.stopPropagation();
  1163. if (activeData.timer){
  1164. setModelOpen();
  1165. }
  1166. }}
  1167. class={[
  1168. styles.switchDisplaySection,
  1169. activeData.model ? '' : styles.sectionAnimate
  1170. ]}>
  1171. <NSpace class={styles.switchSpace}>
  1172. <div
  1173. class={styles.btnItem}
  1174. onClick={async () => {
  1175. if (data.type === 'preview') {
  1176. handleStop();
  1177. data.removeVisiable = true;
  1178. data.removeTitle = '结束预览';
  1179. data.removeContent = '请确认是否结束预览?';
  1180. } else {
  1181. data.removeVisiable = true;
  1182. data.removeTitle = '结束课程';
  1183. data.removeContent = '请确认是否结束课程?';
  1184. }
  1185. }}>
  1186. <img src={iconOverClass} />
  1187. <p>{data.type !== 'preview' ? '结束课程' : '结束预览'}</p>
  1188. </div>
  1189. {data.type !== 'preview' && (
  1190. <div
  1191. class={[
  1192. styles.btnItem,
  1193. data.preStudentNum <= 0 ? styles.btnsDisabled : ''
  1194. ]}
  1195. onClick={async () => {
  1196. // 学生人数必须大于0,才可以布置作业
  1197. if (data.preStudentNum <= 0) return;
  1198. const res = await lessonPreTrainingPage({
  1199. coursewareKnowledgeDetailId: data.detailId,
  1200. subjectId: data.subjectId,
  1201. page: 1,
  1202. rows: 99
  1203. });
  1204. if (res.data.rows && res.data.rows.length) {
  1205. data.modalAttendMessage =
  1206. '本节课已设置课后作业,是否布置?';
  1207. }
  1208. data.modelAttendStatus = true;
  1209. }}>
  1210. <img src={iconWork} />
  1211. <p>布置作业</p>
  1212. </div>
  1213. )}
  1214. <div
  1215. class={styles.btnItem}
  1216. onClick={() =>
  1217. openStudyTool({
  1218. type: 'pen',
  1219. icon: iconNote,
  1220. name: '批注'
  1221. })
  1222. }>
  1223. <img src={iconNote} />
  1224. <p>批注</p>
  1225. </div>
  1226. <div
  1227. class={styles.btnItem}
  1228. onClick={() =>
  1229. openStudyTool({
  1230. type: 'whiteboard',
  1231. icon: iconWhite,
  1232. name: '白板'
  1233. })
  1234. }>
  1235. <img src={iconWhite} />
  1236. <p>白板</p>
  1237. </div>
  1238. <div
  1239. class={styles.btnItem}
  1240. onClick={() => startShowModal('beatIcon')}>
  1241. <img src={iconToneIcon} />
  1242. <p>节拍器</p>
  1243. </div>
  1244. <div
  1245. class={styles.btnItem}
  1246. onClick={() => startShowModal('toneIcon')}>
  1247. <img src={iconSetTime} />
  1248. <p>调音器</p>
  1249. </div>
  1250. <div
  1251. class={styles.btnItem}
  1252. onClick={() => startShowModal('setTimeIcon')}>
  1253. <img src={iconBeatIcon} />
  1254. <p>计时器</p>
  1255. </div>
  1256. </NSpace>
  1257. <NSpace class={styles.switchSpace}>
  1258. <div
  1259. class={styles.btnItem}
  1260. onClick={() => (popupData.chapterOpen = true)}>
  1261. <img src={iconChange} />
  1262. <p>切换章节</p>
  1263. </div>
  1264. <div class={styles.btnItem} onClick={() => (popupData.open = true)}>
  1265. <img src={iconMenu} />
  1266. <p>资源列表</p>
  1267. </div>
  1268. <div
  1269. class={[
  1270. styles.btnItem,
  1271. !isUpArrow.value ? styles.btnsDisabled : ''
  1272. ]}
  1273. onClick={() => {
  1274. if (!isUpArrow.value) return;
  1275. handlePreAndNext('up');
  1276. }}>
  1277. <img src={iconUp} />
  1278. <p>上一个</p>
  1279. </div>
  1280. <div
  1281. class={[
  1282. styles.btnItem,
  1283. !isDownArrow.value ? styles.btnsDisabled : ''
  1284. ]}
  1285. onClick={() => {
  1286. if (!isDownArrow.value) return;
  1287. handlePreAndNext('down');
  1288. }}>
  1289. <img src={iconDown} />
  1290. <p>下一个</p>
  1291. </div>
  1292. </NSpace>
  1293. </div>
  1294. {/* 显示列表 */}
  1295. <NDrawer
  1296. v-model:show={popupData.open}
  1297. class={styles.drawerContainer}
  1298. onAfterLeave={handleClosePopup}
  1299. showMask={false}>
  1300. <NDrawerContent title="资源列表" closable>
  1301. {data.knowledgePointList.map((item: any, index: number) => (
  1302. <div class={styles.cardContainer}>
  1303. <CardType
  1304. item={item}
  1305. isActive={popupData.activeIndex === index}
  1306. isCollect={false}
  1307. isShowCollect={false}
  1308. onClick={(item: any) => {
  1309. popupData.open = false;
  1310. toggleMaterial(item.id);
  1311. }}
  1312. />
  1313. </div>
  1314. ))}
  1315. </NDrawerContent>
  1316. </NDrawer>
  1317. {/* 显示列表 */}
  1318. <NDrawer
  1319. v-model:show={popupData.chapterOpen}
  1320. class={styles.drawerContainer}
  1321. onAfterLeave={handleClosePopup}
  1322. showMask={false}
  1323. displayDirective="show">
  1324. <NDrawerContent title="切换章节" closable>
  1325. <Chapter
  1326. treeList={popupData.chapterDetails}
  1327. itemActive={data.detailId as any}
  1328. onHandleSelect={async (val: any) => {
  1329. popupData.chapterLoading = true;
  1330. try {
  1331. data.detailId = val.itemActive;
  1332. const ids = formatParentId(
  1333. val.itemActive,
  1334. popupData.chapterDetails
  1335. );
  1336. data.lessonCoursewareDetailId = ids[0];
  1337. // 更新上课记录 上课的时候才更新
  1338. if (data.type !== 'preview') {
  1339. await classCourseScheduleUpdate();
  1340. }
  1341. await getDetail();
  1342. popupData.activeIndex = 0;
  1343. popupData.chapterOpen = false;
  1344. } catch {
  1345. //
  1346. }
  1347. popupData.chapterLoading = false;
  1348. }}
  1349. />
  1350. </NDrawerContent>
  1351. </NDrawer>
  1352. {/* 批注 */}
  1353. {studyData.penShow && (
  1354. <Pen
  1355. show={studyData.type === 'pen'}
  1356. type={studyData.type}
  1357. close={() => closeStudyTool()}
  1358. />
  1359. )}
  1360. {studyData.whiteboardShow && (
  1361. <Pen
  1362. show={studyData.type === 'whiteboard'}
  1363. type={studyData.type}
  1364. close={() => closeStudyTool()}
  1365. />
  1366. )}
  1367. {/* 布置作业 */}
  1368. <NModal
  1369. transformOrigin="center"
  1370. v-model:show={data.modelAttendStatus}
  1371. preset="card"
  1372. // class={styles.attendClassModal}
  1373. title={'课后作业'}
  1374. class={['modalTitle', styles.removeVisiable]}>
  1375. <div class={styles.studentRemove}>
  1376. <p>{data.modalAttendMessage}</p>
  1377. {/* <div class={styles.modelAttendContent}>
  1378. {data.modalAttendMessage}
  1379. </div> */}
  1380. <NSpace class={styles.btnGroupModal}>
  1381. <NButton
  1382. type="default"
  1383. round
  1384. onClick={() => {
  1385. data.modelAttendStatus = false;
  1386. handleStop();
  1387. // if (state.application) {
  1388. // emit('close');
  1389. // } else {
  1390. // window.close();
  1391. // }
  1392. data.modelAttendStatus = false;
  1393. }}>
  1394. 暂不布置
  1395. </NButton>
  1396. <NButton
  1397. type="primary"
  1398. round
  1399. onClick={() => {
  1400. data.modelTrainStatus = true;
  1401. data.modelAttendStatus = false;
  1402. }}>
  1403. 布置
  1404. </NButton>
  1405. </NSpace>
  1406. </div>
  1407. </NModal>
  1408. {/* 训练设置 */}
  1409. <NModal
  1410. transformOrigin="center"
  1411. v-model:show={data.modelTrainStatus}
  1412. preset="card"
  1413. class={[styles.attendClassModal, styles.trainClassModal]}
  1414. title={'作业设置'}>
  1415. <TrainSettings
  1416. detailId={data.detailId}
  1417. subjectId={data.subjectId}
  1418. classGroupId={data.classGroupId}
  1419. onClose={() => (data.modelTrainStatus = false)}
  1420. onConfirm={() => {
  1421. // 布置完作业之后直接关闭
  1422. // setTimeout(() => {
  1423. // handleStop();
  1424. // if (state.application) {
  1425. // emit('close');
  1426. // } else {
  1427. // window.close();
  1428. // }
  1429. // }, 1000);
  1430. data.modelTrainStatus = false;
  1431. }}
  1432. />
  1433. </NModal>
  1434. <NModal
  1435. transformOrigin="center"
  1436. class={['modalTitle background']}
  1437. title={'节拍器'}
  1438. preset="card"
  1439. v-model:show={showModalBeat.value}
  1440. style={{ width: '687px' }}>
  1441. <div class={styles.modeWrap}>
  1442. <iframe
  1443. src={`${vaildUrl()}/metronome/?id=${new Date().getTime()}`}
  1444. scrolling="no"
  1445. frameborder="0"
  1446. width="100%"
  1447. height={'650px'}></iframe>
  1448. </div>
  1449. </NModal>
  1450. <NModal
  1451. transformOrigin="center"
  1452. class={['background']}
  1453. v-model:show={showModalTone.value}>
  1454. <div>
  1455. <PlaceholderTone
  1456. onClose={() => {
  1457. showModalTone.value = false;
  1458. }}></PlaceholderTone>
  1459. </div>
  1460. </NModal>
  1461. <NModal
  1462. transformOrigin="center"
  1463. v-model:show={showModalTime.value}
  1464. class={['modalTitle background']}
  1465. title={'计时器'}
  1466. preset="card"
  1467. style={{ width: px2vw(772) }}>
  1468. <div>
  1469. <TimerMeter></TimerMeter>
  1470. </div>
  1471. </NModal>
  1472. <NModal
  1473. transformOrigin="center"
  1474. v-model:show={data.removeVisiable}
  1475. preset="card"
  1476. class={['modalTitle', styles.removeVisiable]}
  1477. title={data.removeTitle}>
  1478. <div class={styles.studentRemove}>
  1479. <p>{data.removeContent}</p>
  1480. <NSpace class={styles.btnGroupModal} justify="center">
  1481. <NButton round onClick={() => (data.removeVisiable = false)}>
  1482. 取消
  1483. </NButton>
  1484. <NButton
  1485. round
  1486. type="primary"
  1487. onClick={() => {
  1488. //
  1489. if (globalState.application) {
  1490. document.exitFullscreen
  1491. ? document.exitFullscreen()
  1492. : document.mozCancelFullScreen
  1493. ? document.mozCancelFullScreen()
  1494. : document.webkitExitFullscreen
  1495. ? document.webkitExitFullscreen()
  1496. : '';
  1497. emit('close');
  1498. } else {
  1499. window.close();
  1500. }
  1501. }}>
  1502. 确定
  1503. </NButton>
  1504. </NSpace>
  1505. </div>
  1506. </NModal>
  1507. </div>
  1508. );
  1509. }
  1510. });