addCourseware.tsx 41 KB

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