form.vue 12 KB

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