zhangjie 8 mesi fa
parent
commit
657f5c5c1f

+ 4 - 1
src/render/ap/recognizeCheck.ts

@@ -105,6 +105,9 @@ export const recognizeArbitrateSave = (
     url: "/api/admin/check/omr/arbitrate/save",
     method: "post",
     data,
+    headers: {
+      "Content-Type": "application/json;charset=UTF-8",
+    },
   });
 
 // 仲裁进度状态
@@ -112,7 +115,7 @@ export const recognizeArbitrateProgress = (
   groupId: number
 ): Promise<RecognizeArbitrateProgressResult> =>
   request({
-    url: "/api/admin/check/omr/arbitrate/get",
+    url: "/api/admin/check/omr/arbitrate/status",
     method: "post",
     data: { groupId },
   });

+ 16 - 2
src/render/styles/pages.less

@@ -283,7 +283,6 @@
   &-modal {
     position: absolute;
     width: 1002px;
-    height: 216px;
     top: 8px;
     left: 50%;
     margin-left: -501px;
@@ -294,7 +293,7 @@
     padding: 16px;
 
     .modal-box {
-      height: 88px;
+      min-height: 88px;
       background: #ffffff;
       border-radius: 6px;
       border: 1px solid #e5e5e5;
@@ -315,10 +314,25 @@
         font-size: 20px;
         line-height: 28px;
       }
+
+      &.is-btn {
+        cursor: pointer;
+
+        &:hover {
+          background: #e8f3ff;
+          color: @brand-color;
+        }
+      }
     }
 
     .modal-origin {
       background-color: @background-color;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      img {
+        height: 40px;
+      }
     }
 
     .modal-options {

+ 4 - 1
src/render/utils/recog/recog.ts

@@ -26,6 +26,8 @@ export interface RecognizeArea {
   optionSizes: RecogOptionSize[];
   result: string[];
   multiple: boolean;
+  // 当前区域的切图
+  sliceImg: string;
 }
 
 export interface RecogDataField {
@@ -109,6 +111,7 @@ export function parseDetailSize(
     optionSizes: [],
     result: [],
     multiple: false,
+    sliceImg: "",
   };
 
   if (!data) return result;
@@ -131,7 +134,7 @@ export function parseDetailSize(
     x: minX,
     y: result.fillPosition[0].y,
     w: maxX - minX,
-    h: result.fillPosition[0].y,
+    h: result.fillSize.h,
   };
 
   result.optionSizes = result.fillPosition.map((item, index) => {

+ 33 - 0
src/render/utils/tool.ts

@@ -329,3 +329,36 @@ export function getFileUrl(url: string) {
   const baseUrl = local.get("baseUrl", "");
   return `${baseUrl}/file/${url}`;
 }
+
+export function getSliceFileUrl(
+  url: string,
+  area: { x: number; y: number; w: number; h: number }
+) {
+  return new Promise((resolve, reject) => {
+    const img = new Image();
+    img.src = getFileUrl(url);
+
+    img.onload = () => {
+      const canvas = document.createElement("canvas");
+      const ctx = canvas.getContext("2d");
+      if (!ctx) return reject(new Error("不支持canvas"));
+
+      canvas.width = area.w;
+      canvas.height = area.h;
+
+      ctx.drawImage(
+        img,
+        area.x,
+        area.y,
+        area.w,
+        area.h,
+        0,
+        0,
+        canvas.width,
+        canvas.height
+      );
+
+      resolve(canvas.toDataURL());
+    };
+  });
+}

+ 123 - 92
src/render/views/RecognizeCheck/RecognizeArbitrate.vue

@@ -30,10 +30,12 @@
             </div>
           </a-col>
           <a-col :span="12">
-            <div class="modal-box modal-origin"></div>
+            <div class="modal-box modal-origin">
+              <img :src="curArbitrateTaskDetail.sliceImg" alt="区域原图" />
+            </div>
           </a-col>
           <a-col :span="6">
-            <div class="modal-box" @click="changePrevTaskDetail">
+            <div class="modal-box is-btn" @click="changePrevTaskDetail">
               <p class="box-title">左键</p>
               <p class="box-cont">上一个</p>
             </div>
@@ -58,7 +60,7 @@
                 v-for="option in curArbitrateTaskDetail.options"
                 :key="option"
                 :type="
-                  curArbitrateTaskDetail.value.result.includes(option)
+                  curArbitrateTaskDetail.result.includes(option)
                     ? 'primary'
                     : 'default'
                 "
@@ -68,7 +70,7 @@
             </div>
           </a-col>
           <a-col :span="6">
-            <div class="modal-box">
+            <div class="modal-box is-btn" @click="onConfirm">
               <p class="box-title">Enter键</p>
               <p class="box-cont">下一个</p>
             </div>
@@ -111,6 +113,7 @@ import {
   RecognizeArbitrateSavePage,
 } from "@/ap/types/recognizeCheck";
 import { parseRecogData, parseDetailSize } from "@/utils/recog/recog";
+import { getSliceFileUrl } from "@/utils/tool";
 
 defineOptions({
   name: "RecognizeArbitrate",
@@ -152,21 +155,33 @@ const curTaskDetailName = computed(() => {
 });
 
 // 获取任务
-let curTask: RecognizeArbitrateItem | null = null;
-async function getTask() {
+// 当前的最新任务
+const recentTask = ref<RecognizeArbitrateItem | null>(null);
+const recentArbitrateTaskDetails = ref([] as RecognizeArbitrateTaskDetail[]);
+const recentPrevTaskId = ref(0);
+const curTask = ref<RecognizeArbitrateItem | null>(null);
+async function getNewTask() {
+  recentPrevTaskId.value = 0;
+
   const res = await recognizeArbitrateTask(groupId).catch(() => false);
-  curTask = res || null;
-  if (!curTask) {
+  curTask.value = res || null;
+  if (!curTask.value) {
+    curArbitrateTaskDetailIndex.value = 0;
+    curArbitrateTaskDetail.value = null;
     return;
   }
 
-  curArbitrateTaskDetails.value = parseDetails(curTask);
+  curArbitrateTaskDetails.value = parseDetails(curTask.value);
+  curArbitrateTaskDetailIndex.value = 0;
+  setCurTaskDetail();
+
+  recentTask.value = curTask.value;
+  recentArbitrateTaskDetails.value = curArbitrateTaskDetails.value;
 }
 
 async function initData() {
-  await getTask();
-  curArbitrateTaskDetailIndex.value = 0;
-  setCurTaskDetail();
+  await getNewTask();
+  updateProgress();
 }
 
 // 解析仲裁任务详情
@@ -178,46 +193,46 @@ function parseDetails(
     const recogData = parseRecogData(page.recogData);
     if (!recogData) return;
 
-    // 缺考
-    if (page.absent) {
-      details.push({
-        ...parseDetailSize(recogData.absent.fill_result[0], "absent", 0, []),
-        result1: page.absent ? page.absent[0] : "",
-        result2: page.absent ? page.absent[1] : "",
-        pageIndex: page.index,
-        groupId: groupId,
-        uri: page.uri,
-      });
-    }
-
-    // 违纪
-    if (page.breach) {
-      details.push({
-        ...parseDetailSize(recogData.breach.fill_result[0], "breach", 0, []),
-        result1: page.breach ? page.breach[0] : "",
-        result2: page.breach ? page.breach[1] : "",
-        pageIndex: page.index,
-        groupId: groupId,
-        uri: page.uri,
-      });
-    }
-
-    // 试卷类型
-    if (page.paperType) {
-      details.push({
-        ...parseDetailSize(
-          recogData.paperType.fill_result[0],
-          "paperType",
-          0,
-          []
-        ),
-        result1: page.paperType ? page.paperType[0] : "",
-        result2: page.paperType ? page.paperType[1] : "",
-        pageIndex: page.index,
-        groupId: groupId,
-        uri: page.uri,
-      });
-    }
+    // // 缺考
+    // if (page.absent) {
+    //   details.push({
+    //     ...parseDetailSize(recogData.absent.fill_result[0], "absent", 0, []),
+    //     result1: page.absent ? page.absent[0] : "",
+    //     result2: page.absent ? page.absent[1] : "",
+    //     pageIndex: page.index,
+    //     groupId: groupId,
+    //     uri: page.uri,
+    //   });
+    // }
+
+    // // 违纪
+    // if (page.breach) {
+    //   details.push({
+    //     ...parseDetailSize(recogData.breach.fill_result[0], "breach", 0, []),
+    //     result1: page.breach ? page.breach[0] : "",
+    //     result2: page.breach ? page.breach[1] : "",
+    //     pageIndex: page.index,
+    //     groupId: groupId,
+    //     uri: page.uri,
+    //   });
+    // }
+
+    // // 试卷类型
+    // if (page.paperType) {
+    //   details.push({
+    //     ...parseDetailSize(
+    //       recogData.paperType.fill_result[0],
+    //       "paperType",
+    //       0,
+    //       []
+    //     ),
+    //     result1: page.paperType ? page.paperType[0] : "",
+    //     result2: page.paperType ? page.paperType[1] : "",
+    //     pageIndex: page.index,
+    //     groupId: groupId,
+    //     uri: page.uri,
+    //   });
+    // }
 
     // 试题
     let index = 0;
@@ -227,8 +242,10 @@ function parseDetails(
         const questionResult = page.question[qRecog.index];
         const arbitrate = questionResult && questionResult.length >= 2;
         if (!arbitrate) return;
+        const sizeData = parseDetailSize(qRecog, "question", qRecog.index, []);
+        if (questionResult[2]) sizeData.result = questionResult[2].split("");
         details.push({
-          ...parseDetailSize(qRecog, "question", qRecog.index, []),
+          ...sizeData,
           result1: questionResult ? questionResult[0] : "",
           result2: questionResult ? questionResult[1] : "",
           pageIndex: page.index,
@@ -243,9 +260,14 @@ function parseDetails(
 }
 
 // 任务执行流程 ----------------- start>
-function setCurTaskDetail() {
-  curArbitrateTaskDetail.value =
-    curArbitrateTaskDetails.value[curArbitrateTaskDetailIndex.value];
+async function setCurTaskDetail() {
+  const val = curArbitrateTaskDetails.value[curArbitrateTaskDetailIndex.value];
+  curArbitrateTaskDetail.value = val;
+
+  curArbitrateTaskDetail.value.sliceImg = await getSliceFileUrl(
+    val.uri,
+    val.fillArea
+  );
 }
 const detailChanging = ref(false);
 async function changeNextTaskDetail() {
@@ -253,8 +275,9 @@ async function changeNextTaskDetail() {
   detailChanging.value = true;
 
   if (nextDetailIsAnotherTask.value) {
-    await submitCurTask().catch(() => {});
-    await getNextTask().catch(() => {});
+    await submitCurTask();
+    await updateProgress();
+    await getNextTask();
     await nextTick(() => {
       detailChanging.value = false;
     });
@@ -292,49 +315,56 @@ async function getPrevTask() {
   let result = true;
   const res = await recognizeArbitrateHistory({
     groupId,
-    id: curTask?.id,
+    id: curTask.value?.id,
   }).catch(() => {
     result = false;
   });
 
-  if (!result) {
-    message.error("获取上一个任务失败!");
+  if (!res) {
+    message.error("获取上一个任务!");
     return;
   }
 
-  if (!res) {
-    message.error("没有上一个任务了!");
-    return;
+  if (!recentPrevTaskId.value) {
+    recentPrevTaskId.value = res.id;
   }
 
-  curTask = res;
-  curArbitrateTaskDetails.value = parseDetails(curTask);
+  curTask.value = res;
+  curArbitrateTaskDetails.value = parseDetails(curTask.value);
   curArbitrateTaskDetailIndex.value = curArbitrateTaskDetails.value.length - 1;
   setCurTaskDetail();
 }
 
 async function getNextTask() {
-  let result = true;
-  const res = await recognizeArbitrateHistory({
-    groupId,
-    id: curTask?.id,
-    next: true,
-  }).catch(() => {
-    result = false;
-  });
+  // 执行待仲裁任务时,取新的任务
+  if (recentTask.value.id === curTask.value.id) {
+    await getNewTask();
+    return;
+  }
 
-  if (!result) {
-    message.error("获取下一个任务失败!");
+  // 执行历史记录最后一个任务时,取缓存的待仲裁任务
+  if (recentPrevTaskId.value === curTask.value.id) {
+    recentPrevTaskId.value = 0;
+    curTask.value = recentTask.value;
+    curArbitrateTaskDetails.value = recentArbitrateTaskDetails.value;
+    curArbitrateTaskDetailIndex.value = 0;
+    setCurTaskDetail();
     return;
   }
 
+  const res = await recognizeArbitrateHistory({
+    groupId,
+    id: curTask.value.id as number,
+    next: true,
+  }).catch(() => null);
+
   if (!res) {
-    message.error("没有下一个任务了!");
+    message.error("未获取到下一个任务!");
     return;
   }
 
-  curTask = res;
-  curArbitrateTaskDetails.value = parseDetails(curTask);
+  curTask.value = res;
+  curArbitrateTaskDetails.value = parseDetails(curTask.value);
 
   curArbitrateTaskDetailIndex.value = 0;
   setCurTaskDetail();
@@ -357,7 +387,7 @@ function selectOption(option: string) {
   }
 
   let result = curArbitrateTaskDetail.value.result.filter(
-    (item) => item === "#"
+    (item) => item !== "#"
   );
   if (result.includes(option)) {
     result = result.filter((item) => item !== option);
@@ -414,7 +444,7 @@ function getTaskTypeResult(
 }
 
 async function submitCurTask() {
-  if (!curTask) return;
+  if (!curTask.value) return;
   const curArbitrateTaskDetailQuestionResults: Record<string, string> = {};
   curArbitrateTaskDetails.value.forEach((item) => {
     if (item.type !== "question") return;
@@ -423,7 +453,7 @@ async function submitCurTask() {
     curArbitrateTaskDetailQuestionResults[k] = item.result.join("");
   });
 
-  const pages = curTask.pages.map((page) => {
+  const pages = curTask.value.pages.map((page) => {
     const absentResult = getTaskTypeResult(
       "absent",
       page.index
@@ -439,15 +469,13 @@ async function submitCurTask() {
     ) as RecognizeArbitrateSavePage["paperType"];
 
     const questionResult: Record<number, string> = {};
-    curTaskDetails.value.forEach((item) => {
-      if (item.type !== "question") return;
-
-      const k = `${item.pageIndex}-${item.index}`;
-      const result = item.arbitrate
-        ? curArbitrateTaskDetailQuestionResults[k]
-        : item.result.join("");
-      questionResult[item.index] = result;
-    });
+    curArbitrateTaskDetails.value
+      .filter((item) => item.pageIndex === page.index)
+      .forEach((item) => {
+        if (item.type !== "question") return;
+
+        questionResult[item.index] = item.result.join("");
+      });
 
     const npage: RecognizeArbitrateSavePage = {
       index: page.index,
@@ -459,8 +487,11 @@ async function submitCurTask() {
     };
     return npage;
   });
+  console.log(pages);
 
-  await recognizeArbitrateSave({ id: curTask.id, pages }).catch(() => false);
+  await recognizeArbitrateSave({ id: curTask.value.id, pages }).catch(
+    () => false
+  );
 }
 
 async function releaseTask() {

+ 91 - 13
src/render/views/RecognizeCheck/index.vue

@@ -11,8 +11,9 @@
       </div>
       <a-button @click="toPage(1)">查询</a-button>
       <a-divider type="vertical" style="margin: 0" />
-      <qm-button type="primary" :icon="h(PlusCircleOutlined)" @click="onAdd"
-        >新增评卷点信息</qm-button
+      <a-button type="primary" @click="onAdd">
+        <template #icon> <PlusCircleOutlined /> </template>
+        新增评卷点信息</a-button
       >
     </a-space>
 
@@ -24,22 +25,59 @@
       :loading="loading"
       bordered
     >
-      <template #bodyCell="{ column, index }">
+      <template #bodyCell="{ column, index, text }">
         <template v-if="column.dataIndex === 'condition'">
           {{ getConditionContent(index) }}
         </template>
+        <template v-if="column.dataIndex === 'stage'">
+          {{ text === "SECOND" ? "第二次复查" : "第一次复查" }}
+        </template>
         <template v-if="column.dataIndex === 'finishCount'">
           {{ getConditionProgress(index) }}
         </template>
         <template v-if="column.dataIndex === 'operation'">
-          <qm-button type="link" @click="onArbitrate(index)">仲裁</qm-button>
-          <qm-button type="link" @click="onEdit(index)">修改</qm-button>
-          <qm-button type="link" @click="onReset(index)">重置</qm-button>
-          <qm-button type="link" @click="onRecheck(index)">复查</qm-button>
-          <qm-button type="link" @click="onBuildTask(index)"
-            >生成任务</qm-button
+          <a-button
+            v-if="checkActionValid(index, 'arbitrate')"
+            type="link"
+            :diabled="checkDangerActionRunning(index)"
+            @click="onArbitrate(index)"
+            >仲裁</a-button
+          >
+          <a-button
+            v-if="checkActionValid(index, 'edit')"
+            type="link"
+            :diabled="checkDangerActionRunning(index)"
+            @click="onEdit(index)"
+            >修改</a-button
+          >
+          <a-button
+            v-if="checkActionValid(index, 'reset')"
+            type="link"
+            :diabled="checkDangerActionRunning(index)"
+            @click="onReset(index)"
+            >重置</a-button
+          >
+          <a-button
+            v-if="checkActionValid(index, 'recheck')"
+            type="link"
+            :diabled="checkDangerActionRunning(index)"
+            @click="onRecheck(index)"
+            >复查</a-button
+          >
+          <a-button
+            v-if="checkActionValid(index, 'buildTask')"
+            type="link"
+            :diabled="checkDangerActionRunning(index)"
+            @click="onBuildTask(index)"
+            >生成任务</a-button
+          >
+          <a-button
+            v-if="checkActionValid(index, 'delete')"
+            type="link"
+            :diabled="checkDangerActionRunning(index)"
+            @click="onDelete(index)"
+            >删除</a-button
           >
-          <qm-button type="link" @click="onDelete(index)">删除</qm-button>
         </template>
       </template>
     </a-table>
@@ -55,7 +93,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, h, reactive, onMounted } from "vue";
+import { ref, reactive, onMounted } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { PlusCircleOutlined } from "@ant-design/icons-vue";
 import { message } from "ant-design-vue";
@@ -102,7 +140,7 @@ const columns: TableProps["columns"] = [
   {
     title: "状态",
     dataIndex: "stage",
-    width: "80px",
+    width: "120px",
   },
   {
     title: "任务总数",
@@ -127,7 +165,7 @@ const columns: TableProps["columns"] = [
   {
     title: "操作",
     dataIndex: "operation",
-    width: "200px",
+    width: "240px",
     customCell: () => {
       return {
         class: "operation-cell",
@@ -140,6 +178,46 @@ const curRow = ref(null as RecognizeCheckTaskSaveParams | null);
 const { dataList, pagination, loading, getList, toPage, deletePageLastItem } =
   useTable<RecognizeCheckListItem>(recognizeCheckListPage, searchModel, false);
 
+type ActionType =
+  | "arbitrate"
+  | "edit"
+  | "reset"
+  | "recheck"
+  | "buildTask"
+  | "delete";
+function checkActionValid(index: number, type: ActionType): boolean {
+  const record = dataList.value[index];
+
+  if (type === "arbitrate") {
+    return record.stage === "SECOND" && record.unarbitrateCount > 0;
+  }
+  if (type === "edit") {
+    return record.totalCount === 0;
+  }
+  if (type === "reset") {
+    return true;
+  }
+  if (type === "recheck") {
+    return (
+      record.stage === "FIRST" &&
+      record.finishCount === record.totalCount &&
+      record.finishCount > 0
+    );
+  }
+  if (type === "buildTask") {
+    return !record.fixed;
+  }
+  if (type === "delete") {
+    return !record.fixed;
+  }
+
+  return true;
+}
+function checkDangerActionRunning(index: number) {
+  const record = dataList.value[index];
+  return record.building || record.deleting || record.reseting;
+}
+
 function getConditionContent(index: number): string {
   const record = dataList.value[index];
   return record.conditions