form.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. <template>
  2. <div class="app-container">
  3. <a-form
  4. :model="form"
  5. ref="formRef"
  6. :label-col="{ span: 4 }"
  7. :wrapper-col="{ span: 16 }"
  8. :rules="rules"
  9. :loading="formLoading"
  10. layout="horizontal"
  11. >
  12. <a-form-item label="任务类型:" name="examType" :rules="rules.examType">
  13. <a-select v-model:value="form.examType" placeholder="请选择任务类型" @change="examTypeChange" allow-clear>
  14. <a-select-option v-for="item in examTypeEnum" :key="item.value" :value="item.value">
  15. {{ item.label }}
  16. </a-select-option>
  17. </a-select>
  18. </a-form-item>
  19. <a-form-item label="任务标题" name="examName" :rules="rules.examName">
  20. <a-input v-model:value="form.examName" placeholder="请输入任务标题" maxlength="50"/>
  21. </a-form-item>
  22. <a-form-item label="选择试卷" name="paperId" :rules="rules.paperId">
  23. <a-input-group compact>
  24. <a-input
  25. v-model:value="selectedPaperName"
  26. placeholder="请选择试卷"
  27. readonly
  28. style="width: calc(100% - 100px)"
  29. />
  30. <a-button type="primary" @click="addPaper" style="width: 100px" :disabled="!form.examType">选择试卷</a-button>
  31. </a-input-group>
  32. </a-form-item>
  33. <a-form-item v-if="form.gradesIdsList.length === 0" label="学期" name="semesterId" :rules="rules.semesterId">
  34. <a-select v-model:value="form.semesterId" placeholder="请选择学期" allowClear @change="handleSemesterChange">
  35. <a-select-option v-for="item in semesterList" :key="item.id" :value="item.id">
  36. {{ item.name }}
  37. </a-select-option>
  38. </a-select>
  39. </a-form-item>
  40. <!-- <a-form-item label="专业" name="majorId">
  41. <a-select v-model:value="form.majorId" placeholder="请选择专业" allowClear @change="handleMajorChange">
  42. <a-select-option v-for="item in majorList" :key="item.id" :value="item.id">
  43. {{ item.majorName }}
  44. </a-select-option>
  45. </a-select>
  46. </a-form-item> -->
  47. <a-form-item label="课程" v-if="form.gradesIdsList.length === 0" name="courseId" :rules="rules.courseId">
  48. <a-select v-model:value="form.courseId" placeholder="请选择课程" allowClear :disabled="!form.semesterId">
  49. <a-select-option v-for="item in courseList" :key="item.courseId" :value="item.courseId">
  50. {{ item.courseName }}
  51. </a-select-option>
  52. </a-select>
  53. </a-form-item>
  54. <a-form-item v-if="!form.semesterId" label="班级" name="gradesIdsList" :rules="rules.gradesIdsList">
  55. <a-select
  56. v-model:value="form.gradesIdsList"
  57. mode="multiple"
  58. :fieldNames="{ label: 'gradesName', value: 'gradesId' }"
  59. :options="gradesQueryListOptions"
  60. placeholder="请选择班级"
  61. :show-search="true"
  62. :filter-option="(input, option) => option.gradesName.toLowerCase().indexOf(input.toLowerCase()) >= 0"
  63. allowClear
  64. />
  65. </a-form-item>
  66. <a-form-item label="开始时间" name="startTime" :rules="rules.startTime">
  67. <a-date-picker
  68. v-model:value="form.startTime"
  69. show-time
  70. format="YYYY-MM-DD HH:mm:ss"
  71. placeholder="请选择开始时间"
  72. style="width: 100%"
  73. :disabledDate="disabledStartDate"
  74. />
  75. </a-form-item>
  76. <a-form-item label="结束时间" name="endTime" :rules="rules.endTime">
  77. <a-date-picker
  78. v-model:value="form.endTime"
  79. show-time
  80. format="YYYY-MM-DD HH:mm:ss"
  81. placeholder="请选择结束时间"
  82. style="width: 100%"
  83. :disabledDate="disabledStartDate"
  84. />
  85. </a-form-item>
  86. <a-form-item label="任务状态" name="examStatus">
  87. <a-radio-group v-model:value="form.examStatus">
  88. <a-radio :value="0">未开始</a-radio>
  89. <a-radio :value="1">已开始</a-radio>
  90. <a-radio :value="2">已结束</a-radio>
  91. </a-radio-group>
  92. </a-form-item>
  93. <a-form-item>
  94. <a-space>
  95. <a-button type="primary" @click="submitForm">提交</a-button>
  96. <a-button @click="resetForm">重置</a-button>
  97. </a-space>
  98. </a-form-item>
  99. </a-form>
  100. <a-modal
  101. v-model:visible="paperPage.showDialog"
  102. width="70%"
  103. title="选择试卷"
  104. @ok="confirmPaperSelect"
  105. @cancel="() => (paperPage.showDialog = false)"
  106. >
  107. <a-form layout="inline">
  108. <a-form-item label="试卷类型">
  109. <a-select v-model:value="paperPage.queryParam.paperType" placeholder="请选择试卷类型" disabled>
  110. <a-select-option v-for="item in paperTypeEnum" :key="item.key" :value="item.key">
  111. {{ item.value }}
  112. </a-select-option>
  113. </a-select>
  114. </a-form-item>
  115. <a-form-item>
  116. <a-button type="primary" @click="examPaperSubmitForm">查询</a-button>
  117. </a-form-item>
  118. </a-form>
  119. <a-table
  120. :dataSource="paperPage.tableData"
  121. :columns="modalColumns"
  122. rowKey="id"
  123. :loading="paperPage.listLoading"
  124. :rowSelection="rowSelection"
  125. bordered
  126. :pagination="false"
  127. style="margin-top: 16px"
  128. >
  129. <!-- <template #bodyCell="{ column, record }">
  130. <template v-if="column.key === 'subjectId'">
  131. {{ subjectEnumFormat(record.subjectId) }}
  132. </template>
  133. </template> -->
  134. </a-table>
  135. <a-pagination
  136. v-show="paperPage.total > 0"
  137. :total="paperPage.total"
  138. :current="paperPage.queryParam.pageIndex"
  139. :pageSize="paperPage.queryParam.pageSize"
  140. @change="onPageChange"
  141. @showSizeChange="onPageSizeChange"
  142. show-size-changer
  143. style="margin-top: 16px; text-align: right"
  144. />
  145. </a-modal>
  146. </div>
  147. </template>
  148. <script setup>
  149. import { ref, reactive, onMounted, computed } from 'vue'
  150. import { message } from 'ant-design-vue'
  151. import { useExamStore } from '@/store/exam.js'
  152. import examManagerApi from '@/api/exam/paper/examManager.js'
  153. import examPaperApi from '@/api/exam/paper/examPaperApi.js'
  154. import resourceAuditApi from '@/api/resourceAudit.js'
  155. import { gradesQueryList } from '@/api/semester/index.js'
  156. import tool from '@/utils/tool'
  157. import dayjs from 'dayjs'
  158. const emit = defineEmits(['success'])
  159. const props = defineProps({
  160. id: {
  161. type: Number,
  162. default: null
  163. }
  164. })
  165. const formRef = ref()
  166. const examStore = useExamStore()
  167. const paperTypeEnum = computed(() => examStore.paperTypeEnum.filter((item) => item.key !== '5'))
  168. const examTypeEnum = tool.dictList('EXAM_TYPE').filter((item) => item.value !== '3')
  169. const formLoading = ref(false)
  170. const semesterList = ref([])
  171. const majorList = ref([])
  172. const courseList = ref([])
  173. const gradesQueryListOptions = ref([]) //班级
  174. const form = reactive({
  175. id: null,
  176. examName: '',
  177. paperId: null,
  178. courseId: null,
  179. startTime: null,
  180. endTime: null,
  181. examStatus: 0,
  182. semesterId: null,
  183. gradesIdsList: []
  184. // majorId: null
  185. })
  186. const rules = {
  187. examType: [{ required: true, message: '请选择任务类型', trigger: 'blur' }],
  188. examName: [{ required: true, message: '请输入考试标题', trigger: 'blur' }],
  189. paperId: [{ required: true, message: '请选择试卷', trigger: 'change' }],
  190. startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
  191. endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
  192. semesterId: [{ required: true, message: '请选择学期', trigger: 'change' }],
  193. courseId: [{ required: true, message: '请选择课程', trigger: 'change' }],
  194. gradesIdsList: [{ required: true, message: '请选择班级', trigger: 'change' }]
  195. }
  196. const selectedPaperName = ref('')
  197. const modalColumns = [
  198. { title: 'Id', dataIndex: 'id', key: 'id', width: 90 },
  199. // { title: '学科', dataIndex: 'subjectId', key: 'subjectId', width: 120 },
  200. { title: '名称', dataIndex: 'name', key: 'name' },
  201. {
  202. title: '创建时间',
  203. dataIndex: 'createTimeStr',
  204. key: 'createTimeStr',
  205. width: 200
  206. }
  207. ]
  208. const paperPage = reactive({
  209. subjectFilter: [],
  210. selectedPaper: null,
  211. showDialog: false,
  212. queryParam: {
  213. paperId: null,
  214. paperType: '6',
  215. pageIndex: 1,
  216. pageSize: 5
  217. },
  218. listLoading: false,
  219. tableData: [],
  220. total: 0
  221. })
  222. // 试卷选择表格单选
  223. const selectedRowKeys = ref([])
  224. const rowSelection = reactive({
  225. type: 'radio',
  226. selectedRowKeys: selectedRowKeys,
  227. onChange: (selectedRowKeysVal, selectedRows) => {
  228. selectedRowKeys.value = selectedRowKeysVal
  229. paperPage.selectedPaper = selectedRows[0] || null
  230. }
  231. })
  232. // 学期变更处理函数
  233. const handleSemesterChange = (value) => {
  234. form.courseId = null
  235. courseList.value = []
  236. // 如果学期和专业都已选择,则查询课程列表
  237. if (value) {
  238. loadCourseList()
  239. }
  240. }
  241. // 专业变更处理函数
  242. // const handleMajorChange = (value) => {
  243. // form.courseId = null
  244. // courseList.value = []
  245. // // 如果学期和专业都已选择,则查询课程列表
  246. // if (value && form.semesterId) {
  247. // loadCourseList()
  248. // }
  249. // }
  250. // 加载课程列表
  251. const loadCourseList = () => {
  252. // 添加加载状态
  253. const courseLoading = message.loading('正在加载课程列表...', 0)
  254. // 根据选择的学期和专业加载课程列表
  255. resourceAuditApi
  256. .courseAllList({
  257. semesterId: form.semesterId
  258. })
  259. .then((res) => {
  260. if (res.code === 200) {
  261. courseList.value = res.data
  262. } else {
  263. message.error('加载课程列表失败:' + (res.msg || '未知错误'))
  264. }
  265. })
  266. .catch((err) => {
  267. message.error('加载课程列表失败:' + (err.message || '网络错误'))
  268. })
  269. .finally(() => {
  270. courseLoading()
  271. })
  272. }
  273. // 禁用开始时间大于今天的日期
  274. const disabledStartDate = (current) => {
  275. // 禁用小于今天的日期
  276. return current && current < dayjs().startOf('day')
  277. }
  278. // 试卷类型变更
  279. const paperTypeChange = () => {
  280. paperPage.queryParam.paperId = null
  281. form.paperId = null
  282. selectedPaperName.value = ''
  283. }
  284. // 考试类型变更
  285. const examTypeChange = (type) => {
  286. switch (type) {
  287. case '1': // 普通考试
  288. paperPage.queryParam.paperType = '6'
  289. break
  290. case '2': // 章节测验
  291. paperPage.queryParam.paperType = '3'
  292. break
  293. case '4': // 课时作业
  294. paperPage.queryParam.paperType = '2'
  295. break
  296. }
  297. }
  298. // 选择试卷
  299. const addPaper = () => {
  300. paperPage.showDialog = true
  301. search()
  302. }
  303. // 查询试卷
  304. const search = async () => {
  305. paperPage.listLoading = true
  306. paperPage.showDialog = true
  307. const params = {
  308. ...paperPage.queryParam,
  309. current: paperPage.queryParam.pageIndex,
  310. size: paperPage.queryParam.pageSize,
  311. paperType: paperPage.queryParam.paperType
  312. }
  313. delete params.pageIndex
  314. delete params.pageSize
  315. const data = await examPaperApi.selectExamList(params)
  316. const re = data
  317. paperPage.tableData = re.records
  318. paperPage.total = re.total
  319. paperPage.queryParam.pageIndex = re.current
  320. paperPage.listLoading = false
  321. }
  322. // 班级下拉
  323. const loadGradesQueryList = () => {
  324. gradesQueryList()
  325. .then((res) => {
  326. gradesQueryListOptions.value = res.data
  327. })
  328. .catch((err) => {
  329. console.log(err)
  330. })
  331. }
  332. // 确认选择试卷
  333. const confirmPaperSelect = () => {
  334. if (!paperPage.selectedPaper) {
  335. message.warning('请选择一个试卷')
  336. return
  337. }
  338. form.paperId = paperPage.selectedPaper.id
  339. selectedPaperName.value = paperPage.selectedPaper.name
  340. paperPage.showDialog = false
  341. selectedRowKeys.value = []
  342. }
  343. // 分页
  344. const onPageChange = (page) => {
  345. paperPage.queryParam.pageIndex = page
  346. search()
  347. }
  348. const onPageSizeChange = (current, size) => {
  349. paperPage.queryParam.pageSize = size
  350. paperPage.queryParam.pageIndex = 1
  351. search()
  352. }
  353. // 查询按钮
  354. const examPaperSubmitForm = () => {
  355. paperPage.queryParam.pageIndex = 1
  356. search()
  357. }
  358. // 提交表单
  359. const submitForm = () => {
  360. formRef.value.validate().then(async () => {
  361. formLoading.value = true
  362. try {
  363. const submitData = {
  364. ...form,
  365. startTime: form.startTime ? form.startTime.format('YYYY-MM-DD HH:mm:ss') : null,
  366. endTime: form.endTime ? form.endTime.format('YYYY-MM-DD HH:mm:ss') : null
  367. }
  368. if (form.id) {
  369. await examManagerApi.edit(submitData)
  370. } else {
  371. await examManagerApi.createExam(submitData)
  372. }
  373. emit('success')
  374. } catch (e) {
  375. console.error(e)
  376. } finally {
  377. formLoading.value = false
  378. }
  379. })
  380. }
  381. // 重置表单
  382. const resetForm = () => {
  383. const lastId = form.id
  384. formRef.value.resetFields()
  385. form.id = lastId
  386. form.examName = ''
  387. form.paperId = null
  388. form.semesterId = null // 重置学期
  389. // form.majorId = null // 重置专业
  390. form.courseId = null
  391. form.startTime = null
  392. form.endTime = null
  393. form.examStatus = 0
  394. selectedPaperName.value = ''
  395. paperPage.queryParam.paperType = null
  396. majorList.value = [] // 清空专业列表
  397. courseList.value = [] // 清空课程列表
  398. }
  399. // 初始化
  400. onMounted(() => {
  401. resetForm()
  402. const id = props.id
  403. if (id && parseInt(id) !== 0) {
  404. formLoading.value = true
  405. examManagerApi
  406. .select(id)
  407. .then((re) => {
  408. Object.assign(form, {
  409. ...re,
  410. startTime: re.startTime ? dayjs(re.startTime) : null,
  411. endTime: re.endTime ? dayjs(re.endTime) : null,
  412. // 解析 gradesIds 字符串为数组
  413. gradesIdsList: re.gradesids ? re.gradesids.split(',').map((id) => parseInt(id.trim())) : []
  414. })
  415. // 如果有试卷ID,需要获取试卷名称显示
  416. if (re.paperId) {
  417. paperPage.queryParam.paperId = re.paperId
  418. // 这里可以根据需要调用接口获取试卷名称
  419. examPaperApi.select(re.paperId).then((r) => {
  420. selectedPaperName.value = r.name
  421. })
  422. }
  423. // 如果是编辑模式,需要根据已有数据加载相应的课程列表
  424. if (re.semesterId) {
  425. // 直接加载课程列表
  426. loadCourseList()
  427. }
  428. formLoading.value = false
  429. })
  430. .catch((err) => {
  431. message.error('加载考试信息失败:' + (err.message || '网络错误'))
  432. formLoading.value = false
  433. })
  434. }
  435. // 加载学期列表
  436. const semesterLoading = message.loading('正在加载学期列表...', 0)
  437. resourceAuditApi
  438. .semesterDownList()
  439. .then((res) => {
  440. if (res.code === 200) {
  441. semesterList.value = res.data
  442. } else {
  443. message.error('加载学期列表失败:' + (res.msg || '未知错误'))
  444. }
  445. })
  446. .catch((err) => {
  447. message.error('加载学期列表失败:' + (err.message || '网络错误'))
  448. })
  449. .finally(() => {
  450. semesterLoading()
  451. })
  452. loadGradesQueryList()
  453. // 加载专业
  454. // const majorLoading = message.loading('正在加载专业列表...', 0)
  455. // resourceAuditApi
  456. // .majordownList()
  457. // .then((res) => {
  458. // if (res.code === 200) {
  459. // majorList.value = res.data
  460. // } else {
  461. // message.error('加载专业列表失败:' + (res.msg || '未知错误'))
  462. // }
  463. // })
  464. // .catch((err) => {
  465. // message.error('加载专业列表失败:' + (err.message || '网络错误'))
  466. // })
  467. // .finally(() => {
  468. // majorLoading()
  469. // })
  470. })
  471. </script>
  472. <style lang="less" scoped>
  473. .app-container {
  474. padding: 24px;
  475. background: #fff;
  476. }
  477. </style>