index.tsx 19 KB

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