index.tsx 78 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417
  1. import {
  2. defineComponent,
  3. onMounted,
  4. reactive,
  5. onUnmounted,
  6. ref,
  7. Transition,
  8. computed,
  9. nextTick,
  10. watch,
  11. toRef
  12. } from 'vue';
  13. import styles from './index.module.less';
  14. import 'plyr/dist/plyr.css';
  15. import MusicScore from './component/musicScore';
  16. // import iconChange from './image/icon-change.png';
  17. // import iconMenu from './image/icon-menu.png';
  18. // import iconUp from './image/icon-up.png';
  19. // import iconDown from './image/icon-down.png';
  20. // import iconNote from './image/icon-note.png';
  21. // import iconWhiteboard from './image/icon-whiteboard.png';
  22. // import iconAssignHomework from './image/icon-assignHomework.png';
  23. // import iconClose from './image/icon-close.png';
  24. // import iconOverPreivew from './image/icon-over-preview.png';
  25. import { Vue3Lottie } from 'vue3-lottie';
  26. import playLoadData from './datas/data.json';
  27. // import Moveable from 'moveable';
  28. import VideoPlay from './component/video-play';
  29. import {
  30. useMessage,
  31. NDrawer,
  32. NDrawerContent,
  33. NModal,
  34. NSpace,
  35. NButton,
  36. NCollapse,
  37. NCollapseItem,
  38. NTooltip
  39. } from 'naive-ui';
  40. import CardType from '@/components/card-type';
  41. import Pen from './component/tools/pen';
  42. import AudioPay from './component/audio-pay';
  43. import TrainSettings from './model/train-settings';
  44. import { useRoute } from 'vue-router';
  45. import {
  46. api_teacherChapterLessonCoursewareDetail,
  47. courseScheduleUpdate,
  48. lessonCoursewareDetail,
  49. lessonPreTrainingPage,
  50. queryCourseware
  51. } from '../prepare-lessons/api';
  52. import { vaildUrl } from '/src/utils/urlUtils';
  53. import TimerMeter from '/src/components/timerMeter';
  54. import { iframeDislableKeyboard, px2vw } from '/src/utils';
  55. import PlaceholderTone from '/src/components/layout/modals/placeholderTone';
  56. import { state as globalState } from '/src/state';
  57. import Chapter from './model/chapter';
  58. import { useRouter } from 'vue-router';
  59. import { useUserStore } from '@/store/modules/users';
  60. import iconNote from './new-image/icon-note.png';
  61. import iconWhite from './new-image/icon-white.png';
  62. import rightIconEnd from './image/right_icon1.png';
  63. import rightIconArrange from './image/right_icon2.png';
  64. import rightIconPostil from './image/right_icon3.png';
  65. import rightIconWhiteboard from './image/right_icon4.png';
  66. import rightIconMetronome from './image/right_icon5.png';
  67. import rightIconTuner from './image/right_icon6.png';
  68. import rightIconTimer from './image/right_icon7.png';
  69. import rightIconCall from './image/right_icon10.png';
  70. import rightIconPackUp from './image/right_icon11.png';
  71. import leftIconPackUp from './image/right_icon8.png';
  72. import rightIconMusic from './image/right_icon9.png';
  73. import bottomIconSwitch from './image/bottom_icon1.png';
  74. import bottomIconResource from './image/bottom_icon2.png';
  75. import bottomIconPre from './image/bottom_icon3.png';
  76. import bottomIconNext from './image/bottom_icon4.png';
  77. import rightIconTool from './image/right_icon12.png';
  78. import rightHideIcon from './image/right_hide_icon.png';
  79. import leftHideIcon from './image/left_hide_icon.png';
  80. import SelectResources from '../prepare-lessons/model/select-resources';
  81. import { getStudentAfterWork, getStudentList } from '../studentList/api';
  82. import TheNoticeBar from '/src/components/TheNoticeBar';
  83. import ClassWork from './model/class-work';
  84. import SelectClass from './model/select-class';
  85. import SourceList from './model/source-list';
  86. import RhythmModal from './component/rhythm-modal';
  87. import InstruemntDetail from '/src/views/prepare-lessons/model/source-instrument/detail';
  88. import TheotyDetail from '/src/views/prepare-lessons/model/source-knowledge/detail';
  89. import MusicDetail from '/src/views/prepare-lessons/model/source-music/detail';
  90. import ListenModal from '/src/components/card-preview/listen-modal';
  91. import Train from '../prepare-lessons/components/lesson-main/train';
  92. import ResourceMain from '../prepare-lessons/components/resource-main';
  93. import { useResizeObserver } from '@vueuse/core';
  94. import { storage } from '/src/utils/storage';
  95. import { ACCESS_TOKEN_ADMIN } from '/src/store/mutation-types';
  96. import useDrag from '@/hooks/useDrag';
  97. import { getGuidanceShow } from '@/hooks/useDrag/useDragGuidance';
  98. import Dragbom from '@/hooks/useDrag/dragbom';
  99. import { api_cousseScheduleDetail } from '/src/api/user';
  100. export type ToolType = 'init' | 'pen' | 'whiteboard' | 'call';
  101. export type ToolItem = {
  102. type: ToolType;
  103. name: string;
  104. icon: string;
  105. };
  106. export default defineComponent({
  107. name: 'CoursewarePlay',
  108. props: {
  109. type: {
  110. type: String,
  111. default: ''
  112. },
  113. courseId: {
  114. type: String,
  115. default: ''
  116. },
  117. instrumentId: {
  118. type: [String, Number],
  119. default: ''
  120. },
  121. // 教材编号
  122. lessonCourseId: {
  123. type: [String, Number],
  124. default: ''
  125. },
  126. detailId: {
  127. type: String,
  128. default: ''
  129. },
  130. // 班级编号
  131. classGroupId: {
  132. type: String,
  133. default: ''
  134. },
  135. // 上课记录编号
  136. classId: {
  137. type: String,
  138. defaault: ''
  139. },
  140. preStudentNum: {
  141. type: [String, Number],
  142. default: ''
  143. }
  144. },
  145. emits: ['close'],
  146. setup(props, { emit }) {
  147. const message = useMessage();
  148. const route = useRoute();
  149. const router = useRouter();
  150. const users = useUserStore();
  151. /** 设置播放容器 16:9 */
  152. const parentContainer = reactive({
  153. width: '100vw'
  154. });
  155. // const NPopoverRef = ref();
  156. // const setContainer = () => {
  157. // const min = Math.min(screen.width, screen.height);
  158. // const max = Math.max(screen.width, screen.height);
  159. // const width = min * (16 / 9);
  160. // if (width > max) {
  161. // parentContainer.width = '100vw';
  162. // return;
  163. // } else {
  164. // parentContainer.width = width + 'px';
  165. // }
  166. // };
  167. const handleInit = (type = 0) => {
  168. //设置容器16:9
  169. // setContainer();
  170. };
  171. handleInit();
  172. onUnmounted(() => {
  173. handleInit(1);
  174. });
  175. const data = reactive({
  176. type: 'class' as '' | 'preview' | 'class', // 预览类型
  177. courseId: '' as any, // 课件编号
  178. instrumentId: '' as any, // 声部编号
  179. lessonCourseId: '' as any, // 教材编号
  180. lessonCoursewareDetailId: '' as any, // 章节
  181. lessonCoursewareSubjectList: [] as any, // 教材上的声部
  182. detailId: '' as any, // 编号 - 课程编号
  183. classGroupId: '' as any, // 上课时需要 班级编号
  184. classId: '' as any, // 上课编号
  185. preStudentNum: '' as any, // 班上学生
  186. // detail: null,
  187. knowledgePointList: [] as any,
  188. itemList: [] as any,
  189. // showHead: true,
  190. // isCourse: false,
  191. // isRecordPlay: false,
  192. videoRefs: {} as any[],
  193. audioRefs: {} as any[],
  194. modelAttendStatus: false, // 布置作业提示弹窗
  195. modalAttendMessage: '本节课未设置课后作业,是否继续?',
  196. modelTrainStatus: false, // 训练设置
  197. selectClassStatus: false, // 选择课件
  198. homeworkStatus: true, // 布置作业完成时
  199. removeVisiable: false,
  200. nextEndShow: false,
  201. removeTitle: '',
  202. removeContent: '',
  203. removeCourseStatus: false, // 是否布置作业
  204. teacherChapterName: '',
  205. lessonPreTrainingId: null,
  206. selectResourceStatus: false,
  207. videoState: 'init' as 'init' | 'play',
  208. videoItemRef: null as any,
  209. animationState: 'start' as 'start' | 'end'
  210. });
  211. const activeData = reactive({
  212. isAutoPlay: false, // 是否自动播放
  213. nowTime: 0,
  214. model: true, // 遮罩
  215. isAnimation: true, // 是否动画
  216. // videoBtns: true, // 视频
  217. // currentTime: 0,
  218. // duration: 0,
  219. timer: null as any,
  220. item: null as any
  221. });
  222. // 键盘事件监听状态
  223. const listenerKeyUpState = ref(false);
  224. const getDetail = async () => {
  225. try {
  226. const res = await api_teacherChapterLessonCoursewareDetail(
  227. data.courseId
  228. );
  229. data.teacherChapterName = res.data.name || '';
  230. // 布置的作业编号
  231. data.lessonPreTrainingId = res.data.lessonPreTrainingId;
  232. // activeData.isAutoPlay = res.data.autoPlay || false; // 自动播放
  233. const tempRows = res.data.chapterKnowledgeList || [];
  234. const temp: any = [];
  235. const allItem: any = [];
  236. tempRows.forEach((row: any, index: number) => {
  237. if (!Array.isArray(row.chapterKnowledgeMaterialList)) {
  238. return;
  239. }
  240. const childList: any[] = [];
  241. row.chapterKnowledgeMaterialList.forEach((child: any) => {
  242. if (!child.removeFlag) {
  243. childList.push({
  244. id: child.id,
  245. materialId: child.bizId,
  246. coverImg: child.bizInfo.coverImg,
  247. type: child.type,
  248. title: child.bizInfo.name,
  249. dataJson: child.dataJson,
  250. isCollect: !!child.favoriteFlag,
  251. isSelected: child.source === 'PLATFORM' ? true : false,
  252. content: child.bizInfo.content,
  253. parentIndex: index
  254. });
  255. }
  256. });
  257. temp.push({
  258. title: row.name,
  259. list: childList
  260. });
  261. allItem.push(...childList);
  262. });
  263. data.knowledgePointList = temp;
  264. data.itemList = allItem?.map((m: any) => {
  265. return {
  266. ...m,
  267. iframeRef: null,
  268. videoEle: null,
  269. audioEle: null,
  270. autoPlay: res.data.autoPlay || false, //加载完成是否自动播放
  271. isprepare: false, // 视频是否加载完成
  272. isRender: false // 是否渲染了
  273. };
  274. });
  275. setTimeout(() => {
  276. data.animationState = 'end';
  277. }, 500);
  278. } catch (e) {
  279. //
  280. console.log(e);
  281. }
  282. };
  283. const showModalBeat = ref(false);
  284. const showModalTone = ref(false);
  285. const showModalTime = ref(false);
  286. // ifram事件处理
  287. const iframeHandle = (ev: MessageEvent) => {
  288. // console.log(ev.data?.api, ev.data, 'ev.data');
  289. if (ev.data?.api === 'headerTogge') {
  290. activeData.model =
  291. ev.data.show || (ev.data.playState == 'play' ? false : true);
  292. }
  293. //
  294. if (ev.data?.api === 'onAttendToggleMenu') {
  295. activeData.model = !activeData.model;
  296. }
  297. if (ev.data?.api === 'api_fingerPreView') {
  298. clearInterval(activeData.timer);
  299. activeData.model = !ev.data.state;
  300. }
  301. //
  302. if (ev.data?.api === 'documentBodyKeyup') {
  303. if (ev.data?.code === 'ArrowUp') {
  304. setModalOpen();
  305. handlePreAndNext('up');
  306. }
  307. if (ev.data?.code === 'ArrowDown') {
  308. setModalOpen();
  309. handlePreAndNext('down');
  310. }
  311. }
  312. // 点名返回
  313. if (ev.data?.api === 'callBack') {
  314. closeStudyTool();
  315. studyData.callShow = false;
  316. }
  317. if (ev.data?.api === 'onLogin') {
  318. const documentDom: any = document;
  319. documentDom.exitFullscreen
  320. ? documentDom.exitFullscreen()
  321. : documentDom.mozCancelFullScreen
  322. ? documentDom.mozCancelFullScreen()
  323. : documentDom.webkitExitFullscreen
  324. ? documentDom.webkitExitFullscreen()
  325. : '';
  326. users.logout();
  327. router.replace('/login');
  328. }
  329. };
  330. onMounted(async () => {
  331. // initMoveable();
  332. const query = route.query;
  333. // console.log(query, props.preStudentNum, '学生人数');
  334. // 先取参数,
  335. data.type = props.type || (query.type as any);
  336. data.courseId = props.courseId || query.courseId;
  337. data.instrumentId = props.instrumentId || query.instrumentId;
  338. data.detailId = props.detailId || query.detailId;
  339. data.lessonCourseId = props.lessonCourseId || query.lessonCourseId;
  340. data.classGroupId = props.classGroupId || query.classGroupId;
  341. data.classId = props.classId || query.classId;
  342. data.preStudentNum = props.preStudentNum || query.preStudentNum;
  343. window.addEventListener('message', iframeHandle);
  344. if (data.classId) {
  345. const res = await api_cousseScheduleDetail(data.classId);
  346. data.courseId = res.data.useChapterLessonCoursewareId;
  347. }
  348. getDetail();
  349. getLessonCoursewareDetail();
  350. if (data.type === 'preview') {
  351. // 预览隐藏点名和布置作业
  352. const hideListIds = [2, 10];
  353. let index = rightList.length - 1;
  354. while (index >= 0) {
  355. if (hideListIds.includes(rightList[index].id)) {
  356. console.log(index);
  357. rightList.splice(index, 1);
  358. }
  359. index--;
  360. }
  361. }
  362. rollCallStudentList();
  363. });
  364. // const onFullScreen = () => {
  365. // if (data.type === 'preview') {
  366. // const el: any = document.querySelector('#app');
  367. // if (el.mozRequestFullScreen) {
  368. // el.mozRequestFullScreen();
  369. // } else if (el.webkitRequestFullscreen) {
  370. // el.webkitRequestFullscreen();
  371. // } else if (el.requestFullScreen) {
  372. // el.requestFullscreen();
  373. // }
  374. // }
  375. // };
  376. const popupData = reactive({
  377. open: false,
  378. activeIndex: 0,
  379. courseActiveIndex: 0, // 课件选择的第几个
  380. toolOpen: false, // 工具弹窗控制
  381. chapterOpen: false, // 切换章节
  382. chapterDetails: [] as any,
  383. courseId: null as any, // 章节编号
  384. chapterLoading: false // 加载数据
  385. });
  386. watch(
  387. () => [popupData.open, popupData.chapterOpen],
  388. val => {
  389. // 为了处理window电脑滑动时的问题 - pointr-events
  390. setTimeout(() => {
  391. const drawers = document.querySelectorAll('.n-drawer-container');
  392. if (val[0] || val[1]) {
  393. drawers?.forEach(drawer => {
  394. drawer.classList.remove('n-drawer-container-relative');
  395. });
  396. } else {
  397. drawers?.forEach(drawer => {
  398. drawer.classList.add('n-drawer-container-relative');
  399. });
  400. }
  401. }, 200);
  402. }
  403. );
  404. const formatParentId = (id: any, list: any, ids = [] as any) => {
  405. for (const item of list) {
  406. if (item.knowledgeList && item.knowledgeList.length > 0) {
  407. const cIds: any = formatParentId(id, item.knowledgeList, [
  408. ...ids,
  409. item.id
  410. ]);
  411. if (cIds.includes(id)) {
  412. return cIds;
  413. }
  414. }
  415. if (item.id === id) {
  416. return [...ids, id];
  417. }
  418. }
  419. return ids;
  420. };
  421. /** 获取章节 */
  422. const getLessonCoursewareDetail = async () => {
  423. try {
  424. const res = await lessonCoursewareDetail({
  425. id: data.lessonCourseId,
  426. instrumentId: data.instrumentId
  427. });
  428. popupData.chapterDetails = res.data.lessonList || [];
  429. const ids = formatParentId(data.detailId, popupData.chapterDetails);
  430. data.lessonCoursewareDetailId = ids[0];
  431. console.log(res.data, 'data');
  432. data.lessonCoursewareSubjectList = res.data.instrumentList || [];
  433. } catch {
  434. //
  435. }
  436. };
  437. /** 更新上课记录 */
  438. const classCourseScheduleUpdate = async () => {
  439. try {
  440. if (!data.classId) return;
  441. await courseScheduleUpdate({
  442. lessonCoursewareKnowledgeDetailId: data.detailId,
  443. id: data.classId
  444. });
  445. } catch {
  446. //
  447. }
  448. };
  449. const activeName = computed(() => {
  450. let name = '';
  451. popupData.chapterDetails.forEach((chapter: any) => {
  452. if (chapter.id === data.lessonCoursewareDetailId) {
  453. // name = chapter.name;
  454. chapter.knowledgeList?.forEach((know: any) => {
  455. if (know.id === data.detailId) {
  456. name = know.name;
  457. }
  458. });
  459. }
  460. });
  461. return name;
  462. });
  463. /**停止所有的播放 */
  464. const handleStop = (isStop = true) => {
  465. // console.log(isStop, 'stop');
  466. for (let i = 0; i < data.itemList.length; i++) {
  467. const activeItem = data.itemList[i];
  468. if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
  469. try {
  470. if (isStop) {
  471. activeItem.videoEle?.currentTime(0);
  472. }
  473. activeItem.videoEle?.pause();
  474. } catch (e: any) {
  475. // console.log(e, 'e');
  476. }
  477. }
  478. if (activeItem.type === 'SONG' && activeItem.audioEle) {
  479. activeItem.audioEle?.stop();
  480. }
  481. // console.log('🚀 ~ activeItem:', activeItem)
  482. // 停止曲谱的播放
  483. if (activeItem.type === 'MUSIC') {
  484. activeItem.iframeRef?.contentWindow?.postMessage(
  485. { api: 'setPlayState' },
  486. '*'
  487. );
  488. }
  489. if (
  490. activeItem.type === 'INSTRUMENT' ||
  491. activeItem.type === 'MUSICIAN' ||
  492. activeItem.type === 'MUSIC_WIKI'
  493. ) {
  494. activeItem.iframeRef?.handleChangeAudio('pause');
  495. }
  496. // console.log(activeItem.type, 'activeItem.type');
  497. if (activeItem.type === 'RHYTHM') {
  498. activeItem.iframeRef?.contentWindow?.postMessage(
  499. { api: 'setPlayState', data: false },
  500. '*'
  501. );
  502. }
  503. if (activeItem.type === 'LISTEN') {
  504. activeItem.iframeRef?.contentWindow?.postMessage(
  505. { api: 'setPlayState' },
  506. '*'
  507. );
  508. }
  509. }
  510. };
  511. // 切换素材
  512. const toggleMaterial = (itemActive: any) => {
  513. const index = data.itemList.findIndex((n: any) => n.id == itemActive);
  514. if (index > -1) {
  515. handleSwipeChange(index);
  516. }
  517. };
  518. /** 延迟收起模态框 */
  519. const setModelOpen = () => {
  520. clearTimeout(activeData.timer);
  521. message.destroyAll();
  522. activeData.timer = setTimeout(() => {
  523. activeData.model = false;
  524. Object.values(data.videoRefs).map((n: any) =>
  525. n?.toggleHideControl(false)
  526. );
  527. Object.values(data.audioRefs).map((n: any) =>
  528. n?.toggleHideControl(false)
  529. );
  530. }, 4000);
  531. };
  532. /** 立即收起所有的模态框 */
  533. const clearModel = () => {
  534. clearTimeout(activeData.timer);
  535. message.destroyAll();
  536. activeData.model = false;
  537. Object.values(data.videoRefs).map((n: any) =>
  538. n?.toggleHideControl(false)
  539. );
  540. Object.values(data.audioRefs).map((n: any) =>
  541. n?.toggleHideControl(false)
  542. );
  543. };
  544. const toggleModel = (type = true) => {
  545. activeData.model = type;
  546. Object.values(data.videoRefs).map((n: any) => n?.toggleHideControl(type));
  547. Object.values(data.audioRefs).map((n: any) => n?.toggleHideControl(type));
  548. };
  549. // 双击
  550. const handleDbClick = (item: any) => {
  551. if (item && item.type === 'VIDEO') {
  552. const videoEle: HTMLVideoElement = item.videoEle;
  553. if (videoEle) {
  554. if (videoEle.paused) {
  555. message.destroyAll();
  556. videoEle.play();
  557. } else {
  558. message.warning('已暂停');
  559. videoEle.pause();
  560. }
  561. }
  562. }
  563. };
  564. // ppt iframe 点击事件
  565. // const iframeClick = () => {
  566. // if(document.all) {
  567. // document.getElementById("iframe-ppt")?.attachEvent("click", () => {
  568. // activeData.model = !activeData.model
  569. // });
  570. // } else {
  571. // document.getElementById("iframe-ppt")?.contentWindow?.postMessage({
  572. // api: 'onAttendToggleMenu'
  573. // }, '*');
  574. // }
  575. // }
  576. // 切换播放
  577. // const togglePlay = (m: any, isPlay: boolean) => {
  578. // if (isPlay) {
  579. // m.videoEle?.play();
  580. // } else {
  581. // m.videoEle?.pause();
  582. // }
  583. // };
  584. // const showIndex = ref(-4);
  585. const effectIndex = ref(3);
  586. const effects = [
  587. {
  588. prev: {
  589. transform: 'translate3d(0, 0, -800px) rotateX(180deg)'
  590. },
  591. next: {
  592. transform: 'translate3d(0, 0, -800px) rotateX(-180deg)'
  593. }
  594. },
  595. {
  596. prev: {
  597. transform: 'translate3d(-100%, 0, -800px)'
  598. },
  599. next: {
  600. transform: 'translate3d(100%, 0, -800px)'
  601. }
  602. },
  603. {
  604. prev: {
  605. transform: 'translate3d(-50%, 0, -800px) rotateY(80deg)'
  606. },
  607. next: {
  608. transform: 'translate3d(50%, 0, -800px) rotateY(-80deg)'
  609. }
  610. },
  611. {
  612. prev: {
  613. transform: 'translate3d(-100%, 0, -800px) rotateY(-120deg)'
  614. },
  615. next: {
  616. transform: 'translate3d(100%, 0, -800px) rotateY(120deg)'
  617. }
  618. },
  619. // 风车4
  620. {
  621. prev: {
  622. transform: 'translate3d(-50%, 50%, -800px) rotateZ(-14deg)',
  623. opacity: 0
  624. },
  625. next: {
  626. transform: 'translate3d(50%, 50%, -800px) rotateZ(14deg)',
  627. opacity: 0
  628. }
  629. },
  630. // 翻页5
  631. {
  632. prev: {
  633. transform: 'translateZ(-800px) rotate3d(0, -1, 0, 90deg)',
  634. opacity: 0
  635. },
  636. next: {
  637. transform: 'translateZ(-800px) rotate3d(0, 1, 0, 90deg)',
  638. opacity: 0
  639. },
  640. current: { transitionDelay: '700ms' }
  641. }
  642. ];
  643. const acitveTimer = ref();
  644. // 轮播切换
  645. const handleSwipeChange = (index: number) => {
  646. // 如果是当前正在播放 或者是视频最后一个
  647. if (popupData.activeIndex == index) return;
  648. data.animationState = 'start';
  649. data.videoState = 'init';
  650. handleStop();
  651. clearTimeout(acitveTimer.value);
  652. activeData.model = true;
  653. checkedAnimation(popupData.activeIndex, index);
  654. popupData.activeIndex = index;
  655. // 处理资源列表滚动
  656. nextTick(() => {
  657. scrollResourceSection();
  658. });
  659. acitveTimer.value = setTimeout(
  660. () => {
  661. const item = data.itemList[index];
  662. if (item) {
  663. if (item.type == 'MUSIC') {
  664. activeData.model = true;
  665. }
  666. if (item.type === 'SONG') {
  667. // 自动播放下一个音频
  668. clearTimeout(activeData.timer);
  669. message.destroyAll();
  670. // item.autoPlay = false;
  671. // nextTick(() => {
  672. // item.audioEle?.onPlay();
  673. // });
  674. }
  675. if (item.type === 'VIDEO') {
  676. // 自动播放下一个视频
  677. clearTimeout(activeData.timer);
  678. message.destroyAll();
  679. nextTick(() => {
  680. if (item.error) {
  681. // console.log(item, 'item error');
  682. item.videoEle?.src(item.content);
  683. item.error = false;
  684. // item.videoEle?.onPlay();
  685. }
  686. data.animationState = 'end';
  687. });
  688. }
  689. if (item.type === 'PPT') {
  690. //
  691. }
  692. }
  693. },
  694. activeData.isAnimation ? 800 : 0
  695. );
  696. };
  697. /** 是否有转场动画 */
  698. const checkedAnimation = (index: number, nextIndex?: number) => {
  699. const item = data.itemList[index];
  700. const nextItem = data.itemList[nextIndex!];
  701. if (nextItem) {
  702. if (nextItem.knowledgePointId != item.knowledgePointId) {
  703. activeData.isAnimation = true;
  704. return;
  705. }
  706. const videoEle = item.videoEle;
  707. const nextVideo = nextItem.videoEle;
  708. if (videoEle && videoEle.duration < 8 && index < nextIndex!) {
  709. activeData.isAnimation = false;
  710. } else if (nextVideo && nextVideo.duration < 8 && index > nextIndex!) {
  711. activeData.isAnimation = false;
  712. } else {
  713. activeData.isAnimation = true;
  714. }
  715. } else {
  716. activeData.isAnimation = item?.adviseStudyTimeSecond < 8 ? false : true;
  717. }
  718. };
  719. // 上一个知识点, 下一个知识点
  720. const handlePreAndNext = async (type: string) => {
  721. if (type === 'up') {
  722. // 判断上面是否还有章节
  723. if (popupData.activeIndex > 0) {
  724. handleSwipeChange(popupData.activeIndex - 1);
  725. return;
  726. }
  727. // 获取当前是哪个章节
  728. let detailIndex = popupData.chapterDetails.findIndex(
  729. (item: any) => item.id == data.lessonCoursewareDetailId
  730. );
  731. const detailItem =
  732. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  733. let lessonIndex = detailItem.findIndex(
  734. (item: any) => item.id == data.detailId
  735. );
  736. let lessonStatus = false; // 当前章节上面是否有内容
  737. let lessonCoursewareDetailId = '';
  738. let coursewareDetailKnowledgeId = '';
  739. while (lessonIndex >= 0) {
  740. lessonIndex--;
  741. if (lessonIndex >= 0) {
  742. if (detailItem[lessonIndex].coursewareNum > 0) {
  743. lessonStatus = true;
  744. lessonCoursewareDetailId =
  745. detailItem[lessonIndex].lessonCoursewareDetailId;
  746. coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
  747. }
  748. }
  749. if (lessonStatus) {
  750. break;
  751. }
  752. }
  753. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  754. if (lessonStatus) {
  755. popupData.courseId = coursewareDetailKnowledgeId;
  756. data.selectClassStatus = true;
  757. return;
  758. }
  759. let prevLessonStatus = false;
  760. while (detailIndex >= 0) {
  761. detailIndex--;
  762. const tempDetail =
  763. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  764. let tempLessonLength = tempDetail.length;
  765. while (tempLessonLength > 0) {
  766. if (tempDetail[tempLessonLength - 1].coursewareNum > 0) {
  767. prevLessonStatus = true;
  768. lessonCoursewareDetailId =
  769. tempDetail[tempLessonLength - 1].lessonCoursewareDetailId;
  770. coursewareDetailKnowledgeId = tempDetail[tempLessonLength - 1].id;
  771. }
  772. tempLessonLength--;
  773. if (prevLessonStatus) {
  774. break;
  775. }
  776. }
  777. if (prevLessonStatus) {
  778. break;
  779. }
  780. }
  781. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  782. if (prevLessonStatus) {
  783. popupData.courseId = coursewareDetailKnowledgeId;
  784. data.selectClassStatus = true;
  785. return;
  786. }
  787. } else {
  788. if (popupData.activeIndex < data.itemList.length - 1) {
  789. handleSwipeChange(popupData.activeIndex + 1);
  790. return;
  791. }
  792. if (!isDownArrow.value) return;
  793. data.nextEndShow = true;
  794. }
  795. };
  796. // 当前课件结束之后选择下一个课件
  797. function handleNextEnd() {
  798. // 获取当前是哪个章节
  799. let detailIndex = popupData.chapterDetails.findIndex(
  800. (item: any) => item.id == data.lessonCoursewareDetailId
  801. );
  802. const detailItem =
  803. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  804. let lessonIndex = detailItem.findIndex(
  805. (item: any) => item.id == data.detailId
  806. );
  807. let lessonStatus = false; // 当前章节下面是否有内容
  808. let lessonCoursewareDetailId = '';
  809. let coursewareDetailKnowledgeId = '';
  810. while (lessonIndex < detailItem.length - 1) {
  811. lessonIndex++;
  812. if (lessonIndex >= 0) {
  813. if (detailItem[lessonIndex].coursewareNum > 0) {
  814. lessonStatus = true;
  815. lessonCoursewareDetailId =
  816. detailItem[lessonIndex].lessonCoursewareDetailId;
  817. coursewareDetailKnowledgeId = detailItem[lessonIndex].id;
  818. }
  819. }
  820. if (lessonStatus) {
  821. break;
  822. }
  823. }
  824. // 判断当前章节下面课程是否有内容,否则往下一个章节走
  825. if (lessonStatus) {
  826. popupData.courseId = coursewareDetailKnowledgeId;
  827. data.selectClassStatus = true;
  828. return;
  829. }
  830. let nextLessonStatus = false;
  831. while (detailIndex <= popupData.chapterDetails.length - 1) {
  832. detailIndex++;
  833. const tempDetail =
  834. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  835. let tempLessonLength = 0;
  836. while (tempLessonLength <= tempDetail.length - 1) {
  837. if (tempDetail[tempLessonLength].coursewareNum > 0) {
  838. nextLessonStatus = true;
  839. lessonCoursewareDetailId =
  840. tempDetail[tempLessonLength].lessonCoursewareDetailId;
  841. coursewareDetailKnowledgeId = tempDetail[tempLessonLength].id;
  842. }
  843. tempLessonLength++;
  844. if (nextLessonStatus) {
  845. break;
  846. }
  847. }
  848. if (nextLessonStatus) {
  849. break;
  850. }
  851. }
  852. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  853. if (nextLessonStatus) {
  854. popupData.courseId = coursewareDetailKnowledgeId;
  855. data.selectClassStatus = true;
  856. return;
  857. }
  858. }
  859. /** 弹窗关闭 */
  860. const handleClosePopup = () => {
  861. const item = data.itemList[popupData.activeIndex];
  862. if (item?.type == 'VIDEO' && !item.videoEle?.paused) {
  863. setModelOpen();
  864. }
  865. if (item?.type == 'SONG' && !item.audioEle?.paused) {
  866. setModelOpen();
  867. }
  868. };
  869. document.body.addEventListener('keyup', (e: KeyboardEvent) => {
  870. if (e.code === 'ArrowUp') {
  871. // if (popupData.activeIndex === 0) return;
  872. setModalOpen();
  873. handlePreAndNext('up');
  874. } else if (e.code === 'ArrowDown') {
  875. // if (popupData.activeIndex === data.itemList.length - 1) return;
  876. setModalOpen();
  877. handlePreAndNext('down');
  878. } else if (e.code === 'Space') {
  879. // const activeItem = data.itemList[popupData.activeIndex];
  880. // console.log(activeItem, activeItem.videoEle);
  881. // // 暂停视频和曲谱的播放
  882. // if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
  883. // activeItem.videoEle?.play();
  884. // }
  885. // if (activeItem.type === 'SONG' && activeItem.audioEle) {
  886. // activeItem.audioEle?.play();
  887. // }
  888. // if (activeItem.type === 'MUSIC') {
  889. // activeItem.iframeRef?.contentWindow?.postMessage(
  890. // { api: 'setPlayState' },
  891. // '*'
  892. // );
  893. // }
  894. }
  895. });
  896. // const toggleListenerKeyUp = (type: string) => {
  897. // if (type === 'remove') {
  898. // document.body.removeEventListener('keyup', () => {
  899. // listenerKeyUpState.value = false;
  900. // });
  901. // } else {
  902. // // 监听页面键盘事件 - 上下切换
  903. // document.body.addEventListener('keyup', (e: KeyboardEvent) => {
  904. // // console.log(e, 'e');
  905. // if (e.code === 'ArrowLeft') {
  906. // // if (popupData.activeIndex === 0) return;
  907. // setModalOpen();
  908. // handlePreAndNext('up');
  909. // } else if (e.code === 'ArrowRight') {
  910. // // if (popupData.activeIndex === data.itemList.length - 1) return;
  911. // setModalOpen();
  912. // handlePreAndNext('down');
  913. // }
  914. // });
  915. // listenerKeyUpState.value = true;
  916. // }
  917. // };
  918. // 监听切换到ppt课件时,手动移除键盘监听器
  919. // watch(() => popupData.activeIndex, () => {
  920. // const activeItem = data.itemList[popupData.activeIndex];
  921. // if (activeItem?.type === 'PPT') {
  922. // toggleListenerKeyUp('remove')
  923. // } else {
  924. // !listenerKeyUpState.value && toggleListenerKeyUp('add')
  925. // }
  926. // })
  927. watch(
  928. () => popupData.activeIndex,
  929. () => {
  930. const item = data.itemList[popupData.activeIndex];
  931. popupData.courseActiveIndex = item.parentIndex;
  932. }
  933. );
  934. const setModalOpen = (status = true) => {
  935. clearTimeout(activeData.timer);
  936. activeData.model = status;
  937. Object.values(data.videoRefs).map((n: any) =>
  938. n?.toggleHideControl(status)
  939. );
  940. Object.values(data.audioRefs).map((n: any) =>
  941. n?.toggleHideControl(status)
  942. );
  943. };
  944. /** 教学数据 */
  945. const studyData = reactive({
  946. type: '' as ToolType,
  947. penShow: false,
  948. whiteboardShow: false,
  949. callShow: false,
  950. callStudentList: [] as any // 学生列表
  951. });
  952. /** 打开教学工具 */
  953. const openStudyTool = (item: ToolItem) => {
  954. handleStop(false);
  955. clearModel();
  956. popupData.toolOpen = false;
  957. studyData.type = item.type;
  958. switch (item.type) {
  959. case 'pen':
  960. studyData.penShow = true;
  961. break;
  962. case 'whiteboard':
  963. studyData.whiteboardShow = true;
  964. break;
  965. case 'call':
  966. studyData.callShow = true;
  967. break;
  968. }
  969. };
  970. /** 关闭教学工具 */
  971. const closeStudyTool = () => {
  972. studyData.type = 'init';
  973. toggleModel();
  974. setModelOpen();
  975. };
  976. const startShowModal = (
  977. val: 'setTimeIcon' | 'beatIcon' | 'toneIcon' | 'iconNote2'
  978. ) => {
  979. if (val == 'setTimeIcon') {
  980. showModalTime.value = true;
  981. }
  982. if (val == 'beatIcon') {
  983. showModalBeat.value = true;
  984. }
  985. if (val == 'toneIcon') {
  986. showModalTone.value = true;
  987. }
  988. };
  989. // 是否允许上一页
  990. const isUpArrow = computed(() => {
  991. /**
  992. * 1,判断当前课程中是否处在第一个资源;
  993. * 2,判断当前课程是否在当前章节的第一个;
  994. * 3,判断当前章节,当前课程上面还没有其它课程,是否有资源;
  995. * 4,判断当前章节上面还没有其它章节;
  996. * 5,判断上面章节里面课程是否有资源;
  997. */
  998. if (popupData.activeIndex > 0) {
  999. return true;
  1000. }
  1001. // 获取当前是哪个章节
  1002. let detailIndex = popupData.chapterDetails.findIndex(
  1003. (item: any) => item.id == data.lessonCoursewareDetailId
  1004. );
  1005. const detailItem =
  1006. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  1007. let lessonIndex = detailItem.findIndex(
  1008. (item: any) => item.id == data.detailId
  1009. );
  1010. // 说明已经是第一单元,第一课
  1011. if (detailIndex <= 0 && lessonIndex <= 0) {
  1012. return false;
  1013. }
  1014. let lessonStatus = false; // 当前章节上面是否有内容
  1015. while (lessonIndex >= 0) {
  1016. lessonIndex--;
  1017. if (lessonIndex >= 0) {
  1018. if (detailItem[lessonIndex].coursewareNum > 0) {
  1019. lessonStatus = true;
  1020. }
  1021. }
  1022. }
  1023. // 判断当前章节下面课程是否有内容,否则往上一个章节走
  1024. if (lessonStatus) {
  1025. return true;
  1026. }
  1027. // 已经是第一个章节了
  1028. if (detailIndex <= 0) {
  1029. return false;
  1030. }
  1031. let prevLessonStatus = false;
  1032. while (detailIndex >= 0) {
  1033. detailIndex--;
  1034. const tempDetail =
  1035. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  1036. let tempLessonLength = tempDetail.length;
  1037. while (tempLessonLength > 0) {
  1038. if (tempDetail[tempLessonLength - 1].coursewareNum > 0) {
  1039. prevLessonStatus = true;
  1040. }
  1041. tempLessonLength--;
  1042. }
  1043. if (prevLessonStatus) {
  1044. return true;
  1045. }
  1046. }
  1047. return false;
  1048. });
  1049. // 是否允许下一页
  1050. const isDownArrow = computed(() => {
  1051. if (popupData.activeIndex < data.itemList.length - 1) {
  1052. return true;
  1053. }
  1054. // 获取当前是哪个章节
  1055. let detailIndex = popupData.chapterDetails.findIndex(
  1056. (item: any) => item.id == data.lessonCoursewareDetailId
  1057. );
  1058. const detailItem =
  1059. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  1060. let lessonIndex = detailItem.findIndex(
  1061. (item: any) => item.id == data.detailId
  1062. );
  1063. // 说明已经是最后-单元,最后一课
  1064. if (
  1065. detailIndex >= popupData.chapterDetails.length - 1 &&
  1066. lessonIndex >= detailItem.length - 1
  1067. ) {
  1068. return false;
  1069. }
  1070. let lessonStatus = false; // 当前章节下面是否有内容
  1071. while (lessonIndex < detailItem.length - 1) {
  1072. lessonIndex++;
  1073. if (lessonIndex >= 0) {
  1074. if (detailItem[lessonIndex].coursewareNum > 0) {
  1075. lessonStatus = true;
  1076. }
  1077. }
  1078. }
  1079. // 判断当前章节下面课程是否有内容,否则往下一个章节走
  1080. if (lessonStatus) {
  1081. return true;
  1082. }
  1083. // 已经是最后一个章节了
  1084. if (detailIndex >= popupData.chapterDetails.length - 1) {
  1085. return false;
  1086. }
  1087. let nextLessonStatus = false;
  1088. while (detailIndex < popupData.chapterDetails.length - 1) {
  1089. detailIndex++;
  1090. const tempDetail =
  1091. popupData.chapterDetails[detailIndex]?.knowledgeList || [];
  1092. let tempLessonLength = 0;
  1093. while (tempLessonLength <= tempDetail.length - 1) {
  1094. if (tempDetail[tempLessonLength].coursewareNum > 0) {
  1095. nextLessonStatus = true;
  1096. }
  1097. tempLessonLength++;
  1098. }
  1099. if (nextLessonStatus) {
  1100. return true;
  1101. }
  1102. }
  1103. return false;
  1104. });
  1105. const activeVideoItem = computed(() => {
  1106. const item = data.itemList[popupData.activeIndex];
  1107. if (item && item.type && item.type.toLocaleUpperCase() === 'VIDEO') {
  1108. return item;
  1109. }
  1110. return {};
  1111. });
  1112. // 右侧菜单栏
  1113. const rightList = reactive([
  1114. {
  1115. name: '上一个',
  1116. icon: bottomIconPre,
  1117. id: 11
  1118. },
  1119. {
  1120. name: '下一个',
  1121. icon: bottomIconNext,
  1122. id: 12
  1123. },
  1124. {
  1125. name: '切换章节',
  1126. icon: bottomIconSwitch,
  1127. id: 13
  1128. },
  1129. {
  1130. name: '资源列表',
  1131. icon: bottomIconResource,
  1132. id: 14
  1133. },
  1134. {
  1135. name: '曲目资源',
  1136. icon: rightIconMusic,
  1137. id: 9
  1138. },
  1139. {
  1140. name: '点名',
  1141. icon: rightIconCall,
  1142. id: 10
  1143. },
  1144. {
  1145. name: '布置作业',
  1146. icon: rightIconArrange,
  1147. id: 2
  1148. },
  1149. {
  1150. name: '工具箱',
  1151. icon: rightIconTool,
  1152. id: 15
  1153. },
  1154. {
  1155. name: '结束课程',
  1156. name2: '结束预览',
  1157. icon: rightIconEnd,
  1158. id: 1
  1159. },
  1160. {
  1161. name: '收起',
  1162. icon: leftIconPackUp,
  1163. id: 8
  1164. }
  1165. ]);
  1166. const tooltipList = [
  1167. {
  1168. name: '节拍器',
  1169. icon: rightIconMetronome,
  1170. id: 5
  1171. },
  1172. {
  1173. name: '计时器',
  1174. icon: rightIconTimer,
  1175. id: 7
  1176. },
  1177. {
  1178. name: '批注',
  1179. icon: rightIconPostil,
  1180. id: 3
  1181. },
  1182. {
  1183. name: '白板',
  1184. icon: rightIconWhiteboard,
  1185. id: 4
  1186. }
  1187. // {
  1188. // name: '调音器',
  1189. // icon: rightIconTuner,
  1190. // id: 6
  1191. // }
  1192. ];
  1193. // 默认收起菜单
  1194. const columnShow = ref(true);
  1195. // 菜单位置
  1196. const columnPos = ref<'left' | 'right'>('left');
  1197. watch(columnPos, () => {
  1198. for (let i = 0; i < data.itemList.length; i++) {
  1199. const activeItem = data.itemList[i];
  1200. if (['RHYTHM', 'MUSIC'].includes(activeItem.type)) {
  1201. activeItem.iframeRef?.contentWindow?.postMessage(
  1202. { api: 'imagePos', data: columnPos.value },
  1203. '*'
  1204. );
  1205. }
  1206. }
  1207. });
  1208. // 右边栏操作
  1209. const operateRightBtn = async (id: number) => {
  1210. if (![8, 11, 12, 13, 14].includes(id)) {
  1211. handleStop(false);
  1212. }
  1213. switch (id) {
  1214. case 1:
  1215. if (data.type === 'preview') {
  1216. data.removeVisiable = true;
  1217. data.removeTitle = '结束预览';
  1218. data.removeContent = '请确认是否结束预览?';
  1219. } else {
  1220. const res = await getStudentAfterWork({
  1221. courseScheduleId: data.classId,
  1222. page: 1,
  1223. rows: 99
  1224. });
  1225. if (res.data.rows && res.data.rows.length) {
  1226. data.removeContent = '请确认是否结束课程?';
  1227. data.removeCourseStatus = false;
  1228. } else {
  1229. data.removeContent = '本次课堂尚未布置作业,是否结束课程?';
  1230. data.removeCourseStatus = true;
  1231. }
  1232. data.removeVisiable = true;
  1233. data.removeTitle = '结束课程';
  1234. }
  1235. break;
  1236. case 2:
  1237. // 学生人数必须大于0,才可以布置作业
  1238. if (data.preStudentNum <= 0) return;
  1239. // const res = await lessonPreTrainingPage({
  1240. // coursewareKnowledgeDetailId: data.detailId,
  1241. // instrumentId: data.instrumentId,
  1242. // page: 1,
  1243. // rows: 99
  1244. // });
  1245. // if (res.data.rows && res.data.rows.length) {
  1246. // data.modalAttendMessage = '本节课已设置课后作业,是否布置?';
  1247. // }
  1248. // data.modelAttendStatus = true;
  1249. const res = await getStudentAfterWork({
  1250. courseScheduleId: data.classId,
  1251. page: 1,
  1252. rows: 99
  1253. });
  1254. if (res.data.rows && res.data.rows.length) {
  1255. // data.modalAttendMessage = '请确认是否结束课程?';
  1256. data.modalAttendMessage = '本次课程已布置作业,是否继续?';
  1257. data.modelAttendStatus = true;
  1258. } else {
  1259. data.modelTrainStatus = true;
  1260. nextTick(() => {
  1261. getModalHeight();
  1262. });
  1263. data.modelAttendStatus = false;
  1264. }
  1265. break;
  1266. case 3:
  1267. openStudyTool({
  1268. type: 'pen',
  1269. icon: iconNote,
  1270. name: '批注'
  1271. });
  1272. break;
  1273. case 4:
  1274. openStudyTool({
  1275. type: 'whiteboard',
  1276. icon: iconWhite,
  1277. name: '白板'
  1278. });
  1279. break;
  1280. case 5:
  1281. startShowModal('beatIcon');
  1282. break;
  1283. case 6:
  1284. startShowModal('toneIcon');
  1285. break;
  1286. case 7:
  1287. startShowModal('setTimeIcon');
  1288. break;
  1289. case 8:
  1290. columnShow.value = false;
  1291. break;
  1292. case 9:
  1293. // 选择曲目时需要暂停所有播放
  1294. handleStop(false);
  1295. data.selectResourceStatus = true;
  1296. break;
  1297. case 10:
  1298. // 点名
  1299. // await rollCallStudentList();
  1300. if (studyData.callStudentList.length > 0) {
  1301. openStudyTool({
  1302. type: 'call',
  1303. icon: iconWhite,
  1304. name: '点名'
  1305. });
  1306. return;
  1307. }
  1308. break;
  1309. case 11:
  1310. if (!isUpArrow.value) return;
  1311. handlePreAndNext('up');
  1312. break;
  1313. case 12:
  1314. if (!isDownArrow.value) return;
  1315. handlePreAndNext('down');
  1316. break;
  1317. case 13:
  1318. popupData.chapterOpen = true;
  1319. break;
  1320. case 14:
  1321. popupData.open = true;
  1322. nextTick(() => {
  1323. scrollResourceSection();
  1324. });
  1325. break;
  1326. default:
  1327. break;
  1328. }
  1329. };
  1330. // 点名学生列表
  1331. const rollCallStudentList = async () => {
  1332. //
  1333. if (!data.classId) return;
  1334. try {
  1335. const res = await getStudentList({
  1336. classGroupId: data.classGroupId,
  1337. page: 1,
  1338. rows: 999
  1339. });
  1340. const result = res.data || {};
  1341. if (Array.isArray(result.rows) && result.rows.length > 0) {
  1342. const tempStudents: any = [];
  1343. result.rows.forEach((row: any) => {
  1344. tempStudents.push({
  1345. name: row.nickname,
  1346. img: row.avatar
  1347. });
  1348. });
  1349. studyData.callStudentList = [...tempStudents];
  1350. }
  1351. } catch {
  1352. //
  1353. }
  1354. };
  1355. // 滚动到某个元素的位置
  1356. const scrollResourceSection = () => {
  1357. const drawerCardItemRefs =
  1358. document.querySelectorAll('.drawerCardItemRef');
  1359. if (
  1360. popupData.activeIndex >= 0 &&
  1361. drawerCardItemRefs[popupData.activeIndex]
  1362. ) {
  1363. drawerCardItemRefs[popupData.activeIndex].scrollIntoView();
  1364. }
  1365. };
  1366. const getModalHeight = () => {
  1367. const dom: any = document.querySelector('#model-homework-height');
  1368. if (dom) {
  1369. useResizeObserver(dom as HTMLElement, (entries: any) => {
  1370. const entry = entries[0];
  1371. const { height } = entry.contentRect;
  1372. dom.style.setProperty('--window-page-lesson-height', height + 'px');
  1373. });
  1374. }
  1375. };
  1376. /* 弹窗加拖动 */
  1377. // 引导页
  1378. getGuidanceShow();
  1379. // 选择课件弹窗
  1380. const selCourBoxClass = 'selCourBoxClass_drag';
  1381. const selCourDragData = useDrag(
  1382. [`${selCourBoxClass}>.n-card-header`, `${selCourBoxClass} .bom_drag`],
  1383. selCourBoxClass,
  1384. toRef(data, 'selectClassStatus'),
  1385. users.info.id
  1386. );
  1387. // 选择资源弹窗
  1388. const selResourBoxClass = 'selResourBoxClass_drag';
  1389. const selResourDragData = useDrag(
  1390. [
  1391. `${selResourBoxClass} .select-resource>.n-tabs>.n-tabs-nav--top.n-tabs-nav`,
  1392. `${selResourBoxClass} .bom_drag`
  1393. ],
  1394. selResourBoxClass,
  1395. toRef(data, 'selectResourceStatus'),
  1396. users.info.id
  1397. );
  1398. // 结束预览 结束课程确认弹窗
  1399. const removeResourBoxClass = 'removeResourBoxClass_drag';
  1400. const removeResourDragData = useDrag(
  1401. [
  1402. `${removeResourBoxClass}>.n-card-header`,
  1403. `${removeResourBoxClass} .bom_drag`
  1404. ],
  1405. removeResourBoxClass,
  1406. toRef(data, 'removeVisiable'),
  1407. users.info.id
  1408. );
  1409. // 章节next弹窗
  1410. const nextEndBoxClass = 'nextEndBoxClass_drag';
  1411. const nextEndBoxDragData = useDrag(
  1412. [`${nextEndBoxClass}>.n-card-header`, `${nextEndBoxClass} .bom_drag`],
  1413. nextEndBoxClass,
  1414. toRef(data, 'nextEndShow'),
  1415. users.info.id
  1416. );
  1417. // 章节切换弹窗
  1418. const chapterConBoxClass = 'chapterConBoxClass_drag';
  1419. const chapterConBoxDragData = useDrag(
  1420. [
  1421. `${chapterConBoxClass}>.n-card-header`,
  1422. `${chapterConBoxClass} .bom_drag`
  1423. ],
  1424. chapterConBoxClass,
  1425. toRef(popupData, 'chapterOpen'),
  1426. users.info.id
  1427. );
  1428. // 资源列表弹窗
  1429. const resourcesConBoxClass = 'resourcesConBoxClass_drag';
  1430. const resourcesConDragData = useDrag(
  1431. [
  1432. `${resourcesConBoxClass}>.n-card-header`,
  1433. `${resourcesConBoxClass} .bom_drag`
  1434. ],
  1435. resourcesConBoxClass,
  1436. toRef(popupData, 'open'),
  1437. users.info.id
  1438. );
  1439. // 计时器
  1440. const timerMeterConBoxClass = 'timerMeterConBoxClass_drag';
  1441. const timerMeterConDragData = useDrag(
  1442. [
  1443. `${timerMeterConBoxClass}>.n-card-header`,
  1444. `${timerMeterConBoxClass} .bom_drag`
  1445. ],
  1446. timerMeterConBoxClass,
  1447. showModalTime,
  1448. users.info.id
  1449. );
  1450. // 节拍器
  1451. const metronomeConBoxClass = 'metronomeConBoxClass_drag';
  1452. const metronomeConBoxDragData = useDrag(
  1453. [
  1454. `${metronomeConBoxClass}>.n-card-header`,
  1455. `${metronomeConBoxClass} .bom_drag`
  1456. ],
  1457. metronomeConBoxClass,
  1458. showModalBeat,
  1459. users.info.id
  1460. );
  1461. // 布置作业弹窗
  1462. const modelTrainStatusConBoxClass = 'modelTrainStatusConBoxClass_drag';
  1463. const modelTrainStatusConBoxDragData = useDrag(
  1464. [
  1465. `${modelTrainStatusConBoxClass}>.n-card-header`,
  1466. `${modelTrainStatusConBoxClass} .bom_drag`
  1467. ],
  1468. modelTrainStatusConBoxClass,
  1469. toRef(data, 'modelTrainStatus'),
  1470. users.info.id
  1471. );
  1472. // 布置作业课后作业
  1473. const modelTrainHomeWordConBoxClass =
  1474. 'modelTrainHomeWordConBoxClassBoxClass_drag';
  1475. const modelTrainHomeWordConBoxDragData = useDrag(
  1476. [
  1477. `${modelTrainHomeWordConBoxClass}>.n-card-header`,
  1478. `${modelTrainHomeWordConBoxClass} .bom_drag`
  1479. ],
  1480. modelTrainHomeWordConBoxClass,
  1481. toRef(data, 'modelAttendStatus'),
  1482. users.info.id
  1483. );
  1484. return () => (
  1485. <div id="playContent" class={[styles.playContent, 'wrap']}>
  1486. <div
  1487. onClick={() => {
  1488. clearTimeout(activeData.timer);
  1489. activeData.model = !activeData.model;
  1490. Object.values(data.videoRefs).map((n: any) =>
  1491. n?.toggleHideControl(activeData.model)
  1492. );
  1493. Object.values(data.audioRefs).map((n: any) =>
  1494. n?.toggleHideControl(activeData.model)
  1495. );
  1496. }}>
  1497. <div
  1498. class={styles.coursewarePlay}
  1499. style={{ width: parentContainer.width }}>
  1500. {!popupData.chapterLoading ? (
  1501. <div class={styles.wraps}>
  1502. <div
  1503. style={
  1504. activeVideoItem.value.type &&
  1505. data.animationState === 'end' &&
  1506. data.videoState === 'play'
  1507. ? {
  1508. zIndex: 15,
  1509. opacity: 1
  1510. }
  1511. : { opacity: 0, zIndex: -1 }
  1512. }
  1513. class={styles.itemDiv}>
  1514. <VideoPlay
  1515. imagePos={columnPos.value}
  1516. ref={(el: any) => (data.videoItemRef = el)}
  1517. item={activeVideoItem.value}
  1518. showModel={activeData.model}
  1519. onClose={setModelOpen}
  1520. onLoadedmetadata={(videoItem: any) => {
  1521. if (data.itemList[popupData.activeIndex]) {
  1522. data.itemList[popupData.activeIndex].videoEle =
  1523. videoItem;
  1524. }
  1525. }}
  1526. onCanplay={() => {
  1527. data.videoState = 'play';
  1528. // activeVideoItem.value.videoEle = videoItem;
  1529. }}
  1530. onPause={() => {
  1531. clearTimeout(activeData.timer);
  1532. activeData.model = true;
  1533. }}
  1534. onEnded={() => {
  1535. // const _index = popupData.activeIndex + 1;
  1536. // if (_index < data.itemList.length) {
  1537. // handleSwipeChange(_index);
  1538. // }
  1539. }}
  1540. />
  1541. </div>
  1542. {data.itemList.map((m: any, mIndex: number) => {
  1543. const isRender = Math.abs(popupData.activeIndex - mIndex) < 2;
  1544. const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
  1545. // if (isRender && m.type === 'PPT') {
  1546. // setTimeout(() => iframeClick() ,500)
  1547. // }
  1548. // if (isRender) {
  1549. // m.isRender = true;
  1550. // }
  1551. // const isRender =
  1552. // m.isRender || Math.abs(popupData.activeIndex - mIndex) < 2;
  1553. // const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
  1554. // if (isRender) {
  1555. // m.isRender = true;
  1556. // }
  1557. return isRender ? (
  1558. <div
  1559. key={'index' + mIndex}
  1560. class={[
  1561. styles.itemDiv,
  1562. popupData.activeIndex === mIndex && styles.itemActive,
  1563. activeData.isAnimation && styles.acitveAnimation,
  1564. Math.abs(popupData.activeIndex - mIndex) < 2
  1565. ? styles.show
  1566. : styles.hide
  1567. ]}
  1568. style={
  1569. mIndex < popupData.activeIndex
  1570. ? effects[effectIndex.value].prev
  1571. : mIndex > popupData.activeIndex
  1572. ? effects[effectIndex.value].next
  1573. : {}
  1574. }
  1575. onClick={(e: Event) => {
  1576. e.stopPropagation();
  1577. clearTimeout(activeData.timer);
  1578. if (Date.now() - activeData.nowTime < 300) {
  1579. handleDbClick(m);
  1580. return;
  1581. }
  1582. activeData.nowTime = Date.now();
  1583. activeData.timer = setTimeout(() => {
  1584. activeData.model = !activeData.model;
  1585. Object.values(data.videoRefs).map((n: any) =>
  1586. n?.toggleHideControl(activeData.model)
  1587. );
  1588. Object.values(data.audioRefs).map((n: any) =>
  1589. n?.toggleHideControl(activeData.model)
  1590. );
  1591. if (activeData.model) {
  1592. setModelOpen();
  1593. }
  1594. }, 300);
  1595. }}>
  1596. {m.type === 'VIDEO' ? (
  1597. <>
  1598. <img
  1599. src={m.coverImg}
  1600. onLoad={() => {
  1601. m.isprepare = true;
  1602. }}
  1603. />
  1604. {/* <VideoPlay
  1605. ref={(v: any) => (data.videoRefs[mIndex] = v)}
  1606. item={m}
  1607. isEmtry={isEmtry}
  1608. onLoadedmetadata={(videoItem: any) => {
  1609. m.videoEle = videoItem;
  1610. m.isprepare = true;
  1611. }}
  1612. onTogglePlay={(paused: boolean) => {
  1613. m.autoPlay = false;
  1614. if (paused || popupData.open) {
  1615. clearTimeout(activeData.timer);
  1616. } else {
  1617. setModelOpen();
  1618. }
  1619. }}
  1620. onReset={() => {
  1621. if (!m.videoEle?.paused) {
  1622. setModelOpen();
  1623. }
  1624. }}
  1625. onError={() => {
  1626. console.log('video error');
  1627. m.error = true;
  1628. }}
  1629. /> */}
  1630. <Transition name="van-fade">
  1631. {
  1632. <div class={styles.loadWrap}>
  1633. <Vue3Lottie
  1634. animationData={playLoadData}></Vue3Lottie>
  1635. </div>
  1636. }
  1637. </Transition>
  1638. </>
  1639. ) : m.type === 'IMG' ? (
  1640. <img src={m.content} />
  1641. ) : m.type === 'SONG' ? (
  1642. <AudioPay
  1643. imagePos={columnPos.value}
  1644. item={m}
  1645. activeStatus={popupData.activeIndex === mIndex}
  1646. ref={(v: any) => (data.audioRefs[mIndex] = v)}
  1647. onLoadedmetadata={(audioItem: any) => {
  1648. m.audioEle = audioItem;
  1649. m.isprepare = true;
  1650. }}
  1651. onTogglePlay={(paused: boolean) => {
  1652. // m.autoPlay = false;
  1653. if (paused || popupData.open) {
  1654. clearTimeout(activeData.timer);
  1655. } else {
  1656. setModelOpen();
  1657. }
  1658. }}
  1659. onEnded={() => {
  1660. // const _index = popupData.activeIndex + 1;
  1661. // if (_index < data.itemList.length) {
  1662. // handleSwipeChange(_index);
  1663. // }
  1664. }}
  1665. onReset={() => {
  1666. if (!m.audioEle?.paused) {
  1667. setModelOpen();
  1668. }
  1669. }}
  1670. />
  1671. ) : // : m.type === 'PPT' ? <div class={styles.iframePpt}>
  1672. // <div class={styles.pptBox}></div>
  1673. // <iframe src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(m.content)}`} width='100%' height='100%' frameborder='1'></iframe>
  1674. // </div>
  1675. m.type === 'PPT' ? (
  1676. <iframe
  1677. src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
  1678. m.content
  1679. )}`}
  1680. width="100%"
  1681. height="100%"
  1682. frameborder="1"></iframe>
  1683. ) : m.type === 'RHYTHM' ? (
  1684. <RhythmModal
  1685. item={m}
  1686. activeStatus={popupData.activeIndex === mIndex}
  1687. imagePos={columnPos.value}
  1688. onSetIframe={(el: any) => {
  1689. m.iframeRef = el;
  1690. }}
  1691. />
  1692. ) : m.type === 'LISTEN' ? (
  1693. <ListenModal
  1694. item={m}
  1695. data-vid={m.id}
  1696. activeStatus={popupData.activeIndex === mIndex}
  1697. onSetIframe={(el: any) => {
  1698. m.iframeRef = el;
  1699. }}
  1700. />
  1701. ) : m.type === 'INSTRUMENT' || m.type === 'MUSICIAN' ? (
  1702. <InstruemntDetail
  1703. type="preview"
  1704. id={m.content}
  1705. contentType={m.type}
  1706. activeStatus={popupData.activeIndex === mIndex}
  1707. ref={(el: any) => (m.iframeRef = el)}
  1708. />
  1709. ) : m.type === 'MUSIC_WIKI' ? (
  1710. <MusicDetail
  1711. type="preview"
  1712. id={m.content}
  1713. contentType={m.type}
  1714. activeStatus={popupData.activeIndex === mIndex}
  1715. ref={(el: any) => (m.iframeRef = el)}
  1716. />
  1717. ) : m.type === 'THEORY' ? (
  1718. <TheotyDetail
  1719. type="preview"
  1720. id={m.content}
  1721. activeStatus={popupData.activeIndex === mIndex}
  1722. ref={(el: any) => (m.iframeRef = el)}
  1723. />
  1724. ) : (
  1725. <MusicScore
  1726. activeModel={activeData.model}
  1727. activeStatus={popupData.activeIndex === mIndex}
  1728. data-vid={m.id}
  1729. music={m}
  1730. imagePos={columnPos.value}
  1731. onSetIframe={(el: any) => {
  1732. m.iframeRef = el;
  1733. }}
  1734. />
  1735. )}
  1736. </div>
  1737. ) : null;
  1738. })}
  1739. </div>
  1740. ) : (
  1741. ''
  1742. )}
  1743. </div>
  1744. </div>
  1745. {/* 右边操作栏 */}
  1746. <div
  1747. class={[
  1748. styles.rightColumn,
  1749. columnShow.value && columnPos.value === 'right'
  1750. ? ''
  1751. : styles.rightColumnHide
  1752. ]}>
  1753. {rightList.map((item: any) => (
  1754. <div class={styles.columnItemBox}>
  1755. <div
  1756. class={[
  1757. styles.columnItem,
  1758. (item.id === 2 && data.preStudentNum <= 0) ||
  1759. (item.id === 10 && studyData.callStudentList.length <= 0) ||
  1760. (item.id === 11 && !isUpArrow.value) ||
  1761. (item.id === 12 && !isDownArrow.value)
  1762. ? styles.itemDisabled
  1763. : ''
  1764. ]}
  1765. onClick={() => operateRightBtn(item.id)}>
  1766. <NTooltip
  1767. showArrow={false}
  1768. placement="left"
  1769. class={[
  1770. item.id === 15
  1771. ? 'columnItemTooltip rightColumnItemTooltip'
  1772. : ''
  1773. ]}>
  1774. {{
  1775. trigger: () => (
  1776. <img src={item.id === 8 ? rightIconPackUp : item.icon} />
  1777. ),
  1778. default: () =>
  1779. item.id === 15 ? (
  1780. <div class="tools">
  1781. {tooltipList.map(i => (
  1782. <div onClick={() => operateRightBtn(i.id)}>
  1783. <img src={i.icon} />
  1784. <div class="tit">{i.name}</div>
  1785. </div>
  1786. ))}
  1787. </div>
  1788. ) : item.id === 1 && data.type === 'preview' ? (
  1789. item.name2
  1790. ) : (
  1791. item.name
  1792. )
  1793. }}
  1794. </NTooltip>
  1795. </div>
  1796. </div>
  1797. ))}
  1798. </div>
  1799. <NTooltip showArrow={false} placement="left">
  1800. {{
  1801. trigger: () => (
  1802. <div
  1803. class={[
  1804. styles.rightHideIcon,
  1805. !(columnShow.value && columnPos.value === 'right')
  1806. ? styles.rightIconShow
  1807. : ''
  1808. ]}
  1809. onClick={() => {
  1810. columnPos.value = 'right';
  1811. columnShow.value = true;
  1812. }}
  1813. />
  1814. ),
  1815. default: () => '按钮镜像'
  1816. }}
  1817. </NTooltip>
  1818. {/* 左边操作栏 */}
  1819. <div
  1820. class={[
  1821. styles.leftColumn,
  1822. columnShow.value && columnPos.value === 'left'
  1823. ? ''
  1824. : styles.leftColumnHide
  1825. ]}>
  1826. {rightList.map((item: any) => (
  1827. <div class={styles.columnItemBox}>
  1828. <div
  1829. class={[
  1830. styles.columnItem,
  1831. (item.id === 2 && data.preStudentNum <= 0) ||
  1832. (item.id === 10 && studyData.callStudentList.length <= 0) ||
  1833. (item.id === 11 && !isUpArrow.value) ||
  1834. (item.id === 12 && !isDownArrow.value)
  1835. ? styles.itemDisabled
  1836. : ''
  1837. ]}
  1838. onClick={() => operateRightBtn(item.id)}>
  1839. <NTooltip
  1840. showArrow={false}
  1841. placement="right"
  1842. class={[item.id === 15 ? 'columnItemTooltip' : '']}>
  1843. {{
  1844. trigger: () => <img src={item.icon} />,
  1845. default: () =>
  1846. item.id === 15 ? (
  1847. <div class="tools">
  1848. {tooltipList.map(i => (
  1849. <div onClick={() => operateRightBtn(i.id)}>
  1850. <img src={i.icon} />
  1851. <div class="tit">{i.name}</div>
  1852. </div>
  1853. ))}
  1854. </div>
  1855. ) : item.id === 1 && data.type === 'preview' ? (
  1856. item.name2
  1857. ) : (
  1858. item.name
  1859. )
  1860. }}
  1861. </NTooltip>
  1862. </div>
  1863. </div>
  1864. ))}
  1865. </div>
  1866. <NTooltip showArrow={false} placement="right">
  1867. {{
  1868. trigger: () => (
  1869. <div
  1870. class={[
  1871. styles.leftHideIcon,
  1872. !(columnShow.value && columnPos.value === 'left')
  1873. ? styles.leftIconShow
  1874. : ''
  1875. ]}
  1876. onClick={() => {
  1877. columnPos.value = 'left';
  1878. columnShow.value = true;
  1879. }}
  1880. />
  1881. ),
  1882. default: () => '按钮镜像'
  1883. }}
  1884. </NTooltip>
  1885. {/* 显示列表 */}
  1886. {/* <NDrawer
  1887. v-model:show={popupData.open}
  1888. class={[styles.drawerContainer, styles.drawerContainerSource]}
  1889. onAfterLeave={handleClosePopup}
  1890. showMask={false}
  1891. blockScroll={false}
  1892. trapFocus={false}>
  1893. <NDrawerContent closable>
  1894. {{
  1895. header: () => (
  1896. <TheNoticeBar text={activeName.value || '资源列表'} />
  1897. ),
  1898. default: () => (
  1899. <SourceList
  1900. teacherChapterName={data.teacherChapterName}
  1901. knowledgePointList={data.knowledgePointList}
  1902. courseActiveIndex={popupData.courseActiveIndex}
  1903. activeItem={data.itemList[popupData.activeIndex]}
  1904. onConfirm={(item: any) => {
  1905. popupData.open = false;
  1906. toggleMaterial(item.id);
  1907. }}
  1908. />
  1909. )
  1910. }}
  1911. </NDrawerContent>
  1912. </NDrawer> */}
  1913. <NModal
  1914. transformOrigin="center"
  1915. v-model:show={popupData.open}
  1916. preset="card"
  1917. class={[
  1918. 'modalTitle background',
  1919. styles.drawerContainer,
  1920. resourcesConBoxClass
  1921. ]}
  1922. style={resourcesConDragData.styleDrag.value}
  1923. title={activeName.value || '资源列表'}>
  1924. <SourceList
  1925. teacherChapterName={data.teacherChapterName}
  1926. knowledgePointList={data.knowledgePointList}
  1927. courseActiveIndex={popupData.courseActiveIndex}
  1928. activeItem={data.itemList[popupData.activeIndex]}
  1929. onConfirm={(item: any) => {
  1930. popupData.open = false;
  1931. toggleMaterial(item.id);
  1932. }}
  1933. />
  1934. <Dragbom></Dragbom>
  1935. </NModal>
  1936. {/* 章节切换 */}
  1937. {/* <NDrawer
  1938. v-model:show={popupData.chapterOpen}
  1939. class={styles.drawerContainer}
  1940. onAfterLeave={handleClosePopup}
  1941. showMask={false}
  1942. maskClosable={data.selectClassStatus ? false : true}
  1943. blockScroll={false}
  1944. trapFocus={false}>
  1945. <NDrawerContent title="切换章节" closable>
  1946. <Chapter
  1947. treeList={popupData.chapterDetails}
  1948. itemActive={data.detailId as any}
  1949. onHandleSelect={async (val: any) => {
  1950. // itemActive: child.id,
  1951. // itemName: child.name
  1952. popupData.courseId = val.itemActive;
  1953. data.selectClassStatus = true;
  1954. }}
  1955. />
  1956. </NDrawerContent>
  1957. </NDrawer> */}
  1958. <NModal
  1959. transformOrigin="center"
  1960. v-model:show={popupData.chapterOpen}
  1961. preset="card"
  1962. class={[
  1963. 'modalTitle background',
  1964. styles.drawerContainer,
  1965. chapterConBoxClass
  1966. ]}
  1967. style={chapterConBoxDragData.styleDrag.value}
  1968. title={'切换章节'}>
  1969. <Chapter
  1970. treeList={popupData.chapterDetails}
  1971. itemActive={data.detailId as any}
  1972. onHandleSelect={async (val: any) => {
  1973. // itemActive: child.id,
  1974. // itemName: child.name
  1975. popupData.courseId = val.itemActive;
  1976. data.selectClassStatus = true;
  1977. }}
  1978. />
  1979. <Dragbom></Dragbom>
  1980. </NModal>
  1981. {/* 批注 */}
  1982. {studyData.penShow && (
  1983. <Pen
  1984. isDrag={true}
  1985. show={studyData.type === 'pen'}
  1986. type={studyData.type}
  1987. close={() => closeStudyTool()}
  1988. />
  1989. )}
  1990. {studyData.whiteboardShow && (
  1991. <Pen
  1992. isDrag={true}
  1993. show={studyData.type === 'whiteboard'}
  1994. type={studyData.type}
  1995. close={() => closeStudyTool()}
  1996. />
  1997. )}
  1998. {studyData.callShow && (
  1999. <Pen
  2000. isDrag={true}
  2001. imagePos={columnPos.value}
  2002. callStudents={studyData.callStudentList}
  2003. show={studyData.type === 'call'}
  2004. type={studyData.type}
  2005. close={() => {
  2006. closeStudyTool();
  2007. studyData.callShow = false;
  2008. }}
  2009. />
  2010. )}
  2011. {/* 选择课件 */}
  2012. <NModal
  2013. transformOrigin="center"
  2014. v-model:show={data.selectClassStatus}
  2015. preset="card"
  2016. class={[
  2017. 'modalTitle background',
  2018. // styles.attendClassModal,
  2019. styles.selectClassModal,
  2020. selCourBoxClass
  2021. ]}
  2022. style={selCourDragData.styleDrag.value}
  2023. title={'选择课件'}>
  2024. <SelectClass
  2025. classId={data.classId}
  2026. courseId={popupData.courseId}
  2027. instrumentId={data.instrumentId}
  2028. lessonCoursewareSubjectList={data.lessonCoursewareSubjectList}
  2029. onConfirm={async (val: any) => {
  2030. popupData.chapterLoading = true;
  2031. try {
  2032. data.selectClassStatus = false;
  2033. data.detailId = val.itemActive;
  2034. data.courseId = val.chapterId;
  2035. const ids = formatParentId(
  2036. val.itemActive,
  2037. popupData.chapterDetails
  2038. );
  2039. data.lessonCoursewareDetailId = ids[0];
  2040. // 更新上课记录 上课的时候才更新
  2041. if (data.type !== 'preview') {
  2042. await classCourseScheduleUpdate();
  2043. }
  2044. await getDetail();
  2045. popupData.activeIndex = 0;
  2046. popupData.chapterOpen = false;
  2047. } catch {
  2048. //
  2049. }
  2050. popupData.chapterLoading = false;
  2051. }}
  2052. />
  2053. <Dragbom></Dragbom>
  2054. </NModal>
  2055. {/* 布置作业 */}
  2056. <NModal
  2057. transformOrigin="center"
  2058. v-model:show={data.modelAttendStatus}
  2059. preset="card"
  2060. title={'课后作业'}
  2061. style={modelTrainHomeWordConBoxDragData.styleDrag.value}
  2062. class={[
  2063. 'modalTitle',
  2064. styles.removeVisiable,
  2065. modelTrainHomeWordConBoxClass
  2066. ]}>
  2067. <div class={styles.studentRemove}>
  2068. <p>{data.modalAttendMessage}</p>
  2069. <NSpace class={styles.btnGroupModal} justify="center">
  2070. <NButton
  2071. type="default"
  2072. round
  2073. onClick={() => {
  2074. data.modelTrainStatus = true;
  2075. nextTick(() => {
  2076. getModalHeight();
  2077. });
  2078. data.modelAttendStatus = false;
  2079. }}>
  2080. 布置作业
  2081. </NButton>
  2082. <NButton
  2083. type="primary"
  2084. round
  2085. onClick={() => {
  2086. handleStop();
  2087. data.modelAttendStatus = false;
  2088. }}>
  2089. 取消
  2090. </NButton>
  2091. </NSpace>
  2092. </div>
  2093. <Dragbom></Dragbom>
  2094. </NModal>
  2095. {/* 训练设置 */}
  2096. {/* <NModal
  2097. transformOrigin="center"
  2098. v-model:show={data.modelTrainStatus}
  2099. preset="card"
  2100. class={[styles.attendClassModal, styles.trainClassModal]}
  2101. title={'作业设置'}>
  2102. <ClassWork
  2103. detailId={data.detailId}
  2104. instrumentId={data.instrumentId}
  2105. courseScheduleId={data.classId}
  2106. activeName={activeName.value}
  2107. classGroupId={data.classGroupId}
  2108. onClose={() => (data.modelTrainStatus = false)}
  2109. />
  2110. </NModal> */}
  2111. <NModal
  2112. v-model:show={data.modelTrainStatus}
  2113. preset="card"
  2114. class={[
  2115. 'modalTitle background',
  2116. styles.workVisiable,
  2117. modelTrainStatusConBoxClass
  2118. ]}
  2119. style={modelTrainStatusConBoxDragData.styleDrag.value}
  2120. title={'布置作业'}>
  2121. <div id="model-homework-height" class={styles.workContainer}>
  2122. <div class={styles.workTrain}>
  2123. <Train
  2124. from={'class'}
  2125. cardType="homeworkRecord"
  2126. lessonPreTraining={{
  2127. title: data.lessonPreTrainingId
  2128. ? ''
  2129. : data.teacherChapterName + '-课后作业',
  2130. chapterId: data.courseId, // 课件编号
  2131. id: data.lessonPreTrainingId // 作业编号
  2132. }}
  2133. coursewareKnowledgeDetailId={data.detailId}
  2134. classGroupId={data.classGroupId}
  2135. courseScheduleId={data.classId}
  2136. onChange={async (val: any) => {
  2137. data.modelTrainStatus = val.status;
  2138. // getCoursewareList();
  2139. if (val.saveWork && data.classId) {
  2140. data.lessonPreTrainingId = val.lessonPreTrainingId;
  2141. }
  2142. }}
  2143. />
  2144. </div>
  2145. <div class={styles.resourceMain}>
  2146. <ResourceMain from={'class'} cardType="homerowk-record" />
  2147. </div>
  2148. </div>
  2149. <Dragbom></Dragbom>
  2150. </NModal>
  2151. <NModal
  2152. transformOrigin="center"
  2153. class={['modalTitle background', metronomeConBoxClass]}
  2154. title={'节拍器'}
  2155. preset="card"
  2156. v-model:show={showModalBeat.value}
  2157. style={{
  2158. width: '687px',
  2159. ...metronomeConBoxDragData.styleDrag.value
  2160. }}>
  2161. <div class={styles.modeWrap}>
  2162. <iframe
  2163. src={`${vaildUrl()}/metronome/?id=${new Date().getTime()}`}
  2164. scrolling="no"
  2165. frameborder="0"
  2166. width="100%"
  2167. onLoad={(val: any) => {
  2168. iframeDislableKeyboard(val.target);
  2169. }}
  2170. height={'650px'}></iframe>
  2171. <Dragbom></Dragbom>
  2172. </div>
  2173. </NModal>
  2174. <NModal
  2175. transformOrigin="center"
  2176. class={['background']}
  2177. v-model:show={showModalTone.value}>
  2178. <div>
  2179. <PlaceholderTone
  2180. onClose={() => {
  2181. showModalTone.value = false;
  2182. }}></PlaceholderTone>
  2183. </div>
  2184. </NModal>
  2185. <NModal
  2186. transformOrigin="center"
  2187. v-model:show={showModalTime.value}
  2188. class={['modalTitle background', timerMeterConBoxClass]}
  2189. title={'计时器'}
  2190. preset="card"
  2191. style={{
  2192. width: px2vw(772),
  2193. ...timerMeterConDragData.styleDrag.value
  2194. }}>
  2195. <div>
  2196. <TimerMeter></TimerMeter>
  2197. <Dragbom></Dragbom>
  2198. </div>
  2199. </NModal>
  2200. <NModal
  2201. v-model:show={data.selectResourceStatus}
  2202. class={['modalTitle', styles.selectMusicModal, selResourBoxClass]}
  2203. style={selResourDragData.styleDrag.value}
  2204. preset="card"
  2205. title={'选择资源'}>
  2206. <SelectResources from="class" />
  2207. <Dragbom></Dragbom>
  2208. </NModal>
  2209. <NModal
  2210. transformOrigin="center"
  2211. v-model:show={data.removeVisiable}
  2212. preset="card"
  2213. class={['modalTitle', styles.removeVisiable, removeResourBoxClass]}
  2214. style={removeResourDragData.styleDrag.value}
  2215. title={data.removeTitle}>
  2216. <div class={styles.studentRemove}>
  2217. <p>{data.removeContent}</p>
  2218. <NSpace class={styles.btnGroupModal} justify="center">
  2219. <NButton
  2220. round
  2221. onClick={() => {
  2222. if (data.removeCourseStatus) {
  2223. if (globalState.application) {
  2224. document.exitFullscreen
  2225. ? document.exitFullscreen()
  2226. : document.mozCancelFullScreen
  2227. ? document.mozCancelFullScreen()
  2228. : document.webkitExitFullscreen
  2229. ? document.webkitExitFullscreen()
  2230. : '';
  2231. emit('close');
  2232. } else {
  2233. window.close();
  2234. }
  2235. } else {
  2236. data.removeVisiable = false;
  2237. }
  2238. }}>
  2239. {data.removeCourseStatus ? '结束课程' : '取消'}
  2240. </NButton>
  2241. <NButton
  2242. round
  2243. type="primary"
  2244. onClick={() => {
  2245. //
  2246. if (data.removeCourseStatus) {
  2247. data.modelTrainStatus = true;
  2248. data.removeVisiable = false;
  2249. nextTick(() => {
  2250. getModalHeight();
  2251. });
  2252. } else {
  2253. if (globalState.application) {
  2254. document.exitFullscreen
  2255. ? document.exitFullscreen()
  2256. : document.mozCancelFullScreen
  2257. ? document.mozCancelFullScreen()
  2258. : document.webkitExitFullscreen
  2259. ? document.webkitExitFullscreen()
  2260. : '';
  2261. emit('close');
  2262. } else {
  2263. window.close();
  2264. if (route.query.source === 'admin') {
  2265. storage.remove(ACCESS_TOKEN_ADMIN);
  2266. window.parent.postMessage(
  2267. {
  2268. api: 'iframe_exit'
  2269. },
  2270. '*'
  2271. );
  2272. }
  2273. }
  2274. }
  2275. }}>
  2276. {data.removeCourseStatus ? '布置作业' : '确定'}
  2277. </NButton>
  2278. </NSpace>
  2279. <Dragbom></Dragbom>
  2280. </div>
  2281. </NModal>
  2282. <NModal
  2283. style={nextEndBoxDragData.styleDrag.value}
  2284. z-index={3000}
  2285. transformOrigin="center"
  2286. v-model:show={data.nextEndShow}
  2287. preset="card"
  2288. title={'提示'}
  2289. class={['modalTitle', styles.removeVisiable, nextEndBoxClass]}>
  2290. <div class={styles.studentRemove}>
  2291. <p>当前课件已结束,是否进入下一章节</p>
  2292. <NSpace class={styles.btnGroupModal} justify="center">
  2293. <NButton
  2294. type="default"
  2295. round
  2296. onClick={() => {
  2297. data.nextEndShow = false;
  2298. }}>
  2299. 取消
  2300. </NButton>
  2301. <NButton
  2302. type="primary"
  2303. round
  2304. onClick={() => {
  2305. data.nextEndShow = false;
  2306. handleNextEnd();
  2307. }}>
  2308. 确认
  2309. </NButton>
  2310. </NSpace>
  2311. <Dragbom></Dragbom>
  2312. </div>
  2313. </NModal>
  2314. </div>
  2315. );
  2316. }
  2317. });