addCourseware.tsx 34 KB

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