刘洋 1 жил өмнө
parent
commit
b098199425

+ 27 - 0
src/api/scoreVerify.ts

@@ -0,0 +1,27 @@
+import { httpApp } from "@/plugins/axiosApp";
+import { AdminPageSettingForImport, CommonResponse, Task } from "@/types";
+
+export async function getInspectedSettingOfImportInspect(studentId?: string) {
+  const form = new FormData();
+  studentId && form.append("studentId", studentId);
+  return httpApp.post<AdminPageSettingForImport>(
+    "/admin/exam/score/verify/needverify",
+    form
+  );
+}
+
+export async function getSingleInspectedTaskOfImportInspect(studentId: string) {
+  const form = new FormData();
+  studentId && form.append("studentId", studentId);
+  return httpApp.post<any>("/admin/exam/score/verify/info", form);
+}
+
+export async function saveInspectedTaskOfImportInspect(
+  studentId: string,
+  isTag: string
+) {
+  const form = new FormData();
+  form.append("studentId", studentId);
+  form.append("isTag", isTag);
+  return httpApp.post<CommonResponse>("/admin/exam/score/verify/tag", form);
+}

+ 3 - 2
src/components/CommonMarkHeader.vue

@@ -4,7 +4,7 @@
     class="tw-flex tw-gap-4 tw-justify-start tw-items-center header-container"
   >
     <div
-      v-if="!isSingleStudent"
+      v-if="!isSingleStudent && !notShowHistoryToggle"
       class="tw-flex tw-place-content-center tw-cursor-pointer tw-relative menu"
       style="margin-right: -16px"
       :class="[store.historyOpen && 'menu-toggled']"
@@ -55,7 +55,7 @@
       <slot />
     </div>
 
-    <div class="tw-flex-1"></div>
+    <!-- <div class="tw-flex-1"></div> -->
     <a-popover
       v-if="store.isScanImage"
       title="小助手"
