浏览代码

现场回归之后的一些需求开发

刘洋 2 年之前
父节点
当前提交
e32ed77b8c
共有 32 个文件被更改,包括 636 次插入206 次删除
  1. 1 1
      src/App.vue
  2. 10 2
      src/components/common/ConfirmButton.vue
  3. 2 1
      src/components/shared/MarkHeader.vue
  4. 2 1
      src/components/shared/ScoringPanel.vue
  5. 23 12
      src/components/shared/ScoringPanelItem.vue
  6. 4 2
      src/components/shared/UserInfo.vue
  7. 8 15
      src/components/shared/message/Message.vue
  8. 1 1
      src/components/shared/message/MessageList.vue
  9. 4 4
      src/components/shared/message/MessageSend.vue
  10. 29 0
      src/components/shared/message/MessageWindow.vue
  11. 2 2
      src/hooks/useMarkHeader.ts
  12. 18 1
      src/hooks/useTableCheck.ts
  13. 2 1
      src/layout/main/MainHeader.vue
  14. 12 3
      src/modules/admin-role/manage/index.vue
  15. 12 2
      src/modules/admin-role/setting/index.vue
  16. 13 3
      src/modules/admin-user/bulk-add-user/index.vue
  17. 13 3
      src/modules/admin-user/edit-user/index.vue
  18. 14 2
      src/modules/admin-user/manage/hooks/useUserManageFilter.ts
  19. 23 2
      src/modules/analysis/marking-progress/components/TotalProgress.vue
  20. 106 54
      src/modules/analysis/personnel-statistics/components/StatisticsGroup.vue
  21. 84 52
      src/modules/analysis/personnel-statistics/components/StatisticsPersonnel.vue
  22. 47 6
      src/modules/analysis/personnel-statistics/hooks/useStatisticsFilter.ts
  23. 94 2
      src/modules/analysis/personnel-statistics/index.vue
  24. 7 4
      src/modules/analysis/view-marked-detail/index.vue
  25. 5 3
      src/modules/expert/assess/index.vue
  26. 17 5
      src/modules/expert/expert/index.vue
  27. 5 3
      src/modules/expert/sample/index.vue
  28. 7 5
      src/modules/marking/inquiry-result/index.vue
  29. 5 1
      src/modules/marking/inquiry/index.vue
  30. 6 4
      src/modules/monitor/system-check/index.vue
  31. 4 3
      src/modules/quality/self-check-detail/index.vue
  32. 56 6
      src/modules/quality/subjective-check/index.vue

+ 1 - 1
src/App.vue

@@ -56,7 +56,7 @@ function keyBoardScroll(e: any) {
   })
 }
 onMounted(() => {
-  document.addEventListener('keydown', keyBoardScroll)
+  // document.addEventListener('keydown', keyBoardScroll)
 })
 </script>
 

+ 10 - 2
src/components/common/ConfirmButton.vue

@@ -1,9 +1,10 @@
 <template>
   <div
-    class="flex items-center"
+    class="flex items-center confirm-button"
     :class="{
       'justify-between': props.between,
       'justify-around': props.around,
+      'justify-end': props.right,
     }"
   >
     <el-button v-bind="props.okProps" type="primary" :loading="props.loading" :size="size" @click="emit('confirm')">
@@ -29,6 +30,7 @@ const props = withDefaults(
     around?: boolean
     okProps?: Partial<ButtonProps>
     cancelProps?: Partial<ButtonProps>
+    right?: boolean
   }>(),
   {
     okText: '确认',
@@ -38,6 +40,7 @@ const props = withDefaults(
     around: false,
     okProps: void 0,
     cancelProps: void 0,
+    right: false,
   }
 )
 
@@ -46,4 +49,9 @@ const size = useSize(computed(() => props.size))
 const emit = defineEmits(['confirm', 'cancel'])
 </script>
 
-<style scoped lang="scss"></style>
+<style scoped lang="scss">
+.confirm-button {
+  &.text-right {
+  }
+}
+</style>

+ 2 - 1
src/components/shared/MarkHeader.vue

