addCourseware.tsx 34 KB

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