Browse Source

feat: 培训监控页面完善

chenhao 2 years ago
parent
commit
37ea2d2375

+ 4 - 0
src/api/statistics.ts

@@ -98,6 +98,10 @@ const StatisticsApi: DefineApiModule<Statistics.ApiMap> = {
   },
   /**培训监控 - 强制考核卷调卷详情 */
   getAssessMonitorDetail: '/api/train/monitor/force/detail',
+  /**培训监控 - 强制考核卷审核 */
+  putAssessMonitorPass: '/api/train/monitor/force/audit',
+  /**培训监控 - 培训卷审核 */
+  putSampleMonitorPass: '/api/train/monitor/sample/audit',
   /** 个人统计(评卷员) */
   getPersonalStatistic: {
     url: '/api/statistic/personal/result',

+ 1 - 0
src/components/shared/ImagePreview.vue

@@ -22,6 +22,7 @@ const visible = useVModel(props)
   margin-left: 70%;
   .preview-content {
     width: 100%;
+    min-width: 200px;
     max-width: 400px;
     text-align: center;
     min-height: 400px;

+ 2 - 0
src/modules/analysis/monitoring/index.vue

@@ -205,6 +205,8 @@ const viewMarkDetail = (row: ExtractArrayValue<ExtractRecordValue<ExtractApiResp
     name: 'AnalysisViewMarked',
     params: {
       markerId: row.markerId,
+    },
+    query: {
       markerName: row.markerName,
     },
   })

+ 2 - 0
src/modules/analysis/statistics/index.vue

@@ -192,6 +192,8 @@ const viewMarkDetail = (row: ExtractArrayValue<ExtractRecordValue<ExtractApiResp
     name: 'AnalysisViewMarked',
     params: {
       markerId: row.markerId,
+    },
+    query: {
       markerName: row.markerName,
     },
   })

+ 1 - 1
src/modules/marking/mark/index.vue

@@ -305,7 +305,7 @@ getMarkStatus()
 
 useFetch('clearCachedTasks')
   .fetch()
-  .then(() => getNextTask())
+  .then(() => getNextTask(true))
 </script>
 
 <style scoped lang="scss">

+ 41 - 5
src/modules/monitor/training-monitoring-detail/index.vue

@@ -19,7 +19,7 @@
       <div class="p-base radius-base fill-blank scroll-auto m-l-base table-view">
         <div class="flex items-center justify-between detail-info-table-header">
           <el-button custom-1 size="small" class="detail-info-label">
-            <span class="">培训卷共:</span>
+            <span class="">{{ PaperType }}共:</span>
             <span class="m-l-extra-small detail-info-label-num">{{ monitorDetail?.length }}</span>
           </el-button>
         </div>
@@ -35,7 +35,7 @@
     </div>
   </div>
   <image-preview v-model="previewModalVisible" :url="current?.filePath"></image-preview>
-  <mark-history-list :id="currentViewHistory?.secretNumber" v-model="visibleHistory" type="secret"></mark-history-list>
+  <mark-history-list :id="currentViewHistory?.taskId" v-model="visibleHistory"></mark-history-list>
 </template>
 
 <script setup lang="ts" name="TrainingMonitoringDetail">
@@ -52,6 +52,7 @@ import BaseTable from '@/components/element/BaseTable.vue'
 import ImagePreview from '@/components/shared/ImagePreview.vue'
 import RightButton from '@/components/shared/RightButton.vue'
 import SvgIcon from '@/components/common/SvgIcon.vue'
+import MarkHistoryList from '@/components/shared/MarkHistoryList.vue'
 
 import type { SetImgBgOption } from '@/hooks/useSetImgBg'
 import type { ExtractApiResponse } from 'api-type'
