addCourseware.tsx 37 KB

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