zhangjie 3 сар өмнө
parent
commit
b87073de66

+ 0 - 2
src/App.vue

@@ -23,8 +23,6 @@ let spinning = $ref(false);
 watch(
   () => markStore.globalMask,
   () => {
-    console.log(markStore.globalMask);
-
     spinning = markStore.globalMask;
   }
 );

+ 1 - 0
src/assets/icons/icon-block.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1741069440344" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3730" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M830.464 63.488q26.624 0 50.176 12.288t41.472 31.232 28.672 43.008 10.752 46.592l0 635.904q0 23.552-11.264 46.592t-30.208 41.472-43.008 30.208-48.64 11.776l-641.024 0q-22.528 0-44.544-10.752t-39.424-28.16-28.16-40.96-10.752-50.176l0-633.856q0-25.6 10.752-50.176t29.696-43.52 43.52-30.208 52.224-11.264l629.76 0z" p-id="3731" fill="#333333"></path></svg>

+ 1 - 0
src/assets/icons/icon-paper.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1741071011561" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11417" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M842.666667 285.866667l-187.733334-187.733334c-14.933333-14.933333-32-21.333333-53.333333-21.333333H234.666667C194.133333 74.666667 160 108.8 160 149.333333v725.333334c0 40.533333 34.133333 74.666667 74.666667 74.666666h554.666666c40.533333 0 74.666667-34.133333 74.666667-74.666666V337.066667c0-19.2-8.533333-38.4-21.333333-51.2z m-44.8 44.8c-2.133333 2.133333-4.266667 0-8.533334 0h-170.666666c-6.4 0-10.666667-4.266667-10.666667-10.666667V149.333333c0-2.133333 0-6.4-2.133333-8.533333 0 0 2.133333 0 2.133333 2.133333l189.866667 187.733334z m-8.533334 554.666666H234.666667c-6.4 0-10.666667-4.266667-10.666667-10.666666V149.333333c0-6.4 4.266667-10.666667 10.666667-10.666666h311.466666c-2.133333 4.266667-2.133333 6.4-2.133333 10.666666v170.666667c0 40.533333 34.133333 74.666667 74.666667 74.666667h170.666666c4.266667 0 6.4 0 10.666667-2.133334V874.666667c0 6.4-4.266667 10.666667-10.666667 10.666666z" fill="#666666" p-id="11418"></path><path d="M640 693.333333H341.333333c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h298.666667c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32zM640 522.666667H341.333333c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h298.666667c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32zM341.333333 416h85.333334c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32h-85.333334c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32z" fill="#666666" p-id="11419"></path></svg>

+ 1 - 0
src/assets/icons/icon-split-block.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1741069962303" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4999" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M941.592381 559.786667h-299.398095a82.407619 82.407619 0 0 0-82.407619 82.407619v299.398095A82.407619 82.407619 0 0 0 642.194286 1024h299.398095A82.407619 82.407619 0 0 0 1024 941.592381v-299.398095a82.407619 82.407619 0 0 0-82.407619-82.407619z m-559.786667 0H82.407619A82.407619 82.407619 0 0 0 0 642.194286v299.398095A82.407619 82.407619 0 0 0 82.407619 1024h299.398095a82.407619 82.407619 0 0 0 82.407619-82.407619v-299.398095a82.407619 82.407619 0 0 0-82.407619-82.407619zM941.592381 0h-299.398095a82.407619 82.407619 0 0 0-82.407619 82.407619v299.398095a82.407619 82.407619 0 0 0 82.407619 82.407619h299.398095A82.407619 82.407619 0 0 0 1024 381.805714V82.407619A82.407619 82.407619 0 0 0 941.592381 0zM381.805714 0H82.407619A82.407619 82.407619 0 0 0 0 82.407619v299.398095a82.407619 82.407619 0 0 0 82.407619 82.407619h299.398095a82.407619 82.407619 0 0 0 82.407619-82.407619V82.407619A82.407619 82.407619 0 0 0 381.805714 0z" fill="#2c2c2c" p-id="5000"></path></svg>

+ 24 - 6
src/features/mark/Mark.vue

@@ -4,7 +4,7 @@
     <mark-tool @allZeroSubmit="allZeroSubmit" />
     <div class="mark-main">
       <mark-history showSearch :getHistory="getHistoryTask" />
