courseConfiguration.tsx 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. import {
  2. NAlert,
  3. NButton,
  4. NCard,
  5. NDialog,
  6. NDrawer,
  7. NDrawerContent,
  8. NDropdown,
  9. NEmpty,
  10. NForm,
  11. NFormItem,
  12. NGi,
  13. NGrid,
  14. NIcon,
  15. NInput,
  16. NModal,
  17. NSelect,
  18. NSpace,
  19. NSpin,
  20. NTooltip,
  21. NUpload,
  22. UploadCustomRequestOptions,
  23. useDialog,
  24. useMessage
  25. } from 'naive-ui'
  26. import { defineComponent, onMounted, reactive, ref } from 'vue'
  27. import { useRoute } from 'vue-router'
  28. import {
  29. lessonCoursewareDetailPage,
  30. lessonCoursewareDetailSave,
  31. lessonCoursewareDetailUpdate,
  32. lessonTrainingPage,
  33. lessonCoursewareDetailRemove,
  34. lessonCoursewareExaminationMapperQueryAll,
  35. lessonCoursewareDetailLock,
  36. api_openFileImportInfoSave,
  37. api_removeTraining,
  38. api_getUsedLevelDetail
  39. } from '../../api'
  40. import styles from '../index.module.less'
  41. import { EditFilled, DeleteFilled, LockFilled, UnlockFilled } from '@vicons/antd'
  42. import CourseKnowledgePoint from '../model/CourseKnowledgePoint'
  43. import SelectAfterClassTraining from '../model/selectAfterClassTraining'
  44. import AddUnitTest from '../model/AddUnitTest'
  45. import TheLink from '@/components/TheLink'
  46. import { api_uploadFile } from '@/plugins/uploadFile'
  47. const courseTypeIds: any = []
  48. for(let i = 1; i <= 20; i++) {
  49. courseTypeIds.push({
  50. label: i,
  51. value: i
  52. })
  53. }
  54. export default defineComponent({
  55. name: 'courseConfiguration',
  56. props: {
  57. course: {
  58. type: Object,
  59. default: () => {}
  60. }
  61. },
  62. setup(props, ctx) {
  63. const message = useMessage()
  64. const dialog = useDialog()
  65. const route = useRoute()
  66. const addFormRef = ref()
  67. const addForm = reactive({
  68. open: false,
  69. id: '',
  70. name: '', //课时名称
  71. lessonTargetDesc: '', //课时名称
  72. lessonTrainingId: '', //课后作业
  73. lockEnable: 'true',
  74. level: null,
  75. accessScope: 0
  76. })
  77. // 课件添加课程
  78. const addCourseware = () => {
  79. addFormRef.value.validate(async (err: any) => {
  80. if (!err) {
  81. const body = {
  82. lessonCoursewareId: route.query.id,
  83. name: addForm.name,
  84. level: addForm.level,
  85. lessonTargetDesc: addForm.lessonTargetDesc,
  86. lockEnable: addForm.lockEnable,
  87. accessScope: addForm.accessScope
  88. }
  89. let res: any
  90. if (addForm.id) {
  91. try {
  92. res = await lessonCoursewareDetailUpdate({
  93. ...body,
  94. id: addForm.id
  95. })
  96. } catch (error) {}
  97. } else {
  98. try {
  99. res = await lessonCoursewareDetailSave(body)
  100. } catch (error) {}
  101. }
  102. if (res?.code == 200) {
  103. message.success('保存成功')
  104. addForm.open = false
  105. getDetail()
  106. }
  107. }
  108. })
  109. }
  110. const state = reactive({
  111. dataList: [] as any,
  112. mapperList: [] as any
  113. })
  114. const getDetail = async () => {
  115. try {
  116. const { data } = await lessonCoursewareDetailPage({
  117. lessonCoursewareId: route.query.id,
  118. page: 1,
  119. rows: 1000
  120. })
  121. if (Array.isArray(data?.rows)) {
  122. state.dataList = data.rows
  123. }
  124. } catch (error) {}
  125. }
  126. /** 查询课件下的所有阶段自测 */
  127. const getMapperAll = async () => {
  128. try {
  129. const { data } = await lessonCoursewareExaminationMapperQueryAll(route.query.id)
  130. if (Array.isArray(data)) {
  131. state.mapperList = data
  132. }
  133. } catch (error) {}
  134. }
  135. /** 获取级别 */
  136. const getLevels = async (item?: any) => {
  137. const { data } = await api_getUsedLevelDetail({
  138. id: item?.id,
  139. lessonCoursewareId: route.query.id
  140. })
  141. if(Array.isArray(data)) {
  142. courseTypeIds.forEach((item: any) => {
  143. item.disabled = data.includes(item.value) ? true : false
  144. })
  145. }
  146. }
  147. onMounted(() => {
  148. getDetail()
  149. getMapperAll()
  150. })
  151. //删除课时
  152. const hanldeDelete = (item: any) => {
  153. const d = dialog.warning({
  154. title: '警告',
  155. content: '是否确认删除此课时?',
  156. positiveText: '确定',
  157. negativeText: '取消',
  158. onPositiveClick: async () => {
  159. d.loading = true
  160. try {
  161. const res: any = await lessonCoursewareDetailRemove(item.id)
  162. if (res?.code == 200) {
  163. message.success('删除成功')
  164. getDetail()
  165. }
  166. } catch (error) {}
  167. d.loading = false
  168. }
  169. })
  170. }
  171. //绑定课后作业
  172. const afterTrain = reactive({
  173. open: false,
  174. id: ''
  175. })
  176. const addAfterTrain = async (row: any) => {
  177. console.log('🚀 ~ row', row)
  178. const body = {
  179. id: addForm.id,
  180. lessonTrainingId: row.id,
  181. name: addForm.name,
  182. lessonTargetDesc: addForm.lessonTargetDesc
  183. }
  184. try {
  185. const res: any = await lessonCoursewareDetailUpdate(body)
  186. if (res?.code == 200) {
  187. message.success('保存成功')
  188. afterTrain.open = false
  189. getDetail()
  190. }
  191. } catch (error) {}
  192. }
  193. // 课程配置
  194. const courseData = reactive({
  195. open: false,
  196. title: '',
  197. item: null
  198. })
  199. //阶段自测
  200. const unitData = reactive({
  201. open: false,
  202. model: {}
  203. })
  204. /** 切换课件是否锁定 */
  205. const toggleLock = (item: any) => {
  206. const d = dialog.warning({
  207. title: '警告',
  208. content: `是否确认${item.lockFlag ? '解锁' : '锁定'}此课时?`,
  209. positiveText: '确定',
  210. negativeText: '取消',
  211. onPositiveClick: async () => {
  212. d.loading = true
  213. try {
  214. const res = await lessonCoursewareDetailLock({ id: item.id, lockFlag: !item.lockFlag })
  215. message.success('操作成功')
  216. getDetail()
  217. } catch (error) {}
  218. d.loading = false
  219. }
  220. })
  221. }
  222. /** 删除选择的作业 */
  223. const hanldeRemoveTraning = (item: any) => {
  224. const d = dialog.warning({
  225. title: '警告',
  226. content: `是否确认删除作业?`,
  227. positiveText: '确定',
  228. negativeText: '取消',
  229. onPositiveClick: async () => {
  230. d.loading = true
  231. try {
  232. const res = await api_removeTraining(item.id)
  233. message.success('删除成功')
  234. getDetail()
  235. } catch (error) {}
  236. d.loading = false
  237. }
  238. })
  239. }
  240. const customRequest_importData = reactive({
  241. loading: false,
  242. dataType: 'EXAMINATION',
  243. importRef: null as any
  244. })
  245. const customRequest_importFile = async (data: UploadCustomRequestOptions) => {
  246. console.log(data.file)
  247. const msg = message.loading('正在上传文件', { duration: 0 })
  248. customRequest_importData.loading = true
  249. try {
  250. const fileUrl = await api_uploadFile(data.file.file, () => {})
  251. const res = await api_openFileImportInfoSave({
  252. dataType: customRequest_importData.dataType,
  253. fileName: data.file.name,
  254. importUrl: fileUrl,
  255. lessonId: route.query.id
  256. })
  257. console.log('🚀 ~ res:', res)
  258. customRequest_importData.loading = false
  259. if (res.data) {
  260. msg.destroy()
  261. // 空表格
  262. if (res.data.insertRow === 0 && res.data.invalidRow === 0) {
  263. message.error('导入失败,表格为空')
  264. return
  265. }
  266. if (res.data.respUrl) {
  267. dialog.error({
  268. title: '信息',
  269. content: () => (
  270. <NSpace>
  271. <div>导入失败,点击下载错误信息</div>
  272. <a href={res.data.respUrl} download>
  273. 下载
  274. </a>
  275. </NSpace>
  276. )
  277. })
  278. return
  279. }
  280. getDetail()
  281. message.success('导入成功')
  282. } else {
  283. message.error('请下载模板后,填写数据再导入')
  284. }
  285. } catch {}
  286. customRequest_importData.loading = false
  287. msg.destroy()
  288. }
  289. return () => (
  290. <div class={styles.courseConfiguration}>
  291. <NSpin show={customRequest_importData.loading}>
  292. <NSpace justify="space-between">
  293. <NSpace>
  294. <NButton
  295. disabled={route.query.isLook ? true : false}
  296. type="primary"
  297. onClick={() => {
  298. addForm.name = ''
  299. addForm.lessonTargetDesc = ''
  300. addForm.lessonTrainingId = ''
  301. addForm.id = ''
  302. addForm.lockEnable = ''
  303. addForm.accessScope = 0
  304. getLevels()
  305. addForm.open = true
  306. }}
  307. >
  308. 添加课程
  309. </NButton>
  310. <NButton
  311. type="primary"
  312. disabled={route.query.isLook ? true : false}
  313. onClick={() => {
  314. unitData.open = true
  315. unitData.model = {}
  316. }}
  317. >
  318. 添加阶段自测
  319. </NButton>
  320. </NSpace>
  321. <NSpace style={{ marginLeft: 'auto' }}>
  322. <NDropdown
  323. size="huge"
  324. trigger="hover"
  325. options={[
  326. {
  327. key: '3',
  328. type: 'render',
  329. render: () => (
  330. <NButton
  331. size="large"
  332. quaternary
  333. tag="a"
  334. //@ts-ignore
  335. href="https://oss.dayaedu.com/daya-docs/%E5%8D%95%E5%85%83%E6%B5%8B%E9%AA%8C%E5%AF%BC%E5%85%A5%E6%A8%A1%E6%9D%BF.xlsx"
  336. download
  337. >
  338. 单元测验模板
  339. </NButton>
  340. )
  341. },
  342. {
  343. key: '1',
  344. type: 'render',
  345. render: () => (
  346. <NButton
  347. size="large"
  348. quaternary
  349. tag="a"
  350. //@ts-ignore
  351. href="https://oss.dayaedu.com/daya-docs/%E8%AF%BE%E5%90%8E%E8%AE%AD%E7%BB%83%E5%AF%BC%E5%85%A5%E6%A8%A1%E6%9D%BF.xlsx"
  352. download
  353. >
  354. 课后训练模板
  355. </NButton>
  356. )
  357. }
  358. ]}
  359. >
  360. <NButton type="primary" v-auth="downloadUnitQuiz1702251742292344833">
  361. 下载模板
  362. </NButton>
  363. </NDropdown>
  364. <div>
  365. <NUpload
  366. multiple={false}
  367. ref={(el: any) => (customRequest_importData.importRef = el)}
  368. showFileList={false}
  369. accept=".xlsx"
  370. customRequest={customRequest_importFile}
  371. >
  372. <NDropdown
  373. size="huge"
  374. trigger="hover"
  375. options={[
  376. { label: '课件', key: 'COURSEWARE' },
  377. { label: '单元测验', key: 'EXAMINATION' },
  378. { label: '课后训练', key: 'HOMEWORK' }
  379. ]}
  380. onSelect={(key: string) => {
  381. customRequest_importData.dataType = key
  382. console.log(customRequest_importData.importRef)
  383. customRequest_importData.importRef?.clear()
  384. customRequest_importData.importRef?.openOpenFileDialog()
  385. }}
  386. >
  387. <NButton type="primary" onClick={(e: Event) => e.stopPropagation()}>
  388. 导入数据
  389. </NButton>
  390. </NDropdown>
  391. </NUpload>
  392. </div>
  393. </NSpace>
  394. </NSpace>
  395. <NAlert class={styles.title} title="教学内容" />
  396. <NGrid xGap={12} yGap={8} cols={3}>
  397. {state.dataList.map((item: any) => {
  398. const mapper = state.mapperList.find(
  399. (n: any) => n.parentCoursewareDetailId == item.id
  400. )
  401. return (
  402. <>
  403. <NGi>
  404. <NCard
  405. // title={item.name}
  406. style={{
  407. '--n-padding-top': '5px',
  408. '--n-padding-bottom': '5px',
  409. height: '100%'
  410. }}
  411. >
  412. {{
  413. header: () => <div>{item.name}{item.level && <span style="color: red">({item.level})</span>}</div>,
  414. default: () => (
  415. <div>
  416. <div style={{ paddingBottom: '12px' }}>{item.lessonTargetDesc}</div>
  417. <table style={{ width: '100%' }}>
  418. <tr>
  419. <td>建议学习时长</td>
  420. <td style={{ 'text-align': 'right' }}>
  421. {item.lessonDurationSecond || '0'}s
  422. </td>
  423. </tr>
  424. <tr>
  425. <td>知识点</td>
  426. <td style={{ 'text-align': 'right' }}>
  427. {item.knowledgePointIds?.split(',').filter(Boolean).length || 0}个
  428. </td>
  429. </tr>
  430. <tr>
  431. <td>课后作业</td>
  432. <td style={{ 'text-align': 'right' }}>
  433. {item.lessonTrainingId ? (
  434. <>
  435. <TheLink
  436. // to={`/#/afterClassTrainingDetail?id=${item.lessonTrainingId}&name=${item.lessonTrainingName}&courseTypeCode=TRUMPET_SINGLE`}
  437. authLink="afterClassTrainingManage1599968711187746818"
  438. to={{
  439. path: '/afterClassTrainingManage',
  440. query: { id: item.lessonTrainingId }
  441. }}
  442. >
  443. {item.lessonTrainingName}
  444. </TheLink>
  445. <NTooltip>
  446. {{
  447. default: () => '删除',
  448. trigger: () => (
  449. <NButton
  450. v-auth="lessonCoursewareDetail/removeTraining1793827126242213890"
  451. quaternary
  452. size="small"
  453. circle
  454. onClick={() => hanldeRemoveTraning(item)}
  455. >
  456. <NIcon component={<DeleteFilled />} />
  457. </NButton>
  458. )
  459. }}
  460. </NTooltip>
  461. </>
  462. ) : (
  463. '无'
  464. )}
  465. </td>
  466. </tr>
  467. </table>
  468. </div>
  469. ),
  470. 'header-extra': () => (
  471. <>
  472. <NTooltip>
  473. {{
  474. default: () => (item.lockFlag ? '解锁' : '锁定'),
  475. trigger: () => (
  476. <NButton
  477. disabled={route.query.isLook ? true : false}
  478. quaternary
  479. circle
  480. onClick={() => toggleLock(item)}
  481. >
  482. <NIcon
  483. component={item.lockFlag ? <LockFilled /> : <UnlockFilled />}
  484. />
  485. </NButton>
  486. )
  487. }}
  488. </NTooltip>
  489. <NTooltip>
  490. {{
  491. default: () => '编辑',
  492. trigger: () => (
  493. <NButton
  494. disabled={route.query.isLook ? true : false}
  495. quaternary
  496. circle
  497. onClick={() => {
  498. addForm.name = item.name
  499. addForm.lessonTargetDesc = item.lessonTargetDesc
  500. addForm.id = item.id
  501. addForm.lockEnable = item.lockEnable + ''
  502. addForm.accessScope = item.accessScope ?? 0
  503. addForm.level = item.level
  504. getLevels(item)
  505. addForm.open = true
  506. }}
  507. >
  508. <NIcon component={<EditFilled />} />
  509. </NButton>
  510. )
  511. }}
  512. </NTooltip>
  513. <NTooltip>
  514. {{
  515. default: () => '删除',
  516. trigger: () => (
  517. <NButton
  518. disabled={route.query.isLook ? true : false}
  519. quaternary
  520. circle
  521. onClick={() => hanldeDelete(item)}
  522. >
  523. <NIcon component={<DeleteFilled />} />
  524. </NButton>
  525. )
  526. }}
  527. </NTooltip>
  528. </>
  529. ),
  530. action: () => (
  531. <NSpace justify="space-around">
  532. <NButton
  533. text
  534. disabled={route.query.isLook ? true : false}
  535. onClick={() => {
  536. courseData.title = item.name
  537. courseData.open = true
  538. courseData.item = item
  539. }}
  540. >
  541. 选择知识点
  542. </NButton>
  543. <NButton
  544. text
  545. disabled={route.query.isLook ? true : false}
  546. onClick={() => {
  547. addForm.id = item.id
  548. addForm.lessonTargetDesc = item.lessonTargetDesc
  549. addForm.name = item.name
  550. afterTrain.open = true
  551. }}
  552. >
  553. 选择课后作业
  554. </NButton>
  555. </NSpace>
  556. )
  557. }}
  558. </NCard>
  559. </NGi>
  560. {mapper && (
  561. <NGi>
  562. <NCard
  563. title={mapper.name}
  564. style={{
  565. '--n-padding-top': '5px',
  566. '--n-padding-bottom': '5px',
  567. '--n-color': 'rgba(238,243,254,1)',
  568. '--n-action-color': 'rgba(238,243,254,1)',
  569. height: '100%'
  570. }}
  571. >
  572. {{
  573. default: () => (
  574. <div>
  575. <div style={{ paddingBottom: '12px' }}>{mapper.desc}</div>
  576. <table style={{ width: '100%' }}>
  577. <tr>
  578. <td>单团学生</td>
  579. <td style={{ 'text-align': 'right' }}>
  580. {mapper?.details?.[0]?.unitExaminationName || '无'}
  581. </td>
  582. </tr>
  583. <tr>
  584. <td>双团学生</td>
  585. <td style={{ 'text-align': 'right' }}>
  586. {mapper?.details?.[1]?.unitExaminationName || '无'}
  587. </td>
  588. </tr>
  589. <tr>
  590. <td>多团学生</td>
  591. <td style={{ 'text-align': 'right' }}>
  592. {mapper?.details?.[2]?.unitExaminationName || '无'}
  593. </td>
  594. </tr>
  595. </table>
  596. </div>
  597. ),
  598. action: () => (
  599. <NSpace justify="space-around">
  600. <NButton
  601. text
  602. disabled={route.query.isLook ? true : false}
  603. onClick={() => {
  604. unitData.open = true
  605. unitData.model = mapper
  606. }}
  607. >
  608. 编辑阶段自测
  609. </NButton>
  610. </NSpace>
  611. )
  612. }}
  613. </NCard>
  614. </NGi>
  615. )}
  616. </>
  617. )
  618. })}
  619. </NGrid>
  620. </NSpin>
  621. {state.dataList.length ? null : <NEmpty />}
  622. {/* 课程编辑 */}
  623. <NModal
  624. preset="dialog"
  625. v-model:show={addForm.open}
  626. title={addForm.id ? '编辑课程' : '添加课程'}
  627. showIcon={false}
  628. >
  629. <NForm ref={addFormRef} model={addForm}>
  630. <NFormItem
  631. label="课程名称"
  632. required
  633. path="name"
  634. rule={[{ required: true, message: '请填写课程名称', trigger: ['blur', 'change'] }]}
  635. >
  636. <NInput v-model:value={addForm.name} />
  637. </NFormItem>
  638. <NFormItem label="课程编号" path="level">
  639. <NSelect
  640. filterable
  641. placeholder="请选择课程编号"
  642. clearable
  643. v-model:value={addForm.level}
  644. options={courseTypeIds}
  645. />
  646. </NFormItem>
  647. <NFormItem
  648. label="是否需要解锁"
  649. required
  650. path="lockEnable"
  651. rule={[{ required: true, message: '请选择是否解锁', trigger: ['blur', 'change'] }]}
  652. >
  653. <NSelect
  654. v-model:value={addForm.lockEnable}
  655. options={
  656. [
  657. {
  658. label: '是',
  659. value: 'true'
  660. },
  661. {
  662. label: '否',
  663. value: 'false'
  664. }
  665. ] as any
  666. }
  667. clearable
  668. ></NSelect>
  669. </NFormItem>
  670. <NFormItem
  671. label="是否免费"
  672. required
  673. path="accessScope"
  674. rule={[
  675. {
  676. type: 'number',
  677. required: true,
  678. message: '设置是否免费',
  679. trigger: ['blur', 'change']
  680. }
  681. ]}
  682. >
  683. <NSelect
  684. v-model:value={addForm.accessScope}
  685. options={
  686. [
  687. {
  688. label: '是',
  689. value: 0
  690. },
  691. {
  692. label: '否',
  693. value: 1
  694. }
  695. ] as any
  696. }
  697. clearable
  698. ></NSelect>
  699. </NFormItem>
  700. <NFormItem
  701. label="教学目标"
  702. required
  703. path="lessonTargetDesc"
  704. rule={[{ required: true, message: '请填写教学目标', trigger: ['blur', 'change'] }]}
  705. >
  706. <NInput
  707. type="textarea"
  708. rows={3}
  709. v-model:value={addForm.lessonTargetDesc}
  710. maxlength={500}
  711. showCount={true}
  712. />
  713. </NFormItem>
  714. <NSpace justify="end">
  715. <NButton onClick={() => (addForm.open = false)}>取消</NButton>
  716. <NButton type="primary" onClick={() => addCourseware()}>
  717. 确认
  718. </NButton>
  719. </NSpace>
  720. </NForm>
  721. </NModal>
  722. {/* 课后作业 */}
  723. <NModal
  724. preset="dialog"
  725. v-model:show={afterTrain.open}
  726. title="选择课后作业"
  727. showIcon={false}
  728. style={{ width: '80vw' }}
  729. >
  730. <SelectAfterClassTraining
  731. courseTypeCode={props.course?.courseTypeCode}
  732. onHandleSuccess={addAfterTrain}
  733. />
  734. </NModal>
  735. {/* 课程详情编辑 */}
  736. <NDrawer v-model:show={courseData.open} width="80vw">
  737. <NDrawerContent title={courseData.title} closable>
  738. {{
  739. default: () => (
  740. <CourseKnowledgePoint
  741. courseTypeCode={props.course?.courseTypeCode}
  742. item={courseData.item as any}
  743. onHandleSuccess={() => {
  744. getDetail()
  745. }}
  746. />
  747. ),
  748. footer: () => (
  749. <NSpace>
  750. <NButton onClick={() => (courseData.open = false)}>取消</NButton>
  751. <NButton type="primary" onClick={() => (courseData.open = false)}>
  752. 确认
  753. </NButton>
  754. </NSpace>
  755. )
  756. }}
  757. </NDrawerContent>
  758. </NDrawer>
  759. {/* 阶段自测 */}
  760. <NModal
  761. preset="dialog"
  762. v-model:show={unitData.open}
  763. title={(unitData.model as any)?.id ? '编辑阶段自测' : '新增阶段自测'}
  764. showIcon={false}
  765. style={{ width: '500px' }}
  766. >
  767. <AddUnitTest
  768. courseTypeCode={props.course?.courseTypeCode}
  769. list={state.dataList}
  770. item={unitData.model}
  771. onClose={(result) => {
  772. if (result) {
  773. getDetail()
  774. getMapperAll()
  775. }
  776. unitData.open = false
  777. }}
  778. />
  779. </NModal>
  780. </div>
  781. )
  782. }
  783. })