index.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. import {
  2. NButton,
  3. NForm,
  4. NFormItem,
  5. NInput,
  6. NScrollbar,
  7. NSelect,
  8. NSpace,
  9. useMessage
  10. } from 'naive-ui';
  11. import { TransitionGroup, defineComponent, reactive, ref } from 'vue';
  12. import styles from './index.module.less';
  13. import UploadFile from '@/components/upload-file';
  14. import { nextTick } from 'vue';
  15. import { scrollToErrorForm } from '@/utils';
  16. import { api_lessonCoursewareSave } from '../../api';
  17. import iconUpload from '../../images/icon-upload.png';
  18. import iconAdd from '../../images/icon-add.svg';
  19. import iconMenu from '../../images/icon-menu.png';
  20. import btnAdd from '../../images/btn-add.svg';
  21. import btnDelete from '../../images/btn-delete.svg';
  22. import btnUp from '../../images/btn-up.svg';
  23. import btnDown from '../../images/btn-down.svg';
  24. import btnRemove from '../../images/btn-remove.svg';
  25. export const BOOK_DATA = {
  26. grades: [
  27. { label: '一年级', value: 1 },
  28. { label: '二年级', value: 2 },
  29. { label: '三年级', value: 3 },
  30. { label: '四年级', value: 4 },
  31. { label: '五年级', value: 5 },
  32. { label: '六年级', value: 6 },
  33. { label: '七年级', value: 7 },
  34. { label: '八年级', value: 8 },
  35. { label: '九年级', value: 9 }
  36. ],
  37. bookTypes: [
  38. { label: '上册', value: 'LAST' },
  39. { label: '下册', value: 'NEXT' }
  40. ]
  41. };
  42. /** 添加单元 */
  43. const createLesson = () => {
  44. return {
  45. key: 'item' + Date.now(),
  46. name: '', // 单元名称
  47. lessonTargetDesc: '', // 课时目标描述
  48. knowledgeList: [
  49. {
  50. key: Date.now() + '' + 0,
  51. name: '' // 章节名称
  52. }
  53. ] // 章节信息
  54. };
  55. };
  56. const initState = () => ({
  57. id: null, // 教材id
  58. name: '',
  59. currentGradeNum: null,
  60. bookType: null, // 上下册
  61. coverImg: '', // 封面
  62. enableFlag: true, // 状态
  63. type: 'COURSEWARE', // 教材类型:COURSEWARE,THEORY,可用值:COURSEWARE,THEORY
  64. lessonList: [createLesson()] // 单元列表
  65. });
  66. export default defineComponent({
  67. name: 'addNatural',
  68. emits: ['close'],
  69. setup(props, { emit }) {
  70. const message = useMessage();
  71. const data = reactive({
  72. uploading: false
  73. });
  74. const formRef = ref();
  75. const form = reactive(initState());
  76. const handleSave = () => {
  77. formRef.value?.validate((err: any) => {
  78. if (err) {
  79. nextTick(scrollToErrorForm);
  80. return;
  81. }
  82. handleSubmit();
  83. });
  84. };
  85. const handleSubmit = async () => {
  86. data.uploading = true;
  87. try {
  88. await api_lessonCoursewareSave(form);
  89. Object.assign(form, initState());
  90. message.success('添加成功');
  91. emit('close', true);
  92. } catch {
  93. //
  94. }
  95. data.uploading = false;
  96. };
  97. return () => (
  98. <div class={styles.container}>
  99. <NScrollbar style={{ 'max-height': '55vh' }}>
  100. <NForm
  101. ref={formRef}
  102. labelPlacement="left"
  103. labelWidth={120}
  104. model={form}>
  105. <div class={styles.topForms}>
  106. <NFormItem
  107. path="coverImg"
  108. rule={[
  109. {
  110. required: true,
  111. message: '请上传教材封面',
  112. trigger: ['change']
  113. }
  114. ]}>
  115. <UploadFile
  116. cropper
  117. // tips="建议尺寸:210*297, 文件大小: 5MB以内;"
  118. v-model:fileList={form.coverImg}
  119. showType="custom"
  120. size={2}
  121. accept=".jpg,jpeg,.png"
  122. options={{
  123. autoCropWidth: 210,
  124. autoCropHeight: 297,
  125. fixedBox: true
  126. }}>
  127. {{
  128. custom: () => (
  129. <div class={styles.uploadContent}>
  130. <img src={iconUpload} class={styles.iconUpload} />
  131. <p>请上传教材封面</p>
  132. </div>
  133. )
  134. }}
  135. </UploadFile>
  136. </NFormItem>
  137. <div class={styles.topFormInput}>
  138. <NFormItem
  139. style={{ minWidth: '360px' }}
  140. path="name"
  141. rule={[
  142. {
  143. required: true,
  144. message: '请输入教材名称',
  145. trigger: ['blur', 'change']
  146. }
  147. ]}>
  148. <NInput
  149. placeholder="请输入教材名称"
  150. maxlength={25}
  151. v-model:value={form.name}
  152. clearable></NInput>
  153. </NFormItem>
  154. <NFormItem
  155. path="currentGradeNum"
  156. rule={{
  157. required: true,
  158. message: '请选择年级',
  159. trigger: 'change',
  160. type: 'number'
  161. }}>
  162. <NSelect
  163. style={{ minWidth: '360px' }}
  164. placeholder="请选择年级"
  165. options={BOOK_DATA.grades}
  166. v-model:value={form.currentGradeNum}
  167. clearable
  168. filterable
  169. />
  170. </NFormItem>
  171. <NFormItem
  172. path="bookType"
  173. style={{ width: '360px' }}
  174. rule={{
  175. required: true,
  176. message: '请选择册别',
  177. trigger: 'change'
  178. }}>
  179. <NSelect
  180. placeholder="请选择册别"
  181. options={BOOK_DATA.bookTypes}
  182. v-model:value={form.bookType}
  183. clearable
  184. />
  185. </NFormItem>
  186. </div>
  187. </div>
  188. <div class={styles.menuTitle}>
  189. <img src={iconMenu} class={styles.iconMenu} />
  190. 目录
  191. </div>
  192. <TransitionGroup name="list" tag="div">
  193. {form.lessonList.map((item, index) => {
  194. return (
  195. <NSpace
  196. class={styles.lessonItem}
  197. wrap={false}
  198. wrapItem={false}
  199. align="start"
  200. key={item.key}>
  201. <NFormItem
  202. label="单元名称"
  203. labelPlacement="top"
  204. path={`lessonList[${index}].name`}
  205. rule={{
  206. required: true,
  207. message: '填写单元名称',
  208. trigger: ['blur', 'change']
  209. }}>
  210. <NInput
  211. placeholder="填写单元名称"
  212. maxlength={25}
  213. v-model:value={item.name}
  214. clearable></NInput>
  215. </NFormItem>
  216. <TransitionGroup name="list" tag="div">
  217. {item.knowledgeList.map((know, knowIndex) => {
  218. return (
  219. <NFormItem
  220. style={{
  221. '--n-label-height': knowIndex === 0 ? '26px' : '0'
  222. }}
  223. labelPlacement="top"
  224. label={knowIndex === 0 ? '章节名称' : ''}
  225. key={know.key}
  226. path={`lessonList[${index}].knowledgeList[${knowIndex}].name`}
  227. rule={{
  228. required: true,
  229. message: '填写章节名称',
  230. trigger: ['blur', 'change']
  231. }}>
  232. <NSpace
  233. wrap={false}
  234. align="center"
  235. class={styles.btnGroupAll}
  236. wrapItem={false}>
  237. <NInput
  238. maxlength={25}
  239. placeholder="填写章节名称"
  240. v-model:value={know.name}
  241. clearable></NInput>
  242. <NButton
  243. quaternary
  244. circle
  245. onClick={() => {
  246. item.knowledgeList.splice(knowIndex + 1, 0, {
  247. name: '',
  248. key: Date.now() + '' + knowIndex
  249. });
  250. }}>
  251. {{
  252. icon: () => (
  253. <img src={btnAdd} class={styles.btnImg} />
  254. )
  255. }}
  256. </NButton>
  257. <NButton
  258. quaternary
  259. circle
  260. disabled={item.knowledgeList.length < 2}
  261. onClick={() => {
  262. item.knowledgeList.splice(knowIndex, 1);
  263. }}>
  264. {{
  265. icon: () => (
  266. <img
  267. src={btnDelete}
  268. class={styles.btnImg}
  269. />
  270. )
  271. }}
  272. </NButton>
  273. <NButton
  274. quaternary
  275. circle
  276. disabled={knowIndex === 0}
  277. onClick={() => {
  278. if (knowIndex === 0) return;
  279. const tmp = item.knowledgeList[knowIndex - 1];
  280. item.knowledgeList[knowIndex - 1] =
  281. item.knowledgeList[knowIndex];
  282. item.knowledgeList[knowIndex] = tmp;
  283. }}>
  284. {{
  285. icon: () => (
  286. <img src={btnUp} class={styles.btnImg} />
  287. )
  288. }}
  289. </NButton>
  290. <NButton
  291. quaternary
  292. circle
  293. disabled={
  294. knowIndex === item.knowledgeList.length - 1
  295. }
  296. onClick={() => {
  297. if (
  298. knowIndex ===
  299. item.knowledgeList.length - 1
  300. )
  301. return;
  302. const tmp = item.knowledgeList[knowIndex + 1];
  303. item.knowledgeList[knowIndex + 1] =
  304. item.knowledgeList[knowIndex];
  305. item.knowledgeList[knowIndex] = tmp;
  306. }}>
  307. {{
  308. icon: () => (
  309. <img src={btnDown} class={styles.btnImg} />
  310. )
  311. }}
  312. </NButton>
  313. </NSpace>
  314. </NFormItem>
  315. );
  316. })}
  317. </TransitionGroup>
  318. <NButton
  319. class={styles.closeBtn}
  320. secondary
  321. circle
  322. size="small"
  323. disabled={form.lessonList.length < 2}
  324. onClick={() => {
  325. form.lessonList.splice(index, 1);
  326. }}>
  327. <img src={btnRemove} />
  328. </NButton>
  329. </NSpace>
  330. );
  331. })}
  332. </TransitionGroup>
  333. <div class={styles.line}></div>
  334. <NButton
  335. block
  336. class={styles.addUnitBtn}
  337. ghost
  338. color="#198CFE"
  339. onClick={() => {
  340. form.lessonList.push(createLesson());
  341. }}>
  342. {{
  343. icon: () => <img src={iconAdd} />,
  344. default: () => '新增单元'
  345. }}
  346. </NButton>
  347. </NForm>
  348. </NScrollbar>
  349. <NSpace class={styles.btnGroup} justify="center">
  350. <NButton round onClick={() => emit('close')}>
  351. 取消
  352. </NButton>
  353. <NButton
  354. round
  355. loading={data.uploading}
  356. type="primary"
  357. onClick={() => handleSave()}>
  358. 保存
  359. </NButton>
  360. </NSpace>
  361. </div>
  362. );
  363. }
  364. });