index.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <template>
  2. <div class="p-base">
  3. <div class="m-b-base flex top-box">
  4. <div class="flex-1 p-base radius-base fill-blank m-r-base form-card">
  5. <base-form
  6. ref="formRef"
  7. :label-width="useVW(62)"
  8. :disabled="loading"
  9. :groups="groups"
  10. :rules="rules"
  11. :items="items"
  12. :model="model"
  13. ></base-form>
  14. </div>
  15. <div class="flex-1 radius-base fill-blank tree-card">
  16. <div class="flex items-center justify-between">
  17. <span>选择评卷员</span>
  18. <el-button size="small" type="primary" @click="onHandOutForceCheck">分发考核卷</el-button>
  19. </div>
  20. <div class="flex direction-column m-t-extra-base p-base radius-base fill-lighter tree-box">
  21. <el-input v-model="filterText" placeholder="输入评卷员账号或名称筛选" clearable></el-input>
  22. <div class="flex-1 m-t-base scroll-y-auto">
  23. <el-tree
  24. ref="treeRef"
  25. v-model="markerIds"
  26. show-checkbox
  27. :filter-node-method="filterTree"
  28. :data="markerTree"
  29. :props="treeProp"
  30. ></el-tree>
  31. </div>
  32. </div>
  33. </div>
  34. </div>
  35. <div class="flex direction-column radius-base fill-blank table-card">
  36. <div class="flex items-center justify-between">
  37. <span>分发进度</span>
  38. <el-button size="small" special @click="onSearch">查询</el-button>
  39. </div>
  40. <base-table class="flex-1" size="small" :columns="columns" :data="handOutProgressList">
  41. <template #empty>
  42. <empty :image-size="150"></empty>
  43. </template>
  44. </base-table>
  45. </div>
  46. </div>
  47. </template>
  48. <script setup lang="ts" name="MarkingAssess">
  49. /** 强制考核分发 */
  50. import { reactive, ref, computed, watch } from 'vue'
  51. import { ElButton, ElInput, ElTree, ElMessage } from 'element-plus'
  52. import BaseForm from '@/components/element/BaseForm.vue'
  53. import BaseTable from '@/components/element/BaseTable.vue'
  54. import Empty from '@/components/common/Empty.vue'
  55. import useForm from '@/hooks/useForm'
  56. import useFetch from '@/hooks/useFetch'
  57. import useOptions from '@/hooks/useOptions'
  58. import useVW from '@/hooks/useVW'
  59. import type { ExtractApiParams, ExtractApiResponse } from '@/api/api'
  60. import type { FormGroup, EpFormItem, EpFormRules, EpTableColumn } from 'global-type'
  61. const markerIds = ref()
  62. const model = reactive<ExtractApiParams<'handOutForceCheck'>>({
  63. subjectCode: '',
  64. mainNumber: void 0,
  65. markerIds: [],
  66. forceGroupNumber: void 0,
  67. })
  68. const { fetch: handOutForceCheck, loading } = useFetch('handOutForceCheck')
  69. const { fetch: getForceCheckGroupList, result: forceCheckGroupList } = useFetch('getForceCheckGroupList')
  70. const { fetch: getMarkerTree, result: markerTree } = useFetch('getMarkerTree')
  71. watch(model, () => {
  72. if ((model.subjectCode, model.mainNumber)) {
  73. getForceCheckGroupList({ subjectCode: model.subjectCode, mainNumber: model.mainNumber })
  74. }
  75. })
  76. watch(forceCheckGroupList, () => {
  77. if (forceCheckGroupList?.value?.length && !model.forceGroupNumber) {
  78. model.forceGroupNumber = forceCheckGroupList.value[0].forceGroupNumber
  79. }
  80. })
  81. const { subjectList, mainQuestionList, dataModel, onOptionInit, changeModelValue } = useOptions(['subject', 'question'])
  82. watch(dataModel, () => {
  83. model.subjectCode = dataModel.subject || ''
  84. model.mainNumber = dataModel.question
  85. })
  86. const { formRef, elFormRef, defineColumn, _ } = useForm()
  87. const Span12 = defineColumn(_, '', { span: 12 })
  88. const rules: EpFormRules = {
  89. subjectCode: [{ required: true, message: '请选择科目' }],
  90. mainNumber: [{ required: true, message: '请选择大题' }],
  91. }
  92. const groups: FormGroup[] = [
  93. {
  94. groupTitle: '选择大题',
  95. rowKeys: ['row-1', 'row-2'],
  96. },
  97. {
  98. groupTitle: '考核卷组',
  99. rowKeys: ['row-3'],
  100. },
  101. ]
  102. const items = computed<EpFormItem[]>(() => {
  103. return [
  104. Span12({
  105. rowKey: 'row-1',
  106. label: '科目',
  107. prop: 'subjectCode',
  108. slotType: 'select',
  109. slot: {
  110. options: subjectList.value,
  111. onChange: changeModelValue('subject'),
  112. },
  113. }),
  114. Span12({
  115. rowKey: 'row-2',
  116. label: '大题',
  117. prop: 'mainNumber',
  118. slotType: 'select',
  119. slot: {
  120. options: mainQuestionList.value,
  121. onChange: changeModelValue('question'),
  122. },
  123. }),
  124. {
  125. rowKey: 'row-3',
  126. prop: 'forceGroupNumber',
  127. slotType: 'radio',
  128. slot: {
  129. options: forceCheckGroupList?.value?.map((v) => ({
  130. label: v.forceGroupNumber,
  131. slotLabel: `第${v.forceGroupNumber}组`,
  132. })),
  133. },
  134. },
  135. ]
  136. })
  137. onOptionInit(() => {
  138. getMarkerTree({ subjectCode: model.subjectCode, mainNumber: model.mainNumber, name: '' })
  139. })
  140. type MarkerItem = ExtractArrayValue<ExtractArrayValue<ExtractApiResponse<'getMarkerTree'>>['markers']>
  141. function isMarker(x: any): x is MarkerItem {
  142. return !!x.loginName
  143. }
  144. const treeProp = {
  145. children: 'markers',
  146. label(treeNode: ExtractArrayValue<ExtractApiResponse<'getMarkerTree'>>) {
  147. if (isMarker(treeNode)) {
  148. return treeNode.name || treeNode.loginName
  149. }
  150. return `第${treeNode.markingGroupNumber}组`
  151. },
  152. } as unknown as InstanceType<typeof ElTree>['props']
  153. const filterText = ref<string>('')
  154. const treeRef = ref<InstanceType<typeof ElTree>>()
  155. const filterTree = ((value: string, data: MarkerItem) => {
  156. if (!value) return true
  157. return data.name?.includes(value) || data.loginName?.includes(value)
  158. }) as unknown as InstanceType<typeof ElTree>['filterNodeMethod']
  159. watch(filterText, () => {
  160. treeRef?.value?.filter(filterText.value)
  161. })
  162. const onHandOutForceCheck = async () => {
  163. try {
  164. if (!model.forceGroupNumber) {
  165. return ElMessage.error('请选择强制考核卷组')
  166. }
  167. const checked = treeRef?.value?.getCheckedNodes()
  168. model.markerIds = checked?.filter((v) => !!v.id).map<number>((v) => v.id) || []
  169. if (!model.markerIds?.length) {
  170. return ElMessage.error('请选择评卷员')
  171. }
  172. handOutForceCheck(model)
  173. } catch (error) {
  174. console.error(error)
  175. }
  176. }
  177. const columns: EpTableColumn<ExtractApiResponse<'getHandOutProgressList'>>[] = [
  178. { label: '科目', prop: 'subject' },
  179. { label: '大题名称', prop: 'mainQuestion' },
  180. { label: '强制卷组', prop: 'forceGroupNumber' },
  181. { label: '小组', prop: 'markingGroupNumber' },
  182. { label: '评卷员', prop: 'marker' },
  183. { label: '未评量', prop: 'pendingCount' },
  184. { label: '已评量', prop: 'finishCount' },
  185. { label: '总量', prop: 'totalCount' },
  186. ]
  187. const { fetch: getHandOutProgressList, result: handOutProgressList } = useFetch('getHandOutProgressList')
  188. const onSearch = async () => {
  189. try {
  190. const valid = await elFormRef?.value?.validate()
  191. valid && (await getHandOutProgressList(model))
  192. } catch (error) {
  193. console.error(error)
  194. }
  195. }
  196. </script>
  197. <style scoped lang="scss">
  198. .top-box {
  199. height: 395px;
  200. .tree-card {
  201. padding: 10px 20px 20px 20px;
  202. .tree-box {
  203. height: 320px;
  204. }
  205. }
  206. }
  207. .table-card {
  208. height: 365px;
  209. padding: 10px 20px 20px 20px;
  210. }
  211. .card-title {
  212. font-size: $MediumFont;
  213. }
  214. </style>