@@ -61,6 +62,10 @@ type RowType = ExtractApiResponse<'getTrainingMonitorDetail'> & { index: number
 
 const { query } = useRoute()
 
+const PaperType = computed(() => {
+  return query.stage === 'FORCE' ? '强制考核卷' : '培训卷'
+})
+
 /** 图片预览 */
 const previewModalVisible = ref<boolean>(false)
 
@@ -81,7 +86,7 @@ const {
 
 /** 刷新 */
 const onRefresh = () => {
-  console.log('刷新')
+  fetchData()
 }
 
 /** 预览试卷 */
@@ -119,8 +124,14 @@ const columns: EpTableColumn<RowType>[] = [
 const { fetch: getTrainingMonitorDetail, result: trainingMonitorDetail } = useFetch('getTrainingMonitorDetail')
 const { fetch: getAssessMonitorDetail, result: assessMonitorDetail } = useFetch('getAssessMonitorDetail')
 
+const fetchData = () => {
+  query.stage === 'FORCE'
+    ? getAssessMonitorDetail({ forceGroupMarkerId: query.forceGroupMarkerId as string })
+    : getTrainingMonitorDetail({ markerId: query.markerId as string, taskType: query.stage as SamplePaperType })
+}
+
 const monitorDetail = computed(() => {
-  return query.type === 'training' ? trainingMonitorDetail.value : assessMonitorDetail.value
+  return query.stage === 'FORCE' ? assessMonitorDetail.value : trainingMonitorDetail.value
 })
 
 const {
@@ -144,6 +155,31 @@ const imgOption = computed<SetImgBgOption>(() => {
 })
 
 const { drawing, dataUrl } = useSetImgBg(imgOption)
+
+fetchData()
 </script>
 
-<style scoped lang="scss"></style>
+<style scoped lang="scss">
+.mark-container {
+  .mark-content {
+    position: relative;
+    .preview {
+      position: absolute;
+      cursor: pointer;
+      top: 10px;
+      right: 20px;
+      font-size: 24px;
+    }
+    .next-button {
+      position: absolute;
+      right: -20px;
+      top: 300px;
+    }
+    .mark-content-paper {
+      img {
+        max-width: 100%;
+      }
+    }
+  }
+}
+</style>

+ 11 - 3
src/modules/monitor/training-monitoring/hooks/useFormFilter.ts

@@ -19,6 +19,10 @@ const useFormFilter = () => {
     diffShow: [],
   })
 
+  const diffShow = computed(() => {
+    return !!model.diffShow.length
+  })
+
   const forceGroupListParams = computed(() => {
     return {
       subjectCode: model.subjectCode,
@@ -44,7 +48,7 @@ const useFormFilter = () => {
     { deep: true }
   )
 
-  const { subjectList, mainQuestionList, groupList, dataModel, onOptionInit } = useOptions([
+  const { subjectList, mainQuestionList, groupList, dataModel, onOptionInit, changeModelValue } = useOptions([
     'subject',
     'question',
     'group',
@@ -72,14 +76,16 @@ const useFormFilter = () => {
         labelWidth: useVW(60),
         slot: {
           options: subjectList.value,
+          onChange: changeModelValue('subject'),
         },
       }),
       OneRowSpan5({
-        prop: 'questionMainNumber',
+        prop: 'mainNumber',
         label: '大题',
         slotType: 'select',
         slot: {
           options: mainQuestionList.value,
+          onChange: changeModelValue('question'),
         },
       }),
       OneRowSpan5({
@@ -88,6 +94,7 @@ const useFormFilter = () => {
         slotType: 'select',
         slot: {
           options: groupList.value,
+          onChange: changeModelValue('group'),
         },
       }),
       OneRowSpan5({
@@ -97,7 +104,7 @@ const useFormFilter = () => {
       TwoRowSpan5({
         label: '试卷类型',
         slotType: 'select',
-        prop: 'paperType',
+        prop: 'markStage',
         labelWidth: useVW(60),
         slot: {
           placeholder: '试卷类型',
@@ -132,6 +139,7 @@ const useFormFilter = () => {
     formRef,
     elFormRef,
     model,
+    diffShow,
     items,
     onOptionInit,
   }

+ 94 - 6
src/modules/monitor/training-monitoring/index.vue

@@ -8,37 +8,89 @@
       </base-form>
     </div>
     <div class="flex-1 p-base">
-      <base-table :columns="columns" :data="trainingMonitor?.data"></base-table>
+      <div class="radius-base p-base fill-blank">
+        <div v-show="hasSelected" class="flex items m-b-base">
+          <el-button size="small" type="primary" @click="onAssessPass(true)">考核通过</el-button>
+          <el-button size="small" type="primary" plain="" @click="onAssessPass(false)">考核不通过</el-button>
+          <!-- <el-button size="small" type="primary" custom-1 @click="viewPaper(false)">查看试卷</el-button> -->
+        </div>
+        <base-table
+          :columns="columns"
+          :data="trainingMonitor?.data"
+          @selection-change="onSectionChange"
+          @row-dblclick="onDbClick"
+        >
+        </base-table>
+      </div>
     </div>
   </div>
 </template>
 
-<script setup lang="ts" name="TrainingMonitoring">
+<script setup lang="tsx" name="TrainingMonitoring">
 /** 培训监控 */
 import { computed } from 'vue'
-import { ElButton } from 'element-plus'
+import { useRouter } from 'vue-router'
+import { ElButton, ElMessage } from 'element-plus'
+import { minus } from '@/utils/common'
 import BaseForm from '@/components/element/BaseForm.vue'
 import BaseTable from '@/components/element/BaseTable.vue'
 import useVW from '@/hooks/useVW'
 import useFetch from '@/hooks/useFetch'
+import useSection from '@/hooks/useSection'
 import useFormFilter from './hooks/useFormFilter'
 
 import type { ExtractApiResponse } from 'api-type'
 import type { EpTableColumn } from 'global-type'
 
-const { model, items, onOptionInit } = useFormFilter()
+type TableDataType = ExtractArrayValue<ExtractApiResponse<'getTrainingMonitor'>['data']>
 
+const { push } = useRouter()
+
+const { diffShow, model, items, onOptionInit } = useFormFilter()
+
+/** 培训监控列表 */
 const { fetch: getTrainingMonitor, result: trainingMonitor, loading } = useFetch('getTrainingMonitor')
+/** 培训卷审核 */
+const { fetch: putSampleMonitorPass } = useFetch('putSampleMonitorPass')
+/** 强制考核卷审核 */
+const { fetch: putAssessMonitorPass } = useFetch('putAssessMonitorPass')
 
-const columns = computed<EpTableColumn<ExtractApiResponse<'getTrainingMonitor'>>[]>(() => {
+const { hasSelected, selectedList, onSectionChange } = useSection<TableDataType>()
+
+const columns = computed<EpTableColumn<TableDataType>[]>(() => {
+  const standardScores = trainingMonitor?.value?.data?.[0]?.scoreList
+  const cols: EpTableColumn<TableDataType>[] =
+    trainingMonitor?.value?.header?.map((h) => ({
+      label: `${h}`,
+      width: 52,
+      formatter(row) {
+        if (!row.markerId) {
+          return `${row.scoreList[h - 1]}`
+        }
+        const score = row.scoreList[h - 1]
+        const standardScore = standardScores[h - 1]
+        return (
+          <span style={{ color: minus(score, standardScore) ? '#f00' : 'inherit' }}>
+            {diffShow.value ? minus(score, standardScore) : score}
+          </span>
+        )
+      },
+    })) || []
   return [
-    { type: 'index' },
+    {
+      type: 'selection',
+      slotName: 'xxx',
+      selectable(row) {
+        return !!row.markerId
+      },
+    },
     { label: '评卷员', prop: 'markerName' },
     { label: '状态', prop: 'status' },
     { label: '平均分', prop: 'avg' },
     { label: '标准差', prop: 'std' },
     { label: '相关系数', prop: 'xyRelate' },
     { label: '差异份数', prop: 'diffCount' },
+    ...cols,
   ]
 })
 
@@ -48,6 +100,42 @@ function onSearch() {
   getTrainingMonitor(params)
 }
 
+/** 通过/不通过 */
+const onAssessPass = async (pass: boolean) => {
+  if (!hasSelected.value) {
+    return ElMessage.warning('未勾选考核人员')
+  }
+  const selectedData = selectedList.reduce((serialize, data) => {
+    serialize[data.stage] ??= []
+    serialize[data.stage].push(data)
+    return serialize
+  }, {} as Record<string, TableDataType[]>)
+
+  const fetchList = Object.entries(selectedData).map(([stage, selected]) => {
+    const forceGroupMarkerIds = selected.map((d) => d.forceGroupMarkerId)
+    const markerIds = selected.map((d) => d.markerId)
+    return stage === 'FORCE'
+      ? putAssessMonitorPass({ forceGroupMarkerIds, pass })
+      : putSampleMonitorPass({ markerIds, markStage: stage as SamplePaperType, pass })
+  })
+  await Promise.allSettled(fetchList)
+  onSearch()
+}
+
+/** 双击跳转详情 */
+const onDbClick = (row: TableDataType) => {
+  if (row.markerId) {
+    push({
+      name: 'TrainingDetail',
+      query: {
+        stage: row.stage,
+        markerId: row.markerId,
+        forceGroupMarkerId: row.forceGroupMarkerId,
+      },
+    })
+  }
+}
+
 onOptionInit(onSearch)
 </script>
 

+ 1 - 5
src/store/main.ts

@@ -12,7 +12,6 @@ interface MainStoreState {
 interface MainStoreActions {
   getMyUserInfo: () => Promise<ExtractApiResponse<'getMyUserInfo'> | undefined>
 }
-const { fetch, loading } = useFetch('getMyUserInfo')
 
 const useMainStore = defineStore<'main', MainStoreState, Record<string, any>, MainStoreActions>('main', {
   state() {
@@ -27,10 +26,7 @@ const useMainStore = defineStore<'main', MainStoreState, Record<string, any>, Ma
   actions: {
     async getMyUserInfo() {
       try {
-        if (loading.value) {
-          return
-        }
-        this.myUserInfo = await fetch()
+        this.myUserInfo = await useFetch('getMyUserInfo').fetch()
         return this.myUserInfo
       } catch (error) {
         console.warn(error)

+ 17 - 3
types/api.d.ts

@@ -1643,6 +1643,7 @@ declare module 'api-type' {
       avg: number
       /** 差异份数 */
       diffCount: number
+      forceGroupMarkerId: number
       /** 评卷员ID */
       markerId: number
       /** 评卷员名称 */
@@ -1650,7 +1651,9 @@ declare module 'api-type' {
       /** 打分列表 */
       scoreList: number[]
       /** 状态 */
-      status: number
+      stage: 'SAMPLE_A' | 'SAMPLE_B' | 'FORCE'
+      /** 状态名称 */
+      status: string
       /** 标准差 */
       std: number
       /** 相关系数 */
@@ -1673,10 +1676,17 @@ declare module 'api-type' {
     }
 
     /**培训监控 - 培训卷调卷详情 */
-    type GetTrainingMonitorDetail = BaseDefine<{ markerId: number; taskType: 'SAMPLE_A' | 'SAMPLE_B' }, MonitorDetail[]>
+    type GetTrainingMonitorDetail = BaseDefine<{ markerId: string; taskType: SamplePaperType }, MonitorDetail[]>
 
     /**培训监控 - 强制考核卷调卷详情 */
-    type GetAssessMonitorDetail = BaseDefine<{ forceGroupMarkerId: number }, MonitorDetail[]>
+    type GetAssessMonitorDetail = BaseDefine<{ forceGroupMarkerId: string }, MonitorDetail[]>
+
+    /**培训监控 - 强制考核卷审核 */
+    type PutAssessMonitorPass = BaseDefine<{ forceGroupMarkerIds: number[]; pass: boolean }>
+
+    /**培训监控 - 培训卷审核 */
+    type PutSampleMonitorPass = BaseDefine<{ markerIds: number[]; markStage: SamplePaperType; pass: boolean }>
+
     /** 个人统计 */
     interface PersonalStatistic {
       avg: number
@@ -1802,6 +1812,10 @@ declare module 'api-type' {
       getTrainingMonitorDetail: GetTrainingMonitorDetail
       /**培训监控 - 强制考核卷调卷详情 */
       getAssessMonitorDetail: GetAssessMonitorDetail
+      /**培训监控 - 强制考核卷审核 */
+      putAssessMonitorPass: PutAssessMonitorPass
+      /**培训监控 - 培训卷审核 */
+      putSampleMonitorPass: PutSampleMonitorPass
       /** 个人统计 */
       getPersonalStatistic: GetPersonalStatistic
       /** 提取阅卷明细 */