index.tsx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. import {
  2. defineComponent,
  3. onMounted,
  4. reactive,
  5. watch,
  6. ref,
  7. PropType,
  8. onUnmounted,
  9. toRef
  10. } from 'vue';
  11. import styles from './index.module.less';
  12. import {
  13. NButton,
  14. NInput,
  15. NModal,
  16. NScrollbar,
  17. NSelect,
  18. NSpace,
  19. NSpin,
  20. useDialog,
  21. useMessage
  22. } from 'naive-ui';
  23. import { usePrepareStore } from '/src/store/modules/prepareLessons';
  24. import { useCatchStore } from '/src/store/modules/catchData';
  25. import TrainType from '/src/views/attend-class/model/train-type';
  26. import TheEmpty from '/src/components/TheEmpty';
  27. import Draggable from 'vuedraggable';
  28. import {
  29. lessonPreTrainingDelete,
  30. lessonPreTrainingV2Detail,
  31. lessonPreTrainingV2Save
  32. } from '../../../api';
  33. import { evaluateDifficult } from '/src/utils/contants';
  34. import TrainUpdate from '/src/views/attend-class/model/train-update';
  35. import AssignHomework from './assign-homework';
  36. // import Trainguide from '@/custom-plugins/guide-page/train-guide';
  37. import { eventGlobal } from '/src/utils';
  38. // import iconTips from '../../../images/icon-tips.png';
  39. import { typeFormat } from '../../resource-main/components/select-music';
  40. import TheMessageDialog from '/src/components/TheMessageDialog';
  41. import { useResizeObserver } from '@vueuse/core';
  42. import useDrag from '@/hooks/useDrag';
  43. import Dragbom from '@/hooks/useDrag/dragbom';
  44. import { useUserStore } from '@/store/modules/users';
  45. import { modalClickMask } from '/src/state';
  46. export default defineComponent({
  47. name: 'courseware-modal',
  48. props: {
  49. lessonPreTraining: {
  50. type: Object,
  51. default: () => ({})
  52. },
  53. cardType: {
  54. type: String as PropType<'' | 'homeworkRecord' | 'prepare'>,
  55. default: ''
  56. },
  57. /** 编辑编号 - 目前从上传传 */
  58. classGroupId: {
  59. type: String,
  60. default: ''
  61. },
  62. /** 编辑编号 - 目前从上传传 */
  63. coursewareKnowledgeDetailId: {
  64. type: String,
  65. default: ''
  66. },
  67. /** 编辑编号 - 目前从上传传 */
  68. courseScheduleId: {
  69. type: String,
  70. default: ''
  71. },
  72. from: {
  73. // 来自哪里
  74. type: String,
  75. default: ''
  76. }
  77. },
  78. emits: ['change'],
  79. setup(props, { emit }) {
  80. console.log(props.courseScheduleId, 'courseScheduleId');
  81. const catchStore = useCatchStore();
  82. const prepareStore = usePrepareStore();
  83. const dialog = useDialog();
  84. const message = useMessage();
  85. const forms = reactive({
  86. title: props.lessonPreTraining?.title,
  87. preBtnLoading: false,
  88. showAttendClass: false,
  89. list: [] as any,
  90. drag: true,
  91. loadingStatus: false,
  92. trainList: [] as any,
  93. assignHomeworkStatus: false,
  94. editStatus: false,
  95. editItem: {} as any,
  96. removeIds: [] as any, // 临时删除的编号
  97. removeVisiable1: false,
  98. preSaveVisiable: false
  99. });
  100. // const showGuide = ref(false);
  101. // 获取列表
  102. const getList = async () => {
  103. forms.loadingStatus = true;
  104. try {
  105. // 判断是否有选择对应的课件
  106. // console.log(props.lessonPreTraining, 'props.lessonPreTraining');
  107. if (!props.lessonPreTraining?.id) return (forms.loadingStatus = false);
  108. const { data } = await lessonPreTrainingV2Detail({
  109. id: props.lessonPreTraining?.id
  110. });
  111. const tempRows = data.lessonPreTrainingDetails || [];
  112. const temp: any = [];
  113. forms.title = data.title;
  114. tempRows.forEach((row: any) => {
  115. let tList: string[] = [];
  116. const configJson = row.trainingConfigJson;
  117. if (row.trainingType === 'EVALUATION') {
  118. tList = [
  119. `${evaluateDifficult[configJson.evaluateDifficult]}`,
  120. `${configJson.practiceChapterBegin || 0}-${
  121. configJson.practiceChapterEnd || 0
  122. }小节`,
  123. `速度${configJson.evaluateSpeed || 0}`,
  124. `${configJson.trainingTimes}分达标`
  125. ];
  126. } else {
  127. tList = [
  128. `${configJson.practiceChapterBegin}-${configJson.practiceChapterEnd}小节`,
  129. `速度${configJson.practiceSpeed || 0}`,
  130. `${configJson.trainingTimes}分钟`
  131. ];
  132. }
  133. temp.push({
  134. typeList: tList || [],
  135. audioPlayTypeArray: row.audioPlayTypes
  136. ? row.audioPlayTypes.split(',')
  137. : [],
  138. ...row
  139. });
  140. });
  141. prepareStore.setTrainList(temp || []);
  142. const tempCourse: any = [];
  143. temp.forEach((item: any) => {
  144. if (!forms.removeIds.includes(item.id)) {
  145. tempCourse.push(item);
  146. }
  147. });
  148. forms.trainList = tempCourse || [];
  149. } catch {
  150. //
  151. }
  152. forms.loadingStatus = false;
  153. };
  154. // 监听选择的key 左侧选择了其它的课
  155. watch(
  156. () => prepareStore.getSelectKey,
  157. () => {
  158. eventGlobal.emit('teacher-slideshow', false);
  159. emit('change', { status: false });
  160. forms.trainList = [];
  161. getList();
  162. }
  163. );
  164. // 删除
  165. const onDelete = (item: any) => {
  166. forms.removeIds.push(item.id);
  167. const index = forms.trainList.findIndex((c: any) => c.id === item.id);
  168. forms.trainList.splice(index, 1);
  169. };
  170. // 单个删除
  171. const onRemove = async (item: any) => {
  172. try {
  173. dialog.warning({
  174. title: '提示',
  175. content: '该训练已下架,是否删除?',
  176. positiveText: '确定',
  177. negativeText: '取消',
  178. maskClosable: false,
  179. onPositiveClick: async () => {
  180. forms.removeIds.push(item.id);
  181. await lessonPreTrainingDelete({ ids: item.id });
  182. message.success('删除成功');
  183. getList();
  184. }
  185. });
  186. } catch {
  187. //
  188. }
  189. };
  190. /** 保存预设 */
  191. const onPreSave = async () => {
  192. forms.preBtnLoading = true;
  193. try {
  194. const lessonPreTrainingDetails: any = [];
  195. forms.trainList.forEach((item: any) => {
  196. lessonPreTrainingDetails.push({
  197. trainingType: item.trainingType,
  198. musicId: item.musicId,
  199. trainingConfigJson: item.trainingConfigJson,
  200. musicName: item.musicName
  201. });
  202. });
  203. const { data } = await lessonPreTrainingV2Save({
  204. title: forms.title,
  205. id: props.lessonPreTraining?.id,
  206. coursewareKnowledgeDetailId:
  207. props.coursewareKnowledgeDetailId || prepareStore.getSelectKey,
  208. lessonPreTrainingDetails,
  209. courseScheduleId: props.courseScheduleId,
  210. chapterLessonCoursewareId: props.lessonPreTraining?.chapterId
  211. });
  212. message.success('保存成功');
  213. prepareStore.setIsEditTrain(false);
  214. forms.removeIds = [];
  215. // getList();
  216. emit('change', {
  217. status: false,
  218. saveWork: true,
  219. lessonPreTrainingId: data.id
  220. });
  221. } catch {
  222. //
  223. }
  224. forms.preBtnLoading = false;
  225. };
  226. const getModalHeight = () => {
  227. const dom: any = document.querySelector('#model-homework-height');
  228. if (dom) {
  229. useResizeObserver(dom as HTMLElement, (entries: any) => {
  230. const entry = entries[0];
  231. const { height } = entry.contentRect;
  232. dom.style.setProperty('--window-page-lesson-height', height + 'px');
  233. });
  234. }
  235. };
  236. onMounted(async () => {
  237. getModalHeight();
  238. await getList();
  239. // 动态添加数据
  240. eventGlobal.on('onTrainAddItem', (item: any) => {
  241. forms.drag = true;
  242. // 临时储存编号
  243. item.id = item.id || new Date().getTime() + '__tmp';
  244. forms.trainList.push(item);
  245. prepareStore.setTrainList(forms.trainList);
  246. });
  247. });
  248. onUnmounted(() => {
  249. forms.trainList = [];
  250. prepareStore.setTrainList([]);
  251. });
  252. // 弹窗拖动
  253. // 作业设置
  254. let workSetingBoxDragData: any;
  255. let workSetingBoxClass: string;
  256. if (props.from === 'class') {
  257. const users = useUserStore();
  258. workSetingBoxClass = 'workSetingBoxClass_drag';
  259. workSetingBoxDragData = useDrag(
  260. [
  261. `${workSetingBoxClass}>.n-card-header`,
  262. `${workSetingBoxClass} .bom_drag`
  263. ],
  264. workSetingBoxClass,
  265. toRef(forms, 'editStatus'),
  266. users.info.id
  267. );
  268. }
  269. // 清空
  270. let workClearBoxDragData: any;
  271. let workClearBoxClass: string;
  272. if (props.from === 'class') {
  273. const users = useUserStore();
  274. workClearBoxClass = 'workClearBoxClass_drag';
  275. workClearBoxDragData = useDrag(
  276. [
  277. `${workClearBoxClass}>.n-card-header`,
  278. `${workClearBoxClass} .bom_drag`
  279. ],
  280. workClearBoxClass,
  281. toRef(forms, 'removeVisiable1'),
  282. users.info.id
  283. );
  284. }
  285. // 清空
  286. let workSaveBoxDragData: any;
  287. let workSaveBoxClass: string;
  288. if (props.from === 'class') {
  289. const users = useUserStore();
  290. workSaveBoxClass = 'workSaveBoxClass_drag';
  291. workSaveBoxDragData = useDrag(
  292. [`${workSaveBoxClass}>.n-card-header`, `${workSaveBoxClass} .bom_drag`],
  293. workSaveBoxClass,
  294. toRef(forms, 'preSaveVisiable'),
  295. users.info.id
  296. );
  297. }
  298. // 立即布置
  299. let workArrangeImmediatelyBoxDragData: any;
  300. let workArrangeImmediatelyBoxClass: string;
  301. if (props.from === 'class') {
  302. const users = useUserStore();
  303. workArrangeImmediatelyBoxClass = 'workArrangeImmediatelyBoxClass_drag';
  304. workArrangeImmediatelyBoxDragData = useDrag(
  305. [
  306. `${workArrangeImmediatelyBoxClass}>.n-card-header`,
  307. `${workArrangeImmediatelyBoxClass} .bom_drag`
  308. ],
  309. workArrangeImmediatelyBoxClass,
  310. toRef(forms, 'assignHomeworkStatus'),
  311. users.info.id
  312. );
  313. }
  314. return () => (
  315. <div class={styles.coursewareModal}>
  316. <div class={styles.btnGroup}>
  317. <NSpace>
  318. <div class={styles.btnItem}>
  319. <span class={styles.btnTitle}>
  320. <i style={{ color: '#ea4132', fontStyle: 'normal' }}>*</i>标题:
  321. </span>
  322. <NInput
  323. placeholder={'请输入标题'}
  324. v-model:value={forms.title}
  325. maxlength={100}
  326. />
  327. </div>
  328. </NSpace>
  329. <div class={styles.spaceBtnGroup}>
  330. <NButton
  331. bordered={false}
  332. type="error"
  333. disabled={forms.trainList.length <= 0}
  334. onClick={() => {
  335. forms.removeVisiable1 = true;
  336. }}>
  337. 清空
  338. </NButton>
  339. <NButton
  340. bordered={false}
  341. type="error"
  342. onClick={() => {
  343. // forms.drag = false;
  344. prepareStore.setIsEditTrain(false);
  345. forms.removeIds = [];
  346. // prepareStore.setTrainList([]);
  347. // getList();
  348. emit('change', { status: false });
  349. }}>
  350. 取消
  351. </NButton>
  352. {(props.cardType !== 'homeworkRecord' ||
  353. props.courseScheduleId) && (
  354. <NButton
  355. bordered={false}
  356. type="default"
  357. onClick={() => {
  358. if (!forms.title) {
  359. message.error('请输入标题');
  360. return;
  361. }
  362. // let count = 0;
  363. // forms.trainList.forEach((item: any) => {
  364. // if (!item.removeFlag) {
  365. // count++;
  366. // }
  367. // });
  368. // if (count <= 0) {
  369. // message.error('作业内容不能为空');
  370. // return;
  371. // }
  372. forms.preSaveVisiable = true;
  373. }}
  374. // loading={forms.preBtnLoading}
  375. >
  376. 保存
  377. </NButton>
  378. )}
  379. {(props.cardType === 'homeworkRecord' ||
  380. props.courseScheduleId) && (
  381. <NButton
  382. type="primary"
  383. disabled={forms.trainList.length <= 0}
  384. onClick={() => {
  385. if (!forms.title) {
  386. message.error('请输入标题');
  387. return;
  388. }
  389. let count = 0;
  390. forms.trainList.forEach((item: any) => {
  391. if (!item.removeFlag) {
  392. count++;
  393. }
  394. });
  395. if (count <= 0) {
  396. message.error('作业内容不能为空');
  397. return;
  398. }
  399. forms.assignHomeworkStatus = true;
  400. }}>
  401. 立即布置
  402. </NButton>
  403. )}
  404. </div>
  405. </div>
  406. <NScrollbar
  407. class={[
  408. styles.listContainer,
  409. 'train-container'
  410. // forms.drag ? styles.listContainerDrag : ''
  411. ]}>
  412. <NSpin show={forms.loadingStatus}>
  413. <div
  414. class={[
  415. styles.listSection,
  416. 'train-listSection',
  417. !forms.loadingStatus && prepareStore.getTrainList.length <= 0
  418. ? styles.emptySection
  419. : ''
  420. ]}
  421. onDragenter={(e: any) => {
  422. e.preventDefault();
  423. }}
  424. onDragover={(e: any) => {
  425. e.preventDefault();
  426. }}
  427. onDrop={(e: any) => {
  428. let dropItem = e.dataTransfer.getData('text');
  429. // console.log(dropItem, 'dropItem', dropItem);
  430. dropItem = dropItem ? JSON.parse(dropItem) : {};
  431. // 判断是否有数据
  432. if (dropItem.id) {
  433. eventGlobal.emit('onTrainDragItem', dropItem);
  434. }
  435. }}>
  436. {forms.trainList.length > 0 && (
  437. <>
  438. <Draggable
  439. v-model:modelValue={forms.trainList}
  440. itemKey="id"
  441. componentData={{
  442. itemKey: 'id',
  443. tag: 'div',
  444. animation: 200,
  445. group: 'description',
  446. disabled: false
  447. }}
  448. class={styles.list}>
  449. {{
  450. item: (element: any) => {
  451. const item = element.element;
  452. return (
  453. <div data-id={item.musicId} class={styles.itemBlock}>
  454. <TrainType
  455. from={props.from}
  456. item={item}
  457. isDelete
  458. type="prepare"
  459. onDelete={(child: any) => onDelete(child)}
  460. offShelf={item.removeFlag ? true : false}
  461. onOffShelf={() => onRemove(item)}
  462. onEdit={(child: any) => {
  463. // console.log(forms.trainList);
  464. const {
  465. trainingConfigJson,
  466. id,
  467. musicId,
  468. ...res
  469. } = child;
  470. forms.editItem = {
  471. ...res,
  472. id: musicId,
  473. trainId: id,
  474. ...trainingConfigJson
  475. };
  476. forms.editStatus = true;
  477. }}
  478. />
  479. </div>
  480. );
  481. }
  482. }}
  483. </Draggable>
  484. </>
  485. )}
  486. {!forms.loadingStatus &&
  487. prepareStore.getTrainList.length <= 0 && (
  488. <TheEmpty description="暂无作业" />
  489. )}
  490. </div>
  491. </NSpin>
  492. </NScrollbar>
  493. {/* 编辑 */}
  494. <NModal
  495. maskClosable={modalClickMask}
  496. style={
  497. props.from === 'class' ? workSetingBoxDragData.styleDrag.value : {}
  498. }
  499. v-model:show={forms.editStatus}
  500. class={[
  501. 'modalTitle background',
  502. styles.trainEditModal,
  503. workSetingBoxClass
  504. ]}
  505. preset="card"
  506. title="作业设置">
  507. <TrainUpdate
  508. item={forms.editItem}
  509. onClose={() => (forms.editStatus = false)}
  510. onConfirm={(item: any) => {
  511. forms.editItem = {};
  512. // prepareStore.setIsAddTrain(true);
  513. const tList = typeFormat(
  514. item.trainingType,
  515. item.trainingConfigJson
  516. );
  517. //
  518. const train = {
  519. ...item,
  520. typeList: tList
  521. };
  522. forms.trainList.forEach((item: any) => {
  523. if (item.id === train.id) {
  524. // item = train;
  525. item.trainingConfigJson = train.trainingConfigJson;
  526. item.trainingType = train.trainingType;
  527. item.typeList = train.typeList;
  528. }
  529. });
  530. prepareStore.setTrainList(forms.trainList);
  531. }}
  532. />
  533. {props.from === 'class' && <Dragbom></Dragbom>}
  534. </NModal>
  535. {/* 添加自定义教材 */}
  536. <NModal
  537. maskClosable={modalClickMask}
  538. style={
  539. props.from === 'class'
  540. ? workArrangeImmediatelyBoxDragData.styleDrag.value
  541. : {}
  542. }
  543. v-model:show={forms.assignHomeworkStatus}
  544. preset="card"
  545. showIcon={false}
  546. class={[
  547. 'modalTitle background',
  548. styles.assignHomework,
  549. workArrangeImmediatelyBoxClass
  550. ]}
  551. title={'布置作业'}
  552. blockScroll={false}>
  553. <AssignHomework
  554. from={props.from}
  555. classGroupId={props.classGroupId}
  556. courseScheduleId={props.courseScheduleId}
  557. chapterLessonCoursewareId={props.lessonPreTraining.chapterId}
  558. homeworkType={props.courseScheduleId ? 'HOMEWORK' : 'CLASSWORK'}
  559. item={{
  560. title: forms.title,
  561. lessonPreTrainingDetails: forms.trainList
  562. }}
  563. // trainList={forms.trainList}
  564. onClose={() => (forms.assignHomeworkStatus = false)}
  565. onConfirm={() => {
  566. if (props.cardType === 'homeworkRecord') {
  567. forms.trainList = [];
  568. prepareStore.setTrainList([]);
  569. emit('change', { state: false });
  570. }
  571. }}
  572. />
  573. {props.from === 'class' && <Dragbom></Dragbom>}
  574. </NModal>
  575. {/* {showGuide.value ? <Trainguide></Trainguide> : null} */}
  576. <NModal
  577. maskClosable={modalClickMask}
  578. style={
  579. props.from === 'class' ? workClearBoxDragData.styleDrag.value : {}
  580. }
  581. v-model:show={forms.removeVisiable1}
  582. preset="card"
  583. class={['modalTitle', styles.removeVisiable1, workClearBoxClass]}
  584. title={'清空资源'}>
  585. <div class={styles.studentRemove}>
  586. <p>
  587. 请确认是否要清空作业?
  588. {/* <span>点击确认后所有的作业内容 将被清空掉。</span> */}
  589. </p>
  590. <NSpace class={styles.btnGroupModal} justify="center">
  591. <NButton
  592. round
  593. type="primary"
  594. onClick={() => {
  595. forms.trainList.forEach((item: any) => {
  596. forms.removeIds.push(item.id);
  597. });
  598. forms.trainList = [];
  599. prepareStore.setTrainList([]);
  600. forms.removeVisiable1 = false;
  601. }}>
  602. 确定
  603. </NButton>
  604. <NButton round onClick={() => (forms.removeVisiable1 = false)}>
  605. 取消
  606. </NButton>
  607. </NSpace>
  608. </div>
  609. {props.from === 'class' && <Dragbom></Dragbom>}
  610. </NModal>
  611. <NModal
  612. maskClosable={modalClickMask}
  613. style={
  614. props.from === 'class' ? workSaveBoxDragData.styleDrag.value : {}
  615. }
  616. v-model:show={forms.preSaveVisiable}
  617. preset="card"
  618. class={['modalTitle', styles.removeVisiable1, workSaveBoxClass]}
  619. title={'保存'}>
  620. <TheMessageDialog
  621. content="是否保存当前页面编辑内容?"
  622. cancelButtonText="取消"
  623. confirmButtonText="保存"
  624. onClose={() => (forms.preSaveVisiable = false)}
  625. onConfirm={() => onPreSave()}
  626. />
  627. {props.from === 'class' && <Dragbom></Dragbom>}
  628. </NModal>
  629. </div>
  630. );
  631. }
  632. });