-      <mark-body @error="removeBrokenTask" />
+      <mark-body />
       <mark-board-track
         v-if="markStore.isTrackMode"
         @submit="saveTaskToServer"
@@ -55,10 +55,11 @@
 </template>
 
 <script setup lang="ts">
-import { onMounted } from "vue";
+import { onMounted, onUnmounted } from "vue";
 import { useTimers } from "@/setups/useTimers";
 import { getHistoryTask } from "@/api/markPage";
 import { useMarkStore } from "@/store";
+import EventBus from "@/plugins/eventBus";
 
 // components
 import MarkHeader from "./toolbar/MarkHeader.vue";
@@ -87,31 +88,48 @@ import useSetting from "./composables/useSetting";
 import useStatus from "./composables/useStatus";
 import useMarkSubmit from "./composables/useMarkSubmit";
 
+// 试评、正评的页面提示
+let statusSpinning = $ref(true);
+// 是否还在加载statusValue
+let loadingStatusSpinning = $ref(true);
+
 const markStore = useMarkStore();
 useTaskWatch();
-const { updateMarkTask, nextTask, removeBrokenTask } = useMarkTask();
+const { clearTask, nextTask, removeBrokenTask } = useMarkTask();
 const { updateSetting } = useSetting();
-const { statusSpinning, loadingStatusSpinning, updateStatus } = useStatus();
+const { updateStatus } = useStatus();
 const { saveTaskToServer, allZeroSubmit, unselectiveSubmit } = useMarkSubmit();
 
 const { addInterval } = useTimers();
 onMounted(async () => {
   let result = true;
   try {
-    await updateMarkTask();
+    await clearTask();
     await updateSetting();
     await updateStatus();
     await nextTask();
   } catch (error) {
-    loadingStatusSpinning.value = false;
     result = false;
   }
 
+  setTimeout(() => {
+    statusSpinning = false;
+  }, 2000);
+
+  loadingStatusSpinning = false;
   if (!result) return;
 
   // 5秒更新一次tasks
   addInterval(nextTask, 5 * 1000);
 });
+
+onUnmounted(() => {
+  markStore.resetInfo();
+});
+
+EventBus.on("body-render-error", () => {
+  removeBrokenTask();
+});
 </script>
 
 <style>

+ 2 - 7
src/features/mark/MarkBody.vue

@@ -1,18 +1,13 @@
 <template>
-  <MarkBodyBase
-    hasMarkResultToRender
-    :makeTrack="makeTrack"
-    @error="$emit('error')"
-  />
+  <MarkBodyBase hasMarkResultToRender :makeTrack="makeTrack" />
   <MarkBodyCursor />
 </template>
 
 <script setup lang="ts">
 import MarkBodyBase from "./MarkBodyBase.vue";
-import useMakeTrack from "./composables/useMakeTrack";
 import MarkBodyCursor from "./MarkBodyCursor.vue";
 
-defineEmits(["error"]);
+import useMakeTrack from "./composables/useMakeTrack";
 
 const { makeTrack } = useMakeTrack();
 </script>

+ 1 - 1
src/features/mark/MarkDrawTrack.vue

@@ -12,7 +12,7 @@
           :id="`a-${track.mainNumber}-${track.subNumber}-${track.offsetY}-${track.offsetX}`"
           class="tw-m-auto"
         >
-          {{ track.unanswered ? "空" : track.markerScore || track.score }}
+          {{ track.unanswered ? "空" : track.score }}
         </span>
       </div>
     </template>

+ 4 - 1
src/features/mark/composables/useDragSplitPane.ts

@@ -40,7 +40,10 @@ export default function useDragSplitPane() {
     e.preventDefault();
     const dy = e.clientY - pos.y;
     const curHeight = initAreaHeight.value + dy;
-    areaHeight = Math.min(maxAreaHeight, Math.max(curHeight, minAreaHeight));
+    areaHeight.value = Math.min(
+      maxAreaHeight,
+      Math.max(curHeight, minAreaHeight)
+    );
 
     return false;
   };

+ 3 - 4
src/features/mark/composables/useMarkSubmit.ts

@@ -1,13 +1,12 @@
 import { useMarkStore } from "@/store";
 import { saveTask, doUnselectiveType } from "@/api/markPage";
 import { message } from "ant-design-vue";
