index.tsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  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. lessonCourseId: prepareStore.getBaseCourseware.id
  202. };
  203. } else {
  204. const { href } = router.resolve({
  205. path: '/attend-class',
  206. query: {
  207. type: 'preview',
  208. subjectId: prepareStore.getSubjectId,
  209. detailId: prepareStore.getSelectKey,
  210. lessonCourseId: prepareStore.getBaseCourseware.id
  211. }
  212. });
  213. window.open(href, +new Date() + '');
  214. }
  215. };
  216. const fscreen = () => {
  217. const el = document.documentElement;
  218. const isFullscreen =
  219. document.fullScreen ||
  220. document.mozFullScreen ||
  221. document.webkitIsFullScreen;
  222. if (!isFullscreen) {
  223. //进入全屏
  224. (el.requestFullscreen && el.requestFullscreen()) ||
  225. (el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
  226. (el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
  227. (el.msRequestFullscreen && el.msRequestFullscreen());
  228. }
  229. };
  230. // 单个删除
  231. const onRemove = async (item: any) => {
  232. try {
  233. dialog.warning({
  234. title: '提示',
  235. content: '该资源已下架,是否删除?',
  236. positiveText: '确定',
  237. negativeText: '取消',
  238. onPositiveClick: async () => {
  239. forms.removeIds.push(item.id);
  240. await teacherKnowledgeMaterialDelete({ ids: item.id });
  241. message.success('删除成功');
  242. getList();
  243. }
  244. });
  245. } catch {
  246. //
  247. }
  248. };
  249. watch(
  250. () => prepareStore.getSubjectList,
  251. () => {
  252. checkSubjectIds();
  253. }
  254. );
  255. const checkSubjectIds = () => {
  256. const subjectList = prepareStore.getSubjectList;
  257. // console.log(subjectList, 'subjectList');
  258. // 并且没有声部时才会更新
  259. if (subjectList.length > 0) {
  260. // 判断浏览器上面是否有
  261. const index = subjectList.findIndex(
  262. (subject: any) => subject.id == forms.subjectId
  263. );
  264. // 并且声部在列表中
  265. if (forms.subjectId && index >= 0) {
  266. prepareStore.setSubjectId(forms.subjectId);
  267. } else if (!prepareStore.getSubjectId) {
  268. // 判断是否有缓存
  269. prepareStore.setSubjectId(subjectList[0].id);
  270. }
  271. }
  272. };
  273. watch(
  274. () => route.query,
  275. async () => {
  276. forms.className = route.query.name as any;
  277. forms.classGroupId = route.query.classGroupId as any;
  278. forms.subjectId = route.query.subjectId
  279. ? Number(route.query.subjectId)
  280. : null;
  281. checkSubjectIds();
  282. await getList();
  283. }
  284. );
  285. onMounted(async () => {
  286. // 获取教材分类列表
  287. checkSubjectIds();
  288. await getList();
  289. // 动态添加数据
  290. eventGlobal.on('onPrepareAddItem', (item: any) => {
  291. forms.coursewareList.push(item);
  292. prepareStore.setCoursewareList(forms.coursewareList);
  293. forms.isEdit = true;
  294. });
  295. });
  296. return () => (
  297. <div class={styles.coursewareModal}>
  298. <div class={styles.btnGroup}>
  299. {forms.drag ? (
  300. <NSpace>
  301. <NButton
  302. type="default"
  303. onClick={() => {
  304. if (forms.isEdit) {
  305. forms.subjectSyncVisiable = true;
  306. } else {
  307. forms.removeVisiable = true;
  308. }
  309. }}>
  310. 完成编辑
  311. </NButton>
  312. <NButton
  313. type="error"
  314. onClick={() => {
  315. forms.drag = false;
  316. forms.isEdit = false;
  317. prepareStore.setIsEditResource(false);
  318. forms.removeIds = [];
  319. getList();
  320. }}>
  321. 取消编辑
  322. </NButton>
  323. <NButton
  324. type="error"
  325. onClick={() => {
  326. forms.removeVisiable1 = true;
  327. forms.isEdit = true;
  328. }}>
  329. 清空资源
  330. </NButton>
  331. <span class={styles.tips}>拖动可将资源进行排序</span>
  332. </NSpace>
  333. ) : (
  334. <NSpace>
  335. {forms.classGroupId && (
  336. <div class={styles.btnItem}>
  337. <span class={styles.btnTitle}>上课班级:</span>
  338. <div
  339. onClick={() => {
  340. forms.showAttendClass = true;
  341. forms.attendClassType = 'change';
  342. }}>
  343. <NSelect
  344. placeholder="选择声部"
  345. labelField="name"
  346. valueField="id"
  347. class={styles.btnClassList}
  348. value={forms.className}
  349. disabled
  350. />
  351. </div>
  352. </div>
  353. )}
  354. <div class={styles.btnItem}>
  355. <span class={styles.btnTitle}>声部:</span>
  356. <NSelect
  357. placeholder="选择声部"
  358. class={styles.btnSubjectList}
  359. options={prepareStore.getSubjectList}
  360. labelField="name"
  361. valueField="id"
  362. value={prepareStore.getSubjectId}
  363. onUpdate:value={(val: any) => {
  364. prepareStore.setSubjectId(val);
  365. getList();
  366. }}
  367. />
  368. </div>
  369. </NSpace>
  370. )}
  371. {/* 编辑 */}
  372. {!forms.drag && (
  373. <NSpace>
  374. <NButton
  375. type="default"
  376. onClick={() => {
  377. forms.drag = true;
  378. prepareStore.setIsEditResource(true);
  379. // forms.subjectSyncVisiable = true;
  380. }}>
  381. 编辑
  382. </NButton>
  383. </NSpace>
  384. )}
  385. </div>
  386. <NScrollbar class={styles.listContainer} {...{ id: 'lessons-2' }}>
  387. <NSpin show={forms.loadingStatus}>
  388. <div
  389. class={[
  390. styles.listSection,
  391. !forms.loadingStatus && forms.coursewareList.length <= 0
  392. ? styles.emptySection
  393. : ''
  394. ]}>
  395. {forms.coursewareList.length > 0 && (
  396. <>
  397. {forms.drag ? (
  398. <Draggable
  399. v-model:modelValue={forms.coursewareList}
  400. itemKey="id"
  401. componentData={{
  402. itemKey: 'id',
  403. tag: 'div',
  404. animation: 200,
  405. group: 'description',
  406. disabled: false
  407. }}
  408. class={styles.list}>
  409. {{
  410. item: (element: any) => {
  411. const item = element.element;
  412. return (
  413. <div
  414. data-id={item.id}
  415. class={[styles.itemBlock, 'row-nav']}>
  416. <CardType
  417. class={[styles.itemContent]}
  418. isShowCollect={false}
  419. offShelf={item.removeFlag ? true : false}
  420. onOffShelf={() => onRemove(item)}
  421. item={item}
  422. />
  423. <div class={styles.itemOperation}>
  424. <img
  425. src={iconDelete}
  426. class={styles.iconDelete}
  427. onClick={(e: MouseEvent) => {
  428. e.stopPropagation();
  429. onDelete(item);
  430. }}
  431. />
  432. </div>
  433. </div>
  434. );
  435. }
  436. }}
  437. </Draggable>
  438. ) : (
  439. <div class={styles.list}>
  440. {forms.coursewareList.map((item: any) => (
  441. <CardType
  442. class={[styles.itemContent, 'handle']}
  443. isShowCollect={false}
  444. item={item}
  445. offShelf={item.removeFlag ? true : false}
  446. onOffShelf={() => onRemove(item)}
  447. disabledMouseHover={false}
  448. onClick={() => {
  449. if (item.type === 'IMG') return;
  450. forms.show = true;
  451. forms.item = item;
  452. }}
  453. />
  454. ))}
  455. </div>
  456. )}
  457. </>
  458. )}
  459. {!forms.loadingStatus && forms.coursewareList.length <= 0 && (
  460. <TheEmpty description="暂无课件" />
  461. )}
  462. </div>
  463. </NSpin>
  464. </NScrollbar>
  465. <div class={styles.btnGroup} style={{ justifyContent: 'flex-end' }}>
  466. <NSpace justify="end">
  467. <NButton type="primary" onClick={onPreviewAttend}>
  468. 预览课件
  469. </NButton>
  470. <NButton
  471. {...{ id: 'lessons-3' }}
  472. type="primary"
  473. class={styles.btnClassStart}
  474. onClick={async () => {
  475. let count = 0;
  476. forms.coursewareList.forEach((item: any) => {
  477. if (!item.removeFlag) {
  478. count++;
  479. }
  480. });
  481. if (count <= 0) {
  482. message.error('课件不能为空');
  483. return;
  484. }
  485. if (forms.classGroupId) {
  486. // 开始上课
  487. const res = await courseScheduleStart({
  488. lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
  489. classGroupId: forms.classGroupId
  490. });
  491. if (window.matchMedia('(display-mode: standalone)').matches) {
  492. state.application = window.matchMedia(
  493. '(display-mode: standalone)'
  494. ).matches;
  495. forms.previewModal = true;
  496. fscreen();
  497. forms.previewParams = {
  498. type: 'class',
  499. classGroupId: forms.classGroupId,
  500. subjectId: prepareStore.getSubjectId,
  501. detailId: prepareStore.getSelectKey,
  502. classId: res.data,
  503. lessonCourseId: prepareStore.getBaseCourseware.id
  504. };
  505. } else {
  506. const { href } = router.resolve({
  507. path: '/attend-class',
  508. query: {
  509. type: 'class',
  510. classGroupId: forms.classGroupId,
  511. subjectId: prepareStore.getSubjectId,
  512. detailId: prepareStore.getSelectKey,
  513. classId: res.data,
  514. lessonCourseId: prepareStore.getBaseCourseware.id
  515. }
  516. });
  517. window.open(href, +new Date() + '');
  518. }
  519. } else {
  520. forms.showAttendClass = true;
  521. forms.attendClassType = 'select';
  522. }
  523. }}>
  524. 开始上课
  525. </NButton>
  526. </NSpace>
  527. </div>
  528. <NModal
  529. v-model:show={forms.showAttendClass}
  530. preset="card"
  531. showIcon={false}
  532. class={['modalTitle background', styles.attendClassModal]}
  533. title={'选择班级'}
  534. blockScroll={false}>
  535. <AttendClass
  536. onClose={() => (forms.showAttendClass = false)}
  537. type={forms.attendClassType}
  538. onPreview={(item: any) => {
  539. if (window.matchMedia('(display-mode: standalone)').matches) {
  540. state.application = window.matchMedia(
  541. '(display-mode: standalone)'
  542. ).matches;
  543. forms.previewModal = true;
  544. forms.previewParams = {
  545. ...item
  546. };
  547. } else {
  548. const { href } = router.resolve({
  549. path: '/attend-class',
  550. query: {
  551. ...item
  552. }
  553. });
  554. window.open(href, +new Date() + '');
  555. }
  556. }}
  557. onConfirm={(item: any) => {
  558. console.log(item, 'confirm');
  559. forms.className = item.name;
  560. forms.classGroupId = item.classGroupId;
  561. forms.subjectId = item.subjectId;
  562. forms.showAttendClass = false;
  563. checkSubjectIds();
  564. // 声部切换时
  565. eventGlobal.emit('onChangeClass', {
  566. lastUseCoursewareId: item.lastUseCoursewareId,
  567. unit: item.unit
  568. });
  569. }}
  570. />
  571. </NModal>
  572. {/* 弹窗查看 */}
  573. <CardPreview v-model:show={forms.show} item={forms.item} />
  574. <NModal
  575. v-model:show={forms.removeVisiable}
  576. preset="card"
  577. class={['modalTitle', styles.removeVisiable]}
  578. title={'提示'}>
  579. <div class={styles.studentRemove}>
  580. <p>是否完成编辑?</p>
  581. <NSpace class={styles.btnGroupModal} justify="center">
  582. <NButton round type="primary" onClick={onOverEdit}>
  583. 确定
  584. </NButton>
  585. <NButton round onClick={() => (forms.removeVisiable = false)}>
  586. 取消
  587. </NButton>
  588. </NSpace>
  589. </div>
  590. </NModal>
  591. <NModal
  592. v-model:show={forms.removeVisiable1}
  593. preset="card"
  594. class={['modalTitle', styles.removeVisiable1]}
  595. title={'清空资源'}>
  596. <div class={styles.studentRemove}>
  597. <p>
  598. 请确认是否要清空资源?
  599. <span>点击确认后所有的素材内容 将被清空掉。</span>
  600. </p>
  601. <NSpace class={styles.btnGroupModal} justify="center">
  602. <NButton
  603. round
  604. type="primary"
  605. onClick={() => {
  606. forms.coursewareList.forEach((item: any) => {
  607. forms.removeIds.push(item.id);
  608. });
  609. forms.coursewareList = [];
  610. forms.removeVisiable1 = false;
  611. // prepareStore.setCoursewareList([]);
  612. console.log(prepareStore.getCoursewareList, 'getCourseware1');
  613. }}>
  614. 确定
  615. </NButton>
  616. <NButton round onClick={() => (forms.removeVisiable1 = false)}>
  617. 取消
  618. </NButton>
  619. </NSpace>
  620. </div>
  621. </NModal>
  622. <PreviewWindow
  623. v-model:show={forms.previewModal}
  624. type="attend"
  625. params={forms.previewParams}
  626. />
  627. {/* 完成编辑时,选择声部 */}
  628. <NModal
  629. v-model:show={forms.subjectSyncVisiable}
  630. preset="card"
  631. class={['modalTitle background', styles.subjectSyncModal]}
  632. title={'同步声部'}>
  633. <SubjectSync
  634. subjectId={prepareStore.getSubjectId as any}
  635. onClose={() => (forms.subjectSyncVisiable = false)}
  636. onConfirm={async (subjectIds: any) => {
  637. //
  638. try {
  639. forms.editSubjectIds = subjectIds.join(',');
  640. await onOverEdit();
  641. forms.subjectSyncVisiable = false;
  642. } catch {
  643. //
  644. }
  645. }}
  646. />
  647. </NModal>
  648. </div>
  649. );
  650. }
  651. });