index.tsx 49 KB

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