zhangjie 1 жил өмнө
parent
commit
5274442180

+ 1 - 0
src/assets/icons/icon-special-tag.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="1698820993708" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13413" width="20" height="20" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M736 410.272h16a16 16 0 0 1 16 16v413.92a16 16 0 0 1-16 16H208a16 16 0 0 1-16-16v-624a16 16 0 0 1 16-16h394.592a16 16 0 0 1 16 16v16a16 16 0 0 1-16 16H240v560h480v-381.92a16 16 0 0 1 16-16z m78.16-229.6l11.312 11.328a16 16 0 0 1 0 22.624l-316.8 316.8a16 16 0 0 1-22.608 0l-11.312-11.328a16 16 0 0 1 0-22.624l316.784-316.8a16 16 0 0 1 22.624 0zM328 672.208h304a16 16 0 0 1 16 16v16a16 16 0 0 1-16 16h-304a16 16 0 0 1-16-16v-16a16 16 0 0 1 16-16z m8-104h64a16 16 0 0 1 16 16v16a16 16 0 0 1-16 16h-64a16 16 0 0 1-16-16v-16a16 16 0 0 1 16-16z" fill="#262626" p-id="13414"></path></svg>

+ 1 - 43
src/features/mark/MarkBoardTrack.vue

@@ -114,49 +114,6 @@
       </div>
     </div>
     <div class="board-scores">
-      <div
-        :class="[
-          'board-score',
-          'score-icon',
-          { 'is-current': Object.is(store.currentScore, -0) },
-        ]"
-        @click="chooseScore(-0)"
-      >
-        <img src="@/assets/icons/icon-right.svg" />
-      </div>
-      <div
-        :class="[
-          'board-score',
-          'score-icon',
-          { 'is-current': Object.is(store.currentScore, -0) },
-        ]"
-        @click="chooseScore(-0)"
-      >
-        <img src="@/assets/icons/icon-wrong.svg" />
-      </div>
-      <div
-        :class="[
-          'board-score',
-          'score-icon',
-          { 'is-current': Object.is(store.currentScore, -0) },
-        ]"
-        title="标记圆圈"
-        @click="chooseScore(-0)"
-      >
-        <img src="@/assets/icons/icon-circle.svg" />
-      </div>
-      <div
-        :class="[
-          'board-score',
-          'score-icon',
-          { 'is-current': Object.is(store.currentScore, -0) },
-        ]"
-        title="标记文本"
-        @click="chooseScore(-0)"
-      >
-        <img class="icon-text" src="@/assets/icons/icon-text.svg" />
-      </div>
-      <br />
       <div
         v-for="(s, i) in questionScoreSteps.slice(1)"
         :key="i"
@@ -336,6 +293,7 @@ function chooseScore(score: number) {
     store.currentScore = score;
     store.currentSpecialTag = undefined;
   }
+  store.currentTextSign = false;
 }
 
 let keyPressTimestamp = 0;

+ 0 - 17
src/features/mark/MarkBody.vue