@@ -398,7 +398,8 @@ const willLogout = async () => {
       display: flex;
       align-items: center;
       .icon-item {
-        width: 45px;
+        // width: 45px;
+        padding: 0 10px;
       }
       &:first-child {
         margin-left: auto;

+ 2 - 1
src/components/shared/ScoringPanel.vue

@@ -23,6 +23,7 @@
           :toggle-modal="props.toggleModal && index === questionList.length - 1"
           :question="question"
           :large="props.large"
+          :allow-submit="allowSubmit"
           @blur="() => onBlur(index)"
           @enter="() => onEnter(index)"
           @focused="() => onFocused(index)"
@@ -138,7 +139,7 @@ const questionList = computed(() => {
 })
 
 const allowSubmit = computed(() => {
-  let filterArr = scoreValues.value.filter((score) => score != undefined)
+  let filterArr = scoreValues.value.filter((score) => score != undefined && score !== '')
   // return scoreValues.value?.length === questionList.value?.length
   return filterArr.length === questionList.value?.length
 })

+ 23 - 12
src/components/shared/ScoringPanelItem.vue

@@ -28,17 +28,26 @@
         {{ scoreItem }}
       </div>
       <toggle-dialog-render>
-        <div class="flex items-center score-result" :class="{ 'score-valid-fail': scoreValidFail }">
-          <div class="flex-1 text-center">给分</div>
-          <input
-            ref="refInput1"
-            class="flex-1 text-center score-input score-num"
-            :value="currentScore"
-            @focus="onInputFocus"
-            @blur="onBlur"
-            @keydown="onValidScoreDebounce"
-            @input="scoreChange"
-          />
+        <div class="flex items-center">
+          <div class="flex items-center score-result" :class="{ 'score-valid-fail': scoreValidFail }">
+            <div class="flex-1 text-center">给分</div>
+            <input
+              ref="refInput1"
+              class="flex-1 text-center score-input score-num"
+              :value="currentScore"
+              @focus="onInputFocus"
+              @blur="onBlur"
+              @keydown="onValidScoreDebounce"
+              @input="scoreChange"
+            />
+          </div>
+          <el-button
+            :disabled="!props.allowSubmit"
+            size="small"
+            type="primary"
+            style="min-width: 44px; margin-left: 5px; margin-bottom: 8px"
+            >确定</el-button
+          >
         </div>
       </toggle-dialog-render>
     </div>
@@ -77,6 +86,7 @@ import useVModel from '@/hooks/useVModel'
 import { getNumbers } from '@/utils/common'
 import { debounce } from 'lodash-es'
 import { useRoute } from 'vue-router'
+import { ElButton } from 'element-plus'
 
 const route = useRoute()
 const isMarkerPage = computed(() => {
@@ -104,6 +114,7 @@ const props = withDefaults(
     scoreValidFail: boolean | undefined
     question: QuestionInfo
     large?: boolean
+    allowSubmit: boolean
   }>(),
   { modal: false, toggleModal: true, score: void 0, scoreValidFail: false, large: true }
 )
@@ -306,7 +317,7 @@ const onToggleClick = () => {
     align-items: center;
     // padding: 12px 20px;
     padding: 2px 20px;
-    height: 50px;
+    // height: 50px;
   }
 
   // .dialog-name {

+ 4 - 2
src/components/shared/UserInfo.vue

@@ -6,14 +6,14 @@
           <svg-icon name="rounduser"></svg-icon>
         </p>
         <div class="flex-1 user-right p-l-base">
-          <p class="u-name">{{ info.userName }}</p>
+          <!-- <p class="u-name">{{ info.userName }}</p> -->
           <p class="u-role">{{ info.roleName }}</p>
         </div>
       </div>
     </template>
     <div class="user-box" @click="visibleUpdatePwd = true">
       <svg-icon name="user" style="font-size: 17px; position: relative; bottom: -2px"></svg-icon>
-      <p class="tip">个人</p>
+      <p class="tip">{{ info.userName }}</p>
     </div>
   </el-tooltip>
 
@@ -50,6 +50,8 @@ const visibleUpdatePwd = ref<boolean>(false)
   .tip {
     font-size: 12px;
     margin-top: 2px;
+    height: 17px;
+    line-height: 17px;
   }
 }
 .user-info {

+ 8 - 15
src/components/shared/message/Message.vue

@@ -1,6 +1,6 @@
 <template>
   <message-component>
-    <div ref="messageIcon" v-bind="$attrs" class="message-icon">
+    <div ref="messageIcon" v-bind="$attrs" class="message-icon" @click="openMsgDialog">
       <span v-show="unReadMessages?.newCount" class="un-read-num">
         {{ (unReadMessages?.newCount || 0) > 99 ? '99+' : unReadMessages?.newCount }}
       </span>
@@ -14,7 +14,7 @@
         </div>
       </el-tooltip>
     </div>
-    <el-popover
+    <!-- <el-popover
       placement="bottom-start"
       :width="'400px'"
       :show-arrow="false"
@@ -23,11 +23,7 @@
       virtual-triggering
     >
       <div class="message-popover-content">
-        <div class="title">
-          <!-- <span class="unread-count">{{ unReadMessages?.newCount || 0 }}</span>
-          <span>条消息</span> -->
-          最近消息
-        </div>
+        <div class="title">最近消息</div>
         <div class="message-list">
           <div
             v-for="message in unReadMessages?.messages?.slice(0, 2)"
@@ -35,13 +31,7 @@
             class="message-row"
             @click="onReceiveMessage(message)"
           >
-            <!-- <div class="message-send-user">
-              <div class="user-name" :title="message.sendUserName">{{ message.sendUserName }}</div>
-              <div class="message-time">{{ dayjs(message.sendTime).format('HH:mm') }}</div>
-            </div>
-            <pre class="flex-1 message-content" v-html="message.content"></pre> -->
             <div class="send-user truncate">{{ message.sendUserName }}</div>
-            <!-- <div class="send-content truncate">{{ message.content }}</div> -->
             <div class="send-content truncate">{{ transContent(message.content) }}</div>
           </div>
         </div>
@@ -54,7 +44,7 @@
           @cancel="onSendMessage"
         ></confirm-button>
       </div>
-    </el-popover>
+    </el-popover> -->
     <message-window
       v-model="visibleMessageWindow"
       v-model:type="messageWindowType"
@@ -164,7 +154,10 @@ const replyUserId = useVModel(props, 'replyUserId')
 
 /** 发送/查看消息Modal */
 const visibleMessageWindow = ref<boolean>(false)
-
+const openMsgDialog = () => {
+  messageWindowType.value = 'view'
+  visibleMessageWindow.value = true
+}
 watch(
   () => props.messageVisible,
   () => {

+ 1 - 1
src/components/shared/message/MessageList.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="flex radius-base message-list-modal">
+  <div class="flex radius-base overflow-hidden message-list-modal">
     <slot></slot>
     <!-- <div class="message-list p-base scroll-y-auto">
       <template v-if="!!(messageList || []).length">

+ 4 - 4
src/components/shared/message/MessageSend.vue

@@ -3,7 +3,7 @@
     <template v-if="!!replyUserId">
       <slot></slot>
     </template>
-    <div v-show="showCheckUser" class="flex direction-column p-base fill-lighter tree-box">
+    <div class="flex direction-column p-base fill-lighter tree-box">
       <el-input v-model="filterText" placeholder="输入评卷员账号或名称筛选" clearable></el-input>
       <div class="flex-1 m-t-base scroll-y-auto">
         <el-tree
@@ -27,9 +27,9 @@
                 <span class="split-names">{{ viewCheckedUser }}</span>
                 <span v-if="checkedUsers.length > 3">共计{{ checkedUsers.length }}个人员</span>
               </p>
-              <el-button v-if="!replyUserId" class="m-l-base" size="small" type="primary" @click="toggleCheckUser">
+              <!-- <el-button v-if="!replyUserId" class="m-l-base" size="small" type="primary" @click="toggleCheckUser">
                 选择收件人
-              </el-button>
+              </el-button> -->
             </span>
           </div>
           <div class="grid pointer m-l-auto close-icon" @click="$emit('close')">
@@ -110,7 +110,7 @@ const replyUserName = useVModel(props, 'replyUserName')
 const previewModalVisible = ref<boolean>(false)
 
 /** 显示收件人tree */
-const showCheckUser = ref<boolean>(false)
+const showCheckUser = ref<boolean>(true)
 
 /** 消息内容 */
 const messageContent = ref<string>('')

+ 29 - 0
src/components/shared/message/MessageWindow.vue

@@ -1,5 +1,9 @@
 <template>
   <base-dialog v-model="visible" unless canoverflow :footer="false" class="message-dialog" destroy-on-close>
+    <div class="left-tabs-box">
+      <div class="left-tab-item" :class="{ active: modalType === 'view' }" @click="modalType = 'view'">消息</div>
+      <div class="left-tab-item" :class="{ active: modalType === 'send' }" @click="modalType = 'send'">联系人</div>
+    </div>
     <component
       :is="MessageWindowContent"
       :reply-user-id="replyUserId"
@@ -83,6 +87,31 @@ const leftMsgClick = (msg: any) => {
 .message-dialog {
   background-color: transparent;
   box-shadow: none;
+  position: relative;
+  .left-tabs-box {
+    position: absolute;
+    width: 50px;
+    height: 160px;
+    left: -50px;
+    top: 6px;
+    .left-tab-item {
+      background-color: #fafafa;
+      height: 50%;
+      border-top-left-radius: 6px;
+      border-bottom-left-radius: 6px;
+      padding: 0 15px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      text-align: center;
+      font-size: 14px;
+      cursor: pointer;
+      &.active {
+        background-color: var(--el-color-primary-light-3);
+        color: #fff;
+      }
+    }
+  }
   .el-dialog__body {
     padding: 0 !important;
   }

+ 2 - 2
src/hooks/useMarkHeader.ts

@@ -82,7 +82,7 @@ const useMarkHeader = () => {
     } else if (e.key === 'F12') {
       e.preventDefault()
       bus.emit('mark-method-toggle')
-    } else if (e.code === 'Space') {
+    } else if (e.code === 'Space' || e.key === 'ArrowUp' || e.key === 'ArrowDown') {
       if (e.target.tagName === 'INPUT' || (e.target.className || '').includes('contenteditable-ele')) {
         return false
       } else {
@@ -92,7 +92,7 @@ const useMarkHeader = () => {
         } else {
           const pWrap = getParentNodeIsScroll(imgWrap)
           let t = (pWrap as any).scrollTop
-          t += 100
+          t += e.key === 'ArrowUp' ? -100 : 100
           pWrap.scrollTo({
             left: 0,
             top: t,

+ 18 - 1
src/hooks/useTableCheck.ts

@@ -1,4 +1,4 @@
-import { ref, computed, watch, nextTick, unref } from 'vue'
+import { ref, computed, watch, nextTick, unref, onMounted, onBeforeUnmount } from 'vue'
 import { isDefine, toggleClass } from '@/utils/common'
 
 import type { Ref, ShallowRef, UnwrapRef } from 'vue'
@@ -89,8 +89,25 @@ const useTableCheck = <T extends TableDataType<InputDataType>>(data: T, auto = t
   }
   const nextRow = () => {
     elTableRef?.value?.setCurrentRow(tableData.value[((current.value?.index || 0) + 1) % tableData.value.length])
+    elTableRef?.value?.scrollTo(0, (current.value?.index || 0) * 36)
   }
+  const arrowDownToNextRow = (e: any) => {
+    if (e.target.tagName === 'INPUT' || (e.target.className || '').includes('contenteditable-ele')) {
+      return false
+    }
+    // e.preventDefault()
 
+    if (e.key === 'ArrowDown') {
+      e.preventDefault()
+      nextRow()
+    }
+  }
+  onMounted(() => {
+    document.addEventListener('keydown', arrowDownToNextRow)
+  })
+  onBeforeUnmount(() => {
+    document.removeEventListener('keydown', arrowDownToNextRow)
+  })
   return {
     tableRef,
     elTableRef,

+ 2 - 1
src/layout/main/MainHeader.vue

@@ -110,7 +110,8 @@ const mainStore = useMainStore()
         padding-left: 10px;
       }
       .icon-item {
-        width: 45px;
+        // width: 45px;
+        padding: 0 10px;
       }
     }
     .close-icon {

+ 12 - 3
src/modules/admin-role/manage/index.vue

@@ -23,14 +23,23 @@ import { useRouter } from 'vue-router'
 import { ElButton, ElCard } from 'element-plus'
 import BaseSelect from '@/components/element/BaseSelect.vue'
 import BaseTable from '@/components/element/BaseTable.vue'
-import { ROLE_OPTION } from '@/constants/dicts'
+// import { ROLE_OPTION } from '@/constants/dicts'
 import useFetch from '@/hooks/useFetch'
 
 import type { ExtractApiResponse } from '@/api/api'
 import type { EpTableColumn } from 'global-type'
-
+const ROLE_OPTION = ref<any[]>([])
 const { push } = useRouter()
-
+useFetch('getRoleList')
+  .fetch()
+  .then((res: any) => {
+    ROLE_OPTION.value = res.map((item: any) => {
+      return {
+        value: item.code,
+        label: item.roleName,
+      }
+    })
+  })
 const role = ref<ROLE>()
 
 const columns: EpTableColumn[] = [

+ 12 - 2
src/modules/admin-role/setting/index.vue

@@ -18,7 +18,7 @@
 /** 角色权限管理 */
 import { computed, nextTick, ref, watch } from 'vue'
 import { ElButton, ElTree, ElMessage } from 'element-plus'
-import { ROLE_OPTION } from '@/constants/dicts'
+// import { ROLE_OPTION } from '@/constants/dicts'
 import useFetch from '@/hooks/useFetch'
 import useMainLayoutStore from '@/store/layout'
 import BaseSelect from '@/components/element/BaseSelect.vue'
@@ -29,7 +29,17 @@ import type { ExtractApiResponse } from '@/api/api'
 type MenuItem = MainLayoutStore.MenuItem
 
 type MenuItemWithId = MenuItem & { id: number }
-
+const ROLE_OPTION = ref<any[]>([])
+useFetch('getRoleList')
+  .fetch()
+  .then((res: any) => {
+    ROLE_OPTION.value = res.map((item: any) => {
+      return {
+        value: item.code,
+        label: item.roleName,
+      }
+    })
+  })
 const props = defineProps<{ role: ROLE }>()
 
 const mainLayoutStore = useMainLayoutStore()

+ 13 - 3
src/modules/admin-user/bulk-add-user/index.vue

@@ -12,7 +12,7 @@
 
 <script setup lang="ts" name="BulkAddUser">
 /** 批量新增用户 */
-import { reactive, watch, nextTick, unref, computed } from 'vue'
+import { reactive, watch, nextTick, unref, computed, ref } from 'vue'
 import { useRouter } from 'vue-router'
 import { ElCard, ElFormItem, ElMessage } from 'element-plus'
 import BaseForm from '@/components/element/BaseForm.vue'
@@ -22,11 +22,21 @@ import useForm from '@/hooks/useForm'
 import useVW from '@/hooks/useVW'
 import useOptions from '@/hooks/useOptions'
 import useMainStore from '@/store/main'
-import { StatusMap, ROLE_OPTION } from '@/constants/dicts'
+import { StatusMap } from '@/constants/dicts'
 
 import type { ExtractApiParams } from '@/api/api'
 import type { EpFormItem, EpFormRules, FormGroup } from 'global-type'
-
+const ROLE_OPTION = ref<any[]>([])
+useFetch('getRoleList')
+  .fetch()
+  .then((res: any) => {
+    ROLE_OPTION.value = res.map((item: any) => {
+      return {
+        value: item.code,
+        label: item.roleName,
+      }
+    })
+  })
 const { back } = useRouter()
 
 const mainStore = useMainStore()

+ 13 - 3
src/modules/admin-user/edit-user/index.vue

@@ -12,7 +12,7 @@
 
 <script setup lang="ts" name="EditUser">
 /** 新增/编辑用户 */
-import { reactive, watch, nextTick, unref, computed } from 'vue'
+import { reactive, watch, nextTick, unref, computed, ref } from 'vue'
 import { useRouter } from 'vue-router'
 import { ElCard, ElFormItem, ElMessage } from 'element-plus'
 import BaseForm from '@/components/element/BaseForm.vue'
@@ -22,11 +22,21 @@ import useForm from '@/hooks/useForm'
 import useVW from '@/hooks/useVW'
 import useOptions from '@/hooks/useOptions'
 import useMainStore from '@/store/main'
-import { StatusMap, ROLE_OPTION } from '@/constants/dicts'
+import { StatusMap } from '@/constants/dicts'
 
 import type { ExtractApiParams } from '@/api/api'
 import type { EpFormItem, EpFormRules } from 'global-type'
-
+const ROLE_OPTION = ref<any[]>([])
+useFetch('getRoleList')
+  .fetch()
+  .then((res: any) => {
+    ROLE_OPTION.value = res.map((item: any) => {
+      return {
+        value: item.code,
+        label: item.roleName,
+      }
+    })
+  })
 const props = defineProps<{ id?: string | number }>()
 
 const isEdit = !!props.id

+ 14 - 2
src/modules/admin-user/manage/hooks/useUserManageFilter.ts

@@ -1,12 +1,24 @@
 import { ref, reactive, computed, watch } from 'vue'
-import { ROLE_OPTION, StatusMap } from '@/constants/dicts'
+import { StatusMap } from '@/constants/dicts'
 import useForm from '@/hooks/useForm'
 import useOptions, { DataModel } from '@/hooks/useOptions'
 import useMainStore from '@/store/main'
+import useFetch from '@/hooks/useFetch'
 import type { EpFormItem, InstanceForm, EpFormRows } from 'global-type'
 import type { ExtractMultipleApiParams } from '@/api/api'
 
 const useUserManageFilter = () => {
+  const ROLE_OPTION = ref<any[]>([])
+  useFetch('getRoleList')
+    .fetch()
+    .then((res: any) => {
+      ROLE_OPTION.value = res.map((item: any) => {
+        return {
+          value: item.code,
+          label: item.roleName,
+        }
+      })
+    })
   const mainStore = useMainStore()
   const { subjectList, mainQuestionList, changeModelValue, dataModel } = useOptions(['subject', 'question'])
 
@@ -71,7 +83,7 @@ const useUserManageFilter = () => {
       prop: 'role',
       slot: {
         placeholder: '用户角色',
-        options: ROLE_OPTION.filter((option) => {
+        options: ROLE_OPTION.value.filter((option) => {
           return !(mainStore.loginInfo?.role === 'CHIEF' && option.value === 'CHIEF')
         }),
         clearable: true,

+ 23 - 2
src/modules/analysis/marking-progress/components/TotalProgress.vue

@@ -69,25 +69,46 @@ const { provideInitOption } = useVueECharts()
 
 provideInitOption({ renderer: 'svg' })
 const canLoadTopChart = ref(false)
-const { subjectList, dataModel, changeModelValue } = useOptions(['subject'])
+// const { subjectList, dataModel, changeModelValue } = useOptions(['subject'])
 
-const model = reactive({
+const { subjectList, mainQuestionList, changeModelValue, dataModel } = useOptions(['subject', 'question'])
+
+const model = reactive<any>({
   subjectCode: dataModel.subject || '',
+  //todo
+  mainNumber: null,
 })
 watch(
   dataModel,
   () => {
     model.subjectCode = dataModel.subject || ''
+    model.mainNumber = dataModel.question || ''
   },
   { deep: true, immediate: true }
 )
 
 const items = computed<EpFormItem[]>(() => [
   {
+    labelWidth: '52px',
     label: '科目',
     slotType: 'select',
     prop: 'subjectCode',
     slot: { options: subjectList.value, onChange: changeModelValue('subject'), disabled: true },
+    rowKey: 'row-1',
+    colProp: { span: 5 },
+  },
+  {
+    labelWidth: '52px',
+    slotType: 'select',
+    label: '大题',
+    prop: 'mainNumber',
+    slot: {
+      placeholder: '选择大题',
+      options: mainQuestionList.value,
+      onChange: changeModelValue('question'),
+      disabled: true,
+    },
+    rowKey: 'row-1',
     colProp: { span: 5 },
   },
 ])

+ 106 - 54
src/modules/analysis/personnel-statistics/components/StatisticsGroup.vue

@@ -26,7 +26,7 @@
   </div> -->
 </template>
 
-<script setup lang="ts" name="StatisticsGroup">
+<script setup lang="tsx" name="StatisticsGroup">
 /** 人员数据统计-按小组 */
 import { watch, computed, ref } from 'vue'
 import BaseTable from '@/components/element/BaseTable.vue'
@@ -42,6 +42,8 @@ import type { EpTableColumn } from 'global-type'
 const props = defineProps<{
   data: ExtractApiResponse<'getStatisticsByGroup'>
   params: ExtractApiParams<'getStatisticsByGroup'> & { expand: boolean }
+  filterColumns?: any
+  result?: any
 }>()
 const rowClassName = (obj: any) => {
   if (obj.row.markingGroupNumber === 0) {
@@ -49,63 +51,113 @@ const rowClassName = (obj: any) => {
   }
 }
 
-const columns: EpTableColumn<ExtractArrayValue<ExtractApiResponse<'getStatisticsByGroup'>>>[] = [
-  {
-    label: '小组',
-    prop: 'markingGroupNumber',
-    width: 60,
-    fixed: 'left',
-    formatter(row: any) {
-      return row.markingGroupNumber === 0 ? '全部' : `${row.markingGroupNumber}`
+const columns = computed(() => {
+  return [
+    {
+      label: '小组',
+      prop: 'markingGroupNumber',
+      width: 60,
+      fixed: 'left',
+      formatter(row: any) {
+        return row.markingGroupNumber === 0 ? '全部' : `${row.markingGroupNumber}`
+      },
     },
-  },
-  {
-    label: '评卷员',
-    prop: 'markerName',
-    minWidth: 84,
-    fixed: 'left',
-    slotName: 'marker',
-    formatter(row: any) {
-      return row.markingGroupNumber === 0 ? '全体' : `第${row.markingGroupNumber}组`
+    {
+      label: '评卷员',
+      prop: 'markerName',
+      minWidth: 84,
+      fixed: 'left',
+      slotName: 'marker',
+      formatter(row: any) {
+        return row.markingGroupNumber === 0 ? '全体' : `第${row.markingGroupNumber}组`
+      },
     },
-  },
-  { align: 'center', label: '评卷份数', prop: 'markingPaperCount', minWidth: 92 },
-  { align: 'center', label: '平均分', prop: 'avg', minWidth: 80 },
-  { align: 'center', label: '相关系数', prop: 'xyRelate', minWidth: 90 },
-  { align: 'center', label: '标准差', prop: 'std', minWidth: 80 },
-  { align: 'center', label: '综合系数', prop: 'integration', minWidth: 90 },
+    { align: 'center', label: '评卷份数', prop: 'markingPaperCount', minWidth: 92 },
+    { align: 'center', label: '平均分', prop: 'avg', minWidth: 80 },
+    {
+      align: 'center',
+      label: '相关系数',
+      prop: 'xyRelate',
+      minWidth: 90,
+      formatter(row: any) {
+        let compareTarget = tableData.value.find((item: any) => item.markingGroupNumber == 0)
+        if (compareTarget) {
+          return row.xyRelate < compareTarget.xyRelate ? <span style="color:red">{row.xyRelate}</span> : row.xyRelate
+        } else {
+          return row.xyRelate
+        }
+      },
+    },
+    { align: 'center', label: '标准差', prop: 'std', minWidth: 80 },
+    { align: 'center', label: '综合系数', prop: 'integration', minWidth: 90 },
 
-  // { align: 'center', label: '评卷份数', prop: 'markingPaperCount', width: 92 },
-  { align: 'center', label: '当日可阅', prop: 'markDayCount', minWidth: 92 },
-  { align: 'center', label: '剩余可阅', prop: 'todoMarkDayCount', minWidth: 92 },
-  { align: 'center', label: '客观题0分量', prop: 'objectiveZero', minWidth: 120 },
-  { align: 'center', label: '客观平均分', prop: 'objectiveAvg', minWidth: 110 },
-  { align: 'center', label: '客观标准差', prop: 'objectiveStd', minWidth: 104 },
-  {
-    align: 'center',
-    label: '重评/待确认',
-    prop: 'reMarkUnConfirmCount',
-    minWidth: 120,
-    formatter(row: any) {
-      return `${row.reMarkCount}/${row.reMarkUnConfirmCount}`
+    // { align: 'center', label: '评卷份数', prop: 'markingPaperCount', width: 92 },
+    { align: 'center', label: '当日可阅', prop: 'markDayCount', minWidth: 92 },
+    { align: 'center', label: '剩余可阅', prop: 'todoMarkDayCount', minWidth: 92 },
+    { align: 'center', label: '客观题0分量', prop: 'objectiveZero', minWidth: 120 },
+    { align: 'center', label: '客观平均分', prop: 'objectiveAvg', minWidth: 110 },
+    { align: 'center', label: '客观标准差', prop: 'objectiveStd', minWidth: 104 },
+    {
+      align: 'center',
+      label: '重评/待确认',
+      prop: 'reMarkUnConfirmCount',
+      minWidth: 120,
+      formatter(row: any) {
+        return `${row.reMarkCount}/${row.reMarkUnConfirmCount}`
+      },
     },
-  },
-  { align: 'center', label: '抽查量', prop: 'checkCount', minWidth: 90 },
-  { align: 'center', label: '抽查改正量', prop: 'checkCorrectCount', minWidth: 110 },
-  // { align: 'center', label: '相关系数', prop: 'xyRelate', width: 90 },
-  // { align: 'center', label: '平均分', prop: 'avg', width: 80 },
-  // { align: 'center', label: '标准差', prop: 'std', width: 80 },
-  { align: 'center', label: '近5分钟最高分', prop: 'scoreTop', minWidth: 124 },
-  { align: 'center', label: '近5分钟最低分', prop: 'scoreLow', minWidth: 124 },
-  { align: 'center', label: '近5分钟客主比', prop: 'objSubRate', minWidth: 124 },
-  { align: 'center', label: '平均客主比', prop: 'objSubAvgRate', minWidth: 110 },
-  { align: 'center', label: '在线', prop: 'online', minWidth: 72 },
-  { align: 'center', label: '状态', prop: 'markingStatus', minWidth: 100 },
-  { align: 'center', label: '速度', prop: 'markingRate', minWidth: 66 },
-  // { align: 'center', label: '综合系数', prop: 'integration', width: 90 },
-].map((col: any) => {
-  if (!['小组', '重评/待确认'].includes(col.label)) col.sortable = 'custom'
-  return col
+    { align: 'center', label: '抽查量', prop: 'checkCount', minWidth: 90 },
+    { align: 'center', label: '抽查改正量', prop: 'checkCorrectCount', minWidth: 110 },
+    // { align: 'center', label: '相关系数', prop: 'xyRelate', width: 90 },
+    // { align: 'center', label: '平均分', prop: 'avg', width: 80 },
+    // { align: 'center', label: '标准差', prop: 'std', width: 80 },
+    { align: 'center', label: '近5分钟最高分', prop: 'scoreTop', minWidth: 124 },
+    { align: 'center', label: '近5分钟最低分', prop: 'scoreLow', minWidth: 124 },
+    { align: 'center', label: '近5分钟客主比', prop: 'objSubRate', minWidth: 124 },
+    { align: 'center', label: '平均客主比', prop: 'objSubAvgRate', minWidth: 110 },
+    {
+      align: 'center',
+      label: '在线',
+      prop: 'online',
+      minWidth: 72,
+      formatter(row: any) {
+        if (row.markingGroupNumber === 0) {
+          return (
+            sortTableData.value
+              .reduce((ar, item: any) => {
+                return [...ar, ...(item.markerDetails || [])]
+              }, [])
+              .reduce((num: number, item: any) => {
+                return num + (item.online ? 1 : 0)
+              }, 0) + '人'
+          )
+        } else {
+          let { markingGroupNumber } = row
+          let targetGroup = sortTableData.value.find((item: any) => item.markingGroupNumber === markingGroupNumber)
+          return (
+            (targetGroup.markerDetails || []).reduce((num: number, item: any) => {
+              return num + (item.online ? 1 : 0)
+            }, 0) + '人'
+          )
+        }
+      },
+    },
+    {
+      align: 'center',
+      label: '状态',
+      prop: 'markingStatus',
+      minWidth: 100,
+    },
+    { align: 'center', label: '速度', prop: 'markingRate', minWidth: 66 },
+    // { align: 'center', label: '综合系数', prop: 'integration', width: 90 },
+  ]
+    .map((col: any) => {
+      if (!['小组', '重评/待确认'].includes(col.label)) col.sortable = 'custom'
+      return col
+    })
+    .filter((col: any) => {
+      return props.filterColumns ? props.filterColumns.indexOf(col.prop) > -1 : col
+    })
 })
 
 const data = computed(() => {

+ 84 - 52
src/modules/analysis/personnel-statistics/components/StatisticsPersonnel.vue

@@ -92,6 +92,8 @@ const rightClick = () => {
 const props = defineProps<{
   data: ExtractApiResponse<'getStatisticsByGroup'>
   params: ExtractApiParams<'getStatisticsByGroup'> & { expand: boolean }
+  filterColumns?: any
+  result?: any
 }>()
 const rowClassName = (obj: any) => {
   if (obj.row.markingGroupNumber === 0) {
@@ -103,63 +105,93 @@ const setReplyUserId = inject<(id: number) => void>('setReplyUserId')
 
 const { push } = useRouter()
 
-const columns: EpTableColumn<ExtractArrayValue<ExtractApiResponse<'getStatisticsByGroup'>>>[] = [
-  {
-    label: '小组',
-    prop: 'markingGroupNumber',
-    width: 60,
-    fixed: 'left',
-    formatter(row: any) {
-      return row.markingGroupNumber === 0 ? '全部' : `第${row.markingGroupNumber}组`
+const columns = computed(() => {
+  return [
+    {
+      label: '小组',
+      prop: 'markingGroupNumber',
+      width: 60,
+      fixed: 'left',
+      formatter(row: any) {
+        return row.markingGroupNumber === 0 ? '全部' : `第${row.markingGroupNumber}组`
+      },
     },
-  },
-  { label: '评卷员', prop: 'markerName', minWidth: 84, slotName: 'marker', fixed: 'left' },
+    { label: '评卷员', prop: 'markerName', minWidth: 84, slotName: 'marker', fixed: 'left' },
 
-  { align: 'center', label: '评卷份数', prop: 'markingPaperCount', minWidth: 92 },
-  { align: 'center', label: '平均分', prop: 'avg', minWidth: 80 },
-  { align: 'center', label: '相关系数', prop: 'xyRelate', minWidth: 90 },
-  { align: 'center', label: '标准差', prop: 'std', minWidth: 80 },
-  { align: 'center', label: '综合系数', prop: 'integration', minWidth: 90 },
+    { align: 'center', label: '评卷份数', prop: 'markingPaperCount', minWidth: 92 },
+    { align: 'center', label: '平均分', prop: 'avg', minWidth: 80 },
+    {
+      align: 'center',
+      label: '相关系数',
+      prop: 'xyRelate',
+      minWidth: 90,
+      formatter(row: any) {
+        let r = props.result || []
+        const { markingGroupNumber } = row
+        let compareTarget = r.find((item: any) => item.markingGroupNumber == markingGroupNumber)
+        if (compareTarget && markingGroupNumber) {
+          return row.xyRelate < compareTarget.xyRelate ? <span style="color:red">{row.xyRelate}</span> : row.xyRelate
+        } else {
+          return row.xyRelate
+        }
+      },
+    },
+    { align: 'center', label: '标准差', prop: 'std', minWidth: 80 },
+    { align: 'center', label: '综合系数', prop: 'integration', minWidth: 90 },
 
-  // { align: 'center', label: '评卷份数', prop: 'markingPaperCount', width: 92 },
-  { align: 'center', label: '当日可阅', prop: 'markDayCount', minWidth: 92 },
-  { align: 'center', label: '剩余可阅', prop: 'todoMarkDayCount', minWidth: 92 },
-  { align: 'center', label: '客观题0分量', prop: 'objectiveZero', minWidth: 120 },
-  { align: 'center', label: '客观平均分', prop: 'objectiveAvg', minWidth: 110 },
-  { align: 'center', label: '客观标准差', prop: 'objectiveStd', minWidth: 104 },
-  {
-    align: 'center',
-    label: '重评/待确认',
-    prop: 'reMarkUnConfirmCount',
-    minWidth: 120,
-    formatter(row: any) {
-      return `${row.reMarkCount}/${row.reMarkUnConfirmCount}`
+    // { align: 'center', label: '评卷份数', prop: 'markingPaperCount', width: 92 },
+    { align: 'center', label: '当日可阅', prop: 'markDayCount', minWidth: 92 },
+    { align: 'center', label: '剩余可阅', prop: 'todoMarkDayCount', minWidth: 92 },
+    { align: 'center', label: '客观题0分量', prop: 'objectiveZero', minWidth: 120 },
+    { align: 'center', label: '客观平均分', prop: 'objectiveAvg', minWidth: 110 },
+    { align: 'center', label: '客观标准差', prop: 'objectiveStd', minWidth: 104 },
+    {
+      align: 'center',
+      label: '重评/待确认',
+      prop: 'reMarkUnConfirmCount',
+      minWidth: 120,
+      formatter(row: any) {
+        return `${row.reMarkCount}/${row.reMarkUnConfirmCount}`
+      },
     },
-  },
-  { align: 'center', label: '抽查量', prop: 'checkCount', minWidth: 90 },
-  { align: 'center', label: '抽查改正量', prop: 'checkCorrectCount', minWidth: 110 },
-  // { align: 'center', label: '相关系数', prop: 'xyRelate', width: 90 },
-  // { align: 'center', label: '平均分', prop: 'avg', width: 80 },
-  // { align: 'center', label: '标准差', prop: 'std', width: 80 },
-  { align: 'center', label: '近5分钟最高分', prop: 'scoreTop', minWidth: 124 },
-  { align: 'center', label: '近5分钟最低分', prop: 'scoreLow', minWidth: 124 },
-  { align: 'center', label: '近5分钟客主比', prop: 'objSubRate', minWidth: 124 },
-  { align: 'center', label: '平均客主比', prop: 'objSubAvgRate', minWidth: 110 },
-  {
-    align: 'center',
-    label: '在线',
-    prop: 'online',
-    minWidth: 72,
-    formatter(row: any) {
-      return row.online ? '在线' : '离线'
+    { align: 'center', label: '抽查量', prop: 'checkCount', minWidth: 90 },
+    { align: 'center', label: '抽查改正量', prop: 'checkCorrectCount', minWidth: 110 },
+    // { align: 'center', label: '相关系数', prop: 'xyRelate', width: 90 },
+    // { align: 'center', label: '平均分', prop: 'avg', width: 80 },
+    // { align: 'center', label: '标准差', prop: 'std', width: 80 },
+    { align: 'center', label: '近5分钟最高分', prop: 'scoreTop', minWidth: 124 },
+    { align: 'center', label: '近5分钟最低分', prop: 'scoreLow', minWidth: 124 },
+    { align: 'center', label: '近5分钟客主比', prop: 'objSubRate', minWidth: 124 },
+    { align: 'center', label: '平均客主比', prop: 'objSubAvgRate', minWidth: 110 },
+    {
+      align: 'center',
+      label: '在线',
+      prop: 'online',
+      minWidth: 72,
+      formatter(row: any) {
+        if (row.markingGroupNumber === 0) {
+          let total = (props.result || [])
+            .filter((item: any) => item.markingGroupNumber !== 0)
+            .reduce((num: number, item: any) => {
+              return num + (item.online ? 1 : 0)
+            }, 0)
+          return total + '人'
+        } else {
+          return row.online ? '在线' : '离线'
+        }
+      },
     },
-  },
-  { align: 'center', label: '状态', prop: 'markingStatus', minWidth: 100 },
-  { align: 'center', label: '速度', prop: 'markingRate', minWidth: 66 },
-  // { align: 'center', label: '综合系数', prop: 'integration', width: 90 },
-].map((col: any) => {
-  if (!['小组', '重评/待确认'].includes(col.label)) col.sortable = 'custom'
-  return col
+    { align: 'center', label: '状态', prop: 'markingStatus', minWidth: 100 },
+    { align: 'center', label: '速度', prop: 'markingRate', minWidth: 66 },
+    // { align: 'center', label: '综合系数', prop: 'integration', width: 90 },
+  ]
+    .map((col: any) => {
+      if (!['小组', '重评/待确认'].includes(col.label)) col.sortable = 'custom'
+      return col
+    })
+    .filter((col: any) => {
+      return props.filterColumns ? props.filterColumns.indexOf(col.prop) > -1 : col
+    })
 })
 const visable = ref(false)
 const rowContextmenu = (row: any, column: any, event: any) => {

+ 47 - 6
src/modules/analysis/personnel-statistics/hooks/useStatisticsFilter.ts

@@ -4,6 +4,7 @@ import useForm from '@/hooks/useForm'
 import useOptions from '@/hooks/useOptions'
 import useVW from '@/hooks/useVW'
 import { getCurDayRange } from '@/utils/common'
+import useFetch from '@/hooks/useFetch'
 
 import type { EpFormItem, EpFormRows } from 'global-type'
 import type { ExtractApiParams } from '@/api/api'
@@ -33,7 +34,22 @@ const useStatisticsFilter = () => {
     hasJudge: ['true'],
     expand: [],
   })
-
+  const { fetch: getMarkerList, result: markerList } = useFetch('getMarkerList')
+  watch(
+    () => [model.subjectCode, model.questionMainNumber, model.markingGroupNumbers],
+    () => {
+      if (model.subjectCode && model.questionMainNumber) {
+        getMarkerList({
+          subjectCode: model.subjectCode,
+          mainNumber: model.questionMainNumber,
+          markingGroupNumber: model.markingGroupNumbers,
+        })
+      }
+    },
+    {
+      immediate: true,
+    }
+  )
   const fetchModel = computed<ExtractApiParams<'getStatisticsByGroup'> & { expand: boolean }>(() => {
     return Object.assign(omit(model, 'time'), {
       startTime: model.time?.[0],
@@ -59,11 +75,15 @@ const useStatisticsFilter = () => {
   })
 
   const OneRowSpan5 = defineColumn(_, 'row-1', { span: 5 })
+  const OneRowSpan4 = defineColumn(_, 'row-1', { span: 4 })
+  const OneRowSpan3 = defineColumn(_, 'row-1', { span: 3 })
+  const OneRowSpan1 = defineColumn(_, 'row-1', { span: 1 })
   const TwoRowSpan5 = defineColumn(_, 'row-2', { span: 5 })
+  const TwoRowSpan8 = defineColumn(_, 'row-2', { span: 8 })
   const TwoRowSpan3 = defineColumn(_, 'row-2', { span: 3 })
 
   const items = computed<EpFormItem[]>(() => [
-    OneRowSpan5({
+    OneRowSpan4({
       labelWidth: '52px',
       label: '科目',
       prop: 'subjectCode',
@@ -74,7 +94,8 @@ const useStatisticsFilter = () => {
         disabled: true,
       },
     }),
-    OneRowSpan5({
+    OneRowSpan4({
+      labelWidth: '52px',
       prop: 'questionMainNumber',
       label: '大题',
       slotType: 'select',
@@ -84,7 +105,8 @@ const useStatisticsFilter = () => {
         disabled: true,
       },
     }),
-    OneRowSpan5({
+    OneRowSpan4({
+      labelWidth: '52px',
       prop: 'markingGroupNumbers',
       label: '小组',
       slotType: 'select',
@@ -93,11 +115,30 @@ const useStatisticsFilter = () => {
         onChange: changeModelValue('group'),
       },
     }),
-    OneRowSpan5({
+    OneRowSpan4({
+      labelWidth: '65px',
+      label: '评卷员',
+      slotType: 'select',
+      prop: 'markerId',
+      slot: {
+        options: markerList?.value?.map((marker) => ({
+          value: marker.id,
+          label: marker.loginName ? `${marker.loginName}-${marker.name}` : marker.name,
+        })),
+        filterable: true,
+        clearable: true,
+      },
+    }),
+    OneRowSpan4({
       labelWidth: '20px',
       slotName: 'button-group',
     }),
-    TwoRowSpan5({
+
+    OneRowSpan4({
+      labelWidth: '0px',
+      slotName: 'columns-setting',
+    }),
+    TwoRowSpan8({
       labelWidth: '52px',
       label: '时间',
       slotType: 'dateTime',

+ 94 - 2
src/modules/analysis/personnel-statistics/index.vue

@@ -6,6 +6,13 @@
           <el-button :loading="loading" type="primary" @click="onSearch">查询</el-button>
           <el-button :loading="exporting" type="primary" custom-1 @click="onExport">导出</el-button>
         </template>
+        <template #form-item-columns-setting>
+          <div class="setting-box">
+            <el-icon color="#333" :size="24" class="setting-icon" @click="showColumnsSetting = true">
+              <setting />
+            </el-icon>
+          </div>
+        </template>
       </base-form>
     </div>
     <div v-loading="loading" class="flex-1 p-small">
@@ -13,16 +20,36 @@
         :is="fetchModel.expand ? StatisticsPersonnel : StatisticsGroup"
         :data="data"
         :params="fetchModel"
+        :filter-columns="filterColumns"
+        :result="result"
       ></component>
     </div>
+    <base-dialog
+      v-model="showColumnsSetting"
+      class="columns-setting-dialog"
+      title="表格列展示配置"
+      center
+      width="780px"
+      @open="dialogOpen"
+      @close="dialogClose"
+    >
+      <el-checkbox-group v-model="checkedList">
+        <div v-for="item in checkboxList" :key="item.value" class="check-item">
+          <el-checkbox :label="item.value">{{ item.content }}</el-checkbox>
+        </div>
+      </el-checkbox-group>
+      <template #footer>
+        <confirm-button right @confirm="sure" @cancel="cancel"></confirm-button>
+      </template>
+    </base-dialog>
   </div>
 </template>
 
 <script setup lang="ts" name="AnalysisPersonnelStatistics">
 /** 人员数据统计 */
-import { computed, onMounted } from 'vue'
+import { computed, onMounted, ref, reactive } from 'vue'
 import { omit } from 'lodash-es'
-import { ElButton } from 'element-plus'
+import { ElButton, ElIcon, ElCheckboxGroup, ElCheckbox } from 'element-plus'
 import BaseForm from '@/components/element/BaseForm.vue'
 import useFetch from '@/hooks/useFetch'
 import useVW from '@/hooks/useVW'
@@ -30,11 +57,58 @@ import useStatisticsFilter from './hooks/useStatisticsFilter'
 import useMainStore from '@/store/main'
 import StatisticsGroup from './components/StatisticsGroup.vue'
 import StatisticsPersonnel from './components/StatisticsPersonnel.vue'
+import { Setting } from '@element-plus/icons-vue'
+import BaseDialog from '@/components/element/BaseDialog.vue'
+import ConfirmButton from '@/components/common/ConfirmButton.vue'
 
 import type { ExtractApiResponse } from '@/api/api'
 
 const { model, fetchModel, items, onOptionInit } = useStatisticsFilter()
+const showColumnsSetting = ref(false)
+function cancel() {
+  showColumnsSetting.value = false
+}
 
+const columnMap: any = {
+  markingGroupNumber: '小组',
+  markerName: '评卷员',
+  markingPaperCount: '评卷份数',
+  avg: '平均分',
+  xyRelate: '相关系数',
+  std: '标准差',
+  integration: '综合系数',
+  markDayCount: '当日可阅',
+  todoMarkDayCount: '剩余可阅',
+  objectiveZero: '客观题0分量',
+  objectiveAvg: '客观平均分',
+  objectiveStd: '客观标准差',
+  reMarkUnConfirmCount: '重评/待确认',
+  checkCount: '抽查量',
+  checkCorrectCount: '抽查改正量',
+  scoreTop: '近5分钟最高分',
+  scoreLow: '近5分钟最低分',
+  objSubRate: '近5分钟客主比',
+  objSubAvgRate: '平均客主比',
+  online: '在线',
+  markingStatus: '状态',
+  markingRate: '速度',
+}
+const labelList = Object.keys(columnMap)
+const checkedList = ref(JSON.parse(JSON.stringify(labelList)))
+const checkboxList = ref(labelList.map((item: any) => ({ value: item, content: columnMap[item] })))
+const filterColumns = ref(JSON.parse(JSON.stringify(labelList)))
+let initCheckedList: any = []
+function dialogOpen() {
+  initCheckedList = checkedList.value
+}
+function dialogClose() {
+  checkedList.value = initCheckedList
+}
+function sure() {
+  filterColumns.value = checkedList.value
+  initCheckedList = checkedList.value
+  showColumnsSetting.value = false
+}
 onMounted(() => {
   const mainStore = useMainStore()
   if (mainStore.myUserInfo && mainStore.myUserInfo.role === 'DEPUTY') {
@@ -75,9 +149,27 @@ function onExport() {
 
 <style scoped lang="scss">
 .personnel-statistics-view {
+  .check-item {
+    width: 146px;
+    float: left;
+  }
   .filter-form {
     border-bottom: $OnePixelLine;
   }
+  :deep(.setting-box) {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+    padding-right: 15px;
+    .setting-icon {
+      cursor: pointer;
+      font-weight: bold;
+      &:hover {
+        color: var(--el-color-primary);
+      }
+    }
+  }
   :deep(.el-form-item--small) {
     margin-bottom: 10px;
   }

+ 7 - 4
src/modules/analysis/view-marked-detail/index.vue

@@ -5,9 +5,9 @@
       :paper-path="current?.filePath"
       @click="onOperationClick"
     >
-      <el-button :disabled="!current?.taskId" class="m-l-base" size="small" type="primary" @click="onEditScore"
+      <!-- <el-button :disabled="!current?.taskId" class="m-l-base" size="small" type="primary" @click="onEditScore"
         >修改给分</el-button
-      >
+      > -->
       <el-button :disabled="!current?.taskId" class="m-l-base m-r-auto" size="small" type="primary" @click="onSendBack"
         >打回</el-button
       >
@@ -144,7 +144,7 @@ const onRejected = () => {
   ElMessage.success('打回成功')
 }
 /** 给分板 */
-const editScoreVisible = ref<boolean>(false)
+const editScoreVisible = ref<boolean>(true)
 
 /** 图片预览 */
 const previewModalVisible = ref<boolean>(false)
@@ -212,6 +212,7 @@ const columns: EpTableColumn<RowType>[] = [
 const { pagination, currentPage, data, fetchTable } = useTable('getPersonalMarkDetail', {
   markerId: props.markerId,
   score: (query.score as string) || '',
+  pageSize: 50,
 })
 
 const {
@@ -223,6 +224,7 @@ const {
   visibleHistory,
   onDbClick,
   onCurrentChange,
+  nextRow,
 } = useTableCheck(data)
 
 /** 确定给分 */
@@ -234,8 +236,9 @@ const onSubmit = async () => {
     await updatePersonalMarkDetailScore({ taskId: current.value.taskId, scores: modelScore.value })
     current.value.markerScore = add(...scores)
     ElMessage.success('修改成功')
-    editScoreVisible.value = false
+    // editScoreVisible.value = false
     // onRefresh()
+    nextRow()
   }
 }
 // watch(current, () => {

+ 5 - 3
src/modules/expert/assess/index.vue

@@ -5,7 +5,7 @@
       :paper-path="currentAssessPaper?.filePath"
       @click="onOperationClick"
     >
-      <el-button class="m-l-base m-r-auto" size="small" type="primary" @click="onEditScore">修改给分</el-button>
+      <!-- <el-button class="m-l-base m-r-auto" size="small" type="primary" @click="onEditScore">修改给分</el-button> -->
     </mark-header>
     <div class="flex flex-1 overflow-hidden p-base mark-container">
       <div
@@ -99,7 +99,7 @@ import type { MarkHeaderInstance, EpFormItem, EpTableColumn } from 'global-type'
 type RowType = ExtractMultipleApiResponse<'getExpertAssessList'> & { index: number }
 
 /** 给分板 */
-const editScoreVisible = ref<boolean>(false)
+const editScoreVisible = ref<boolean>(true)
 
 /** 图片预览 */
 const previewModalVisible = ref<boolean>(false)
@@ -222,6 +222,7 @@ const {
   current: currentAssessPaper,
   next: checkNext,
   onCurrentChange,
+  nextRow,
 } = useTableCheck(rfSampleList)
 
 const onSearch = async () => {
@@ -239,8 +240,9 @@ const onSubmit = async () => {
     await updateMarkScore({ id: currentAssessPaper.value.id, scores: modelScore.value })
     currentAssessPaper.value.score = add(...scores)
     ElMessage.success('修改成功')
-    editScoreVisible.value = false
+    // editScoreVisible.value = false
     // onSearch()
+    nextRow()
   }
 }
 

+ 17 - 5
src/modules/expert/expert/index.vue

@@ -5,7 +5,7 @@
       :paper-path="currentExpertPaper?.filePath"
       @click="onOperationClick"
     >
-      <el-button class="m-l-base m-r-auto" size="small" type="primary" @click="onEditScore">修改给分</el-button>
+      <!-- <el-button class="m-l-base m-r-auto" size="small" type="primary" @click="onEditScore">修改给分</el-button> -->
     </mark-header>
     <div class="flex flex-1 overflow-hidden p-base mark-container">
       <div
@@ -75,7 +75,7 @@
 /** 专家卷浏览-专家挑选卷 */
 import { reactive, ref, computed, watch, nextTick } from 'vue'
 import { ElButton, ElMessage } from 'element-plus'
-import { ROLE_OPTION, PaperMap } from '@/constants/dicts'
+import { PaperMap } from '@/constants/dicts'
 import { add } from '@/utils/common'
 import { useSetImgBg } from '@/hooks/useSetImgBg'
 import useFetch from '@/hooks/useFetch'
@@ -97,9 +97,19 @@ import type { ExtractMultipleApiResponse, ExtractApiParams, ExtractApiResponse }
 import type { MarkHeaderInstance, EpFormItem, EpTableColumn } from 'global-type'
 
 type RowType = ExtractMultipleApiResponse<'getExpertPickList'> & { index: number }
-
+const ROLE_OPTION = ref<any[]>([])
+useFetch('getRoleList')
+  .fetch()
+  .then((res: any) => {
+    ROLE_OPTION.value = res.map((item: any) => {
+      return {
+        value: item.code,
+        label: item.roleName,
+      }
+    })
+  })
 /** 给分板 */
-const editScoreVisible = ref<boolean>(false)
+const editScoreVisible = ref<boolean>(true)
 
 /** 图片预览 */
 const previewModalVisible = ref<boolean>(false)
@@ -282,6 +292,7 @@ const {
   current: currentExpertPaper,
   next: checkNext,
   onCurrentChange,
+  nextRow,
 } = useTableCheck(expertPaperList)
 
 const paperType = ref<string>('')
@@ -302,8 +313,9 @@ const onSubmit = async () => {
     await updateMarkScore({ id: currentExpertPaper.value.id, scores: modelScore.value })
     currentExpertPaper.value.score = add(...scores)
     ElMessage.success('修改成功')
-    editScoreVisible.value = false
+    // editScoreVisible.value = false
     // onSearch()
+    nextRow()
   }
 }
 

+ 5 - 3
src/modules/expert/sample/index.vue

@@ -5,7 +5,7 @@
       :paper-path="currentRfPaper?.filePath"
       @click="onOperationClick"
     >
-      <el-button class="m-l-base m-r-auto" size="small" type="primary" @click="onEditScore">修改给分</el-button>
+      <!-- <el-button class="m-l-base m-r-auto" size="small" type="primary" @click="onEditScore">修改给分</el-button> -->
     </mark-header>
     <div class="flex flex-1 overflow-hidden p-base mark-container">
       <div
@@ -99,7 +99,7 @@ import type { MarkHeaderInstance, EpFormItem, EpTableColumn } from 'global-type'
 type RowType = ExtractMultipleApiResponse<'getRfSampleList'> & { index: number }
 
 /** 给分板 */
-const editScoreVisible = ref<boolean>(false)
+const editScoreVisible = ref<boolean>(true)
 
 /** 图片预览 */
 const previewModalVisible = ref<boolean>(false)
@@ -200,6 +200,7 @@ const {
   next: checkNext,
   onDbClick,
   onCurrentChange,
+  nextRow,
 } = useTableCheck(rfSampleList)
 
 const onSearch = async () => {
@@ -217,8 +218,9 @@ const onSubmit = async () => {
       await updateMarkScore({ id: currentRfPaper.value.id, scores: modelScore.value })
       currentRfPaper.value.score = add(...scores)
       ElMessage.success('修改成功')
-      editScoreVisible.value = false
+      // editScoreVisible.value = false
       // onSearch()
+      nextRow()
     }
   } catch (error) {
     console.error(error)

+ 7 - 5
src/modules/marking/inquiry-result/index.vue

@@ -5,9 +5,9 @@
       :paper-path="current?.filePath"
       @click="onOperationClick"
     >
-      <el-button :disabled="!current?.taskId" class="m-l-base" size="small" type="primary" @click="onEditScore"
+      <!-- <el-button :disabled="!current?.taskId" class="m-l-base" size="small" type="primary" @click="onEditScore"
         >修改给分</el-button
-      >
+      > -->
       <el-button :disabled="!current?.taskId" class="m-l-base m-r-auto" size="small" type="primary" @click="onSendBack"
         >打回</el-button
       >
@@ -152,7 +152,7 @@ const onRejected = () => {
 }
 
 /** 给分板 */
-const editScoreVisible = ref<boolean>(false)
+const editScoreVisible = ref<boolean>(true)
 
 /** 图片预览 */
 const previewModalVisible = ref<boolean>(false)
@@ -256,7 +256,7 @@ const transformQuery = (query: Record<string, string>) => {
 const { pagination, currentPage, data, fetchTable } = useTable(
   'getCustomQueryTasks',
   transformQuery(query as Record<string, string>),
-  { pageSize: 20 }
+  { pageSize: 50 }
 )
 
 const {
@@ -268,6 +268,7 @@ const {
   visibleHistory,
   onDbClick,
   onCurrentChange,
+  nextRow,
 } = useTableCheck(data)
 
 watch(current, () => {
@@ -378,7 +379,8 @@ const onSubmit = async () => {
     current.value.markScores = scores
     // onRefresh()
     ElMessage.success('修改成功')
-    editScoreVisible.value = false
+    // editScoreVisible.value = false
+    nextRow()
   }
 }
 

+ 5 - 1
src/modules/marking/inquiry/index.vue

@@ -211,7 +211,11 @@ const items = computed<EpFormItem[]>(() => [
       slotType: 'select',
       prop: 'markerId',
       slot: {
-        options: markerList?.value?.map((marker) => ({ value: marker.id, label: marker.name || marker.loginName })),
+        options: markerList?.value?.map((marker) => ({
+          value: marker.id,
+          label: marker.loginName ? `${marker.loginName}-${marker.name}` : marker.name,
+        })),
+        filterable: true,
         clearable: true,
       },
     },

+ 6 - 4
src/modules/monitor/system-check/index.vue

@@ -6,7 +6,7 @@
       @click="onOperationClick"
     >
       <template v-if="!hideHeaderButtons">
-        <el-button class="m-l-base" size="small" type="primary" @click="onEditScore">修改给分</el-button>
+        <!-- <el-button class="m-l-base" size="small" type="primary" @click="onEditScore">修改给分</el-button> -->
         <el-button class="m-l-base m-r-auto" size="small" type="primary" @click="onSendBack">打回</el-button>
       </template>
     </mark-header>
@@ -146,7 +146,7 @@ const {
 } = useMarkHeader()
 
 /** 给分板 */
-const scoringPanelVisible = ref<boolean>(false)
+const scoringPanelVisible = ref<boolean>(true)
 
 /** 图片预览 */
 const previewModalVisible = ref<boolean>(false)
@@ -282,7 +282,7 @@ const { fetch: getSystemCheckLevel, result: levelOptions } = useFetch('getSystem
 getSystemCheckLevel()
 
 const { pagination, currentPage, data, fetchTable, loading, total } = useTable('getSystemSpotList', formModel, {
-  pageSize: 20,
+  pageSize: 50,
 })
 const {
   tableRef,
@@ -293,6 +293,7 @@ const {
   visibleHistory,
   onDbClick,
   onCurrentChange,
+  nextRow,
   // } = useTableCheck(systemSpotList)
 } = useTableCheck(data)
 
@@ -319,9 +320,10 @@ const onSubmit = async () => {
     currentSystemCheckPaper.value.headerScore = add(...scores)
     currentSystemCheckPaper.value.markScores = scores
     ElMessage.success('修改成功')
-    scoringPanelVisible.value = false
+    // scoringPanelVisible.value = false
     // onSearch()
     // fetchTable()
+    nextRow()
   }
 }
 

+ 4 - 3
src/modules/quality/self-check-detail/index.vue

@@ -5,7 +5,7 @@
       :paper-path="current?.filePath"
       @click="onOperationClick"
     >
-      <el-button class="m-l-base" size="small" type="primary" @click="onEditScore">修改给分</el-button>
+      <!-- <el-button class="m-l-base" size="small" type="primary" @click="onEditScore">修改给分</el-button> -->
       <el-button class="m-l-base m-r-auto" size="small" type="primary" @click="onSendBack">打回</el-button>
     </mark-header>
     <div class="flex flex-1 overflow-hidden p-base mark-container">
@@ -109,7 +109,7 @@ type RowType = ExtractApiResponse<'getSelfCheckDataDetail'> & { index: number }
 const { query } = useRoute()
 
 /** 给分板 */
-const editScoreVisible = ref<boolean>(false)
+const editScoreVisible = ref<boolean>(true)
 
 /** 图片预览 */
 const previewModalVisible = ref<boolean>(false)
@@ -209,7 +209,8 @@ const onSubmit = async () => {
     await markSelfCheckData({ taskId: current.value.taskId, scores: modelScore.value })
     // current.value.markerScore = add(...modelScore.value)
     ElMessage.success('修改成功')
-    editScoreVisible.value = false
+    // editScoreVisible.value = false
+    nextRow()
   }
 }
 

+ 56 - 6
src/modules/quality/subjective-check/index.vue

@@ -5,7 +5,7 @@
       :paper-path="currentSubjectiveCheck?.filePath"
       @click="onOperationClick"
     >
-      <el-button type="primary" size="small" class="m-l-base" @click="onEditScore">修改给分</el-button>
+      <!-- <el-button type="primary" size="small" class="m-l-base" @click="onEditScore">修改给分</el-button> -->
       <el-button type="primary" size="small" class="m-l-base m-r-auto" @click="onConfirm">提交确认</el-button>
     </mark-header>
     <div class="flex flex-1 overflow-hidden p-base mark-container">
@@ -76,7 +76,7 @@
             </div>
           </pane>
           <pane max-size="100" size="30">
-            <base-table
+            <!-- <base-table
               v-if="currentSubjectiveCheck"
               border
               stripe
@@ -89,7 +89,22 @@
               @row-dblclick="onDbClick"
             >
               <template #empty> 暂无数据 </template>
-            </base-table>
+            </base-table> -->
+            <div v-if="currentSubjectiveCheck">
+              <div class="bottom-title">给分记录( {{ currentSubjectiveCheck?.secretNumber }} )</div>
+              <base-table
+                border
+                stripe
+                class="m-t-base"
+                size="small"
+                height="150px"
+                :data="currentHistoryData"
+                :columns="currentHistoryColumns"
+                :cell-style="{ padding: '6px 0' }"
+              >
+                <template #empty> 暂无数据 </template>
+              </base-table>
+            </div>
           </pane>
         </splitpanes>
       </div>
@@ -133,8 +148,27 @@ import 'splitpanes/dist/splitpanes.css'
 
 type RowType = ExtractMultipleApiResponse<'getSubjectiveCheckList'> & { index: number }
 
+const { fetch: getMarkScoreHistoryListWithTask, result: scoreHistoryList } = useFetch('getMarkScoreHistoryListWithTask')
+const currentHistoryData = computed(() => {
+  return scoreHistoryList.value
+})
+
+const currentHistoryColumns = computed(() => [
+  { label: '评卷员', prop: 'markerName', fixed: 'left' },
+  {
+    label: `分数`,
+    prop: 'markScore',
+    width: 48,
+    formatter(row: any) {
+      return `${row.markScore === null ? '' : row.markScore}`
+    },
+  },
+  { label: '类型', prop: 'historyType' },
+  { label: '评卷时间', prop: 'markTime', width: 130 },
+])
+
 /** 给分板 */
-const editScoreVisible = ref<boolean>(false)
+const editScoreVisible = ref<boolean>(true)
 
 /** 图片预览 */
 const previewModalVisible = ref<boolean>(false)
@@ -266,6 +300,14 @@ const columns: EpTableColumn<RowType>[] = [
   { label: '序号', type: 'index', width: 60 },
   { label: '密号', prop: 'secretNumber', minWidth: 100 },
   { label: '大题名称', prop: 'mainName', minWidth: 100 },
+  {
+    label: '成绩',
+    prop: 'headerScore',
+    minWidth: 60,
+    formatter(row: any) {
+      return row.headerScore || row.markScore
+    },
+  },
   { label: '处理结果', prop: 'status', minWidth: 100 },
   { label: '处理人', prop: 'headerName', minWidth: 100 },
   { label: '处理时间', prop: 'updateTime', minWidth: 130 },
@@ -289,8 +331,11 @@ const {
   visibleHistory,
   onDbClick,
   onCurrentChange,
+  nextRow,
 } = useTableCheck(subjectiveCheckList)
-
+watch(currentSubjectiveCheck, () => {
+  getMarkScoreHistoryListWithTask({ taskId: currentSubjectiveCheck.value.taskId })
+})
 const statusText = ref<string>('未处理')
 
 const onSearch = async () => {
@@ -307,8 +352,9 @@ const onSubmit = async () => {
     await subjectiveCheckMark({ taskId: currentSubjectiveCheck.value.taskId, scores: modelScore.value })
     currentSubjectiveCheck.value.markScore = add(...scores)
     ElMessage.success('修改成功')
-    editScoreVisible.value = false
+    // editScoreVisible.value = false
     // onSearch()
+    nextRow()
   }
 }
 
@@ -359,6 +405,10 @@ const { drawing, dataUrl } = useSetImgBg(imgOption, frontColor, setFrontColor)
   }
   .table-view {
     // width: 580px;
+    .bottom-title {
+      color: #000;
+      margin-top: 10px;
+    }
     width: 35%;
     .detail-info-label {
       .detail-info-label-num {