@@ -182,6 +182,7 @@ const {
   clearTasks?: () => Promise<AxiosResponse<void, any>>;
   showPaperAndAnswer?: boolean;
   showScoreBoard?: boolean;
+  notShowHistoryToggle?: boolean;
 }>();
 
 async function updateClearTask() {

+ 8 - 2
src/components/QmButton.vue

@@ -15,7 +15,7 @@ export default {
 </script>
 
 <script setup lang="ts">
-import { reactive, useAttrs } from "vue";
+import { reactive, useAttrs, watch } from "vue";
 
 // 默认loading一秒,以免重复点击
 const { clickTimeout = 1000 } = defineProps<{ clickTimeout?: number }>();
@@ -25,7 +25,13 @@ let newAttrs: typeof attrs = reactive({});
 Object.assign(newAttrs, attrs);
 let parentOnClick = attrs.onClick as (e: Event) => void;
 delete newAttrs["onClick"];
-
+watch(
+  () => attrs,
+  () => {
+    Object.assign(newAttrs, attrs);
+  },
+  { deep: true }
+);
 let inInterval = $ref(false);
 const insideClick = (e: PointerEvent | MouseEvent) => {
   // console.log(e.pointerType);

+ 5 - 3
src/features/mark/MarkDrawTrack.vue

@@ -2,7 +2,7 @@
   <transition-group name="track-score" tag="div">
     <template v-for="track in trackList">
       <div
-        v-if="store.shouldShowTrack && !track.isByMultMark"
+        v-if="store.shouldShowTrack && (doubleTrack || !track.isByMultMark)"
         :key="`key-${track.mainNumber}-${track.subNumber}-${track.offsetY}-${track.offsetX}`"
         class="score-container"
         :class="[focusedTrack(track) && 'score-animation']"
@@ -28,10 +28,12 @@
 
 <script setup lang="ts">
 import type { SpecialTag, Track } from "@/types";
-import { toRefs, watch, nextTick } from "vue";
+import { toRefs, watch, nextTick, computed } from "vue";
 import { store } from "@/store/store";
 import { message } from "ant-design-vue";
-
+const doubleTrack = computed(() => {
+  return !!store.setting?.doubleTrack;
+});
 const props = defineProps<{
   trackList: Array<Track>;
   specialTagList: Array<SpecialTag>;

+ 155 - 54
src/features/mark/MarkHeader.vue

@@ -1,26 +1,35 @@
 <template>
   <div
-v-if="store.setting && store.setting.subject.name"
-    class="tw-flex tw-gap-2 tw-justify-between tw-items-center header-container">
+    v-if="store.setting && store.setting.subject.name"
+    class="tw-flex tw-gap-2 tw-justify-between tw-items-center header-container"
+  >
     <a-tooltip>
       <template #title>回评</template>
       <div
-class="tw-flex tw-place-content-center tw-cursor-pointer tw-relative menu"
-        :class="[store.historyOpen && 'menu-toggled']" @click="store.toggleHistory">
+        class="tw-flex tw-place-content-center tw-cursor-pointer tw-relative menu"
+        :class="[store.historyOpen && 'menu-toggled']"
+        @click="store.toggleHistory"
+      >
         <span class="tw-inline-flex tw-place-content-center">
-          <img src="./images/left-menu.svg" :class="[store.historyOpen && 'svg-red']" />
+          <img
+            src="./images/left-menu.svg"
+            :class="[store.historyOpen && 'svg-red']"
+          />
         </span>
         <div v-if="store.historyOpen" class="triangle"></div>
       </div>
     </a-tooltip>
     <div style="max-width: 12%; margin-left: -20px">
       <a
-class="tw-text-white tw-block tw-overflow-ellipsis tw-overflow-hidden tw-whitespace-nowrap header-big-text"
-        :title="store.setting.subject.name" href="/mark/subject-select" @dragstart.prevent>
+        class="tw-text-white tw-block tw-overflow-ellipsis tw-overflow-hidden tw-whitespace-nowrap header-big-text"
+        :title="store.setting.subject.name"
+        href="/mark/subject-select"
+        @dragstart.prevent
+      >
         {{
-        `${store.setting.subject.code ?? ""}-${
-        store.setting.subject.name ?? ""
-        }`
+          `${store.setting.subject.code ?? ""}-${
+            store.setting.subject.name ?? ""
+          }`
         }}
       </a>
     </div>
@@ -32,8 +41,10 @@ class="tw-text-white tw-block tw-overflow-ellipsis tw-overflow-hidden tw-whitesp
           待仲裁{{ store.status.arbitrateCount }}
         </template>
         <img
-src="./images/problems.svg" :class="questionMarkShouldChange && 'question-mark-animation'"
-          @mouseover="questionMarkShouldChange = false" />
+          src="./images/problems.svg"
+          :class="questionMarkShouldChange && 'question-mark-animation'"
+          @mouseover="questionMarkShouldChange = false"
+        />
       </a-tooltip>
     </div>
     <!-- <div v-if="store.setting.statusValue === 'TRIAL'">试评</div> -->
@@ -44,18 +55,41 @@ src="./images/problems.svg" :class="questionMarkShouldChange && 'question-mark-a
           {{ store.currentTask?.secretNumber ?? "-" }}
         </span>
       </div>
-      <div v-if="store.currentTask && store.currentTask.objectiveScore !== null">
+      <div
+        v-if="
+          store.currentTask &&
+          store.currentTask.objectiveScore !== null &&
+          !!store.setting?.showObjectiveScore
+        "
+      >
         <span class="header-small-text">客观分</span>
         <span class="highlight-text">
           {{ store.currentTask.objectiveScore }}
         </span>
       </div>
-      <div v-if="props.showTotalScore && store.currentTask && store.currentTask.objectiveScore !== null" style="margin-left: 0.5em">
+      <div
+        v-if="
+          props.showTotalScore &&
+          store.currentTask &&
+          store.currentTask.objectiveScore !== null
+        "
+        style="margin-left: 0.5em"
+      >
         <span class="header-small-text">成绩</span>
-        <span class="highlight-text" style="margin-left: 0.2em; margin-top: 0.1em">
+        <span
+          class="highlight-text"
+          style="margin-left: 0.2em; margin-top: 0.1em"
+        >
           {{
-            parseFloat((((Math.max(store.currentTask.objectiveScore || 0, 0) * 100 +
-          Math.max(store.currentTask.markResult?.markerScore || 0, 0) * 100) | 0) / 100).toFixed(2))
+            parseFloat(
+              (
+                ((Math.max(store.currentTask.objectiveScore || 0, 0) * 100 +
+                  Math.max(store.currentTask.markResult?.markerScore || 0, 0) *
+                    100) |
+                  0) /
+                100
+              ).toFixed(2)
+            )
           }}
         </span>
       </div>
@@ -64,7 +98,11 @@ src="./images/problems.svg" :class="questionMarkShouldChange && 'question-mark-a
       <span style="display: inline-flex; height: 16px; min-width: 55px">
         <span class="header-small-text">已评</span>
         <transition-group name="count-animation" tag="span">
-          <span :key="store.status.personCount || 0" class="highlight-text" style="display: block">
+          <span
+            :key="store.status.personCount || 0"
+            class="highlight-text"
+            style="display: block"
+          >
             {{ store.status.personCount }}
           </span>
         </transition-group>
@@ -76,7 +114,11 @@ src="./images/problems.svg" :class="questionMarkShouldChange && 'question-mark-a
       <span style="display: inline-flex; height: 16px; min-width: 55px">
         <span class="header-small-text">未评</span>
         <transition-group name="count-animation" tag="span">
-          <span :key="todoCount || 0" class="highlight-text" style="display: block">
+          <span
+            :key="todoCount || 0"
+            class="highlight-text"
+            style="display: block"
+          >
             {{ todoCount }}
           </span>
         </transition-group>
@@ -84,7 +126,11 @@ src="./images/problems.svg" :class="questionMarkShouldChange && 'question-mark-a
       <span style="display: inline-flex; height: 16px; min-width: 60px">
         <span class="header-small-text">进度</span>
         <transition-group name="count-animation" tag="span">
-          <span :key="progress || '-'" class="highlight-text" style="display: block">
+          <span
+            :key="progress || '-'"
+            class="highlight-text"
+            style="display: block"
+          >
             {{ progress }}%
           </span>
         </transition-group>
@@ -96,18 +142,22 @@ src="./images/problems.svg" :class="questionMarkShouldChange && 'question-mark-a
           评卷时间段
           <br />
           {{
-          store.setting.startTime > 0
-          ? $filters.datetimeFilter(store.setting.startTime)
-          : "-"
+            store.setting.startTime > 0
+              ? $filters.datetimeFilter(store.setting.startTime)
+              : "-"
           }}
           <br />~<br />
           {{
-          store.setting.endTime > 0
-          ? $filters.datetimeFilter(store.setting.endTime)
-          : "-"
+            store.setting.endTime > 0
+              ? $filters.datetimeFilter(store.setting.endTime)
+              : "-"
           }}
         </template>
-        <img src="./images/time.png" style="width: 16px; height: 16px" class="svg-red-hover" />
+        <img
+          src="./images/time.png"
+          style="width: 16px; height: 16px"
+          class="svg-red-hover"
+        />
       </a-tooltip>
     </div>
     <div class="tw-flex">
@@ -120,25 +170,33 @@ src="./images/problems.svg" :class="questionMarkShouldChange && 'question-mark-a
           </a-menu>
         </template>
         <a-button
-style="
+          style="
             color: rgba(255, 255, 255, 0.5);
             border: none;
             display: flex;
             align-items: center;
-          ">
+          "
+        >
           <img
-src="./images/trackmode.png" style="
+            src="./images/trackmode.png"
+            style="
               width: 11px;
               height: 12px;
               display: inline;
               margin-right: 2px;
-            " />
+            "
+          />
           {{ modeName }}
           <div v-if="!store.setting.forceMode" class="dropdown-triangle"></div>
         </a-button>
       </a-dropdown>
     </div>
-    <a-popover v-if="!store.isScanImage" title="小助手" trigger="hover" class="tw-cursor-pointer">
+    <a-popover
+      v-if="!store.isScanImage"
+      title="小助手"
+      trigger="hover"
+      class="tw-cursor-pointer"
+    >
       <template #content>
         <table class="assistant-table">
           <tr v-if="store.setting.statusValue !== 'TRIAL'">
@@ -150,7 +208,10 @@ src="./images/trackmode.png" style="
         </table>
       </template>
       <div class="tw-flex tw-items-center">
-        <img src="./images/assistant.png" style="width: 10px; height: 12px; margin-right: 2px" />
+        <img
+          src="./images/assistant.png"
+          style="width: 10px; height: 12px; margin-right: 2px"
+        />
         <span>小助手</span>
         <div class="dropdown-triangle"></div>
       </div>
@@ -165,13 +226,17 @@ src="./images/trackmode.png" style="
           <tr v-if="store.setting.subject.paperUrl">
             <td>试卷</td>
             <td>
-              <a-switch v-model:checked="store.setting.uiSetting['paper.modal']" />
+              <a-switch
+                v-model:checked="store.setting.uiSetting['paper.modal']"
+              />
             </td>
           </tr>
           <tr v-if="store.setting.subject.answerUrl">
             <td>答案</td>
             <td>
-              <a-switch v-model:checked="store.setting.uiSetting['answer.modal']" />
+              <a-switch
+                v-model:checked="store.setting.uiSetting['answer.modal']"
+              />
             </td>
           </tr>
           <tr>
@@ -189,24 +254,31 @@ src="./images/trackmode.png" style="
           <tr>
             <td>缩略图</td>
             <td>
-              <a-switch v-model:checked="store.setting.uiSetting['minimap.modal']" />
+              <a-switch
+                v-model:checked="store.setting.uiSetting['minimap.modal']"
+              />
             </td>
           </tr>
           <tr>
             <td>特殊标记</td>
             <td>
-              <a-switch v-model:checked="store.setting.uiSetting['specialTag.modal']" />
+              <a-switch
+                v-model:checked="store.setting.uiSetting['specialTag.modal']"
+              />
             </td>
           </tr>
           <tr v-if="store.setting.statusValue !== 'TRIAL'">
             <td>问题卷</td>
             <td>
               <a-button
-type="text" style="
+                type="text"
+                style="
                   color: var(--app-primary-button-bg-color);
                   margin-right: -15px;
                   height: 25px;
-                " @click="openProblemModal">
+                "
+                @click="openProblemModal"
+              >
                 选择问题类型
               </a-button>
             </td>
@@ -215,14 +287,20 @@ type="text" style="
             <td>分数/标记大小</td>
             <td>
               <a-slider
-v-model:value="store.setting.uiSetting['score.fontSize.scale']" :min="0.5" :step="0.1" :max="2"
-                style="margin: 0" />
+                v-model:value="store.setting.uiSetting['score.fontSize.scale']"
+                :min="0.5"
+                :step="0.1"
+                :max="2"
+                style="margin: 0"
+              />
             </td>
           </tr>
           <tr v-if="store.isScanImage">
             <td>快捷键</td>
             <td>
-              <a-switch v-model:checked="store.setting.uiSetting['shortCut.modal']" />
+              <a-switch
+                v-model:checked="store.setting.uiSetting['shortCut.modal']"
+              />
             </td>
           </tr>
         </table>
@@ -237,33 +315,54 @@ v-model:value="store.setting.uiSetting['score.fontSize.scale']" :min="0.5" :step
       </div>
     </a-popover>
     <div
-class="tw-flex tw-place-content-center tw-cursor-pointer tw-items-center" style="max-width: 8%"
-      :title="store.setting.groupTitle + '-' + store.setting.groupNumber" @click="openSwitchGroupModal">
-      <img src="./images/group.png" style="width: 10px; height: 12px; margin-right: 2px" />
+      class="tw-flex tw-place-content-center tw-cursor-pointer tw-items-center"
+      style="max-width: 8%"
+      :title="store.setting.groupTitle + '-' + store.setting.groupNumber"
+      @click="openSwitchGroupModal"
+    >
+      <img
+        src="./images/group.png"
+        style="width: 10px; height: 12px; margin-right: 2px"
+      />
       <div class="tw-overflow-ellipsis tw-overflow-hidden tw-whitespace-nowrap">
         {{ "分组:" + store.setting.groupNumber }}
       </div>
       <div v-if="store.groups.length > 1" class="dropdown-triangle"></div>
     </div>
     <div class="tw-flex tw-gap-4">
-      <div class="tw-flex tw-place-items-center tw-cursor-pointer" @click="openProfileModal">
+      <div
+        class="tw-flex tw-place-items-center tw-cursor-pointer"
+        @click="openProfileModal"
+      >
         <!-- <UserOutlined /> -->
         {{ store.setting.userName }}
       </div>
-      <div class="tw-flex tw-place-items-center tw-cursor-pointer" @click="logout">
+      <div
+        class="tw-flex tw-place-items-center tw-cursor-pointer"
+        @click="logout"
+      >
         <!-- <PoweroffOutlined /> -->
         退出
       </div>
       <a-tooltip placement="bottomRight" :overlayStyle="{ width: '58px' }">
         <template #title>弹出给分板</template>
         <div
-class="tw-flex tw-place-content-center tw-cursor-pointer menu" :class="[
-          store.isScoreBoardVisible && store.currentTask && 'menu-toggled',
-        ]" @click="store.toggleScoreBoard">
+          class="tw-flex tw-place-content-center tw-cursor-pointer menu"
+          :class="[
+            store.isScoreBoardVisible && store.currentTask && 'menu-toggled',
+          ]"
+          @click="store.toggleScoreBoard"
+        >
           <span class="tw-inline-flex tw-place-content-center tw-relative">
-            <img src="./images/right-menu.svg" :class="[store.isScoreBoardVisible && 'svg-red']" />
+            <img
+              src="./images/right-menu.svg"
+              :class="[store.isScoreBoardVisible && 'svg-red']"
+            />
           </span>
-          <div v-if="store.isScoreBoardVisible && store.currentTask" class="triangle"></div>
+          <div
+            v-if="store.isScoreBoardVisible && store.currentTask"
+            class="triangle"
+          ></div>
         </div>
       </a-tooltip>
     </div>
@@ -440,11 +539,13 @@ watch(
 }
 
 .svg-red {
-  filter: invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);
+  filter: invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg)
+    brightness(104%) contrast(97%);
 }
 
 .svg-red-hover:hover {
-  filter: invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);
+  filter: invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg)
+    brightness(104%) contrast(97%);
 }
 
 .triangle {

+ 14 - 3
src/features/mark/MarkHistory.vue

@@ -102,6 +102,7 @@
       </div>
     </a-spin>
     <div
+      v-if="!hasLimit"
       class="tw-flex tw-justify-between tw-place-content-center tw-mt-2 pager-container"
     >
       <div class="tw-font-bold" style="line-height: 30px">
@@ -137,7 +138,7 @@ import type {
   MarkHistorySortField,
   Task,
 } from "@/types";
-import { watch } from "vue";
+import { watch, computed } from "vue";
 import { store } from "@/store/store";
 import {
   SearchOutlined,
@@ -148,7 +149,16 @@ import { cloneDeep } from "lodash-es";
 import EventBus from "@/plugins/eventBus";
 import { addFileServerPrefixToTask, preDrawImageHistory } from "@/utils/utils";
 import { message } from "ant-design-vue";
-
+const remarkCount = computed(() => {
+  return store.setting?.remarkCount;
+});
+const limitPageSize = computed(() => {
+  return typeof remarkCount.value == "number" ? remarkCount.value : 20;
+});
+const hasLimit = computed(() => {
+  console.log("remarkCount.value:", remarkCount.value);
+  return typeof remarkCount.value == "number";
+});
 const {
   title = "回评",
   showOrder = false,
@@ -254,6 +264,7 @@ EventBus.on("should-reload-history", () => {
         groupNumber,
         markerId,
         markerScore,
+        pageSize: limitPageSize.value,
       });
       if (res?.data) {
         let data = cloneDeep(res.data);
@@ -292,7 +303,7 @@ async function updateHistoryTask({
   loading = true;
   let params = {
     pageNumber,
-    pageSize,
+    pageSize: limitPageSize.value,
     order,
     sort,
     subjectCode,

+ 1 - 0
src/features/student/importInspect/MarkHeader.vue

@@ -3,6 +3,7 @@
     :isSingleStudent="isSingleStudent"
     :clearTasks="clearTasks"
     showScoreBoard
+    :notShowHistoryToggle="true"
   >
     <slot name="taskInfo">
       <div>

+ 3 - 1
src/features/student/scoreVerify/MarkBoardInspect.vue

@@ -115,7 +115,9 @@ watch(
   }
 );
 const groups = $computed(() => {
-  const gs = store.currentTaskEnsured.questionList.map((q) => q.groupNumber);
+  const gs = (store.currentTaskEnsured?.questionList || []).map(
+    (q) => q.groupNumber
+  );
   return [...new Set(gs)].sort((a, b) => a - b);
 });
 

+ 3 - 2
src/features/student/scoreVerify/MarkHeader.vue

@@ -3,6 +3,7 @@
     :isSingleStudent="isSingleStudent"
     :clearTasks="clearTasks"
     showScoreBoard
+    :notShowHistoryToggle="true"
   >
     <slot name="taskInfo">
       <div>
@@ -19,13 +20,13 @@
       </div>
     </slot>
     <span>
-      <span class="header-small-text">待复核</span>
+      <span class="header-small-text">待校验</span>
       <span class="highlight-text">{{
         store.status.totalCount - store.status.markedCount ?? "-"
       }}</span>
     </span>
 
-    <template #studentInfo
+    <template v-if="route.query?.studentId" #studentInfo
       ><div class="highlight-text">
         考生:
         {{

+ 35 - 21
src/features/student/scoreVerify/ScoreVerify.vue

@@ -17,11 +17,16 @@
 
 <script setup lang="ts">
 import { onMounted } from "vue";
+// import {
+//   getInspectedSettingOfImportInspect,
+//   getSingleInspectedTaskOfImportInspect,
+//   saveInspectedTaskOfImportInspect,
+// } from "@/api/importInspectPage";
 import {
   getInspectedSettingOfImportInspect,
   getSingleInspectedTaskOfImportInspect,
   saveInspectedTaskOfImportInspect,
-} from "@/api/importInspectPage";
+} from "@/api/scoreVerify";
 import { store } from "@/store/store";
 import MarkHeader from "./MarkHeader.vue";
 import MinimapModal from "@/features/mark/MinimapModal.vue";
@@ -34,26 +39,32 @@ import { addFileServerPrefixToTask } from "@/utils/utils";
 
 const route = useRoute();
 const { studentId } = route.query as {
-  studentId: string;
+  studentId: string | number;
 };
 
-let studentIds: number[] = $ref([]);
-let tagIds: number[] = $ref([]);
-let currentStudentId = $ref(0);
+let studentIds: (number | string)[] = $ref([]);
+// let tagIds: number[] = $ref([]);
+let currentStudentId = $ref<string | number>(0);
 
 async function updateSetting() {
-  const settingRes = await getInspectedSettingOfImportInspect(studentId);
+  const settingRes = await getInspectedSettingOfImportInspect(
+    studentId as string
+  );
   const { examType, fileServer } = settingRes.data;
   store.initSetting({ examType, fileServer } as AdminPageSetting);
-  store.status.totalCount = settingRes.data.inspectCount;
-  store.status.markedCount = 0;
+  // store.status.totalCount = settingRes.data.inspectCount;
+  // store.status.markedCount = 0;
 
-  if (!settingRes.data.inspectCount) {
-    store.message = settingRes.data.message;
+  // if (!settingRes.data.inspectCount) {
+  //   store.message = settingRes.data.message;
+  // } else {
+  if (studentId) {
+    studentIds = [studentId];
   } else {
     studentIds = settingRes.data.studentIds;
-    tagIds = settingRes.data.tagIds;
   }
+  // tagIds = settingRes.data.tagIds;
+  // }
 }
 // 要通过fetchTask调用
 async function updateTask() {
@@ -64,19 +75,20 @@ async function updateTask() {
   void message.info({ content: "获取任务中...", duration: 1.5, key: mkey });
   let res = await getSingleInspectedTaskOfImportInspect("" + currentStudentId);
   void message.success({
-    content: res.data.studentId ? "获取成功" : "无任务",
+    content: res.data.task?.studentId ? "获取成功" : "无任务",
     key: mkey,
   });
-
-  if (res.data.studentId) {
-    let rawTask = res.data;
+  isCurrentTagged = !!res.data.flagged;
+  if (res.data.task?.studentId) {
+    let rawTask = res.data.task;
     store.currentTask = addFileServerPrefixToTask(rawTask);
   } else {
     store.message = res.data.message;
   }
 }
+let isCurrentTagged = $ref(false);
 
-const isCurrentTagged = $computed(() => tagIds.includes(currentStudentId));
+// const isCurrentTagged = $computed(() => tagIds.includes(currentStudentId));
 const isFirst = $computed(() => studentIds.indexOf(currentStudentId) === 0);
 const isLast = $computed(
   () => studentIds.indexOf(currentStudentId) === studentIds.length - 1
@@ -94,6 +106,7 @@ async function fetchTask(next: boolean, init?: boolean) {
       studentIds[studentIds.indexOf(currentStudentId) + (next ? 1 : -1)];
   }
   if (!currentStudentId) return; // 无currentStudentId不调用
+  store.status.totalCount = studentIds.length;
   store.status.markedCount = studentIds.indexOf(currentStudentId) + 1;
   await updateTask();
 }
@@ -116,11 +129,12 @@ const saveTaskToServer = async () => {
       key: mkey,
       duration: 2,
     });
-    if (isCurrentTagged) {
-      tagIds.splice(tagIds.indexOf(currentStudentId), 1);
-    } else {
-      tagIds.push(currentStudentId);
-    }
+    isCurrentTagged = !isCurrentTagged;
+    // if (isCurrentTagged) {
+    //   tagIds.splice(tagIds.indexOf(currentStudentId), 1);
+    // } else {
+    //   tagIds.push(currentStudentId);
+    // }
   } else {
     console.log(res.data.message);
     void message.error({ content: res.data.message, key: mkey, duration: 10 });

+ 5 - 1
src/features/student/studentInspect/MarkBoardInspect.vue

@@ -101,6 +101,7 @@
       </qm-button>
       <qm-button
         v-else-if="checkedQuestions.length === 0"
+        :disabled="!hasScrollToBottom"
         type="primary"
         class="full-width-btn"
         @click="inspect"
@@ -144,7 +145,10 @@ const willAddFocusTrack = (
   // }
 };
 
-const { isMultComments } = defineProps<{ isMultComments: boolean }>();
+const { isMultComments, hasScrollToBottom } = defineProps<{
+  isMultComments: boolean;
+  hasScrollToBottom: boolean;
+}>();
 const emit = defineEmits(["inspect", "reject"]);
 let checkedQuestions: Question[] = reactive([]);
 let reviewReturnVisible = $ref(false);

+ 10 - 2
src/features/student/studentInspect/MarkBody.vue

@@ -2,6 +2,7 @@
   <div
     ref="dragContainer"
     class="mark-body-container tw-flex-auto tw-p-2 tw-pt-0"
+    @scroll="viewScroll"
   >
     <div v-if="!store.currentTask" class="tw-text-center">
       {{ store.message }}
@@ -53,10 +54,17 @@ interface SliceImage {
 const { origImageUrls = "sliceUrls" } = defineProps<{
   origImageUrls?: "sheetUrls" | "sliceUrls";
 }>();
-const emit = defineEmits(["error", "getIsMultComments"]);
+const emit = defineEmits(["error", "getIsMultComments", "getScrollStatus"]);
 
 const { dragContainer } = dragImage();
-
+const viewScroll = () => {
+  if (
+    dragContainer.value.scrollTop + dragContainer.value.offsetHeight + 50 >=
+    dragContainer.value.scrollHeight
+  ) {
+    emit("getScrollStatus");
+  }
+};
 const { addTimeout } = useTimers();
 
 let sliceImagesWithTrackList: SliceImage[] = reactive([]);

+ 41 - 8
src/features/student/studentInspect/StudentInspect.vue

@@ -8,8 +8,17 @@
         orderTimeField="inspectTime"
         :getHistory="getInspectedHistory"
       />
-      <mark-body @error="renderError" @getIsMultComments="getIsMultComments" />
-      <MarkBoardInspect :isMultComments="isMultComments" @inspect="saveTaskToServer" @reject="rejectQuestions" />
+      <mark-body
+        @error="renderError"
+        @getIsMultComments="getIsMultComments"
+        @getScrollStatus="getScrollStatus"
+      />
+      <MarkBoardInspect
+        :isMultComments="isMultComments"
+        :hasScrollToBottom="hasScrollToBottom"
+        @inspect="saveTaskToServer"
+        @reject="rejectQuestions"
+      />
     </div>
   </div>
   <MinimapModal />
@@ -40,10 +49,21 @@ import { getInspectedHistory } from "@/api/inspectPage";
 import EventBus from "@/plugins/eventBus";
 import { addFileServerPrefixToTask } from "@/utils/utils";
 
-let isMultComments = $ref(false);  //是否双评
-const getIsMultComments = (bool:boolean)=>{
+let isMultComments = $ref(false); //是否双评
+const getIsMultComments = (bool: boolean) => {
   isMultComments = bool;
-}
+};
+let hasScrollToBottom = $ref(false);
+const getScrollStatus = () => {
+  hasScrollToBottom = true;
+};
+watch(
+  () => store.setting,
+  () => {
+    hasScrollToBottom = !store.setting?.inspectScroll;
+  },
+  { deep: true }
+);
 const route = useRoute();
 let isSingleStudent = !!route.query.studentId;
 const {
@@ -76,8 +96,16 @@ async function updateClearTask() {
 
 async function updateSetting() {
   const settingRes = await getAdminPageSetting(subjectCode);
-  const { examType, fileServer, subject, userName, splitConfig, enableSplit } =
-    settingRes.data;
+  const {
+    examType,
+    fileServer,
+    subject,
+    userName,
+    splitConfig,
+    enableSplit,
+    doubleTrack,
+    inspectScroll,
+  } = settingRes.data;
   store.initSetting({
     examType,
     fileServer,
@@ -85,6 +113,8 @@ async function updateSetting() {
     userName,
     splitConfig,
     enableSplit,
+    doubleTrack,
+    inspectScroll,
   });
   if (store.setting.subject?.paperUrl && store.isMultiMedia) {
     await getPaper(store);
@@ -205,7 +235,10 @@ const rejectQuestions = async ({
     void message.success({ content: "打回成功", key: mkey, duration: 2 });
     if (!store.historyOpen) {
       store.currentTask = undefined;
-      if (!isSingleStudent) await fetchTask();
+      if (!isSingleStudent) {
+        await fetchTask();
+        hasScrollToBottom = false;
+      }
     } else {
       EventBus.emit("should-reload-history");
     }

+ 10 - 0
src/types/index.ts

@@ -106,6 +106,14 @@ export interface Setting {
   endTime: number;
   /** 是否是未选做类型 */
   selective: boolean;
+  /** 可回评数量是否有限制数 */
+  remarkCount?: any;
+  /** 是否展示双评的轨迹 */
+  doubleTrack?: boolean;
+  /** 全卷复核页面是否要滑到页面底部才允许点击复核按钮 */
+  inspectScroll?: boolean;
+  /** 评卷员页头是否展示客观分 */
+  showObjectiveScore?: boolean;
 }
 
 /** 科目信息(试卷和答案功能) */
@@ -139,6 +147,8 @@ export interface AdminPageSetting {
   /** 使用裁切整图时的裁切配置 [0,1]|[0,0.3,0.25,0.55], */
   splitConfig: Array<number>;
   enableSplit: boolean;
+  doubleTrack?: boolean;
+  inspectScroll?: boolean;
 }
 
 export interface AdminPageSettingForImport extends AdminPageSetting {