-import { isNumber } from "lodash-es";
+import { isNumber, cloneDeep } from "lodash-es";
 import { h } from "vue";
 import EventBus from "@/plugins/eventBus";
 import type { Question, MarkResult } from "@/types";
 import useStatus from "./useStatus";
 import useMarkTask from "./useMarkTask";
-import { deepCopy } from "@/utils/utils";
 
 export default function useMarkSubmit() {
   const markStore = useMarkStore();
@@ -98,7 +97,7 @@ export default function useMarkSubmit() {
 
   // 获取保存评卷任务的数据
   function getSaveTaskResult() {
-    const datas = deepCopy(markStore.currentTask.markResult);
+    const datas = cloneDeep(markStore.currentTask.markResult);
     datas.spent = Date.now() - markStore.currentTask.__markStartTime;
     datas.questionList = datas.questionList.map((q) => {
       q.markerTrackList = datas.markerTrackList.filter(
@@ -132,7 +131,7 @@ export default function useMarkSubmit() {
 
     console.log("save task to server");
     void message.loading({ content: "保存评卷任务..." });
-    const res = await saveTask(getSaveTaskResult());
+    const res = await saveTask(getSaveTaskResult()).catch(() => false);
     if (!res) return;
 
     // 故意不在此处同步等待,因为不必等待

+ 14 - 7
src/features/mark/composables/useMarkTask.ts

@@ -10,7 +10,7 @@ export default function useMarkTask() {
 
   let preDrawing = false;
 
-  async function updateMarkTask() {
+  async function clearTask() {
     await clearMarkTask();
   }
 
@@ -48,11 +48,18 @@ export default function useMarkTask() {
   }
 
   function nextTask() {
-    if (!preDrawing) {
-      if (markStore.tasks.length < (markStore.setting.prefetchCount ?? 3)) {
-        if (!markStore.historyOpen)
-          return updateTask().catch((e) => console.log("定时获取任务出错", e));
-      }
+    // console.log("preDrawing", preDrawing);
+
+    if (preDrawing) return;
+    // console.log("markStore.tasks.length", markStore.tasks.length);
+    // console.log(
+    //   "markStore.setting.prefetchCount",
+    //   markStore.setting.prefetchCount
+    // );
+
+    if (markStore.tasks.length < (markStore.setting.prefetchCount ?? 3)) {
+      if (!markStore.historyOpen)
+        return updateTask().catch((e) => console.log("定时获取任务出错", e));
     }
   }
 
@@ -68,7 +75,7 @@ export default function useMarkTask() {
   }
 
   return {
-    updateMarkTask,
+    clearTask,
     updateTask,
     nextTask,
     removeBrokenTask,

+ 1 - 3
src/features/mark/composables/useSliceTrack.ts

@@ -7,8 +7,6 @@ import EventBus from "@/plugins/eventBus";
 import useDraw from "./useDraw";
 // 计算裁切图和裁切图上的分数轨迹和特殊标记轨迹
 export default function useSliceTrack(hasMarkResultToRender = false) {
-  // const emit = defineEmits(["error"]);
-
   const markStore = useMarkStore();
   const { getDataUrlForSliceConfig, getDataUrlForSplitConfig } = useDraw();
 
@@ -330,7 +328,7 @@ export default function useSliceTrack(hasMarkResultToRender = false) {
       sliceImagesWithTrackList.splice(0);
       console.trace("render error ", error);
       // 图片加载出错,自动加载下一个任务
-      // emit("error");
+      EventBus.emit("body-render-error");
     } finally {
       markStore.renderLock = false;
       markStore.globalMask = false;

+ 0 - 25
src/features/mark/composables/useStatus.ts

@@ -1,40 +1,15 @@
 import { useMarkStore } from "@/store";
 import { getStatus } from "@/api/markPage";
-import { useTimers } from "@/setups/useTimers";
-import { ref, watch } from "vue";
 
 export default function useStatus() {
   const markStore = useMarkStore();
 
-  const { addTimeout } = useTimers();
-  // 试评、正评的页面提示
-  const statusSpinning = ref(true);
-  // 是否还在加载statusValue
-  const loadingStatusSpinning = ref(true);
-
   async function updateStatus() {
     const res = await getStatus(markStore.setting.questionModel);
     markStore.setInfo({ status: res.data });
   }
 
-  function setupStatusWatch() {
-    watch(
-      () => markStore.setting.statusValue,
-      () => {
-        if (markStore.setting.statusValue) {
-          loadingStatusSpinning.value = false;
-          addTimeout(() => (statusSpinning.value = false), 3000);
-        }
-      }
-    );
-  }
-
-  setupStatusWatch();
-
   return {
-    statusSpinning,
-    loadingStatusSpinning,
     updateStatus,
-    setupStatusWatch,
   };
 }

+ 16 - 8
src/features/mark/composables/useTaskWatch.ts

@@ -36,17 +36,25 @@ export default function useTaskWatch() {
           statusValue,
           questionList: task.questionList,
           markerTrackList: task.questionList
-            .map((q) =>
-              q.headerTrackList && q.headerTrackList.length
-                ? q.headerTrackList
-                : q.markerTrackList
-            )
+            .map((q) => {
+              const trackList =
+                q.headerTrackList && q.headerTrackList.length
+                  ? q.headerTrackList
+                  : q.markerTrackList;
+              return trackList || [];
+            })
             .flat(),
           markerTagList: task.questionList
-            .map((q) => q.markerTagList || [])
+            .map((q) => {
+              const tagList =
+                q.headerTagList && q.headerTagList.length
+                  ? q.headerTagList
+                  : q.markerTagList;
+              return tagList || [];
+            })
             .flat(),
-          scoreList: task.questionList.map((q) => q.markerScore),
-          markerScore: null, // 后期通过 scoreList 自动更新
+          scoreList: task.questionList.map((q) => q.markerScore || 0),
+          markerScore: 0, // 后期通过 scoreList 自动更新
         };
         task.markResult.markerTrackList.forEach((t) => {
           if (t.unanswered) {

+ 14 - 6
src/features/mark/toolbar/MarkTool.vue

@@ -13,11 +13,11 @@
         v-if="checkValid('paper') && markStore.setting.subject.paperUrl"
         :class="[
           'mark-tool-item',
-          { 'is-active': markStore.setting.uiSetting['answer.modal'] },
+          { 'is-active': markStore.setting.uiSetting['paper.modal'] },
         ]"
         @click="toPaper"
       >
-        <img src="@/assets/icons/icon-answer.svg" />
+        <img src="@/assets/icons/icon-paper.svg" />
         <p>试卷</p>
       </div>
       <div
@@ -147,8 +147,16 @@
       </div>
     </div>
     <div>
-      <div class="mark-tool-item" @click="toSwitchQuestionModal">
-        <img src="@/assets/icons/icon-eye-green.svg" />
+      <div
+        class="mark-tool-item"
+        style="width: 70px"
+        @click="toSwitchQuestionModal"
+      >
+        <img
+          v-if="markStore.setting.questionModel === 'SINGLE'"
+          src="@/assets/icons/icon-split-block.svg"
+        />
+        <img v-else src="@/assets/icons/icon-block.svg" />
         <p>{{ questionModalName }}</p>
       </div>
       <div class="mark-tool-item" @click="toEyecare">
@@ -302,8 +310,8 @@ function toEyecare() {
 // 阅卷模式切换
 const questionModalName = computed(() => {
   return markStore.setting.questionModel === "SINGLE"
-    ? "按小题阅卷"
-    : "阅全部题目";
+    ? "阅全部题目"
+    : "按小题阅卷";
 });
 const toSwitchQuestionModal = () => {
   const qname =

+ 1 - 0
src/plugins/eventBus.ts

@@ -3,6 +3,7 @@ import mitt from "mitt";
 type Events = {
   "should-reload-history": undefined;
   "draw-change": any;
+  "body-render-error": any;
 };
 
 const EventBus = mitt<Events>();

+ 3 - 0
src/types/index.ts

@@ -314,6 +314,7 @@ interface RawQuestion {
   status: "WAIT_ARBITRATE" | "PROBLEM" | "WAITING" | "MARKED";
   /** 轨迹列表 */
   markerTrackList: Array<Track>;
+  // 科组长轨迹
   headerTrackList?: Array<Track>;
   /** 无轨迹情况下评卷员打分信息 */
   markerList: null | Array<{
@@ -326,6 +327,8 @@ interface RawQuestion {
   }>;
   // 特殊标记列表
   markerTagList: Array<SpecialTag> | null;
+  // 科组长特殊标记
+  headerTagList: Array<SpecialTag> | null;
   // 打回原因
   rejectReason: string | null;
   // 打回前给分数据