index.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <template>
  2. <div class="flex direction-column">
  3. <div class="flex items-center fill-blank p-medium-base">
  4. <span class="subject-name">{{ subjectInfo?.name }}</span>
  5. <confirm-button
  6. class="m-l-auto"
  7. size="small"
  8. ok-text="新增"
  9. cancel-text="返回"
  10. @confirm="onAddMainQuestion"
  11. @cancel="back"
  12. ></confirm-button>
  13. </div>
  14. <div class="flex-1 p-base">
  15. <el-card shadow="never">
  16. <base-table size="small" border stripe :data="tableData" :columns="columns">
  17. <template #column-mainNumber="{ row }">
  18. <p v-if="!!row.relationMainNumber" style="display: flex; align-items: center; justify-content: center">
  19. <span>{{ row.mainNumber }}</span>
  20. <el-icon :size="16" color="#0091ff" style="margin-left: 2px">
  21. <flag />
  22. </el-icon>
  23. </p>
  24. <span v-else>{{ row.mainNumber }}</span>
  25. </template>
  26. <template #column-operation="{ row }">
  27. <el-button type="primary" link @click="onEditSubQuestion(row)">编辑</el-button>
  28. <el-popconfirm :width="'220px'" hide-icon :title="`确认删除题目?`" @confirm="onDelete(row)">
  29. <template #reference>
  30. <el-button type="primary" link>删除</el-button>
  31. </template>
  32. </el-popconfirm>
  33. <el-button v-if="row.firstMain" type="primary" link @click="onEditMainQuestion(row)">大题设置</el-button>
  34. <el-button v-if="row.firstMain" type="primary" link @click="recovery(row)">手动回收</el-button>
  35. <el-button v-if="!row.relationMainNumber" type="primary" link @click="syncTask(row)">同步任务</el-button>
  36. </template>
  37. </base-table>
  38. </el-card>
  39. </div>
  40. <base-dialog v-model="visibleSubQuestionEditor" unless :footer="false" destroy-on-close>
  41. <base-form
  42. ref="formRef"
  43. :label-width="'72px'"
  44. :model="editSubInfo"
  45. :items="items"
  46. :rules="rules"
  47. :disabled="saving"
  48. >
  49. <template #form-item-mainQuestion> {{ [editSubInfo.mainNumber, editSubInfo.mainTitle].join('-') }} </template>
  50. <template #form-item-subQuestion> {{ editSubInfo.subNumber }} </template>
  51. <el-form-item class="m-t-base">
  52. <confirm-button :loading="saving" @confirm="onSubmit" @cancel="toggleVisible(false)"></confirm-button>
  53. </el-form-item>
  54. </base-form>
  55. </base-dialog>
  56. <base-dialog v-model="showSyncTaskDialog" destroy-on-close :width="400" title="同步任务">
  57. <div class="dialog-text">
  58. 待同步任务:<span class="red">{{ syncCount?.count }}</span>
  59. </div>
  60. <div class="dialog-text">是否确认从关联大题中 <b>同步人机仲裁</b> 任务</div>
  61. <template #footer>
  62. <div class="flex justify-center">
  63. <confirm-button @confirm="syncSubmit" @cancel="showSyncTaskDialog = false"></confirm-button>
  64. </div>
  65. </template>
  66. </base-dialog>
  67. </div>
  68. </template>
  69. <script setup lang="tsx" name="SubjectStructManage">
  70. /** 科目试卷结构管理 */
  71. import { reactive, ref, computed } from 'vue'
  72. import { useRouter } from 'vue-router'
  73. import { ElButton, ElCard, ElFormItem, ElPopconfirm, ElMessage, ElIcon } from 'element-plus'
  74. import ConfirmButton from '@/components/common/ConfirmButton.vue'
  75. import BaseTable from '@/components/element/BaseTable.vue'
  76. import BaseForm from '@/components/element/BaseForm.vue'
  77. import BaseDialog from '@/components/element/BaseDialog.vue'
  78. import useFetch from '@/hooks/useFetch'
  79. import useForm from '@/hooks/useForm'
  80. import { Flag } from '@element-plus/icons-vue'
  81. import type { EpTableColumn, EpFormItem, EpFormRules } from 'global-type'
  82. import type { ExtractApiResponse } from '@/api/api'
  83. const showSyncTaskDialog = ref(false)
  84. const { back, push } = useRouter()
  85. const props = defineProps<{ id: number | string }>()
  86. const { fetch: getSubjectInfo, result: subjectInfo } = useFetch('getSubjectInfo')
  87. const { fetch: getSubQuestionList, result: subQuestionList } = useFetch('getSubQuestionList')
  88. const { fetch: getSyncCount, result: syncCount } = useFetch('getSyncCount')
  89. type SubQuestion = ExtractArrayValue<ExtractApiResponse<'getSubQuestionList'>>
  90. type WithFirstMainTag = SubQuestion & { firstMain: boolean }
  91. const tableData = computed<any>(() => {
  92. const MainMap: Record<number | string, boolean> = {}
  93. return subQuestionList.value?.map((sub) => {
  94. const mainHas = !!MainMap[sub.mainNumber]
  95. if (!mainHas) {
  96. MainMap[sub.mainNumber] = true
  97. }
  98. return { ...sub, firstMain: !mainHas }
  99. })
  100. })
  101. const columns: EpTableColumn[] = [
  102. {
  103. label: '大题号',
  104. // prop: 'mainNumber',
  105. slotName: 'mainNumber',
  106. // formatter(row: any) {
  107. // return row.relationMainNumber ? (
  108. // row.mainNumber
  109. // ) : (
  110. // <p style="display:flex;align-items:center;justify-content:center">
  111. // <span>{row.mainNumber}</span>
  112. // <el-icon>
  113. // <Flag />
  114. // </el-icon>
  115. // </p>
  116. // )
  117. // },
  118. },
  119. { label: '大题名称', prop: 'mainTitle' },
  120. { label: '小题号', prop: 'subNumber' },
  121. { label: '小题满分', prop: 'totalScore' },
  122. { label: '间隔分', prop: 'intervalScore' },
  123. { label: '操作', slotName: 'operation', width: 320 },
  124. ]
  125. if (props.id) {
  126. getSubjectInfo({ id: +props.id }).then((info) => {
  127. getSubQuestionList({ subjectCode: info.code })
  128. })
  129. }
  130. /** 新增大题 */
  131. function onAddMainQuestion() {
  132. if (subjectInfo.value.code) {
  133. push({ name: 'EditStruct', params: { subjectCode: subjectInfo.value.code } })
  134. }
  135. }
  136. /** 编辑大题 */
  137. function onEditMainQuestion(row: ExtractArrayValue<ExtractApiResponse<'getSubQuestionList'>>) {
  138. if (subjectInfo.value.code) {
  139. push({ name: 'EditStruct', params: { subjectCode: subjectInfo.value.code, mainNumber: row.mainNumber } })
  140. }
  141. }
  142. function recovery(row: any) {
  143. useFetch('recoveryTask')
  144. .fetch({ subjectCode: subjectInfo.value.code, mainNumber: row.mainNumber })
  145. .then((res) => {
  146. ElMessage.success(`回收成功`)
  147. })
  148. }
  149. const curRow = ref<any>()
  150. function syncTask(row: any) {
  151. getSyncCount({
  152. mainNumber: row.mainNumber,
  153. subjectCode: subjectInfo.value.code,
  154. }).then(() => {
  155. showSyncTaskDialog.value = true
  156. curRow.value = row
  157. })
  158. }
  159. const { fetch: syncFetch, loading: syncLoading } = useFetch('syncSubmit')
  160. const syncSubmit = () => {
  161. syncFetch({
  162. mainNumber: curRow.value.mainNumber,
  163. subjectCode: subjectInfo.value.code,
  164. }).then(() => {
  165. showSyncTaskDialog.value = false
  166. ElMessage.success(`同步成功`)
  167. })
  168. }
  169. const { fetch: editSubQuestion, loading: saving } = useFetch('editSubQuestion')
  170. const editSubInfo = reactive<Partial<ExtractArrayValue<ExtractApiResponse<'getSubQuestionList'>>>>({})
  171. const { formRef, elFormRef } = useForm()
  172. const rules = computed<EpFormRules>(() => {
  173. return {
  174. intervalScore: [{ required: true, message: '请填写间隔分' }],
  175. }
  176. })
  177. const items = computed<EpFormItem[]>(() => {
  178. return [
  179. { label: '大题', slotName: 'mainQuestion' },
  180. { label: '小题号', slotName: 'subQuestion' },
  181. {
  182. label: '间隔分',
  183. prop: 'intervalScore',
  184. slotType: 'inputNumber',
  185. slot: { placeholder: '间隔分', stepStrictly: true, step: 1 },
  186. },
  187. ]
  188. })
  189. const visibleSubQuestionEditor = ref<boolean>(false)
  190. function toggleVisible(visible: boolean) {
  191. visibleSubQuestionEditor.value = visible
  192. }
  193. /** 编辑小题 */
  194. function onEditSubQuestion(row: ExtractArrayValue<ExtractApiResponse<'getSubQuestionList'>>) {
  195. Object.assign(editSubInfo, row)
  196. toggleVisible(true)
  197. }
  198. async function onSubmit() {
  199. try {
  200. const valid = await elFormRef?.value?.validate()
  201. valid && (await editSubQuestion({ id: editSubInfo.id, intervalScore: editSubInfo.intervalScore }))
  202. ElMessage.success(`保存成功`)
  203. subjectInfo?.value?.code && getSubQuestionList({ subjectCode: subjectInfo.value.code })
  204. toggleVisible(false)
  205. } catch (error) {
  206. console.error(error)
  207. }
  208. }
  209. /** 删除 */
  210. const { fetch: deleteSubQuestion } = useFetch('deleteSubQuestion')
  211. function onDelete(row: ExtractArrayValue<ExtractApiResponse<'getSubQuestionList'>>) {
  212. row.id &&
  213. deleteSubQuestion({ id: row.id }).then(() => {
  214. subjectInfo?.value?.code && getSubQuestionList({ subjectCode: subjectInfo.value.code })
  215. })
  216. }
  217. </script>
  218. <style scoped lang="scss">
  219. .dialog-text {
  220. font-size: 14px;
  221. color: #333;
  222. line-height: 1.8;
  223. .red {
  224. color: red;
  225. font-size: 16px;
  226. font-weight: bold;
  227. }
  228. }
  229. </style>