addCourseware.tsx 37 KB

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