form.vue 12 KB

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