index.tsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. import { defineComponent, onMounted, reactive, watch } from 'vue';
  2. import styles from './index.module.less';
  3. import {
  4. NButton,
  5. NModal,
  6. NScrollbar,
  7. NSelect,
  8. NSpace,
  9. NSpin,
  10. useMessage,
  11. useDialog
  12. } from 'naive-ui';
  13. import CardType from '/src/components/card-type';
  14. import AttendClass from '/src/views/prepare-lessons/model/attend-class';
  15. import { usePrepareStore } from '/src/store/modules/prepareLessons';
  16. import { useCatchStore } from '/src/store/modules/catchData';
  17. import TheEmpty from '/src/components/TheEmpty';
  18. import {
  19. courseScheduleStart,
  20. queryCourseware,
  21. saveCourseware,
  22. teacherKnowledgeMaterialDelete
  23. } from '../../../api';
  24. import Draggable from 'vuedraggable';
  25. import iconDelete from '../../../images/icon-delete.png';
  26. import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
  27. import deepClone from '/src/helpers/deep-clone';
  28. import CardPreview from '/src/components/card-preview';
  29. import PreviewWindow from '/src/views/preview-window';
  30. import { state } from '/src/state';
  31. import SubjectSync from '../../../model/subject-sync';
  32. import { eventGlobal } from '/src/utils';
  33. export default defineComponent({
  34. name: 'courseware-modal',
  35. setup() {
  36. const catchStore = useCatchStore();
  37. const prepareStore = usePrepareStore();
  38. const route = useRoute();
  39. const router = useRouter();
  40. const dialog = useDialog();
  41. const message = useMessage();
  42. const forms = reactive({
  43. className: route.query.name as any,
  44. classGroupId: route.query.classGroupId,
  45. subjectId: route.query.subjectId ? Number(route.query.subjectId) : null,
  46. coursewareList: [] as any,
  47. loadingStatus: false,
  48. showAttendClass: false,
  49. attendClassType: 'select', //
  50. removeIds: [] as any, // 临时删除的编号
  51. drag: false,
  52. isEdit: false, // 是否更新数据
  53. editSubjectIds: '', // 声部编号
  54. removeVisiable: false,
  55. removeVisiable1: false,
  56. subjectSyncVisiable: false, // 同步声部
  57. show: false,
  58. item: {} as any,
  59. previewModal: false,
  60. previewParams: {
  61. type: '',
  62. subjectId: '',
  63. detailId: ''
  64. } as any
  65. });
  66. // 获取列表
  67. const getList = async () => {
  68. forms.loadingStatus = true;
  69. try {
  70. // 判断是否有选择对应的课件 或声部
  71. if (!prepareStore.getSelectKey || !prepareStore.getSubjectId)
  72. return (forms.loadingStatus = false);
  73. const { data } = await queryCourseware({
  74. coursewareDetailKnowledgeId: prepareStore.getSelectKey,
  75. subjectId: prepareStore.getSubjectId,
  76. page: 1,
  77. rows: 99
  78. });
  79. const tempRows = data.rows || [];
  80. const temp: any = [];
  81. tempRows.forEach((row: any) => {
  82. temp.push({
  83. id: row.id,
  84. materialId: row.materialId,
  85. coverImg: row.coverImg,
  86. type: row.materialType,
  87. title: row.materialName,
  88. isCollect: !!row.favoriteFlag,
  89. isSelected: row.source === 'PLATFORM' ? true : false,
  90. content: row.content,
  91. removeFlag: row.removeFlag
  92. });
  93. });
  94. prepareStore.setCoursewareList(temp || []);
  95. const tempCourse: any = [];
  96. temp.forEach((item: any) => {
  97. if (!forms.removeIds.includes(item.id)) {
  98. tempCourse.push(item);
  99. }
  100. });
  101. forms.coursewareList = tempCourse;
  102. } catch {
  103. //
  104. }
  105. forms.loadingStatus = false;
  106. };
  107. // 监听选择的key 左侧选择了其它的课
  108. watch(
  109. () => prepareStore.getSelectKey,
  110. () => {
  111. forms.drag = false;
  112. getList();
  113. }
  114. );
  115. // 声部变化时
  116. watch(
  117. () => prepareStore.getSubjectId,
  118. () => {
  119. getList();
  120. }
  121. );
  122. watch(
  123. () => prepareStore.getIsAddResource,
  124. (val: boolean) => {
  125. if (val) {
  126. getList();
  127. prepareStore.setIsAddResource(false);
  128. }
  129. }
  130. );
  131. // 删除
  132. const onDelete = (item: any) => {
  133. //
  134. forms.removeIds.push(item.id);
  135. const index = forms.coursewareList.findIndex(
  136. (c: any) => c.id === item.id
  137. );
  138. forms.coursewareList.splice(index, 1);
  139. forms.isEdit = true;
  140. // prepareStore.setCoursewareList(forms.coursewareList);
  141. // console.log(prepareStore.getCoursewareList, 'getCourseware');
  142. };
  143. // 完成编辑
  144. const onOverEdit = async () => {
  145. try {
  146. const temp: any = [];
  147. forms.coursewareList.forEach((item: any) => {
  148. temp.push({
  149. materialName: item.title,
  150. materialType: item.type,
  151. materialId: item.materialId,
  152. id: item.id
  153. });
  154. });
  155. // 保存课件
  156. // 判断是否编辑,如果编辑则取选择的声部
  157. await saveCourseware({
  158. coursewareDetailKnowledgeId: prepareStore.getSelectKey,
  159. lessonCoursewareId: prepareStore.getLessonCoursewareId,
  160. lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
  161. subjectId: forms.isEdit
  162. ? forms.editSubjectIds
  163. : prepareStore.getSubjectId,
  164. materialList: [...temp]
  165. });
  166. forms.drag = false;
  167. message.success('编辑成功');
  168. forms.removeVisiable = false;
  169. prepareStore.setIsEditResource(false);
  170. // 重置临时删除编号
  171. forms.removeIds = [];
  172. await getList();
  173. } catch {
  174. //
  175. }
  176. };
  177. // 预览上课
  178. const onPreviewAttend = () => {
  179. // 获取上架的数据
  180. let count = 0;
  181. forms.coursewareList.forEach((item: any) => {
  182. if (!item.removeFlag) {
  183. count++;
  184. }
  185. });
  186. if (count <= 0) {
  187. message.error('课件不能为空');
  188. return;
  189. }
  190. // 判断是否在应用里面
  191. if (window.matchMedia('(display-mode: standalone)').matches) {
  192. state.application = window.matchMedia(
  193. '(display-mode: standalone)'
  194. ).matches;
  195. forms.previewModal = true;
  196. fscreen();
  197. forms.previewParams = {
  198. type: 'preview',
  199. subjectId: prepareStore.getSubjectId,
  200. detailId: prepareStore.getSelectKey
  201. };
  202. } else {
  203. const { href } = router.resolve({
  204. path: '/attend-class',
  205. query: {
  206. type: 'preview',
  207. subjectId: prepareStore.getSubjectId,
  208. detailId: prepareStore.getSelectKey
  209. }
  210. });
  211. window.open(href, +new Date() + '');
  212. }
  213. };
  214. const fscreen = () => {
  215. const el = document.documentElement;
  216. const isFullscreen =
  217. document.fullScreen ||
  218. document.mozFullScreen ||
  219. document.webkitIsFullScreen;
  220. if (!isFullscreen) {
  221. //进入全屏
  222. (el.requestFullscreen && el.requestFullscreen()) ||
  223. (el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
  224. (el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
  225. (el.msRequestFullscreen && el.msRequestFullscreen());
  226. }
  227. };
  228. // 单个删除
  229. const onRemove = async (item: any) => {
  230. try {
  231. dialog.warning({
  232. title: '提示',
  233. content: '该资源已下架,是否删除?',
  234. positiveText: '确定',
  235. negativeText: '取消',
  236. onPositiveClick: async () => {
  237. forms.removeIds.push(item.id);
  238. await teacherKnowledgeMaterialDelete({ ids: item.id });
  239. message.success('删除成功');
  240. getList();
  241. }
  242. });
  243. } catch {
  244. //
  245. }
  246. };
  247. watch(
  248. () => prepareStore.getSubjectList,
  249. () => {
  250. checkSubjectIds();
  251. }
  252. );
  253. const checkSubjectIds = () => {
  254. const subjectList = prepareStore.getSubjectList;
  255. // console.log(subjectList, 'subjectList');
  256. // 并且没有声部时才会更新
  257. if (subjectList.length > 0) {
  258. // 判断浏览器上面是否有
  259. const index = subjectList.findIndex(
  260. (subject: any) => subject.id == forms.subjectId
  261. );
  262. // 并且声部在列表中
  263. if (forms.subjectId && index >= 0) {
  264. prepareStore.setSubjectId(forms.subjectId);
  265. } else if (!prepareStore.getSubjectId) {
  266. // 判断是否有缓存
  267. prepareStore.setSubjectId(subjectList[0].id);
  268. }
  269. }
  270. };
  271. watch(
  272. () => route.query,
  273. async () => {
  274. forms.className = route.query.name as any;
  275. forms.classGroupId = route.query.classGroupId as any;
  276. forms.subjectId = route.query.subjectId
  277. ? Number(route.query.subjectId)
  278. : null;
  279. checkSubjectIds();
  280. await getList();
  281. }
  282. );
  283. onMounted(async () => {
  284. // 获取教材分类列表
  285. checkSubjectIds();
  286. await getList();
  287. // 动态添加数据
  288. eventGlobal.on('onPrepareAddItem', (item: any) => {
  289. forms.coursewareList.push(item);
  290. prepareStore.setCoursewareList(forms.coursewareList);
  291. forms.isEdit = true;
  292. });
  293. });
  294. return () => (
  295. <div class={styles.coursewareModal}>
  296. <div class={styles.btnGroup}>
  297. {forms.drag ? (
  298. <NSpace>
  299. <NButton
  300. type="default"
  301. onClick={() => {
  302. if (forms.isEdit) {
  303. forms.subjectSyncVisiable = true;
  304. } else {
  305. forms.removeVisiable = true;
  306. }
  307. }}>
  308. 完成编辑
  309. </NButton>
  310. <NButton
  311. type="error"
  312. onClick={() => {
  313. forms.drag = false;
  314. forms.isEdit = false;
  315. prepareStore.setIsEditResource(false);
  316. forms.removeIds = [];
  317. getList();
  318. }}>
  319. 取消编辑
  320. </NButton>
  321. <NButton
  322. type="error"
  323. onClick={() => {
  324. forms.removeVisiable1 = true;
  325. forms.isEdit = true;
  326. }}>
  327. 清空资源
  328. </NButton>
  329. <span class={styles.tips}>拖动可将资源进行排序</span>
  330. </NSpace>
  331. ) : (
  332. <NSpace>
  333. {forms.classGroupId && (
  334. <div class={styles.btnItem}>
  335. <span class={styles.btnTitle}>上课班级:</span>
  336. <div
  337. onClick={() => {
  338. forms.showAttendClass = true;
  339. forms.attendClassType = 'change';
  340. }}>
  341. <NSelect
  342. placeholder="选择声部"
  343. labelField="name"
  344. valueField="id"
  345. class={styles.btnClassList}
  346. value={forms.className}
  347. disabled
  348. />
  349. </div>
  350. </div>
  351. )}
  352. <div class={styles.btnItem}>
  353. <span class={styles.btnTitle}>声部:</span>
  354. <NSelect
  355. placeholder="选择声部"
  356. class={styles.btnSubjectList}
  357. options={prepareStore.getSubjectList}
  358. labelField="name"
  359. valueField="id"
  360. value={prepareStore.getSubjectId}
  361. onUpdate:value={(val: any) => {
  362. prepareStore.setSubjectId(val);
  363. getList();
  364. }}
  365. />
  366. </div>
  367. </NSpace>
  368. )}
  369. {/* 编辑 */}
  370. {!forms.drag && (
  371. <NSpace>
  372. <NButton
  373. type="default"
  374. onClick={() => {
  375. forms.drag = true;
  376. prepareStore.setIsEditResource(true);
  377. // forms.subjectSyncVisiable = true;
  378. }}>
  379. 编辑
  380. </NButton>
  381. </NSpace>
  382. )}
  383. </div>
  384. <NScrollbar class={styles.listContainer} {...{ id: 'lessons-2' }}>
  385. <NSpin show={forms.loadingStatus}>
  386. <div
  387. class={[
  388. styles.listSection,
  389. !forms.loadingStatus && forms.coursewareList.length <= 0
  390. ? styles.emptySection
  391. : ''
  392. ]}>
  393. {forms.coursewareList.length > 0 && (
  394. <>
  395. {forms.drag ? (
  396. <Draggable
  397. v-model:modelValue={forms.coursewareList}
  398. itemKey="id"
  399. componentData={{
  400. itemKey: 'id',
  401. tag: 'div',
  402. animation: 200,
  403. group: 'description',
  404. disabled: false
  405. }}
  406. class={styles.list}>
  407. {{
  408. item: (element: any) => {
  409. const item = element.element;
  410. return (
  411. <div
  412. data-id={item.id}
  413. class={[styles.itemBlock, 'row-nav']}>
  414. <CardType
  415. class={[styles.itemContent]}
  416. isShowCollect={false}
  417. offShelf={item.removeFlag ? true : false}
  418. onOffShelf={() => onRemove(item)}
  419. item={item}
  420. />
  421. <div class={styles.itemOperation}>
  422. <img
  423. src={iconDelete}
  424. class={styles.iconDelete}
  425. onClick={(e: MouseEvent) => {
  426. e.stopPropagation();
  427. onDelete(item);
  428. }}
  429. />
  430. </div>
  431. </div>
  432. );
  433. }
  434. }}
  435. </Draggable>
  436. ) : (
  437. <div class={styles.list}>
  438. {forms.coursewareList.map((item: any) => (
  439. <CardType
  440. class={[styles.itemContent, 'handle']}
  441. isShowCollect={false}
  442. item={item}
  443. offShelf={item.removeFlag ? true : false}
  444. onOffShelf={() => onRemove(item)}
  445. disabledMouseHover={false}
  446. onClick={() => {
  447. if (item.type === 'IMG') return;
  448. forms.show = true;
  449. forms.item = item;
  450. }}
  451. />
  452. ))}
  453. </div>
  454. )}
  455. </>
  456. )}
  457. {!forms.loadingStatus && forms.coursewareList.length <= 0 && (
  458. <TheEmpty description="暂无课件" />
  459. )}
  460. </div>
  461. </NSpin>
  462. </NScrollbar>
  463. <div class={styles.btnGroup} style={{ justifyContent: 'flex-end' }}>
  464. <NSpace justify="end">
  465. <NButton type="primary" onClick={onPreviewAttend}>
  466. 预览课件
  467. </NButton>
  468. <NButton
  469. {...{ id: 'lessons-3' }}
  470. type="primary"
  471. class={styles.btnClassStart}
  472. onClick={async () => {
  473. let count = 0;
  474. forms.coursewareList.forEach((item: any) => {
  475. if (!item.removeFlag) {
  476. count++;
  477. }
  478. });
  479. if (count <= 0) {
  480. message.error('课件不能为空');
  481. return;
  482. }
  483. if (forms.classGroupId) {
  484. // 开始上课
  485. await courseScheduleStart({
  486. lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
  487. classGroupId: forms.classGroupId
  488. });
  489. if (window.matchMedia('(display-mode: standalone)').matches) {
  490. state.application = window.matchMedia(
  491. '(display-mode: standalone)'
  492. ).matches;
  493. forms.previewModal = true;
  494. forms.previewParams = {
  495. type: 'class',
  496. classGroupId: forms.classGroupId,
  497. subjectId: prepareStore.getSubjectId,
  498. detailId: prepareStore.getSelectKey
  499. };
  500. } else {
  501. const { href } = router.resolve({
  502. path: '/attend-class',
  503. query: {
  504. type: 'class',
  505. classGroupId: forms.classGroupId,
  506. subjectId: prepareStore.getSubjectId,
  507. detailId: prepareStore.getSelectKey
  508. }
  509. });
  510. window.open(href, +new Date() + '');
  511. }
  512. } else {
  513. forms.showAttendClass = true;
  514. forms.attendClassType = 'select';
  515. }
  516. }}>
  517. 开始上课
  518. </NButton>
  519. </NSpace>
  520. </div>
  521. <NModal
  522. v-model:show={forms.showAttendClass}
  523. preset="card"
  524. showIcon={false}
  525. class={['modalTitle background', styles.attendClassModal]}
  526. title={'选择班级'}
  527. blockScroll={false}>
  528. <AttendClass
  529. onClose={() => (forms.showAttendClass = false)}
  530. type={forms.attendClassType}
  531. onPreview={(item: any) => {
  532. if (window.matchMedia('(display-mode: standalone)').matches) {
  533. state.application = window.matchMedia(
  534. '(display-mode: standalone)'
  535. ).matches;
  536. forms.previewModal = true;
  537. forms.previewParams = {
  538. ...item
  539. };
  540. } else {
  541. const { href } = router.resolve({
  542. path: '/attend-class',
  543. query: {
  544. ...item
  545. }
  546. });
  547. window.open(href, +new Date() + '');
  548. }
  549. }}
  550. onConfirm={(item: any) => {
  551. console.log(item, 'confirm');
  552. forms.className = item.name;
  553. forms.classGroupId = item.classGroupId;
  554. forms.subjectId = item.subjectId;
  555. forms.showAttendClass = false;
  556. checkSubjectIds();
  557. // 声部切换时
  558. eventGlobal.emit('onChangeClass', {
  559. lastUseCoursewareId: item.lastUseCoursewareId,
  560. unit: item.unit
  561. });
  562. }}
  563. />
  564. </NModal>
  565. {/* 弹窗查看 */}
  566. <CardPreview v-model:show={forms.show} item={forms.item} />
  567. <NModal
  568. v-model:show={forms.removeVisiable}
  569. preset="card"
  570. class={['modalTitle', styles.removeVisiable]}
  571. title={'提示'}>
  572. <div class={styles.studentRemove}>
  573. <p>是否完成编辑?</p>
  574. <NSpace class={styles.btnGroupModal} justify="center">
  575. <NButton round type="primary" onClick={onOverEdit}>
  576. 确定
  577. </NButton>
  578. <NButton round onClick={() => (forms.removeVisiable = false)}>
  579. 取消
  580. </NButton>
  581. </NSpace>
  582. </div>
  583. </NModal>
  584. <NModal
  585. v-model:show={forms.removeVisiable1}
  586. preset="card"
  587. class={['modalTitle', styles.removeVisiable1]}
  588. title={'清空资源'}>
  589. <div class={styles.studentRemove}>
  590. <p>
  591. 请确认是否要清空资源?
  592. <span>点击确认后所有的素材内容 将被清空掉。</span>
  593. </p>
  594. <NSpace class={styles.btnGroupModal} justify="center">
  595. <NButton
  596. round
  597. type="primary"
  598. onClick={() => {
  599. forms.coursewareList.forEach((item: any) => {
  600. forms.removeIds.push(item.id);
  601. });
  602. forms.coursewareList = [];
  603. forms.removeVisiable1 = false;
  604. // prepareStore.setCoursewareList([]);
  605. console.log(prepareStore.getCoursewareList, 'getCourseware1');
  606. }}>
  607. 确定
  608. </NButton>
  609. <NButton round onClick={() => (forms.removeVisiable1 = false)}>
  610. 取消
  611. </NButton>
  612. </NSpace>
  613. </div>
  614. </NModal>
  615. <PreviewWindow
  616. v-model:show={forms.previewModal}
  617. type="attend"
  618. params={forms.previewParams}
  619. />
  620. {/* 完成编辑时,选择声部 */}
  621. <NModal
  622. v-model:show={forms.subjectSyncVisiable}
  623. preset="card"
  624. class={['modalTitle background', styles.subjectSyncModal]}
  625. title={'同步声部'}>
  626. <SubjectSync
  627. subjectId={prepareStore.getSubjectId as any}
  628. onClose={() => (forms.subjectSyncVisiable = false)}
  629. onConfirm={async (subjectIds: any) => {
  630. //
  631. try {
  632. forms.editSubjectIds = subjectIds.join(',');
  633. await onOverEdit();
  634. forms.subjectSyncVisiable = false;
  635. } catch {
  636. //
  637. }
  638. }}
  639. />
  640. </NModal>
  641. </div>
  642. );
  643. }
  644. });