@@ -156,23 +156,6 @@ const makeSpecialTagTrack = (
   track.positionY =
     (track.offsetY - item.dy + item.accumTopHeight) / theFinalHeight;
 
-  // const isIllegalRange = (testNum: number, min: number, max: number) => {
-  //   return testNum < min || testNum > max;
-  // };
-
-  // if (
-  //   isIllegalRange(track.offsetX, 0, target.naturalWidth) ||
-  //   isIllegalRange(track.offsetY, 0, target.naturalHeight) ||
-  //   isIllegalRange(track.positionX, 0, 1) ||
-  //   isIllegalRange(track.positionY, 0, 1)
-  // ) {
-  //   console.error("错误的track", track);
-  //   void message.error("系统错误,请联系管理员!");
-  // }
-  // if (track.offsetX > item.effectiveWidth + item.dx) {
-  //   console.log("不在有效宽度内,轨迹不生效");
-  //   return;
-  // }
   if (
     item.tagList.some((t) => {
       return (

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

@@ -1,5 +1,5 @@
 <template>
-  <div :class="['mark-history', { hide: store.historyOpen }]">
+  <div :class="['mark-history', { hide: !store.historyOpen }]">
     <div class="mark-history-title">
       {{ title }}
     </div>

+ 14 - 0
src/features/mark/MarkTool.vue

@@ -41,6 +41,16 @@
         <img src="@/assets/icons/icon-shortcut.svg" />
         <p>快捷键</p>
       </div>
+      <div
+        :class="[
+          'mark-tool-item',
+          { 'is-active': store.setting.uiSetting['specialTag.modal'] },
+        ]"
+        @click="toSpecialTag"
+      >
+        <img src="@/assets/icons/icon-special-tag.svg" />
+        <p>特殊标记</p>
+      </div>
     </div>
     <div>
       <div class="mark-tool-item" @click="toAllZero">
@@ -96,6 +106,10 @@ const toShortcut = () => {
   store.setting.uiSetting["shortCut.modal"] =
     !store.setting.uiSetting["shortCut.modal"];
 };
+const toSpecialTag = () => {
+  store.setting.uiSetting["specialTag.modal"] =
+    !store.setting.uiSetting["specialTag.modal"];
+};
 
 const toAllZero = () => {
   Modal.confirm({

+ 92 - 71
src/features/mark/SpecialTagModal.vue

@@ -2,67 +2,97 @@
   <qm-dialog
     v-if="store.setting.uiSetting['specialTag.modal']"
     top="10%"
-    width="320px"
-    height="180px"
+    width="362px"
+    height="200px"
     title="特殊标记"
     :enableResize="false"
+    customClass="board-track-dialog"
     @close="close"
   >
-    <div
-      class="tw-flex tw-place-content-between tw-m-4 tw-text-xl tw-cursor-pointer"
-      style="color: var(--app-main-text-color)"
-    >
-      <div
-        :class="[store.currentSpecialTag === '√' && 'tag-selected', 'tag']"
-        @click="toggleTag('√')"
-      >
-        √
+    <div class="mark-board-track" style="height: 100%">
+      <div class="board-scores">
+        <div
+          :class="[
+            'board-score',
+            'score-icon',
+            { 'is-current': Object.is(store.currentSpecialTag, '√') },
+          ]"
+          @click="chooseSpecialTag('√')"
+        >
+          <img src="@/assets/icons/icon-right.svg" />
+        </div>
+        <div
+          :class="[
+            'board-score',
+            'score-icon',
+            { 'is-current': Object.is(store.currentSpecialTag, '乄') },
+          ]"
+          @click="chooseSpecialTag('乄')"
+        >
+          乄
+        </div>
+        <div
+          :class="[
+            'board-score',
+            'score-icon',
+            { 'is-current': Object.is(store.currentSpecialTag, 'X') },
+          ]"
+          @click="chooseSpecialTag('X')"
+        >
+          <img src="@/assets/icons/icon-wrong.svg" />
+        </div>
+        <div
+          :class="[
+            'board-score',
+            'score-icon',
+            { 'is-current': Object.is(store.currentSpecialTag, '○') },
+          ]"
+          title="标记圆圈"
+          @click="chooseSpecialTag('○')"
+        >
+          <img src="@/assets/icons/icon-circle.svg" />
+        </div>
+        <div
+          :class="[
+            'board-score',
+            'score-icon',
+            { 'is-current': store.currentTextSign },
+          ]"
+          title="标记文本"
+          @click="chooseTextSign"
+        >
+          <img class="icon-text" src="@/assets/icons/icon-text.svg" />
+        </div>
       </div>
-      <div
-        :class="[store.currentSpecialTag === 'X' && 'tag-selected', 'tag']"
-        @click="toggleTag('X')"
-      >
-        X
-      </div>
-      <div
-        :class="[store.currentSpecialTag === '乄' && 'tag-selected', 'tag']"
-        @click="toggleTag('乄')"
-      >
-        乄
-      </div>
-      <div
-        :class="[store.currentSpecialTag === '——' && 'tag-selected', 'tag']"
-        style="width: 70px; border-radius: 5px"
-        @click="toggleTag('——')"
-      >
-        <u>下划线</u>
-      </div>
-    </div>
 
-    <div class="tw-flex tw-place-content-between tw-mt-8 tw-mx-2">
-      <qm-button
-        type="primary"
-        shape="round"
-        size="large"
-        style="
-          background-color: var(--app-undo-button-bg-color);
-          border-color: var(--app-undo-button-bg-color);
-        "
-        :clickTimeout="300"
-        @click="clearLatestTagOfCurrentTask"
-      >
-        回退
-      </qm-button>
+      <div class="board-footer">
+        <qm-button
+          class="board-goback"
+          :clickTimeout="300"
+          @click="clearLatestTagOfCurrentTask"
+        >
+          <template #icon>
+            <img src="@/assets/icons/icon-goback.svg" />
+          </template>
+          回退
+        </qm-button>
 
-      <qm-button
-        type="primary"
-        shape="round"
-        size="large"
-        :clickTimeout="300"
-        @click="clearAllTagsOfCurrentTask"
-      >
-        清除全部
-      </qm-button>
+        <qm-button
+          class="board-clear"
+          :clickTimeout="300"
+          data-test="clear-score"
+          @click="clearAllTagsOfCurrentTask"
+        >
+          <template #icon>
+            <img class="icon-common" src="@/assets/icons/icon-clear.svg" />
+            <img
+              class="icon-active"
+              src="@/assets/icons/icon-clear-white.svg"
+            />
+          </template>
+          清空
+        </qm-button>
+      </div>
     </div>
   </qm-dialog>
 </template>
@@ -80,32 +110,23 @@ function clearAllTagsOfCurrentTask() {
   store.currentTask.markResult.specialTagList = [];
 }
 
-const toggleTag = (tagName: string) => {
+function chooseSpecialTag(tagName: string) {
   if (store.currentSpecialTag === tagName) {
     store.currentSpecialTag = undefined;
   } else {
     store.currentSpecialTag = tagName;
     store.currentScore = undefined;
   }
-};
+  store.currentTextSign = false;
+}
+function chooseTextSign() {
+  store.currentTextSign = !store.currentTextSign;
+  store.currentSpecialTag = undefined;
+  store.currentScore = undefined;
+}
 
 const close = () => {
   store.currentSpecialTag = undefined;
   store.setting.uiSetting["specialTag.modal"] = false;
 };
 </script>
-
-<style>
-.tag {
-  width: 30px;
-  height: 30px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  padding: 2px;
-  border-radius: 30px;
-}
-.tag-selected {
-  background-color: var(--app-main-bg-color);
-}
-</style>

+ 226 - 225
src/store/store.ts

@@ -1,225 +1,226 @@
-import { Setting, MarkStore, AdminPageSetting, Task } from "@/types";
-import { watch } from "vue";
-import { defineStore } from "pinia";
-
-const initState: MarkStore = {
-  setting: {
-    mode: "TRACK",
-    examType: "SCAN_IMAGE",
-    forceMode: false,
-    sheetView: false,
-    autoScroll: false,
-    sheetConfig: [],
-    enableAllZero: false,
-    enableSplit: true,
-    fileServer: "",
-    userName: "",
-    subject: <Setting["subject"]>{},
-    forceSpecialTag: false,
-    uiSetting: {
-      "answer.paper.scale": 1,
-      "score.board.collapse": false,
-      "normal.mode": "keyboard",
-      "paper.modal": false,
-      "answer.modal": false,
-      "minimap.modal": false,
-      "specialTag.modal": false,
-      "shortCut.modal": false,
-      "score.fontSize.scale": 1,
-    },
-    statusValue: null,
-    problemTypes: [],
-    groupNumber: -987654, // 默认不可能的值
-    groupTitle: "",
-    topCount: 0,
-    splitConfig: [],
-    prefetchCount: 3,
-    startTime: 0,
-    endTime: 0,
-    selective: false,
-  },
-  status: <MarkStore["status"]>{},
-  groups: [],
-  tasks: [],
-  message: null,
-  currentTask: undefined,
-  currentQuestion: undefined,
-  currentScore: undefined,
-  currentSpecialTag: undefined,
-  historyOpen: false,
-  historyTasks: [],
-  removeScoreTracks: [],
-  focusTracks: [],
-  maxModalZIndex: 1020,
-  minimapScrollToX: 0,
-  minimapScrollToY: 0,
-  allPaperModal: false,
-  sheetViewModal: false,
-  globalMask: false,
-};
-
-const useMarkStore = defineStore("mark", {
-  state: () => {
-    return initState;
-  },
-  getters: {
-    /** 获得statusValue的中文名 */
-    getStatusValueName() {
-      const st = store.setting.statusValue;
-      if (!st) return "";
-      if (st === "FORMAL") return "正评";
-      if (st === "TRIAL") return "试评";
-      return "";
-    },
-    /** 当前任务。确保不为空,需在上文已经检查过 store.currentTask 不为空 */
-    currentTaskEnsured(): Task {
-      return store.currentTask;
-    },
-    /** 是否是评卷端的轨迹模式 */
-    isTrackMode(): boolean {
-      return store.setting.mode && store.setting.mode === "TRACK";
-    },
-    /** 评卷端的轨迹模式显示轨迹 && 管理后台都显示轨迹 */
-    shouldShowTrack(): boolean {
-      // FIXME: 不是最优雅的方式来判断是否是阅卷端
-      const isWebMark = location.pathname === "/web/mark";
-      return !isWebMark || this.isTrackMode;
-    },
-    /* 是否是扫描阅卷 */
-    isScanImage(): boolean {
-      return this.setting.examType === "SCAN_IMAGE";
-    },
-    isMultiMedia(): boolean {
-      return this.setting.examType === "MULTI_MEDIA";
-    },
-    /* 返回正在评卷的状态 '' | 回评 | 打回 */
-    getMarkStatus(): string {
-      if (!this.currentTask) return "";
-      if (this.currentTask.previous) return "回评";
-      if (this.currentTask.rejected) return "打回";
-
-      return store.getStatusValueName;
-    },
-    shouldShowMarkBoardKeyBoard(): boolean {
-      return (
-        store.setting.mode === "COMMON" &&
-        store.setting.uiSetting["normal.mode"] === "keyboard"
-      );
-    },
-    shouldShowMarkBoardMouse(): boolean {
-      return (
-        store.setting.mode === "COMMON" &&
-        store.setting.uiSetting["normal.mode"] === "mouse"
-      );
-    },
-    isScoreBoardCollapsed(): boolean {
-      return store.setting.uiSetting["score.board.collapse"];
-    },
-    isScoreBoardVisible(): boolean {
-      return !store.setting.uiSetting["score.board.collapse"];
-    },
-  },
-  actions: {
-    initSetting(adminPageSetting: AdminPageSetting): void {
-      Object.assign(this.setting, adminPageSetting, {
-        mode: "COMMON" as Setting["mode"],
-        uiSetting: {
-          "answer.paper.scale": 1,
-          "score.board.collapse": false,
-          "normal.mode": "keyboard",
-          "score.fontSize.scale": 1,
-        } as Setting["uiSetting"],
-      });
-      const fileServer = this.setting.fileServer;
-      if (this.setting.subject?.answerUrl) {
-        this.setting.subject.answerUrl =
-          fileServer + this.setting.subject?.answerUrl;
-      }
-      if (this.setting.subject?.paperUrl) {
-        this.setting.subject.paperUrl =
-          fileServer + this.setting.subject?.paperUrl;
-      }
-    },
-    toggleHistory(): void {
-      this.historyOpen = !this.historyOpen;
-    },
-    toggleScoreBoard(): void {
-      this.setting.uiSetting["score.board.collapse"] =
-        !this.setting.uiSetting["score.board.collapse"];
-    },
-  },
-});
-
-export let store = null as unknown as ReturnType<typeof useMarkStore>;
-
-export const initMarkStore = () => {
-  store = useMarkStore();
-
-  watch(
-    () => store.currentTask,
-    () => {
-      // 初始化 task.markResult ,始终保证 task 下有 markResult
-      // 1. 评卷时,如果没有 markResult ,则初始化一个 markResult 给它
-      // 1. 回评时,先清空它的 markResult ,然后初始化一个 markResult 给它
-      if (!store.currentTask) return;
-
-      const task = store.currentTask;
-      if (task.previous && task.markResult) {
-        task.markResult = undefined;
-      }
-      if (!task.markResult) {
-        // 管理后台可能不设置 questionList, 而且它不用 markResult
-        if (!task.questionList) {
-          task.questionList = [];
-          // return;
-        }
-        // 初始化 __index
-        task.questionList.forEach((q, i, ar) => (ar[i].__index = i));
-
-        task.__markStartTime = Date.now();
-        const statusValue = store.setting.statusValue;
-        const { libraryId, studentId } = task;
-        task.markResult = {
-          statusValue: statusValue,
-          libraryId: libraryId,
-          studentId: studentId,
-          spent: 0,
-
-          trackList: task.questionList.map((q) => q.trackList).flat(),
-          specialTagList: [...(task.specialTagList ?? [])],
-          scoreList: task.questionList.map((q) => q.score),
-          markerScore: null, // 后期通过 scoreList 自动更新
-
-          problem: false,
-          problemTypeId: 0,
-          unselective: false,
-        };
-        task.markResult.trackList.forEach((t) => {
-          if (t.unanswered) {
-            t.score = -0;
-          }
-        });
-      }
-    }
-  );
-
-  // 唯一根据 scoreList 自动更新 markerScore
-  watch(
-    () => store.currentTask?.markResult.scoreList,
-    () => {
-      if (!store.currentTask) return;
-      const scoreList = store.currentTask.markResult.scoreList.filter(
-        (v) => v !== null
-      );
-      const result =
-        scoreList.length === 0
-          ? null
-          : scoreList.reduce((acc, v) => (acc += Math.round(v * 1000)), 0) /
-            1000;
-      store.currentTask.markResult.markerScore = result;
-    },
-    { deep: true }
-  );
-
-  // scoreList 被 trackList 和用户手动更新
-};
+import { Setting, MarkStore, AdminPageSetting, Task } from "@/types";
+import { watch } from "vue";
+import { defineStore } from "pinia";
+
+const initState: MarkStore = {
+  setting: {
+    mode: "TRACK",
+    examType: "SCAN_IMAGE",
+    forceMode: false,
+    sheetView: false,
+    autoScroll: false,
+    sheetConfig: [],
+    enableAllZero: false,
+    enableSplit: true,
+    fileServer: "",
+    userName: "",
+    subject: <Setting["subject"]>{},
+    forceSpecialTag: false,
+    uiSetting: {
+      "answer.paper.scale": 1,
+      "score.board.collapse": false,
+      "normal.mode": "keyboard",
+      "paper.modal": false,
+      "answer.modal": false,
+      "minimap.modal": false,
+      "specialTag.modal": false,
+      "shortCut.modal": false,
+      "score.fontSize.scale": 1,
+    },
+    statusValue: null,
+    problemTypes: [],
+    groupNumber: -987654, // 默认不可能的值
+    groupTitle: "",
+    topCount: 0,
+    splitConfig: [],
+    prefetchCount: 3,
+    startTime: 0,
+    endTime: 0,
+    selective: false,
+  },
+  status: <MarkStore["status"]>{},
+  groups: [],
+  tasks: [],
+  message: null,
+  currentTask: undefined,
+  currentQuestion: undefined,
+  currentScore: undefined,
+  currentSpecialTag: undefined,
+  currentTextSign: false,
+  historyOpen: false,
+  historyTasks: [],
+  removeScoreTracks: [],
+  focusTracks: [],
+  maxModalZIndex: 1020,
+  minimapScrollToX: 0,
+  minimapScrollToY: 0,
+  allPaperModal: false,
+  sheetViewModal: false,
+  globalMask: false,
+};
+
+const useMarkStore = defineStore("mark", {
+  state: () => {
+    return initState;
+  },
+  getters: {
+    /** 获得statusValue的中文名 */
+    getStatusValueName() {
+      const st = store.setting.statusValue;
+      if (!st) return "";
+      if (st === "FORMAL") return "正评";
+      if (st === "TRIAL") return "试评";
+      return "";
+    },
+    /** 当前任务。确保不为空,需在上文已经检查过 store.currentTask 不为空 */
+    currentTaskEnsured(): Task {
+      return store.currentTask;
+    },
+    /** 是否是评卷端的轨迹模式 */
+    isTrackMode(): boolean {
+      return store.setting.mode && store.setting.mode === "TRACK";
+    },
+    /** 评卷端的轨迹模式显示轨迹 && 管理后台都显示轨迹 */
+    shouldShowTrack(): boolean {
+      // FIXME: 不是最优雅的方式来判断是否是阅卷端
+      const isWebMark = location.pathname === "/web/mark";
+      return !isWebMark || this.isTrackMode;
+    },
+    /* 是否是扫描阅卷 */
+    isScanImage(): boolean {
+      return this.setting.examType === "SCAN_IMAGE";
+    },
+    isMultiMedia(): boolean {
+      return this.setting.examType === "MULTI_MEDIA";
+    },
+    /* 返回正在评卷的状态 '' | 回评 | 打回 */
+    getMarkStatus(): string {
+      if (!this.currentTask) return "";
+      if (this.currentTask.previous) return "回评";
+      if (this.currentTask.rejected) return "打回";
+
+      return store.getStatusValueName;
+    },
+    shouldShowMarkBoardKeyBoard(): boolean {
+      return (
+        store.setting.mode === "COMMON" &&
+        store.setting.uiSetting["normal.mode"] === "keyboard"
+      );
+    },
+    shouldShowMarkBoardMouse(): boolean {
+      return (
+        store.setting.mode === "COMMON" &&
+        store.setting.uiSetting["normal.mode"] === "mouse"
+      );
+    },
+    isScoreBoardCollapsed(): boolean {
+      return store.setting.uiSetting["score.board.collapse"];
+    },
+    isScoreBoardVisible(): boolean {
+      return !store.setting.uiSetting["score.board.collapse"];
+    },
+  },
+  actions: {
+    initSetting(adminPageSetting: AdminPageSetting): void {
+      Object.assign(this.setting, adminPageSetting, {
+        mode: "COMMON" as Setting["mode"],
+        uiSetting: {
+          "answer.paper.scale": 1,
+          "score.board.collapse": false,
+          "normal.mode": "keyboard",
+          "score.fontSize.scale": 1,
+        } as Setting["uiSetting"],
+      });
+      const fileServer = this.setting.fileServer;
+      if (this.setting.subject?.answerUrl) {
+        this.setting.subject.answerUrl =
+          fileServer + this.setting.subject?.answerUrl;
+      }
+      if (this.setting.subject?.paperUrl) {
+        this.setting.subject.paperUrl =
+          fileServer + this.setting.subject?.paperUrl;
+      }
+    },
+    toggleHistory(): void {
+      this.historyOpen = !this.historyOpen;
+    },
+    toggleScoreBoard(): void {
+      this.setting.uiSetting["score.board.collapse"] =
+        !this.setting.uiSetting["score.board.collapse"];
+    },
+  },
+});
+
+export let store = null as unknown as ReturnType<typeof useMarkStore>;
+
+export const initMarkStore = () => {
+  store = useMarkStore();
+
+  watch(
+    () => store.currentTask,
+    () => {
+      // 初始化 task.markResult ,始终保证 task 下有 markResult
+      // 1. 评卷时,如果没有 markResult ,则初始化一个 markResult 给它
+      // 1. 回评时,先清空它的 markResult ,然后初始化一个 markResult 给它
+      if (!store.currentTask) return;
+
+      const task = store.currentTask;
+      if (task.previous && task.markResult) {
+        task.markResult = undefined;
+      }
+      if (!task.markResult) {
+        // 管理后台可能不设置 questionList, 而且它不用 markResult
+        if (!task.questionList) {
+          task.questionList = [];
+          // return;
+        }
+        // 初始化 __index
+        task.questionList.forEach((q, i, ar) => (ar[i].__index = i));
+
+        task.__markStartTime = Date.now();
+        const statusValue = store.setting.statusValue;
+        const { libraryId, studentId } = task;
+        task.markResult = {
+          statusValue: statusValue,
+          libraryId: libraryId,
+          studentId: studentId,
+          spent: 0,
+
+          trackList: task.questionList.map((q) => q.trackList).flat(),
+          specialTagList: [...(task.specialTagList ?? [])],
+          scoreList: task.questionList.map((q) => q.score),
+          markerScore: null, // 后期通过 scoreList 自动更新
+
+          problem: false,
+          problemTypeId: 0,
+          unselective: false,
+        };
+        task.markResult.trackList.forEach((t) => {
+          if (t.unanswered) {
+            t.score = -0;
+          }
+        });
+      }
+    }
+  );
+
+  // 唯一根据 scoreList 自动更新 markerScore
+  watch(
+    () => store.currentTask?.markResult.scoreList,
+    () => {
+      if (!store.currentTask) return;
+      const scoreList = store.currentTask.markResult.scoreList.filter(
+        (v) => v !== null
+      );
+      const result =
+        scoreList.length === 0
+          ? null
+          : scoreList.reduce((acc, v) => (acc += Math.round(v * 1000)), 0) /
+            1000;
+      store.currentTask.markResult.markerScore = result;
+    },
+    { deep: true }
+  );
+
+  // scoreList 被 trackList 和用户手动更新
+};

+ 11 - 2
src/styles/page.less

@@ -575,13 +575,22 @@
     cursor: pointer;
 
     &:hover {
+      border-color: #165dff;
+      color: #165dff;
+    }
+
+    &.is-current {
       border-color: #165dff;
       background-color: #165dff;
       color: #fff;
     }
 
-    &.score-icon:hover {
-      background-color: #fff;
+    &.score-icon {
+      &.is-current {
+        img {
+          filter: brightness(1000%);
+        }
+      }
     }
 
     > img {

+ 2 - 0
src/types/index.ts

@@ -40,6 +40,8 @@ export interface MarkStore {
   currentQuestion?: Question;
   currentScore?: number;
   currentSpecialTag?: string;
+  // 是否开启文本标记
+  currentTextSign?: boolean;
   /** 是否打开回评侧边栏 */
   historyOpen: boolean;
   historyTasks: Array<Task>;