소스 검색

暂停评卷功能

刘洋 1 년 전
부모
커밋
dd83cff66f

+ 2 - 2
server.config.ts

@@ -3,11 +3,11 @@ import type { ServerOptions } from 'vite'
 const server: ServerOptions = {
   proxy: {
     '^/?(api|file)/': {
-      // target: 'http://192.168.10.41:8200',
+      target: 'http://192.168.10.41:8200',
       // target: 'http://192.168.10.178:8200',
       // target: 'http://192.168.10.108:8200',
       // target: 'http://cet-test.markingtool.cn',
-      target: 'http://192.168.10.136:80',
+      // target: 'http://192.168.10.136:80',
       // target: 'http://cet-dev.markingtool.cn:8200',
     },
   },

+ 2 - 0
src/App.vue

@@ -4,6 +4,7 @@ import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
 import useMainStore from '@/store/main'
 import useMainLayoutStore from '@/store/layout'
 import LockScreen from '@/layout/LockScreen.vue'
+import PauseMarker from '@/layout/PauseMarker.vue'
 import LoadingFlag from '@/components/common/LoadingFlag.vue'
 import RowNextBottomDialog from '@/components/common/RowNextBottomDialog.vue'
 import { onMounted, watch } from 'vue'
@@ -90,6 +91,7 @@ onMounted(() => {
       </keep-alive>
     </router-view>
     <lock-screen v-if="mainStore.lockScreenStatus"></lock-screen>
+    <pause-marker v-if="mainStore.markerPausedLimit > 0"></pause-marker>
     <loading-flag v-if="mainStore.globalLoading"></loading-flag>
     <row-next-bottom-dialog />
   </el-config-provider>

+ 4 - 0
src/api/api-types/marking.d.ts

@@ -183,6 +183,8 @@ export namespace Marking {
 
   // 主动抽查浏览
   type ViewActiveCheck = BaseDefine<{ taskId: number }>
+  type PauseMark = BaseDefine<{ userId: number }>
+  type PauseMarkStart = BaseDefine<any>
 
   interface MarkScoreHistory {
     id: number
@@ -387,5 +389,7 @@ export namespace Marking {
     /** 提交雷同卷 */
     submitSimilarPaper: SubmitSimilarPaper
     viewActiveCheck: ViewActiveCheck
+    pauseMark: PauseMark
+    pauseMarkStart: PauseMarkStart
   }
 }

+ 4 - 1
src/api/api-types/message.d.ts

@@ -22,7 +22,10 @@ export namespace Message {
   /** 发送/回复消息 */
   type SendMessage = BaseDefine<{ content: string; receiveUserIds: number[] }>
   /** 未读消息 - 最新5条 */
-  type GetUnReadMessage = BaseDefine<null, { newCount: number; messages: BaseMessageResponse[] }>
+  type GetUnReadMessage = BaseDefine<
+    null,
+    { newCount: number; messages: BaseMessageResponse[]; markerPausedLimit?: number }
+  >
   /** 已读 */
   type HandleReadMessage = BaseDefine<{ id: number }>
   export interface ApiMap {

+ 1 - 0
src/api/api-types/user.d.ts

@@ -28,6 +28,7 @@ export namespace User {
     subjectCode: string
     /** needRealName */
     needRealName: boolean
+    markerPausedLimit?: number | undefined
   }
   type UserLogin = BaseDefine<
     {

+ 2 - 0
src/api/marking.ts

@@ -84,6 +84,8 @@ const MarkingApi: DefineApiModule<Marking.ApiMap> = {
   /** 提交雷同卷 */
   submitSimilarPaper: '/api/mark/same',
   viewActiveCheck: '/api/leader/operate/view',
+  pauseMark: '/api/user/marker/paused',
+  pauseMarkStart: '/api/user/marker/paused/start',
 }
 
 export default MarkingApi

BIN
src/assets/images/waiting.png


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

@@ -58,7 +58,7 @@ const add = () => {
 }
 const minus = () => {
   const w = imagePreview.value?.clientWidth
-  if (w < 290) return
+  if (w <= 290) return
   if (w) {
     imagePreview.value.style.width = (Math.floor(w * 0.9) < 290 ? 290 : Math.floor(w * 0.9)) + 'px'
     let l = parseFloat(imagePreview.value?.style?.left)

+ 13 - 2
src/components/shared/StandardDialog.vue

@@ -11,6 +11,7 @@
       <base-form class="p-t-base" size="small" :model="formModel" :items="formItems" :label-width="'78px'">
         <template #form-item-search>
           <el-button :loading="loading" type="primary" @click="onSearch">查询</el-button>
+          <span v-if="mainStore.markerPausedLimit > 0" class="limit-time">{{ limitTime }}</span>
         </template>
       </base-form>
       <iframe
@@ -49,7 +50,7 @@ watch(dataModel, () => {
 })
 const { defineColumn, _ } = useForm()
 
-const span10 = defineColumn(_, '', { span: 10 })
+const span10 = defineColumn(_, '', { span: 9 })
 
 const formItems = computed<any[]>(() => [
   span10({
@@ -72,7 +73,7 @@ const formItems = computed<any[]>(() => [
       disabled: !isExpert.value && !isLeader.value,
     },
   }),
-  { rowKey: 'row-1', slotName: 'search', labelWidth: '10px', colProp: { span: 4 } },
+  { rowKey: 'row-1', slotName: 'search', labelWidth: '10px', colProp: { span: 6 } },
 ])
 
 const props = defineProps<{
@@ -98,6 +99,10 @@ const onSearch = () => {
   })
 }
 onOptionInit(onSearch)
+
+const limitTime = computed(() => {
+  return Math.ceil(mainStore.markerPausedLimit / 1000)
+})
 </script>
 //
 <style scoped lang="scss">
@@ -136,6 +141,12 @@ onOptionInit(onSearch)
     flex: 1;
     padding: 2px;
     position: relative;
+    .limit-time {
+      font-size: 16px;
+      font-weight: bold;
+      color: #000;
+      margin-left: 10px;
+    }
     #my-iframe-mask {
       position: absolute;
       left: 0;

+ 1 - 1
src/directives/dialogResizeStandard.ts

@@ -33,7 +33,7 @@ export const dialogResizeStandard = {
     el.fullscreen = false
     const winHeight = window.innerHeight
     // 弹框可拉伸最小宽高
-    const minWidth = 400
+    const minWidth = 500
     const minHeight = winHeight / 2
     const winWidth = window.innerWidth
     // 弹窗

+ 1 - 0
src/hooks/useFetch.ts

@@ -30,6 +30,7 @@ const needLoadingApiList: any[] = [
   '/api/statistic/marking/progress/check/for/unmark/list',
   '/api/statistic/marking/progress/ending',
   '/api/train/monitor/list',
+  '/api/user/marker/paused',
 ]
 function useFetch<K extends ApiKeys>(key: K): ReturnType<K>
 function useFetch<K extends ApiKeys>(key: K, config: AxiosRequestConfig, method?: HttpMethod): ReturnType<K>

+ 59 - 0
src/layout/PauseMarker.vue

@@ -0,0 +1,59 @@
+<template>
+  <div class="lock-screen flex justify-center items-center">
+    <div class="flex items-center">
+      <img src="../assets/images/waiting.png" class="m-r-base" />
+      <el-form ref="ruleFormRef">
+        <el-form-item label="">
+          <el-input
+            class="tip-input"
+            size="large"
+            clearable
+            value="老师幸苦了,请稍作休息,以保证质量"
+            readonly
+          ></el-input>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" size="large" class="m-l-base" @click="confirm">确定</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <standard-dialog v-model="standardVisible" :can-resize="'can-resize3'" resize-key="can-resize3"></standard-dialog>
+  </div>
+</template>
+<script lang="ts" setup name="PauseMarker">
+import { ElInput, ElButton, ElForm, ElMessage } from 'element-plus'
+import { onMounted, reactive, ref } from 'vue'
+import StandardDialog from '@/components/shared/StandardDialog.vue'
+import useFetch from '@/hooks/useFetch'
+import useMainStore from '@/store/main'
+const mainStore = useMainStore()
+const standardVisible = ref(false)
+onMounted(() => {
+  useFetch('pauseMarkStart').fetch()
+})
+const confirm = () => {
+  standardVisible.value = true
+}
+</script>
+<style lang="scss" scoped>
+.lock-screen {
+  position: fixed;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  top: 0;
+  z-index: 3000;
+  background: rgba(0, 0, 0, 0.9);
+  .tip-input {
+    width: 290px;
+
+    :deep(input.el-input__inner) {
+      font-weight: bold;
+      color: #333;
+    }
+  }
+  img {
+    height: 40px;
+  }
+}
+</style>

+ 7 - 6
src/modules/analysis/marker-statistics/index.vue

@@ -433,6 +433,7 @@ const columns: any = computed(() => {
       label: '评卷员',
       prop: 'markerName',
       minWidth: 90,
+      sortable: true,
       formatter(row: any) {
         return (
           <SecNumberStatus
@@ -448,12 +449,12 @@ const columns: any = computed(() => {
       prop: 'secretNumber',
       minWidth: 110,
     },
-    { label: '给分', prop: 'markerScore', minWidth: 70 },
-    { label: '客观分', prop: 'objectiveScore', minWidth: 70 },
-    { label: '客主比', prop: 'markerRatio', minWidth: 80 },
-    { label: '成绩客主比', prop: 'ratio', minWidth: 110 },
-    { label: '成绩', prop: 'markScore', minWidth: 70 },
-    { label: '评卷时间', prop: 'markTime', minWidth: 140 },
+    { label: '给分', prop: 'markerScore', minWidth: 70, sortable: true },
+    { label: '客观分', prop: 'objectiveScore', minWidth: 80, sortable: true },
+    { label: '客主比', prop: 'markerRatio', minWidth: 80, sortable: true },
+    { label: '成绩客主比', prop: 'ratio', minWidth: 110, sortable: true },
+    { label: '成绩', prop: 'markScore', minWidth: 70, sortable: true },
+    { label: '评卷时间', prop: 'markTime', minWidth: 140, sortable: true },
   ]
   if (isMult.value) {
     cols.unshift({

+ 15 - 1
src/modules/analysis/personnel-statistics/components/RightKeyMenu.vue

@@ -15,6 +15,13 @@
     >
       发送消息
     </div>
+    <div
+      v-if="!curRow.paused"
+      class="rightKeyMenuItem"
+      @click="curRow?.markerId && emits('onPauseMark', unref(curRow))"
+    >
+      暂停评卷
+    </div>
     <div
       class="rightKeyMenuItem"
       :class="{ disabled: !curRow.markerId }"
@@ -36,7 +43,14 @@ import { ref, onUnmounted, unref } from 'vue'
 import useMainStore from '@/store/main'
 const mainStore = useMainStore()
 let rkm = ref()
-const emits = defineEmits(['rightClick', 'onSetWorkload', 'onSendMessage', 'forceAssessment', 'onShowScoreLines'])
+const emits = defineEmits([
+  'rightClick',
+  'onSetWorkload',
+  'onSendMessage',
+  'onPauseMark',
+  'forceAssessment',
+  'onShowScoreLines',
+])
 const fun = () => {
   emits('rightClick')
 }

+ 13 - 23
src/modules/analysis/personnel-statistics/components/StatisticsPersonnel.vue

@@ -18,31 +18,10 @@
       @sort-change="sortChange"
     >
       <template #column-marker="{ row }">
-        <!-- <el-popover
-          v-if="row.markerId"
-          :ref="(popover: any) => setPopoverRefs(row.markerId,popover)"
-          trigger="contextmenu"
-          placement="right"
-          :show-arrow="false"
-          transition="none"
-          @show="onPopoverShow(row.markerId)"
-        >
-          <template #reference>
-            <el-button size="small" link type="primary">{{ row.markerName }}</el-button>
-          </template>
-          <el-menu @select="() => {}">
-            <el-menu-item index="setting" @click="onSetWorkload(row)">
-              <el-button link>设置工作量</el-button>
-            </el-menu-item>
-            <el-menu-item index="send" @click="onSendMessage(row)">
-              <el-button link>发送消息</el-button>
-            </el-menu-item>
-          </el-menu>
-        </el-popover> -->
         <span v-if="row.markerId">
           <span
             :style="{
-              backgroundColor: row.online ? '#00B42A' : '#ddd',
+              backgroundColor: row.paused ? '#E6A23C' : row.online ? '#00B42A' : '#ddd',
               display: 'inline-block',
               width: '10px',
               height: '10px',
@@ -80,6 +59,7 @@
     @on-send-message="onSendMessage"
     @force-assessment="forceAssessment"
     @on-show-score-lines="onShowScoreLines"
+    @on-pause-mark="onPauseMark"
   ></right-key-menu>
 </template>
 
@@ -87,7 +67,7 @@
 /** 人员数据统计-按人员展开 */
 import { ref, inject, computed, watch, nextTick, unref } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
-import { ElButton, ElPopover, ElMenu, ElMenuItem, ElIcon } from 'element-plus'
+import { ElButton, ElPopover, ElMenu, ElMenuItem, ElIcon, ElMessage } from 'element-plus'
 import VueECharts from 'vue-echarts'
 import BaseTable from '@/components/element/BaseTable.vue'
 import SetWorkload from './SetWorkload.vue'
@@ -323,6 +303,16 @@ function onSendMessage(data: ExtractArrayValue<ExtractApiResponse<'getStatistics
   setReplyUserId?.(data.markerId)
   setMessageVisible?.(true)
 }
+
+// 暂停某评卷员评卷
+function onPauseMark(row: any) {
+  useFetch('pauseMark')
+    .fetch({ userId: row.markerId })
+    .then(() => {
+      ElMessage.success('操作成功')
+      row.paused = true
+    })
+}
 function onShowScoreLines(row: any) {
   showCharts.value = true
   ;(elTableRef as any).value!.setCurrentRow(row)

+ 1 - 12
src/modules/bootstrap/login/index.vue

@@ -83,20 +83,8 @@ const formItems = shallowRef<EpFormItem[]>([
     },
   },
   {
-    // slotType: 'input',
     prop: 'password',
     slotName: 'password',
-    // slot: {
-    //   type: 'password',
-    //   placeholder: '请输入登录密码',
-    //   clearable: true,
-    //   showPassword: true,
-    //   onKeydown(event) {
-    //     if ((event as KeyboardEvent).key === 'Enter') {
-    //       onSubmit()
-    //     }
-    //   },
-    // },
   },
 ])
 const pwdKeyDown = (e: any) => {
@@ -118,6 +106,7 @@ async function onSubmit() {
 }
 
 function loginSuccess(loginInfo: ExtractApiResponse<'userLogin'>) {
+  mainStore.setMarkerPausedLimit(loginInfo.markerPausedLimit || 0)
   /** sessionStorage 缓存login result */
   sessionStorage.set('LOGIN_RESULT', loginInfo)
   let config: any = localStorage.get('USER_CONFIG')

+ 4 - 2
src/modules/marking/repeat/index.vue

@@ -239,8 +239,10 @@ const formItems = computed<EpFormItem[]>(() => [
 /** 查询重评卷列表 */
 const columns: EpTableColumn[] = [
   { label: '密号', prop: 'secretNumber', minWidth: 110, fixed: 'left' },
-  { label: '评卷员', prop: 'markerName', minWidth: 80 },
+  { label: '评卷员', prop: 'markerName', minWidth: 80, sortable: true },
+  { label: '原分值', prop: 'rejectScore', minWidth: 80, sortable: true },
   { label: '给分', prop: 'markerScore', minWidth: 74, sortable: true },
+  { label: '打回人', prop: 'rejectByName', minWidth: 80, sortable: true },
   // { label: '重评时间', prop: 'reMarkTime', width: 160 },
 
   { label: '确认状态', prop: 'confirmName', minWidth: 72 },
@@ -248,7 +250,7 @@ const columns: EpTableColumn[] = [
   { label: '客观分', prop: 'objectiveScore', minWidth: 92, sortable: true },
   { label: '客主比', prop: 'ratio', minWidth: 92, sortable: true },
   { label: '成绩', prop: 'markScore', minWidth: 92, sortable: true },
-  { label: '重评时间', prop: 'reMarkTime', minWidth: 130 },
+  { label: '重评时间', prop: 'reMarkTime', minWidth: 130, sortable: true },
 ]
 
 // const { fetch: getReMarkPaperList, result: reMarkPaperList, loading } = useFetch('getReMarkPaperList')

+ 22 - 1
src/store/main.ts

@@ -18,13 +18,15 @@ interface MainStoreState {
   globalLoading: boolean
   paneSizeConfig: any
   showRowNextBottomDialog: boolean
+  markerPausedLimit: number
+  markerPausedTimer: any
 }
 
 interface MainStoreActions {
   getMyUserInfo: () => Promise<ExtractApiResponse<'getMyUserInfo'> | undefined>
   getUserMarkConfig: () => Promise<ExtractApiResponse<'getUserMarkConfig'> | undefined>
   setUserMarkConfig: (config: ExtractApiResponse<'getUserMarkConfig'>) => void
-  setNewMsgs: (msgData: { newCount: number; messages: any[] }) => void
+  setNewMsgs: (msgData: { newCount: number; messages: any[]; markerPausedLimit?: number }) => void
   setOnline: (bool: boolean) => void
   setLockScreen: (bool: boolean) => void
   setKeepAliveViews: (name: string) => void
@@ -32,6 +34,7 @@ interface MainStoreActions {
   setGlobalLoading: (name: boolean) => void
   setPaneSizeConfig: (path: string, size: number) => void
   setRowNextBottomDialogStatus: (bool: boolean) => void
+  setMarkerPausedLimit: (time: number) => void
 }
 
 const useMainStore = defineStore<'main', MainStoreState, Record<string, any>, MainStoreActions>('main', {
@@ -60,9 +63,24 @@ const useMainStore = defineStore<'main', MainStoreState, Record<string, any>, Ma
         ? JSON.parse(localStorage.getItem('paneSizeConfig') as string)
         : {},
       showRowNextBottomDialog: false,
+      markerPausedLimit: 0,
+      markerPausedTimer: null,
     }
   },
   actions: {
+    setMarkerPausedLimit(time: number) {
+      if (time > 0 && this.markerPausedLimit == 0 && !this.markerPausedTimer) {
+        this.markerPausedLimit = time
+        this.markerPausedTimer = setInterval(() => {
+          this.markerPausedLimit = this.markerPausedLimit - 1000 < 0 ? 0 : this.markerPausedLimit - 1000
+          if (this.markerPausedLimit == 0) {
+            this.markerPausedLimit = 0
+            clearInterval(this.markerPausedTimer)
+            this.markerPausedTimer = null
+          }
+        }, 1000)
+      }
+    },
     setRowNextBottomDialogStatus(bool: boolean) {
       this.showRowNextBottomDialog = bool
     },
@@ -93,6 +111,9 @@ const useMainStore = defineStore<'main', MainStoreState, Record<string, any>, Ma
     },
     setNewMsgs(msgData) {
       this.newMsgs = msgData
+      if (this.markerPausedLimit == 0) {
+        this.setMarkerPausedLimit(msgData.markerPausedLimit || 0)
+      }
     },
     async getMyUserInfo() {
       try {