addCourseware.tsx 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982
  1. import {
  2. defineComponent,
  3. nextTick,
  4. onMounted,
  5. onUnmounted,
  6. reactive,
  7. ref,
  8. watch
  9. } from 'vue';
  10. import styles from './addCourseware.module.less';
  11. import {
  12. NButton,
  13. NModal,
  14. NScrollbar,
  15. NSelect,
  16. NSpace,
  17. NSpin,
  18. useMessage,
  19. useDialog,
  20. NSwitch,
  21. NInput,
  22. NTooltip,
  23. NImage,
  24. NIcon
  25. } from 'naive-ui';
  26. import CardType from '/src/components/card-type';
  27. // import AttendClass from '/src/views/prepare-lessons/model/attend-class';
  28. import { usePrepareStore } from '/src/store/modules/prepareLessons';
  29. // import { useCatchStore } from '/src/store/modules/catchData';
  30. // import TheEmpty from '/src/components/TheEmpty';
  31. import {
  32. api_teacherChapterLessonCoursewareAdd,
  33. api_teacherChapterLessonCoursewareUpdate,
  34. api_teacherChapterLessonCoursewareDetail
  35. // courseScheduleStart,
  36. // queryCourseware,
  37. // saveCourseware
  38. } from '../../../api';
  39. import Draggable from 'vuedraggable';
  40. import iconDelete from '../../../images/icon-delete-default.png';
  41. import iconAddMusic from '../../../images/icon-add-music.png';
  42. // import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
  43. // import deepClone from '/src/helpers/deep-clone';
  44. import CardPreview from '/src/components/card-preview';
  45. import PreviewWindow from '/src/views/preview-window';
  46. // import { state } from '/src/state';
  47. // import SubjectSync from '../../../model/subject-sync';
  48. import { eventGlobal } from '/src/utils';
  49. // import iconTips from '../../../images/icon-tips.png';
  50. import TheMessageDialog from '/src/components/TheMessageDialog';
  51. import AddItemModel from '../../../model/add-item-model';
  52. import AddOtherSource from '../../../model/add-other-source';
  53. import deepClone from '/src/helpers/deep-clone';
  54. import AddCoursewareProtocol from '../../../model/add-courseware-protocol';
  55. import { useUserStore } from '/src/store/modules/users';
  56. export default defineComponent({
  57. name: 'courseware-modal',
  58. props: {
  59. groupItem: {
  60. type: Object,
  61. default: () => ({})
  62. }
  63. },
  64. emits: ['change'],
  65. setup(props, { emit }) {
  66. // const catchStore = useCatchStore();
  67. const userStore = useUserStore();
  68. const prepareStore = usePrepareStore();
  69. // const route = useRoute();
  70. // const router = useRouter();
  71. // const dialog = useDialog();
  72. const message = useMessage();
  73. const forms = reactive({
  74. subjects: [] as any,
  75. openFlagEnable: true, // 是否支持修改公开状态
  76. name: '',
  77. openFlag: false,
  78. baseCoursewareList: [
  79. {
  80. name: '',
  81. id: null,
  82. list: [] as any
  83. }
  84. ] as any, // 基础数据
  85. coursewareList: [
  86. {
  87. name: '',
  88. id: null,
  89. list: [] as any
  90. }
  91. ] as any,
  92. loadingStatus: false,
  93. showAttendClass: false,
  94. attendClassType: 'change', //
  95. removeIds: [] as any, // 临时删除的编号
  96. editSubjectIds: '', // 声部编号
  97. addCoursewareVisiable: false,
  98. addCoursewareItem: {} as any,
  99. messageCallBack: null as any,
  100. messageOperation: {
  101. visiable: false,
  102. loading: false, // 是否显示加载
  103. type: 'delete' as 'delete' | 'addItem' | 'save' | 'pageLive',
  104. contentDirection: 'center' as 'left' | 'center' | 'right',
  105. title: '删除知识点',
  106. content: '请确认是否删除该知识点,删除知识点后将同步删除知识点下的资源',
  107. cancelButtonText: '取消',
  108. confirmButtonText: '确认',
  109. index: 0
  110. },
  111. show: false,
  112. item: {} as any,
  113. previewModal: false,
  114. previewParams: {
  115. type: '',
  116. subjectId: '',
  117. detailId: ''
  118. } as any,
  119. addOtherSource: false,
  120. addOtherIndex: 0 // 添加其它的索引
  121. });
  122. const showModalMask = ref(false);
  123. // 获取列表
  124. const getList = async () => {
  125. forms.loadingStatus = true;
  126. try {
  127. if (!props.groupItem.id) return (forms.loadingStatus = false);
  128. const { data } = await api_teacherChapterLessonCoursewareDetail(
  129. props.groupItem.id
  130. );
  131. const tempRows = data.chapterKnowledgeList || [];
  132. forms.name = data.name;
  133. forms.subjects = data.subjectIds
  134. ? data.subjectIds.split(',').map((s: any) => {
  135. return Number(s);
  136. })
  137. : [];
  138. forms.openFlag = data.openFlag;
  139. forms.openFlagEnable = data.openFlagEnable;
  140. const temp: any = [];
  141. tempRows.forEach((row: any) => {
  142. const child: any = row.chapterKnowledgeMaterialList;
  143. const childList: any[] = [];
  144. if (Array.isArray(child) && child.length > 0) {
  145. child.forEach((sub: any) => {
  146. childList.push({
  147. id: sub.id,
  148. materialId: sub.bizId,
  149. coverImg: sub.bizInfo.coverImg,
  150. type: sub.type,
  151. title: sub.bizInfo.name,
  152. dataJson: sub.dataJson,
  153. // isCollect: !!sub.favoriteFlag,
  154. isSelected: sub.source === 'PLATFORM' ? true : false,
  155. content: sub.bizInfo.content,
  156. removeFlag: sub.removeFlag
  157. });
  158. });
  159. }
  160. temp.push({
  161. name: row.name,
  162. id: row.id,
  163. list: [...childList]
  164. });
  165. });
  166. forms.coursewareList = temp;
  167. forms.baseCoursewareList = deepClone(temp);
  168. } catch (e) {
  169. //
  170. console.log(e);
  171. }
  172. forms.loadingStatus = false;
  173. };
  174. // 删除
  175. const onDelete = (j: number, index: number) => {
  176. const coursewareItem = forms.coursewareList[index];
  177. if (!coursewareItem) return;
  178. coursewareItem.list.splice(j, 1);
  179. };
  180. // 完成编辑
  181. // const onOverEdit = async () => {
  182. // try {
  183. // const temp: any = [];
  184. // forms.coursewareList.forEach((item: any) => {
  185. // temp.push({
  186. // materialName: item.name,
  187. // materialType: item.type,
  188. // materialId: item.materialId,
  189. // id: item.id
  190. // });
  191. // });
  192. // // 保存课件
  193. // // 判断是否编辑,如果编辑则取选择的声部
  194. // await saveCourseware({
  195. // coursewareDetailKnowledgeId: prepareStore.getSelectKey,
  196. // lessonCoursewareId: prepareStore.getLessonCoursewareId,
  197. // lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
  198. // // subjectId: forms.isEdit
  199. // // ? forms.editSubjectIds
  200. // // : prepareStore.getSubjectId,
  201. // materialList: [...temp]
  202. // });
  203. // message.success('编辑成功');
  204. // // forms.removeVisiable = false;
  205. // prepareStore.setIsEditResource(false);
  206. // // 重置临时删除编号
  207. // forms.removeIds = [];
  208. // await getList();
  209. // } catch {
  210. // //
  211. // }
  212. // };
  213. const isPointInsideElement = (element: any, x: number, y: number) => {
  214. const rect = element.getBoundingClientRect();
  215. return (
  216. x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
  217. );
  218. };
  219. const isPointOnLeft = (element: any, x: number) => {
  220. const rect = element.getBoundingClientRect();
  221. const elementCenterX = rect.left + rect.width / 2;
  222. return x < elementCenterX;
  223. };
  224. // 操作
  225. const onChangePoint = (type: string, index: number, item?: any) => {
  226. if (type === 'up') {
  227. // 向上移动
  228. if (index === 0) return;
  229. const temp = forms.coursewareList[index - 1];
  230. forms.coursewareList[index - 1] = forms.coursewareList[index];
  231. forms.coursewareList[index] = temp;
  232. } else if (type === 'down') {
  233. // 向下移动
  234. if (index >= forms.coursewareList.length - 1) return;
  235. const temp = forms.coursewareList[index + 1];
  236. forms.coursewareList[index + 1] = forms.coursewareList[index];
  237. forms.coursewareList[index] = temp;
  238. } else if (type === 'remove') {
  239. forms.messageOperation = {
  240. visiable: true,
  241. type: 'delete',
  242. contentDirection: 'left',
  243. title: '删除知识点',
  244. loading: false,
  245. content: `请确认是否删除${
  246. item.name ? '【' + item.name + '】' : '该知识点'
  247. },删除知识点后将同步删除知识点下的资源`,
  248. cancelButtonText: '取消',
  249. confirmButtonText: '确认',
  250. index
  251. };
  252. }
  253. };
  254. //
  255. const onMessageConfirm = async () => {
  256. const type = forms.messageOperation.type;
  257. if (type === 'delete') {
  258. forms.coursewareList.splice(forms.messageOperation.index, 1);
  259. } else if (type === 'addItem') {
  260. forms.coursewareList.push({ name: '', list: [] });
  261. addCoursewareItem(forms.addCoursewareItem);
  262. } else if (type === 'save' || type === 'pageLive') {
  263. if (forms.messageOperation.loading) return;
  264. if (!forms.name) {
  265. message.error('请输入课件标题');
  266. forms.messageOperation.visiable = false;
  267. return;
  268. }
  269. if (forms.subjects.length <= 0) {
  270. message.error('请选择声部');
  271. forms.messageOperation.visiable = false;
  272. return;
  273. }
  274. if (forms.coursewareList.length <= 0) {
  275. message.error('未配置知识点');
  276. forms.messageOperation.visiable = false;
  277. return;
  278. }
  279. let isNotAdd = false;
  280. for (const item of forms.coursewareList) {
  281. if (!item.name) {
  282. message.error('请输入知识点名称');
  283. forms.messageOperation.visiable = false;
  284. return;
  285. }
  286. if (Array.isArray(item.list) && item.list.length <= 0) {
  287. isNotAdd = true;
  288. }
  289. }
  290. if (isNotAdd) {
  291. message.error('请至少添加一个资源');
  292. forms.messageOperation.visiable = false;
  293. return;
  294. }
  295. forms.messageOperation.loading = true;
  296. const resultStatus = await onSaveCourseWare();
  297. forms.messageOperation.loading = false;
  298. if (resultStatus) {
  299. if (
  300. type === 'pageLive' &&
  301. typeof forms.messageCallBack === 'function'
  302. ) {
  303. forms.messageCallBack();
  304. }
  305. emit('change', { status: false });
  306. eventGlobal.emit('teacher-slideshow', false);
  307. }
  308. }
  309. forms.messageOperation.visiable = false;
  310. };
  311. const addCoursewareItem = (item: any, point?: any) => {
  312. nextTick(() => {
  313. if (point) {
  314. const rowGroupDom = document.querySelectorAll('.row-group');
  315. const dom = rowGroupDom[item.index].querySelectorAll('.row-nav');
  316. // const dom = document.querySelectorAll('.row-nav');
  317. let isAdd = false;
  318. dom.forEach((child: any, index: number) => {
  319. // console.log(child);
  320. const status = isPointInsideElement(child, point.x, point.y);
  321. if (status) {
  322. const array: any =
  323. forms.coursewareList[item.index || 0].list || [];
  324. const left = isPointOnLeft(child, point.x);
  325. if (!left) {
  326. array.splice(index + 1, 0, item);
  327. } else {
  328. array.splice(index, 0, item);
  329. }
  330. isAdd = true;
  331. forms.coursewareList[item.index || 0].list = array;
  332. }
  333. });
  334. if (!isAdd) {
  335. forms.coursewareList[item.index || 0].list.push(item);
  336. }
  337. } else {
  338. forms.coursewareList[item.index || 0].list.push(item);
  339. message.success('添加成功');
  340. }
  341. });
  342. };
  343. // 提交
  344. const onSubmit = async () => {
  345. try {
  346. if (!forms.name) {
  347. message.error('请输入课件标题');
  348. return;
  349. }
  350. if (forms.subjects.length <= 0) {
  351. message.error('请选择声部');
  352. return;
  353. }
  354. if (forms.coursewareList.length <= 0) {
  355. message.error('请至少添加一个知识点');
  356. return;
  357. }
  358. let isNotAdd = false;
  359. for (const item of forms.coursewareList) {
  360. if (!item.name) {
  361. message.error('请输入知识点名称');
  362. return;
  363. }
  364. if (Array.isArray(item.list) && item.list.length <= 0) {
  365. isNotAdd = true;
  366. }
  367. }
  368. if (isNotAdd) {
  369. message.error('请至少添加一个资源');
  370. return;
  371. }
  372. if (forms.openFlag && !userStore.getReadCoursewareOpenAgreement) {
  373. showModalMask.value = true;
  374. return;
  375. }
  376. const resultStatus = await onSaveCourseWare();
  377. if (resultStatus) {
  378. emit('change', { status: false });
  379. eventGlobal.emit('teacher-slideshow', false);
  380. }
  381. } catch {
  382. //
  383. }
  384. };
  385. const onSaveCourseWare = async () => {
  386. try {
  387. const params = {
  388. name: forms.name,
  389. subjectIds: forms.subjects.join(','),
  390. openFlag: forms.openFlag,
  391. coursewareDetailKnowledgeId: prepareStore.getSelectKey,
  392. chapterKnowledgeList: [] as any
  393. };
  394. forms.coursewareList.forEach((item: any) => {
  395. let tempItem: any = [];
  396. if (Array.isArray(item.list) && item.list.length > 0) {
  397. tempItem = item.list.map((child: any) => {
  398. return {
  399. bizId: child.materialId,
  400. type: child.type,
  401. dataJson: !['IMG', 'VIDEO', 'SONG', 'MUSIC', 'PPT'].includes(
  402. child.type
  403. )
  404. ? JSON.stringify({
  405. setting: child.dataJson,
  406. coverImg: child.coverImg,
  407. bizId: child.bizId,
  408. content: child.content,
  409. name: child.title
  410. })
  411. : ''
  412. };
  413. });
  414. }
  415. params.chapterKnowledgeList.push({
  416. name: item.name,
  417. chapterKnowledgeMaterialList: tempItem
  418. });
  419. });
  420. if (props.groupItem?.id) {
  421. await api_teacherChapterLessonCoursewareUpdate({
  422. id: props.groupItem.id,
  423. ...params
  424. });
  425. } else {
  426. await api_teacherChapterLessonCoursewareAdd(params);
  427. }
  428. message.success('保存成功');
  429. return true;
  430. } catch {
  431. //
  432. return false;
  433. }
  434. };
  435. const addItem = (item: any, point?: any) => {
  436. if (forms.coursewareList.length <= 0) {
  437. // 添加到临时对象
  438. forms.addCoursewareItem = item;
  439. forms.messageOperation = {
  440. visiable: true,
  441. type: 'addItem',
  442. contentDirection: 'center',
  443. title: '添加到知识点',
  444. loading: false,
  445. content: '当前课件暂无知识点,请添加知识点后操作',
  446. cancelButtonText: '取消',
  447. confirmButtonText: '添加知识点',
  448. index: 0
  449. };
  450. } else if (forms.coursewareList.length > 1 && item.addType !== 'drag') {
  451. forms.addCoursewareVisiable = true;
  452. forms.addCoursewareItem = item;
  453. } else {
  454. addCoursewareItem(item, point);
  455. }
  456. };
  457. // 当页面离开时
  458. const onPageBeforeLeave = (event: any) => {
  459. const objA = JSON.stringify(forms.coursewareList);
  460. const objB = JSON.stringify(forms.baseCoursewareList);
  461. if (objA === objB) {
  462. if (typeof event === 'function') {
  463. event();
  464. emit('change', { status: false });
  465. eventGlobal.emit('teacher-slideshow', false);
  466. }
  467. } else {
  468. forms.messageCallBack = event;
  469. forms.messageOperation = {
  470. visiable: true,
  471. type: 'pageLive',
  472. loading: false,
  473. contentDirection: 'center',
  474. title: '保存课件',
  475. content: '当前课件暂未保存,是否保存?',
  476. cancelButtonText: '不保存',
  477. confirmButtonText: '保存',
  478. index: 0
  479. };
  480. }
  481. };
  482. onMounted(async () => {
  483. // 修改时重置默认数据
  484. if (props.groupItem?.id) {
  485. forms.coursewareList = [];
  486. forms.baseCoursewareList = [];
  487. }
  488. await getList();
  489. // 动态添加数据
  490. eventGlobal.on('onPrepareAddItem', addItem);
  491. eventGlobal.on('pageBeforeLeave', onPageBeforeLeave);
  492. });
  493. onUnmounted(() => {
  494. eventGlobal.off('onPrepareAddItem', addItem);
  495. eventGlobal.off('pageBeforeLeave', onPageBeforeLeave);
  496. });
  497. // 当列表数据更新时同步缓存数据
  498. watch(
  499. () => forms.coursewareList,
  500. () => {
  501. prepareStore.setCoursewareList = forms.coursewareList;
  502. },
  503. {
  504. deep: true
  505. }
  506. );
  507. // 全选
  508. const chioseAll = (list: any) => {
  509. forms.subjects = list.map((child: any) => {
  510. return child.id;
  511. }) as any;
  512. };
  513. return () => (
  514. <div class={styles.coursewareModal}>
  515. <div class={styles.btnGroup}>
  516. <NSpace>
  517. <div class={styles.btnItem}>
  518. <span class={styles.btnTitle}>
  519. <span>*</span>标题:
  520. </span>
  521. <NInput
  522. placeholder="请输入课件标题"
  523. v-model:value={forms.name}
  524. maxlength={20}
  525. clearable
  526. />
  527. </div>
  528. <div class={styles.btnItem}>
  529. <span class={styles.btnTitle}>
  530. <span>*</span>声部:
  531. </span>
  532. <NSelect
  533. placeholder="请选择声部(可多选)"
  534. class={styles.btnSubjectList}
  535. options={prepareStore.getSubjectList}
  536. labelField="name"
  537. valueField="id"
  538. multiple
  539. maxTagCount={1}
  540. size="small"
  541. v-model:value={forms.subjects}
  542. clearable
  543. v-slots={{
  544. action: () => (
  545. <>
  546. <NButton
  547. text
  548. style=" --n-width: 100% "
  549. size="small"
  550. onClick={() => chioseAll(prepareStore.getSubjectList)}>
  551. 全选
  552. </NButton>
  553. </>
  554. )
  555. }}
  556. />
  557. </div>
  558. <div class={styles.btnItem}>
  559. <span class={styles.btnTitle}>公开:</span>
  560. {!forms.openFlagEnable ? (
  561. <NTooltip style={{ maxWidth: '200px' }} showArrow={false}>
  562. {{
  563. trigger: () => (
  564. <NSwitch
  565. v-model:value={forms.openFlag}
  566. disabled={!forms.openFlagEnable}
  567. />
  568. ),
  569. default: () =>
  570. '为尊重课件原作者,在“相关课件”中添加的课件不支持公开'
  571. }}
  572. </NTooltip>
  573. ) : (
  574. <NSwitch
  575. v-model:value={forms.openFlag}
  576. disabled={!forms.openFlagEnable}
  577. />
  578. )}
  579. </div>
  580. </NSpace>
  581. {/* 编辑 */}
  582. <NSpace>
  583. <NButton
  584. type="error"
  585. onClick={() => {
  586. const objA = JSON.stringify(forms.coursewareList);
  587. const objB = JSON.stringify(forms.baseCoursewareList);
  588. if (objA === objB) {
  589. emit('change', { status: false });
  590. eventGlobal.emit('teacher-slideshow', false);
  591. } else {
  592. forms.messageOperation = {
  593. visiable: true,
  594. type: 'save',
  595. loading: false,
  596. contentDirection: 'center',
  597. title: '保存课件',
  598. content: '当前课件暂未保存,是否保存?',
  599. cancelButtonText: '不保存',
  600. confirmButtonText: '保存',
  601. index: 0
  602. };
  603. }
  604. }}>
  605. 取消
  606. </NButton>
  607. <NButton
  608. type="primary"
  609. onClick={onSubmit}
  610. disabled={forms.coursewareList.length <= 0}>
  611. 保存课件
  612. </NButton>
  613. {/* <NButton
  614. type="primary"
  615. onClick={() => {
  616. forms.coursewareList = [{ name: '', list: [] }];
  617. }}>
  618. 请空
  619. </NButton> */}
  620. </NSpace>
  621. </div>
  622. <NScrollbar class={[styles.listContainer]} {...{ id: 'lessons-2' }}>
  623. <NSpin show={forms.loadingStatus}>
  624. <div class={[styles.listSection]}>
  625. {forms.coursewareList.map((item: any, index: number) => (
  626. <div
  627. class={[styles.listItems, 'row-group']}
  628. onDragenter={(e: any) => {
  629. e.preventDefault();
  630. }}
  631. onDragover={(e: any) => {
  632. e.preventDefault();
  633. }}
  634. onDrop={(e: any) => {
  635. let dropItem = e.dataTransfer.getData('text');
  636. dropItem =
  637. dropItem && e.dataTransfer.effectAllowed === 'all'
  638. ? JSON.parse(dropItem)
  639. : {};
  640. // 判断是否有数据
  641. if (dropItem.id) {
  642. // 获取拖拽的目标元素
  643. eventGlobal.emit(
  644. 'onPrepareAddItem',
  645. {
  646. materialId: dropItem.id,
  647. coverImg: dropItem.coverImg,
  648. type: dropItem.type,
  649. title: dropItem.title,
  650. isCollect: dropItem.isCollect,
  651. isSelected: dropItem.isSelected,
  652. content: dropItem.content,
  653. removeFlag: false,
  654. index,
  655. addType: 'drag'
  656. },
  657. {
  658. x: e.clientX,
  659. y: e.clientY
  660. }
  661. );
  662. }
  663. }}>
  664. <div class={styles.knowledgePoint}>
  665. <div class={styles.btnItem}>
  666. <span class={styles.btnTitle}>
  667. <span>*</span>知识点名称:
  668. </span>
  669. <NInput
  670. placeholder="未命名知识点"
  671. v-model:value={item.name}
  672. maxlength={15}
  673. clearable
  674. />
  675. </div>
  676. </div>
  677. <NSpace class={styles.operationGroup}>
  678. {index > 0 && (
  679. <NTooltip showArrow={false}>
  680. {{
  681. trigger: () => (
  682. <i
  683. class={styles.iconCUp}
  684. onClick={() => onChangePoint('up', index)}></i>
  685. ),
  686. default: () => '上移知识点'
  687. }}
  688. </NTooltip>
  689. )}
  690. {index < forms.coursewareList.length - 1 && (
  691. <NTooltip showArrow={false}>
  692. {{
  693. trigger: () => (
  694. <i
  695. class={styles.iconCDown}
  696. onClick={() => onChangePoint('down', index)}></i>
  697. ),
  698. default: () => '下移知识点'
  699. }}
  700. </NTooltip>
  701. )}
  702. <NTooltip showArrow={false}>
  703. {{
  704. trigger: () => (
  705. <i
  706. class={styles.iconCRemove}
  707. onClick={() =>
  708. onChangePoint('remove', index, item)
  709. }></i>
  710. ),
  711. default: () => '删除知识点'
  712. }}
  713. </NTooltip>
  714. </NSpace>
  715. {item.list.length > 0 && (
  716. <Draggable
  717. v-model:modelValue={item.list}
  718. itemKey="id"
  719. componentData={{
  720. itemKey: 'id',
  721. tag: 'div',
  722. animation: 200,
  723. group: 'description',
  724. disabled: false
  725. }}
  726. class={styles.list}>
  727. {{
  728. item: (element: any) => {
  729. const item = element.element;
  730. return (
  731. <div
  732. data-id={item.id}
  733. class={[
  734. styles.itemWrap,
  735. styles.itemBlock,
  736. 'row-nav'
  737. ]}>
  738. <div class={styles.itemWrapBox}>
  739. <CardType
  740. class={[styles.itemContent]}
  741. isShowCollect={false}
  742. offShelf={item.removeFlag ? true : false}
  743. // onOffShelf={() => onRemove(item)}
  744. item={item}
  745. disabledMouseHover={false}
  746. onClick={() => {
  747. if (item.type === 'IMG') return;
  748. forms.show = true;
  749. forms.item = item;
  750. }}
  751. />
  752. <div class={styles.itemOperation}>
  753. <img
  754. src={iconDelete}
  755. class={styles.iconDelete}
  756. onClick={(e: MouseEvent) => {
  757. e.stopPropagation();
  758. onDelete(element.index, index);
  759. }}
  760. />
  761. </div>
  762. </div>
  763. </div>
  764. );
  765. },
  766. footer: () => (
  767. <div class={styles.itemWrap}>
  768. <div class={styles.itemWrapBox}>
  769. <div
  770. class={[
  771. styles.itemContent,
  772. styles.addMusicItem,
  773. 'handle'
  774. ]}
  775. onClick={() => {
  776. forms.addOtherSource = true;
  777. forms.addOtherIndex = index;
  778. }}>
  779. <img src={iconAddMusic} />
  780. <p class={styles.addMusicName}>添加资源</p>
  781. </div>
  782. </div>
  783. </div>
  784. )
  785. }}
  786. </Draggable>
  787. )}
  788. {item.list <= 0 && (
  789. <div class={styles.list}>
  790. <div class={styles.itemWrap}>
  791. <div class={styles.itemWrapBox}>
  792. <div
  793. class={[
  794. styles.itemContent,
  795. styles.addMusicItem,
  796. 'handle'
  797. ]}
  798. onClick={() => {
  799. forms.addOtherSource = true;
  800. forms.addOtherIndex = index;
  801. }}>
  802. <img src={iconAddMusic} />
  803. <p class={styles.addMusicName}>添加资源</p>
  804. </div>
  805. </div>
  806. </div>
  807. </div>
  808. )}
  809. </div>
  810. ))}
  811. {!forms.loadingStatus && (
  812. <NButton
  813. block
  814. type="primary"
  815. secondary
  816. class={styles.addKnowledgePoint}
  817. onClick={() => {
  818. forms.coursewareList.push({
  819. name: '',
  820. list: []
  821. });
  822. }}>
  823. <i class={styles.iconCAdd}></i>
  824. 添加知识点
  825. </NButton>
  826. )}
  827. </div>
  828. </NSpin>
  829. </NScrollbar>
  830. {/* 弹窗查看 */}
  831. <CardPreview
  832. size={
  833. ['INSTRUMENT', 'THEORY', 'MUSIC_WIKI', 'MUSICIAN'].includes(
  834. forms.item.type
  835. )
  836. ? 'large'
  837. : ''
  838. }
  839. v-model:show={forms.show}
  840. item={forms.item}
  841. />
  842. <NModal
  843. v-model:show={forms.addCoursewareVisiable}
  844. preset="card"
  845. class={['modalTitle', styles.addCourseware]}
  846. title={'添加到知识点'}>
  847. <AddItemModel
  848. coursewareList={forms.coursewareList}
  849. onClose={() => (forms.addCoursewareVisiable = false)}
  850. onConfirm={(selects: number[]) => {
  851. if (Array.isArray(selects) && selects.length > 0) {
  852. selects.forEach(select => {
  853. addCoursewareItem({
  854. ...forms.addCoursewareItem,
  855. index: select
  856. });
  857. });
  858. forms.addCoursewareVisiable = false;
  859. } else {
  860. message.error('请选择需要添加的知识点');
  861. }
  862. }}
  863. />
  864. </NModal>
  865. <NModal
  866. v-model:show={forms.messageOperation.visiable}
  867. preset="card"
  868. class={['modalTitle', styles.removeVisiable1]}
  869. title={forms.messageOperation.title}>
  870. <TheMessageDialog
  871. content={forms.messageOperation.content}
  872. contentDirection={forms.messageOperation.contentDirection}
  873. cancelButtonText={forms.messageOperation.cancelButtonText}
  874. confirmButtonText={forms.messageOperation.confirmButtonText}
  875. loading={forms.messageOperation.loading}
  876. onClose={() => {
  877. forms.messageOperation.visiable = false;
  878. if (
  879. forms.messageOperation.type === 'save' ||
  880. forms.messageOperation.type === 'pageLive'
  881. ) {
  882. emit('change', { status: false });
  883. eventGlobal.emit('teacher-slideshow', false);
  884. if (
  885. forms.messageOperation.type === 'pageLive' &&
  886. typeof forms.messageCallBack === 'function'
  887. ) {
  888. forms.messageCallBack();
  889. }
  890. }
  891. }}
  892. onConfirm={() => onMessageConfirm()}
  893. />
  894. </NModal>
  895. <PreviewWindow
  896. v-model:show={forms.previewModal}
  897. type="attend"
  898. params={forms.previewParams}
  899. />
  900. {/* 添加其它类型的资源 */}
  901. <NModal
  902. v-model:show={forms.addOtherSource}
  903. preset="card"
  904. class={['modalTitle background', styles.addOtherSource]}
  905. title={'添加资源'}>
  906. <AddOtherSource
  907. onClose={() => (forms.addOtherSource = false)}
  908. onComfirm={item => {
  909. if (Array.isArray(item)) {
  910. item.forEach((child: any) => {
  911. addCoursewareItem({ ...child, index: forms.addOtherIndex });
  912. });
  913. } else {
  914. addCoursewareItem({ ...item, index: forms.addOtherIndex });
  915. }
  916. }}
  917. />
  918. </NModal>
  919. <NModal v-model:show={showModalMask.value}>
  920. <AddCoursewareProtocol
  921. onClose={() => (showModalMask.value = false)}
  922. onConfirm={async () => {
  923. try {
  924. const resultStatus = await onSaveCourseWare();
  925. if (resultStatus) {
  926. userStore.setReadCoursewareOpenAgreement(true);
  927. emit('change', { status: false });
  928. eventGlobal.emit('teacher-slideshow', false);
  929. }
  930. } catch {
  931. //
  932. }
  933. }}
  934. />
  935. </NModal>
  936. </div>
  937. );
  938. }
  939. });