index.tsx 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  1. import {
  2. computed,
  3. defineComponent,
  4. onMounted,
  5. reactive,
  6. ref,
  7. watch
  8. } from 'vue';
  9. import styles from './index.module.less';
  10. import {
  11. NButton,
  12. NTooltip,
  13. NIcon,
  14. NImage,
  15. NModal,
  16. NScrollbar,
  17. NSpin,
  18. NTabPane,
  19. NTabs,
  20. useMessage,
  21. NPopselect,
  22. NPopover,
  23. NForm,
  24. NFormItem,
  25. NInput,
  26. NSpace,
  27. NCascader,
  28. NSwitch
  29. } from 'naive-ui';
  30. import { usePrepareStore } from '/src/store/modules/prepareLessons';
  31. import add from '@/views/studentList/images/add.png';
  32. // import iconSlideRight from '../../../images/icon-slide-right.png';
  33. import CoursewareType from '../../../model/courseware-type';
  34. import TheEmpty from '/src/components/TheEmpty';
  35. import RelatedClass from '../../../model/related-class';
  36. import { modalClickMask, state } from '/src/state';
  37. import { useResizeObserver } from '@vueuse/core';
  38. import AttendClass from '/src/views/prepare-lessons/model/attend-class';
  39. import {
  40. api_addByOpenCourseware,
  41. api_teacherChapterLessonCoursewareRemove,
  42. teacherChapterLessonCoursewareList,
  43. courseScheduleStart,
  44. api_courseScheduleCheck
  45. } from '../../../api';
  46. import { useRoute, useRouter } from 'vue-router';
  47. import TheMessageDialog from '/src/components/TheMessageDialog';
  48. import { eventGlobal, fscreen } from '/src/utils';
  49. import PreviewWindow from '/src/views/preview-window';
  50. import Related from './related';
  51. import Train from '../train';
  52. import ResourceMain from '../../resource-main';
  53. import { useCatchStore } from '/src/store/modules/catchData';
  54. import deepClone from '/src/helpers/deep-clone';
  55. import {
  56. api_teacherChapterLessonCoursewareAdd,
  57. api_coursewareToTeacherCourseware,
  58. api_updateCoursewareInfo
  59. } from '../../../api';
  60. import { vaildPPTUrl } from '/src/utils/urlUtils';
  61. import { useUserStore } from '/src/store/modules/users';
  62. export default defineComponent({
  63. name: 'courseware-presets',
  64. props: {
  65. addParam: {
  66. type: Object,
  67. default: () => ({})
  68. }
  69. },
  70. emits: ['change'],
  71. setup(props, { emit }) {
  72. const prepareStore = usePrepareStore();
  73. const message = useMessage();
  74. const route = useRoute();
  75. const router = useRouter();
  76. const localStorageSubjectId = localStorage.getItem(
  77. 'prepareLessonSubjectId'
  78. );
  79. const forms = reactive({
  80. // 选取参数带的,后取缓存
  81. leftWidth: '100%',
  82. rightWidth: '0',
  83. messageLoading: false,
  84. instrumentId: route.query.instrumentId
  85. ? Number(route.query.instrumentId)
  86. : localStorageSubjectId
  87. ? Number(localStorageSubjectId)
  88. : '',
  89. courseScheduleSubjectId: route.query.courseScheduleSubjectId,
  90. classGroupId: route.query.classGroupId,
  91. preStudentNum: route.query.preStudentNum,
  92. bodyWidth: '100%',
  93. loading: false,
  94. openLoading: false,
  95. showRelatedClass: false,
  96. tableList: [] as any,
  97. openTableShow: true, // 是否显示
  98. openTableList: [] as any,
  99. selectItem: {} as any,
  100. editTitleVisiable: false,
  101. editTitle: null,
  102. editBtnLoading: false,
  103. preRemoveVisiable: false,
  104. addVisiable: false, // 是否有添加的课件
  105. carouselIndex: 0,
  106. showAttendClass: false,
  107. attendClassType: 'change', //
  108. attendClassItem: {} as any,
  109. attendClassId: null as any,
  110. previewModal: false,
  111. previewParams: {
  112. type: '',
  113. courseId: '',
  114. instrumentId: '',
  115. detailId: ''
  116. } as any,
  117. workVisiable: false,
  118. wikiCategoryIdChild: null,
  119. instrumentErrorVisiable: false,
  120. instrumentErrorContent: ''
  121. });
  122. const getCoursewareList = async () => {
  123. forms.loading = true;
  124. try {
  125. // 判断是否有选择对应的课件 或声部
  126. if (!prepareStore.getSelectKey) return (forms.loading = false);
  127. const { data } = await teacherChapterLessonCoursewareList({
  128. instrumentId: prepareStore.getInstrumentId,
  129. coursewareDetailKnowledgeId: prepareStore.getSelectKey
  130. });
  131. if (!Array.isArray(data)) {
  132. return;
  133. }
  134. const tempList: any = [];
  135. data.forEach((item: any) => {
  136. // const firstItem: any =
  137. // item.chapterKnowledgeList[0]?.chapterKnowledgeMaterialList[0];
  138. const firstItem: any =
  139. item.chapterKnowledgeList[0]?.chapterKnowledgeMaterialList;
  140. tempList.push({
  141. id: item.id,
  142. lessonPreTrainingId: item.lessonPreTrainingId,
  143. openFlag: item.openFlag,
  144. openFlagEnable: item.openFlagEnable,
  145. instrumentNames: item.instrumentNames,
  146. fromChapterLessonCoursewareId: item.fromChapterLessonCoursewareId,
  147. name: item.name,
  148. coverImg: firstItem && firstItem[0]?.bizInfo.coverImg,
  149. type: firstItem && firstItem[0]?.bizInfo.type,
  150. isNotWork: item.lessonPreTrainingNum <= 0 ? true : false, // 是否布置作业
  151. coursewareType: item.coursewareType,
  152. instrumentIds: item.instrumentIds,
  153. pptId: firstItem && firstItem[0]?.id,
  154. teacherSaveFlag: item.teacherSaveFlag
  155. });
  156. });
  157. forms.tableList = tempList;
  158. } catch {
  159. //
  160. }
  161. forms.loading = false;
  162. };
  163. // 监听选择的key 左侧选择了其它的课
  164. let timer: any = null;
  165. watch(
  166. () => [prepareStore.getSelectKey, prepareStore.getInstrumentId],
  167. async () => {
  168. clearTimeout(timer);
  169. timer = setTimeout(async () => {
  170. if (!prepareStore.getInstrumentId) {
  171. // 获取教材分类列表
  172. checkInstrumentIds();
  173. } else {
  174. getInitInstrumentId();
  175. }
  176. eventGlobal.emit('openCoursewareChanged');
  177. await getCoursewareList();
  178. subjectRef.value?.syncBarPosition();
  179. }, 100);
  180. }
  181. );
  182. watch(
  183. () => prepareStore.getInstrumentList,
  184. () => {
  185. checkInstrumentIds();
  186. }
  187. );
  188. const checkInstrumentIds = () => {
  189. const instrumentsList = prepareStore.getSingleInstrumentList;
  190. // 并且没有声部时才会更新
  191. if (instrumentsList.length > 0) {
  192. // const prepareLessonCourseWareSubjectIsNull = sessionStorage.getItem(
  193. // 'prepareLessonCourseWareSubjectIsNull'
  194. // );
  195. // if (prepareLessonCourseWareSubjectIsNull === 'true') {
  196. // prepareStore.setInstrumentId('');
  197. // return;
  198. // }
  199. // 并且声部在列表中
  200. const localStorageSubjectId = localStorage.getItem(
  201. 'prepareLessonSubjectId'
  202. );
  203. // // 先取 上次上课声部,在取班级声部 最后取缓存
  204. let instrumentId = null;
  205. let index = -1;
  206. if (forms.courseScheduleSubjectId) {
  207. // 判断浏览器上面是否有
  208. index = instrumentsList.findIndex(
  209. (subject: any) => subject.id == forms.courseScheduleSubjectId
  210. );
  211. if (index >= 0) {
  212. instrumentId = Number(forms.courseScheduleSubjectId);
  213. }
  214. }
  215. // 判断班级上面声部 & 还没有声部
  216. if (forms.instrumentId && !instrumentId) {
  217. // 判断浏览器上面是否有
  218. index = instrumentsList.findIndex(
  219. (subject: any) => subject.id == forms.instrumentId
  220. );
  221. if (index >= 0) {
  222. instrumentId = Number(forms.instrumentId);
  223. }
  224. }
  225. // 缓存声部 & 还没有声部
  226. if (localStorageSubjectId && !instrumentId) {
  227. // 判断浏览器上面是否有
  228. index = instrumentsList.findIndex(
  229. (subject: any) => subject.id == localStorageSubjectId
  230. );
  231. if (index >= 0) {
  232. instrumentId = Number(localStorageSubjectId);
  233. }
  234. }
  235. // 判断是否选择为空
  236. if (instrumentId && index >= 0) {
  237. prepareStore.setSubjectId(instrumentId);
  238. // forms.instrumentId = instrumentId;
  239. } else {
  240. // 判断是否有缓存
  241. // prepareStore.setSubjectId(instrumentsList[0].id);
  242. // forms.instrumentId = instrumentsList[0].id;
  243. }
  244. // 保存
  245. localStorage.setItem(
  246. 'prepareLessonSubjectId',
  247. prepareStore.getInstrumentId as any
  248. );
  249. subjectRef.value?.syncBarPosition();
  250. } else {
  251. if (forms.instrumentId) {
  252. prepareStore.setInstrumentId(forms.instrumentId + '');
  253. }
  254. }
  255. };
  256. const getInitInstrumentId = () => {
  257. let instrumentId: any = '';
  258. prepareStore.getInstrumentList.forEach((item: any) => {
  259. if (Array.isArray(item.instruments)) {
  260. item.instruments.forEach((child: any) => {
  261. if (child.id === prepareStore.getInstrumentId) {
  262. instrumentId = child.id;
  263. }
  264. });
  265. }
  266. });
  267. if (instrumentId) {
  268. forms.wikiCategoryIdChild = instrumentId;
  269. }
  270. };
  271. const subjectRef = ref();
  272. onMounted(async () => {
  273. useResizeObserver(
  274. document.querySelector('#presetsLeftRef') as HTMLElement,
  275. (entries: any) => {
  276. const entry = entries[0];
  277. const { width } = entry.contentRect;
  278. forms.leftWidth = width + 'px';
  279. }
  280. );
  281. useResizeObserver(
  282. document.querySelector('#presetsRightRef') as HTMLElement,
  283. (entries: any) => {
  284. const entry = entries[0];
  285. const { width } = entry.contentRect;
  286. forms.rightWidth = width + 'px';
  287. }
  288. );
  289. prepareStore.setClassGroupId(route.query.classGroupId as any);
  290. if (!prepareStore.getInstrumentId) {
  291. // 获取教材分类列表
  292. checkInstrumentIds();
  293. } else {
  294. getInitInstrumentId();
  295. }
  296. await getCoursewareList();
  297. if (props.addParam.isAdd) {
  298. forms.addVisiable = true;
  299. }
  300. });
  301. // 删除
  302. const onRemove = async () => {
  303. forms.messageLoading = true;
  304. try {
  305. await api_teacherChapterLessonCoursewareRemove({
  306. id: forms.selectItem.id
  307. });
  308. message.success('删除成功');
  309. getCoursewareList();
  310. // getOpenCoursewareList();
  311. eventGlobal.emit('openCoursewareChanged');
  312. forms.preRemoveVisiable = false;
  313. } catch {
  314. //
  315. }
  316. setTimeout(() => {
  317. forms.messageLoading = false;
  318. }, 100);
  319. };
  320. // 添加课件
  321. const onAddCourseware = async (item: any) => {
  322. if (forms.messageLoading) return;
  323. forms.messageLoading = true;
  324. try {
  325. await api_addByOpenCourseware({ id: item.id });
  326. message.success('添加成功');
  327. getCoursewareList();
  328. // getOpenCoursewareList();
  329. eventGlobal.emit('openCoursewareChanged');
  330. } catch {
  331. //
  332. }
  333. setTimeout(() => {
  334. forms.messageLoading = false;
  335. }, 100);
  336. };
  337. // 预览上课
  338. const onPreviewAttend = (id: string) => {
  339. // 判断是否在应用里面
  340. if (window.matchMedia('(display-mode: standalone)').matches) {
  341. state.application = window.matchMedia(
  342. '(display-mode: standalone)'
  343. ).matches;
  344. forms.previewModal = true;
  345. fscreen();
  346. forms.previewParams = {
  347. type: 'preview',
  348. courseId: id,
  349. instrumentId: prepareStore.getInstrumentId,
  350. detailId: prepareStore.getSelectKey,
  351. lessonCourseId: prepareStore.getBaseCourseware.id
  352. };
  353. } else {
  354. const { href } = router.resolve({
  355. path: '/attend-class',
  356. query: {
  357. type: 'preview',
  358. courseId: id,
  359. instrumentId: prepareStore.getInstrumentId,
  360. detailId: prepareStore.getSelectKey,
  361. lessonCourseId: prepareStore.getBaseCourseware.id
  362. }
  363. });
  364. window.open(href, +new Date() + '');
  365. }
  366. };
  367. const onStartClass = async (
  368. item: any,
  369. classGroupId: any,
  370. instrumentId?: any
  371. ) => {
  372. if (classGroupId) {
  373. // 开始上课
  374. const res = await courseScheduleStart({
  375. lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
  376. classGroupId: classGroupId,
  377. useChapterLessonCoursewareId: item.id
  378. // instrumentId: prepareStore.getInstrumentId
  379. });
  380. if (window.matchMedia('(display-mode: standalone)').matches) {
  381. state.application = window.matchMedia(
  382. '(display-mode: standalone)'
  383. ).matches;
  384. forms.previewModal = true;
  385. fscreen();
  386. forms.previewParams = {
  387. type: 'class',
  388. classGroupId: classGroupId,
  389. courseId: item.id,
  390. instrumentId: prepareStore.getInstrumentId, // 产品说预览和上课都取当前选中的乐器
  391. // instrumentId: instrumentId || route.query.instrumentId,
  392. detailId: prepareStore.getSelectKey,
  393. classId: res.data,
  394. lessonCourseId: prepareStore.getBaseCourseware.id,
  395. preStudentNum: forms.preStudentNum
  396. };
  397. } else {
  398. const { href } = router.resolve({
  399. path: '/attend-class',
  400. query: {
  401. type: 'class',
  402. classGroupId: classGroupId,
  403. courseId: item.id,
  404. instrumentId: prepareStore.getInstrumentId,
  405. // instrumentId: instrumentId || route.query.instrumentId,
  406. detailId: prepareStore.getSelectKey,
  407. classId: res.data,
  408. lessonCourseId: prepareStore.getBaseCourseware.id,
  409. preStudentNum: forms.preStudentNum
  410. }
  411. });
  412. window.open(href, +new Date() + '');
  413. }
  414. forms.showAttendClass = false;
  415. forms.instrumentErrorVisiable = false;
  416. } else {
  417. forms.showAttendClass = true;
  418. forms.attendClassType = 'change';
  419. forms.attendClassItem = item;
  420. }
  421. };
  422. /** 上课前检测声部 */
  423. const onClassCheckInstrument = async (
  424. item: any,
  425. classGroupId: any,
  426. instrumentId?: any
  427. ) => {
  428. try {
  429. if (classGroupId) {
  430. const { data } = await api_courseScheduleCheck({
  431. classGroupId,
  432. chapterLessonCoursewareId: item.id
  433. });
  434. forms.attendClassItem = item;
  435. forms.attendClassId = classGroupId;
  436. if (!data.chapterLessonCoursewareFlag) {
  437. forms.instrumentErrorVisiable = true;
  438. forms.instrumentErrorContent =
  439. '课件支持的乐器与班级不符,是否继续使用该课件上课?';
  440. } else if (!data.materialFlag) {
  441. forms.instrumentErrorVisiable = true;
  442. forms.instrumentErrorContent =
  443. '课件中含有不符合班级乐器的资源,是否继续使用该课件上课?';
  444. } else {
  445. onStartClass(item, classGroupId, instrumentId);
  446. }
  447. } else {
  448. onStartClass(item, classGroupId, instrumentId);
  449. }
  450. } catch {
  451. //
  452. }
  453. };
  454. const selectChildObj = (item: any) => {
  455. const obj: any = {};
  456. item?.forEach((child: any) => {
  457. if (child.id === forms.wikiCategoryIdChild) {
  458. obj.selected = true;
  459. obj.name = child.name;
  460. obj.label = child.name;
  461. }
  462. });
  463. return obj;
  464. };
  465. const tabInstrumentValue = computed(() => {
  466. let instrumentId: any = prepareStore.getInstrumentId
  467. ? prepareStore.getInstrumentId
  468. : '';
  469. prepareStore.getFormatInstrumentList.forEach((item: any) => {
  470. if (Array.isArray(item.instruments)) {
  471. item.instruments.forEach((child: any) => {
  472. if (child.id === prepareStore.getInstrumentId) {
  473. instrumentId = item.id + '';
  474. }
  475. });
  476. }
  477. });
  478. return instrumentId;
  479. });
  480. /* ppt课件 */
  481. const userStore = useUserStore();
  482. const pptCourseware = reactive({
  483. pptCoursewareShow: false,
  484. id: '',
  485. name: '',
  486. subjects: [],
  487. openFlagEnable: true,
  488. openFlag: false
  489. });
  490. const pptFormsRef = ref();
  491. const subjectList = ref<any[]>([]);
  492. const updateSubjectList = (ids?: any[]) => {
  493. // 获取适用乐器编号 ,修改的乐器编号,集合
  494. ids = ids || [];
  495. const courseIds: any = [];
  496. prepareStore.getInstrumentList.forEach((item: any) => {
  497. if (Array.isArray(item.instruments)) {
  498. item.instruments.forEach((child: any) => {
  499. courseIds.push(child.id);
  500. });
  501. }
  502. });
  503. const allIds = [...new Set([...courseIds, ...ids])];
  504. const tempList: any = [];
  505. useCatchStore().getSubjectList.forEach((item: any) => {
  506. const temp = deepClone(item);
  507. temp.enableFlag = false;
  508. if (Array.isArray(temp.instruments)) {
  509. temp.instruments.forEach((child: any) => {
  510. child.enableFlag = false;
  511. if (allIds.includes(child.id)) {
  512. child.enableFlag = true;
  513. temp.enableFlag = true;
  514. }
  515. });
  516. }
  517. tempList.push(temp);
  518. });
  519. const tempSubjects: any[] = [];
  520. tempList.forEach((subject: any) => {
  521. if (subject.enableFlag) {
  522. const { instruments, ...r } = subject;
  523. if (instruments && instruments.length > 0) {
  524. const tempChild: any[] = [];
  525. instruments?.forEach((instrument: any) => {
  526. if (instrument.enableFlag) {
  527. tempChild.push(instrument);
  528. }
  529. });
  530. if (tempChild.length > 0)
  531. tempSubjects.push({ ...r, instruments: tempChild });
  532. }
  533. }
  534. });
  535. subjectList.value = tempSubjects;
  536. };
  537. const chioseAll = (list: any) => {
  538. // 全选
  539. const ids = [] as any;
  540. list.map((item: any) => {
  541. if (Array.isArray(item.instruments)) {
  542. item.instruments.forEach((c: any) => {
  543. ids.push(c.value);
  544. });
  545. }
  546. }) as any;
  547. pptCourseware.subjects = ids;
  548. };
  549. function handlePptConfirm() {
  550. pptFormsRef.value?.validate(async (err: any) => {
  551. if (err) {
  552. return;
  553. }
  554. const { id, name, subjects, openFlag, openFlagEnable } = pptCourseware;
  555. if (id) {
  556. const params = {
  557. id,
  558. name,
  559. instrumentIds: subjects.join(','),
  560. openFlag,
  561. autoPlay: false,
  562. openFlagEnable
  563. };
  564. api_updateCoursewareInfo(params).then(res => {
  565. if (res.code === 200) {
  566. pptCourseware.pptCoursewareShow = false;
  567. getCoursewareList();
  568. eventGlobal.emit('openCoursewareChanged');
  569. }
  570. });
  571. } else {
  572. const params = {
  573. name,
  574. instrumentIds: subjects.join(','),
  575. openFlag,
  576. autoPlay: false,
  577. coursewareDetailKnowledgeId: prepareStore.getSelectKey,
  578. coursewareType: 'PPT'
  579. };
  580. api_teacherChapterLessonCoursewareAdd(params).then(res => {
  581. if (res.code === 200) {
  582. pptCourseware.pptCoursewareShow = false;
  583. getCoursewareList();
  584. eventGlobal.emit('openCoursewareChanged');
  585. const { teacherSaveFlag, id, chapterKnowledgeList } = res.data;
  586. const instrumentIdTab = prepareStore.getInstrumentId;
  587. handleRouterPPT(
  588. {
  589. teacherSaveFlag,
  590. id,
  591. pptId:
  592. chapterKnowledgeList[0]?.chapterKnowledgeMaterialList[0]?.id
  593. },
  594. pptCourseware.subjects.includes(instrumentIdTab)
  595. ? instrumentIdTab
  596. : pptCourseware.subjects[0]
  597. );
  598. }
  599. });
  600. }
  601. });
  602. }
  603. function handlePptEdit(item: Record<string, any>) {
  604. updateSubjectList();
  605. pptCourseware.id = item.id;
  606. pptCourseware.name = item.name;
  607. pptCourseware.subjects = item.instrumentIds
  608. ? item.instrumentIds.split(',')
  609. : [];
  610. pptCourseware.openFlag = item.openFlag;
  611. pptCourseware.openFlagEnable = item.openFlagEnable;
  612. pptCourseware.pptCoursewareShow = true;
  613. }
  614. async function handleRouterPPT(
  615. item: Record<string, any>,
  616. instrumentId: string
  617. ) {
  618. // 当teacherSaveFlag为false时候,需要把平台数据转为老师自己的课件
  619. if (!item.teacherSaveFlag) {
  620. const dataRes = await api_coursewareToTeacherCourseware(item.id);
  621. item.teacherSaveFlag = true;
  622. // 这里需要拿最新的id
  623. item.pptId =
  624. dataRes.data.chapterKnowledgeList[0].chapterKnowledgeMaterialList[0].id;
  625. }
  626. const href = `${vaildPPTUrl()}/#/pptEditor?id=${
  627. item.pptId
  628. }&Authorization=${
  629. userStore.getToken
  630. }&instrumentId=${instrumentId}&lessonCoursewareKnowledgeId=${
  631. prepareStore.getSelectKey
  632. }&fromType=TEACHER`;
  633. window.open(href);
  634. getCoursewareList();
  635. }
  636. onMounted(async () => {
  637. await useCatchStore().getSubjects();
  638. });
  639. return () => (
  640. <div
  641. class={[
  642. styles.coursewarePresetsContainer,
  643. forms.openTableShow && styles.rightLineShow
  644. ]}>
  645. <div
  646. class={styles.presetsLeft}
  647. id="presetsLeftRef"
  648. style={{ width: `calc(${forms.leftWidth} - ${forms.rightWidth})` }}>
  649. <NTabs
  650. ref={subjectRef}
  651. defaultValue=""
  652. paneClass={styles.paneTitle}
  653. justifyContent="start"
  654. paneWrapperClass={styles.paneWrapperContainer}
  655. value={tabInstrumentValue.value}
  656. onUpdate:value={(val: any) => {
  657. prepareStore.getFormatInstrumentList.forEach((item: any) => {
  658. if (item.value.toString() === val.toString()) {
  659. prepareStore.setInstrumentId(val);
  660. // 保存
  661. forms.instrumentId = val;
  662. forms.wikiCategoryIdChild = null;
  663. }
  664. });
  665. // if (!val) {
  666. // prepareStore.setInstrumentId(val);
  667. // // 保存
  668. // forms.instrumentId = val;
  669. // forms.wikiCategoryIdChild = null;
  670. // sessionStorage.setItem(
  671. // 'prepareLessonCourseWareSubjectIsNull',
  672. // val ? 'false' : 'true'
  673. // );
  674. // }
  675. }}
  676. v-slots={{
  677. suffix: () => (
  678. <NPopover
  679. placement="bottom"
  680. trigger="hover"
  681. showArrow={false}
  682. to={false}
  683. duration={50}>
  684. {{
  685. trigger: () => (
  686. <NButton
  687. class={styles.addBtn}
  688. type="primary"
  689. bordered={false}>
  690. <NImage
  691. class={styles.addBtnIcon}
  692. previewDisabled
  693. src={add}></NImage>
  694. 创建课件
  695. </NButton>
  696. ),
  697. default: () => (
  698. <div>
  699. <div
  700. class={styles.popoverItem}
  701. onClick={() => {
  702. eventGlobal.emit('teacher-slideshow', true);
  703. emit('change', {
  704. status: true,
  705. type: 'create'
  706. });
  707. }}>
  708. <span>数字化课件</span>
  709. </div>
  710. <div
  711. class={styles.popoverItem}
  712. onClick={() => {
  713. updateSubjectList();
  714. Object.assign(pptCourseware, {
  715. pptCoursewareShow: true,
  716. id: '',
  717. name: '',
  718. subjects: [],
  719. openFlagEnable: true,
  720. openFlag: false
  721. });
  722. }}>
  723. <span>PPT</span>
  724. </div>
  725. </div>
  726. )
  727. }}
  728. </NPopover>
  729. )
  730. }}>
  731. {[
  732. // { name: '全部乐器', id: '', label: '全部乐器', value: '' },
  733. ...prepareStore.getFormatInstrumentList
  734. ].map((item: any, index: number) => (
  735. <NTabPane
  736. name={`${item.value}`}
  737. tab={item.label}
  738. disabled={item.instruments?.length > 0}
  739. displayDirective="if">
  740. {{
  741. tab: () =>
  742. item.instruments?.length > 0 ? (
  743. <NPopselect
  744. options={item.instruments}
  745. trigger="hover"
  746. v-model:value={forms.wikiCategoryIdChild}
  747. onUpdate:value={(val: any) => {
  748. // onSearch();
  749. prepareStore.setInstrumentId(val);
  750. // 保存
  751. forms.instrumentId = val;
  752. // if (!val) {
  753. // sessionStorage.setItem(
  754. // 'prepareLessonCourseWareSubjectIsNull',
  755. // val ? 'false' : 'true'
  756. // );
  757. // }
  758. }}
  759. key={item.id}
  760. class={styles.popSelect}>
  761. <span
  762. class={[
  763. styles.textBtn,
  764. selectChildObj(item.instruments).selected &&
  765. styles.textBtnActive
  766. ]}>
  767. {selectChildObj(item.instruments).label || item.label}
  768. <i class={styles.iconArrow}></i>
  769. </span>
  770. </NPopselect>
  771. ) : (
  772. item.label
  773. )
  774. }}
  775. </NTabPane>
  776. ))}
  777. </NTabs>
  778. <NSpin show={forms.loading}>
  779. <NScrollbar class={styles.coursewarePresets}>
  780. <div style={{ overflow: 'hidden' }}>
  781. <div
  782. class={[
  783. styles.list,
  784. !forms.loading &&
  785. forms.tableList.length <= 0 &&
  786. styles.listEmpty
  787. ]}>
  788. {forms.tableList.map((item: any) => (
  789. <div class={[styles.itemWrap, styles.itemBlock, 'row-nav']}>
  790. <div class={styles.itemWrapBox}>
  791. <CoursewareType
  792. operate
  793. isEditName
  794. item={item}
  795. onClick={() => onPreviewAttend(item.id)}
  796. // onEditName={() => {
  797. // forms.selectItem = item;
  798. // forms.editTitle = item.name;
  799. // forms.editTitleVisiable = true;
  800. // }}
  801. onEdit={type => {
  802. //
  803. if (item.coursewareType === 'PPT') {
  804. if (type === 'PPT') {
  805. // 进入 ppt 编辑页面
  806. handleRouterPPT(
  807. item,
  808. prepareStore.getInstrumentId
  809. );
  810. } else {
  811. handlePptEdit(item);
  812. }
  813. } else {
  814. eventGlobal.emit('teacher-slideshow', true);
  815. emit('change', {
  816. status: true,
  817. type: 'update',
  818. groupItem: { id: item.id }
  819. });
  820. }
  821. }}
  822. onStartClass={() =>
  823. onClassCheckInstrument(item, forms.classGroupId)
  824. }
  825. onDelete={() => {
  826. forms.selectItem = item;
  827. forms.preRemoveVisiable = true;
  828. }}
  829. // 布置作业
  830. onWork={() => {
  831. forms.workVisiable = true;
  832. forms.selectItem = item;
  833. }}
  834. />
  835. </div>
  836. </div>
  837. ))}
  838. {!forms.loading && forms.tableList.length <= 0 && (
  839. <TheEmpty
  840. class={styles.empty1}
  841. description="当前章节暂无课件,快点击右上角创建课件吧"
  842. />
  843. )}
  844. </div>
  845. </div>
  846. </NScrollbar>
  847. </NSpin>
  848. </div>
  849. <div class={styles.presetsRight} id="presetsRightRef">
  850. <NTooltip
  851. showArrow={false}
  852. show={false}
  853. animated={false}
  854. duration={0}
  855. delay={0}>
  856. {{
  857. trigger: () => (
  858. <div
  859. class={[
  860. styles.presetsArrar,
  861. !forms.openTableShow && styles.presetsArrarActive
  862. ]}
  863. onClick={() => (forms.openTableShow = !forms.openTableShow)}>
  864. <NIcon>
  865. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  866. <path
  867. d="M16.62 2.99a1.25 1.25 0 0 0-1.77 0L6.54 11.3a.996.996 0 0 0 0 1.41l8.31 8.31c.49.49 1.28.49 1.77 0s.49-1.28 0-1.77L9.38 12l7.25-7.25c.48-.48.48-1.28-.01-1.76z"
  868. fill="currentColor"></path>
  869. </svg>
  870. </NIcon>
  871. </div>
  872. ),
  873. default: () => <div>{forms.openTableShow ? '收起' : '展开'}</div>
  874. }}
  875. </NTooltip>
  876. <Related
  877. onMore={() => (forms.showRelatedClass = true)}
  878. onAdd={(item: any) => {
  879. onAddCourseware(item);
  880. }}
  881. onLook={(item: any) => {
  882. onPreviewAttend(item.id);
  883. }}
  884. />
  885. </div>
  886. {/* )} */}
  887. <NModal
  888. maskClosable={modalClickMask}
  889. v-model:show={forms.showRelatedClass}
  890. preset="card"
  891. showIcon={false}
  892. class={['modalTitle background', styles.attendClassModal1]}
  893. title={'相关课件'}
  894. blockScroll={false}>
  895. <RelatedClass
  896. tableList={forms.tableList}
  897. instrumentList={prepareStore.getInstrumentList}
  898. instrumentId={prepareStore.getInstrumentId as any}
  899. coursewareDetailKnowledgeId={prepareStore.getSelectKey}
  900. onClose={() => (forms.showRelatedClass = false)}
  901. onAdd={(item: any) => onAddCourseware(item)}
  902. onClick={(item: any) => {
  903. onPreviewAttend(item.id);
  904. forms.showRelatedClass = false;
  905. }}
  906. />
  907. </NModal>
  908. {/* <NModal
  909. v-model:show={forms.editTitleVisiable}
  910. preset="card"
  911. class={['modalTitle', styles.removeVisiable1]}
  912. title={'课件重命名'}>
  913. <div class={styles.studentRemove}>
  914. <NInput
  915. placeholder="请输入课件名称"
  916. v-model:value={forms.editTitle}
  917. maxlength={15}
  918. onKeyup={(e: any) => {
  919. if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {
  920. e.stopPropagation();
  921. }
  922. }}
  923. />
  924. <NSpace class={styles.btnGroupModal} justify="center">
  925. <NButton round onClick={() => (forms.editTitleVisiable = false)}>
  926. 取消
  927. </NButton>
  928. <NButton
  929. round
  930. type="primary"
  931. onClick={onEditTitleSubmit}
  932. loading={forms.editBtnLoading}>
  933. 确定
  934. </NButton>
  935. </NSpace>
  936. </div>
  937. </NModal> */}
  938. <NModal
  939. maskClosable={modalClickMask}
  940. v-model:show={forms.preRemoveVisiable}
  941. preset="card"
  942. class={['modalTitle', styles.removeVisiable1]}
  943. title={'删除课件'}>
  944. <TheMessageDialog
  945. content={`<p style="text-align: left;">请确认是否删除【${forms.selectItem.name}】,删除后不可恢复</p>`}
  946. cancelButtonText="取消"
  947. confirmButtonText="确认"
  948. loading={forms.messageLoading}
  949. onClose={() => (forms.preRemoveVisiable = false)}
  950. onConfirm={() => onRemove()}
  951. />
  952. </NModal>
  953. <NModal
  954. maskClosable={modalClickMask}
  955. v-model:show={forms.addVisiable}
  956. preset="card"
  957. class={['modalTitle', styles.removeVisiable1]}
  958. title={'保存成功'}>
  959. <TheMessageDialog
  960. content={`<p style="text-align: left;">【${props.addParam.name}】暂未设置课件作业,是否现在去设置课件作业</p>`}
  961. cancelButtonText="稍后设置"
  962. confirmButtonText="立即设置"
  963. // loading={forms.messageLoading}
  964. onClose={() => (forms.addVisiable = false)}
  965. onConfirm={() => {
  966. forms.addVisiable = false;
  967. forms.workVisiable = true;
  968. forms.selectItem = {
  969. id: props.addParam.id,
  970. name: props.addParam.name
  971. };
  972. }}
  973. />
  974. </NModal>
  975. <NModal
  976. maskClosable={modalClickMask}
  977. v-model:show={forms.instrumentErrorVisiable}
  978. preset="card"
  979. class={['modalTitle', styles.removeVisiable1]}
  980. title={'温馨提示'}>
  981. <TheMessageDialog
  982. content={forms.instrumentErrorContent}
  983. contentDirection="left"
  984. onClose={() => {
  985. forms.instrumentErrorVisiable = false;
  986. }}
  987. onConfirm={() => {
  988. //
  989. onStartClass(forms.attendClassItem, forms.attendClassId);
  990. }}
  991. />
  992. </NModal>
  993. {/* 应用内预览或上课 */}
  994. <PreviewWindow
  995. v-model:show={forms.previewModal}
  996. type="attend"
  997. params={forms.previewParams}
  998. />
  999. <NModal
  1000. maskClosable={modalClickMask}
  1001. v-model:show={forms.showAttendClass}
  1002. preset="card"
  1003. showIcon={false}
  1004. class={['modalTitle background', styles.attendClassModal]}
  1005. title={'选择班级'}
  1006. blockScroll={false}>
  1007. <AttendClass
  1008. onClose={() => (forms.showAttendClass = false)}
  1009. type={forms.attendClassType}
  1010. onPreview={(item: any) => {
  1011. if (window.matchMedia('(display-mode: standalone)').matches) {
  1012. state.application = window.matchMedia(
  1013. '(display-mode: standalone)'
  1014. ).matches;
  1015. forms.previewModal = true;
  1016. forms.previewParams = {
  1017. ...item
  1018. };
  1019. } else {
  1020. const { href } = router.resolve({
  1021. path: '/attend-class',
  1022. query: {
  1023. ...item
  1024. }
  1025. });
  1026. window.open(href, +new Date() + '');
  1027. }
  1028. }}
  1029. onConfirm={async (item: any) => {
  1030. onClassCheckInstrument(
  1031. forms.attendClassItem,
  1032. item.classGroupId,
  1033. item.instrumentId
  1034. );
  1035. }}
  1036. />
  1037. </NModal>
  1038. <NModal
  1039. maskClosable={modalClickMask}
  1040. v-model:show={forms.workVisiable}
  1041. preset="card"
  1042. class={['modalTitle background', styles.workVisiable]}
  1043. title={
  1044. forms.selectItem.lessonPreTrainingId ? '编辑作业' : '创建作业'
  1045. }>
  1046. <div id="model-homework-height" class={styles.workContainer}>
  1047. <div class={styles.workTrain}>
  1048. <Train
  1049. cardType="prepare"
  1050. lessonPreTraining={{
  1051. title: forms.selectItem.name + '-课后作业',
  1052. chapterId: forms.selectItem.id, // 课件编号
  1053. id: forms.selectItem.lessonPreTrainingId // 作业编号
  1054. }}
  1055. onChange={(val: any) => {
  1056. forms.workVisiable = val.status;
  1057. getCoursewareList();
  1058. }}
  1059. />
  1060. </div>
  1061. <div class={styles.resourceMain}>
  1062. <ResourceMain cardType="prepare" />
  1063. </div>
  1064. </div>
  1065. </NModal>
  1066. {/* 新建ppt课件 */}
  1067. <NModal
  1068. maskClosable={modalClickMask}
  1069. v-model:show={pptCourseware.pptCoursewareShow}
  1070. preset="card"
  1071. class={['modalTitle', styles.pptCoursewareModal]}
  1072. title={'课件设置'}>
  1073. <NForm
  1074. ref={pptFormsRef}
  1075. model={pptCourseware}
  1076. labelAlign="right"
  1077. labelPlacement="left">
  1078. <NFormItem
  1079. label="课件名称"
  1080. path="name"
  1081. rule={[
  1082. {
  1083. required: true,
  1084. message: '请输入课件名称',
  1085. trigger: ['blur', 'change']
  1086. }
  1087. ]}>
  1088. <NInput
  1089. placeholder="请输入课件名称"
  1090. v-model:value={pptCourseware.name}
  1091. maxlength={20}
  1092. clearable
  1093. />
  1094. </NFormItem>
  1095. <NFormItem
  1096. label="适用乐器"
  1097. path="subjects"
  1098. rule={[
  1099. {
  1100. required: true,
  1101. message: '请选择适用乐器',
  1102. trigger: ['blur', 'change'],
  1103. type: 'array'
  1104. }
  1105. ]}>
  1106. <NCascader
  1107. placeholder="请选择乐器(可多选)"
  1108. class={styles.btnSubjectList}
  1109. options={subjectList.value}
  1110. checkStrategy="child"
  1111. showPath={false}
  1112. childrenField="instruments"
  1113. expandTrigger="hover"
  1114. labelField="name"
  1115. valueField="id"
  1116. clearable
  1117. filterable
  1118. multiple
  1119. maxTagCount={1}
  1120. v-model:value={pptCourseware.subjects}
  1121. v-slots={{
  1122. action: () => (
  1123. <>
  1124. <NButton
  1125. text
  1126. style=" --n-width: 100% "
  1127. size="small"
  1128. onClick={() => chioseAll(subjectList.value)}>
  1129. 全选
  1130. </NButton>
  1131. </>
  1132. )
  1133. }}
  1134. />
  1135. </NFormItem>
  1136. <div class={styles.btnItem}>
  1137. <span class={styles.btnTitle}>
  1138. 公开课件
  1139. <NTooltip style={{ maxWidth: '200px' }} showArrow={false}>
  1140. {{
  1141. trigger: () => <i class={styles.iconQuestion}></i>,
  1142. default: () => '公开课件后,其它老师可以使用该课件上课'
  1143. }}
  1144. </NTooltip>
  1145. </span>
  1146. {!pptCourseware.openFlagEnable ? (
  1147. <NTooltip style={{ maxWidth: '200px' }} showArrow={false}>
  1148. {{
  1149. trigger: () => (
  1150. <NSwitch
  1151. size="large"
  1152. v-model:value={pptCourseware.openFlag}
  1153. disabled={!pptCourseware.openFlagEnable}
  1154. />
  1155. ),
  1156. default: () =>
  1157. '为尊重课件原作者,在“相关课件”中添加的课件不支持公开'
  1158. }}
  1159. </NTooltip>
  1160. ) : (
  1161. <NSwitch
  1162. size="large"
  1163. v-model:value={pptCourseware.openFlag}
  1164. disabled={!pptCourseware.openFlagEnable}
  1165. />
  1166. )}
  1167. </div>
  1168. <NSpace class={styles.updateBtnGroup}>
  1169. <NButton
  1170. strong
  1171. type="default"
  1172. round
  1173. onClick={() => {
  1174. pptCourseware.pptCoursewareShow = false;
  1175. }}>
  1176. 取消
  1177. </NButton>
  1178. <NButton strong type="primary" round onClick={handlePptConfirm}>
  1179. 确认
  1180. </NButton>
  1181. </NSpace>
  1182. </NForm>
  1183. </NModal>
  1184. </div>
  1185. );
  1186. }
  1187. });