Browse Source

仲裁完成

zhangjie 1 year ago
parent
commit
905e61285f

+ 73 - 68
src/api/arbitratePage.ts

@@ -1,104 +1,109 @@
 import { httpApp } from "@/plugins/axiosApp";
-import {
-  CommonResponse,
-  HistoryQueryParams,
-  MarkDetail,
-  Setting,
-  Task,
-} from "@/types";
+import { CommonResponse, MarkDetail, Setting, Task } from "@/types";
+
+interface paramType {
+  paperNumber?: string;
+  groupNumber?: number;
+  examId?: string;
+  arbitrateId?: string;
+}
 
 /** 清理仲裁任务(taskId 与其他参数互斥填写) */
-export async function clearArbitrateTask(
-  // 这里libraryId为string,是因为它也会从query里面取值,同时利用为null的情况不传值到后台
-  taskId?: string,
-  subjectCode?: string,
-  groupNumber?: string
-) {
-  const form = new FormData();
-  taskId && form.append("groupNumber", taskId);
-  subjectCode && form.append("subjectCode", subjectCode);
-  groupNumber && form.append("groupNumber", groupNumber);
-  return httpApp.post<void>("/api/admin/exam/arbitrate/clear", form);
+export async function clearArbitrateTask(params: paramType) {
+  return httpApp.post<void>(
+    "/api/admin/mark/arbitrate/clear",
+    {},
+    {
+      params,
+    }
+  );
 }
 
 /** 查看单个学生的仲裁任务 */
-export async function getSingleArbitrateTask(historyId: string) {
-  const form = new FormData();
-  historyId && form.append("historyId", historyId);
-  return httpApp.post<Task>("/ap/admin/exam/arbitrate/getTask", form);
+export async function getSingleArbitrateTask(arbitrateId: string) {
+  return httpApp.post<Task>(
+    "/api/admin/mark/arbitrate/getTask",
+    {},
+    { params: { arbitrateId } }
+  );
 }
 
 /** 查看仲裁任务2次分数 */
-export async function getArbitrateList(historyId: string) {
-  const form = new FormData();
-  historyId && form.append("historyId", historyId);
+export async function getArbitrateList(arbitrateId: string) {
   return httpApp.post<MarkDetail[]>(
-    "/ap/admin/exam/arbitrate/getArbitrationList",
-    form
+    "/api/admin/mark/arbitrate/getArbitrationList",
+    {},
+    { params: { arbitrateId } }
   );
 }
 
 /** 批量仲裁得到单个学生的仲裁任务 */
-export async function getOneOfArbitrateTask(
-  subjectCode: string,
-  groupNumber: string
-) {
-  const form = new FormData();
-  form.append("subjectCode", subjectCode);
-  form.append("groupNumber", groupNumber);
-  return httpApp.post<Task>("/ap/admin/exam/arbitrate/getTask", form, {
-    setGlobalMask: true,
-  });
+export async function getOneOfArbitrateTask(params: paramType) {
+  return httpApp.post<Task>(
+    "/api/admin/mark/arbitrate/getTask",
+    {},
+    {
+      setGlobalMask: true,
+      params,
+    }
+  );
 }
 
 /** 批量仲裁得到任务总数 */
-export async function getArbitrateTaskStatus(
-  subjectCode: string,
-  groupNumber: string
-) {
-  const form = new FormData();
-  form.append("subjectCode", subjectCode);
-  form.append("groupNumber", groupNumber);
+export async function getArbitrateTaskStatus(params: paramType) {
   return httpApp.post<{
     valid: boolean;
     totalCount: number;
     markedCount: number;
-  }>("/ap/admin/exam/arbitrate/getStatus", form);
+  }>(
+    "/api/admin/mark/arbitrate/getStatus",
+    {},
+    {
+      params,
+    }
+  );
 }
 
 /** 批量仲裁设置 */
-export async function getArbitrateSetting(
-  historyId: string,
-  subjectCode?: string,
-  groupNumber?: string
-) {
-  const form = new FormData();
-  historyId && form.append("historyId", historyId);
-  subjectCode && form.append("subjectCode", subjectCode);
-  groupNumber && form.append("groupNumber", groupNumber);
-  return httpApp.post<Setting>("/ap/admin/exam/arbitrate/getSetting", form);
+export async function getArbitrateSetting(params: paramType) {
+  return httpApp.post<Setting>(
+    "/api/admin/mark/arbitrate/getSetting",
+    {},
+    {
+      params,
+    }
+  );
 }
 
 /** 批量仲裁历史 */
 export async function getArbitrateHistory({
-  subjectCode,
+  paperNumber,
   groupNumber,
+  examId,
   pageNumber = 1,
   pageSize = 20,
   order = "markerTime",
   sort = "DESC",
   secretNumber = null,
-}: HistoryQueryParams) {
-  if (!subjectCode) return;
-  const form = new FormData();
-  form.append("subjectCode", subjectCode);
-  groupNumber && form.append("groupNumber", groupNumber);
-  form.append("pageNumber", pageNumber + "");
-  form.append("pageSize", pageSize + "");
-  form.append("order", order);
-  form.append("sort", sort);
-  secretNumber && form.append("secretNumber", secretNumber);
-  return httpApp.post<Task[]>("/ap/admin/exam/arbitrate/getHistory", form);
+}) {
+  if (!paperNumber) return;
+
+  return httpApp.post<Task[]>(
+    "/api/admin/mark/arbitrate/getHistory",
+    {},
+    {
+      params: {
+        paperNumber,
+        pageSize,
+        order,
+        sort,
+        pageNumber,
+        examId,
+        groupNumber,
+        secretNumber: secretNumber || undefined,
+      },
+    }
+  );
 }
 
 /** 保存仲裁任务 */
@@ -111,7 +116,7 @@ export async function saveArbitrateTask(
   trackList?: any,
   specialTagList?: any
 ) {
-  return httpApp.post<CommonResponse>("/ap/admin/exam/arbitrate/saveTask", {
+  return httpApp.post<CommonResponse>("/api/admin/mark/arbitrate/saveTask", {
     taskId,
     studentId,
     markerScore,

+ 3 - 1
src/devLogin.ts

@@ -4,7 +4,8 @@ import { getBase64 } from "./utils/crypto";
 import { httpApp } from "@/plugins/axiosApp";
 import vls from "./utils/storage";
 
-const { loginName, password, schoolCode, studentIds, mark } = LOGIN_CONFIG;
+const { loginName, password, schoolCode, studentIds, mark, arbitrate } =
+  LOGIN_CONFIG;
 
 export async function initLogin() {
   const res = await httpApp.post("/api/admin/common/login", {
@@ -30,4 +31,5 @@ export async function initLogin() {
     ? vls.set("check-students", studentIds)
     : vls.remove("check-students");
   mark ? vls.set("mark", mark) : vls.remove("mark");
+  arbitrate ? vls.set("arbitrate", arbitrate) : vls.remove("arbitrate");
 }

+ 34 - 16
src/devLoginParams.ts

@@ -1,25 +1,43 @@
 // 192.168.11.167:7001
 // 评卷员
-// export const LOGIN_CONFIG = {
-//   isAdmin: false,
-//   loginName: "java",
-//   password: "12345678",
-//   mark: {
-//     examId: "437537682336260096",
-//     paperNumber: "bh102101",
-//     groupNumber: 1,
-//   },
-//   schoolCode: "test-school-1",
-// };
 export const LOGIN_CONFIG = {
-  isAdmin: true,
+  isAdmin: false,
   loginName: "admin",
   password: "12345678",
+  arbitrate: {
+    // historyId: "1722865426532139010",
+    examId: "455729804847611904",
+    groupNumber: 2,
+    paperNumber: "bh111002",
+  },
   // mark: {
-  //   examId: "437537682336260096",
-  //   paperNumber: "bh102101",
-  //   groupNumber: 1,
+  //   examId: "455729804847611904",
+  //   paperNumber: "bh111002",
+  //   groupNumber: 2,
   // },
   schoolCode: "test-school-1",
-  studentIds: ["448443226152513536", "448443226014101504"],
 };
+// export const LOGIN_CONFIG = {
+//   isAdmin: true,
+//   loginName: "admin1",
+//   password: "12345678",
+//   // mark: {
+//   //   examId: "437537682336260096",
+//   //   paperNumber: "bh102101",
+//   //   groupNumber: 1,
+//   // },
+//   schoolCode: "test-school-1",
+//   studentIds: "448443226152513536",
+// };
+// export const LOGIN_CONFIG = {
+//   isAdmin: true,
+//   loginName: "admin",
+//   password: "12345678",
+//   // mark: {
+//   //   examId: "437537682336260096",
+//   //   paperNumber: "bh102101",
+//   //   groupNumber: 1,
+//   // },
+//   schoolCode: "test-school-1",
+//   studentIds: ["448443226152513536", "448443226014101504"],
+// };

+ 42 - 74
src/features/arbitrate/Arbitrate.vue

@@ -1,22 +1,22 @@
 <template>
-  <div class="my-container">
-    <mark-header />
-    <div class="tw-flex tw-gap-1">
+  <div class="mark arbitrate">
+    <mark-header :isSingleStudent="isSingleStudent" />
+    <mark-tool :actions="['minimap', 'sizeScale', 'imgScale']" />
+    <div class="mark-main">
       <mark-history
         v-if="!isSingleStudent"
-        :subjectCode="subjectCode"
+        :subjectCode="paperNumber"
         :groupNumber="groupNumber"
         :getHistory="getArbitrateHistory"
       />
       <ArbitrateMarkList />
       <mark-body @error="renderError" />
-      <template v-if="store.isTrackMode">
-        <mark-board-track
-          @unselectiveSubmit="saveTaskToServer(true)"
-          @submit="saveTaskToServerByTrack"
-        />
-      </template>
-      <template v-else>
+      <mark-board-track
+        v-if="store.isTrackMode"
+        @unselectiveSubmit="saveTaskToServer(true)"
+        @submit="saveTaskToServerByTrack"
+      />
+      <template v-if="!store.isTrackMode">
         <mark-board-key-board
           v-if="store.shouldShowMarkBoardKeyBoard"
           @submit="saveTaskToServer(false)"
@@ -39,7 +39,7 @@
 import { onMounted, watch, h } from "vue";
 import { store } from "@/store/store";
 import MarkHeader from "./MarkHeader.vue";
-import { useRoute } from "vue-router";
+import MarkTool from "../mark/MarkTool.vue";
 import MarkBody from "./MarkBody.vue";
 import MarkBoardKeyBoard from "@/features/mark/MarkBoardKeyBoard.vue";
 import MarkBoardMouse from "@/features/mark/MarkBoardMouse.vue";
@@ -58,65 +58,41 @@ import {
 import ArbitrateMarkList from "./ArbitrateMarkList.vue";
 import AnswerModal from "../mark/AnswerModal.vue";
 import PaperModal from "../mark/PaperModal.vue";
-import { getPaper } from "@/api/jsonMark";
 import { getArbitrateHistory } from "@/api/arbitratePage";
 import EventBus from "@/plugins/eventBus";
-import { addFileServerPrefixToTask, preDrawImage } from "@/utils/utils";
 import { isNumber } from "lodash-es";
 import type { Question } from "@/types";
+import vls from "@/utils/storage";
 
-const route = useRoute();
-let isSingleStudent = !!route.query.historyId;
-const {
-  subjectCode,
-  groupNumber,
-  historyId: taskId,
-} = route.query as {
-  subjectCode: string;
-  groupNumber: string;
-  historyId: string;
-};
+const { paperNumber, groupNumber, examId, arbitrateId } = vls.get(
+  "arbitrate",
+  {}
+);
+let isSingleStudent = !!arbitrateId;
+
+const params = isSingleStudent
+  ? { arbitrateId }
+  : { paperNumber, groupNumber, examId };
 
 async function updateClearTask() {
-  await clearArbitrateTask(taskId, subjectCode);
+  await clearArbitrateTask(params);
 }
 
 async function updateSetting() {
-  const settingRes = await getArbitrateSetting(
-    taskId,
-    subjectCode,
-    groupNumber
-  );
-  const { examType, fileServer, subject, userName, splitConfig, enableSplit } =
-    settingRes.data;
-  store.initSetting({
-    examType,
-    fileServer,
-    subject,
-    userName,
-    splitConfig,
-    enableSplit,
+  const settingRes = await getArbitrateSetting(params);
+  store.setting = Object.assign({}, store.setting, {
+    selective: false,
+    examType: "SCAN_IMAGE",
+    subject: {
+      name: settingRes.data.courseName,
+      code: settingRes.data.courseCode,
+    },
+    mode: settingRes.data.markMode,
+    enableSplit: false,
   });
-  /*****************************根据本地临时会话存储的mode内容********************************* */
-  let arbitrateLocalMode = sessionStorage.getItem("arbitrate_local_mode");
-  if (
-    arbitrateLocalMode &&
-    (arbitrateLocalMode === "TRACK" || arbitrateLocalMode === "COMMON")
-  ) {
-    store.setting.mode = arbitrateLocalMode;
-    sessionStorage.removeItem("arbitrate_local_mode");
-  } else {
-    store.setting.mode = settingRes.data.mode;
-  }
-  /************************************************************** */
-  store.setting.selective = settingRes.data.selective;
-
-  if (store.setting.subject?.paperUrl && store.isMultiMedia) {
-    await getPaper(store);
-  }
 }
 async function updateStatus() {
-  const res = await getArbitrateTaskStatus(subjectCode, groupNumber);
+  const res = await getArbitrateTaskStatus(params);
   if (res.data.valid) Object.assign(store.status, res.data);
 }
 async function updateTask() {
@@ -135,11 +111,10 @@ async function updateTask() {
 
   if (res.data.studentId) {
     let rawTask = res.data;
-    let t = addFileServerPrefixToTask(rawTask);
-    if (store.isScanImage && !!t) {
-      await preDrawImage(t);
-    }
-    store.currentTask = t;
+    // newTask.sheetUrls = newTask.sheetUrls || [];
+    rawTask.sheetUrls = ["/1-1.jpg", "/1-2.jpg"];
+    rawTask.sliceUrls = [...rawTask.sheetUrls];
+    store.currentTask = rawTask;
   } else {
     store.message = res.data.message;
   }
@@ -176,11 +151,11 @@ watch(
 );
 
 async function getSingleStuTask() {
-  return getSingleArbitrateTask(taskId);
+  return getSingleArbitrateTask(params);
 }
 
 async function getOneOfStuTask() {
-  return getOneOfArbitrateTask(subjectCode, groupNumber);
+  return getOneOfArbitrateTask(params);
 }
 
 const saveTaskToServerByTrack = async () => {
@@ -203,8 +178,8 @@ const saveTaskToServer = async (
   let res;
   if (unselective) {
     res = await saveArbitrateTask(
-      store.currentTask.taskId + "",
-      store.currentTask.studentId + "",
+      store.currentTask.taskId,
+      store.currentTask.studentId,
       -1,
       [],
       true
@@ -277,10 +252,3 @@ const renderError = () => {
   store.message = "加载失败,请重新加载。";
 };
 </script>
-
-<style scoped>
-.my-container {
-  width: 100%;
-  overflow: clip;
-}
-</style>

+ 25 - 29
src/features/arbitrate/ArbitrateMarkList.vue

@@ -1,36 +1,32 @@
 <template>
-  <div class="container tw-mt-6 tw-mr-2">
-    <div v-for="(markDetail, index) of list" :key="index">
-      <div
-        class="tw-mb-4 tw-py-2"
-        style="background-color: white; border-radius: 5px"
-      >
-        <div class="tw-flex" style="color: var(--app-small-header-text-color)">
-          <div class="col-1">评卷员</div>
-          <div class="col-2">{{ markDetail.markerName }}</div>
-        </div>
-        <div class="tw-flex" style="color: var(--app-bold-text-color)">
-          <div class="col-1 tw-font-bold">时间</div>
-          <div class="col-2">
-            {{
-              markDetail.markerTime &&
-              $filters.datetimeFilter(markDetail.markerTime)
-            }}
-          </div>
-        </div>
-        <div class="tw-flex" style="color: var(--app-bold-text-color)">
-          <div class="col-1 tw-font-bold">总分</div>
-          <div class="col-2">
-            {{
-              markDetail.totalScore === -1 ? "未选做" : markDetail.totalScore
-            }}
-          </div>
+  <div class="arbitrate-mark">
+    <div
+      v-for="(markDetail, index) of list"
+      :key="index"
+      class="arbitrate-mark-item"
+    >
+      <div class="noun-item">
+        <div class="noun-item-title">评卷员</div>
+        <div class="noun-item-content">{{ markDetail.userName }}</div>
+      </div>
+      <div class="noun-item">
+        <div class="noun-item-title">时间</div>
+        <div class="noun-item-content">
+          {{
+            markDetail.markTime && $filters.datetimeFilter(markDetail.markTime)
+          }}
         </div>
-        <div class="tw-flex" style="color: var(--app-bold-text-color)">
-          <div class="col-1 tw-font-bold">详情</div>
-          <div class="col-2">{{ markDetail.scoreList }}</div>
+      </div>
+      <div class="noun-item">
+        <div class="noun-item-title">总分</div>
+        <div class="noun-item-content">
+          {{ markDetail.totalScore === -1 ? "未选做" : markDetail.totalScore }}
         </div>
       </div>
+      <div class="noun-item">
+        <div class="noun-item-title">详情</div>
+        <div class="noun-item-content">{{ markDetail.scoreList }}</div>
+      </div>
     </div>
   </div>
 </template>

+ 0 - 1
src/features/arbitrate/MarkBody.vue

@@ -1,6 +1,5 @@
 <template>
   <CommonMarkBody
-    v-if="store"
     :hasMarkResultToRender="true"
     :makeTrack="makeTrack"
     @error="$emit('error')"

+ 73 - 106
src/features/arbitrate/MarkHeader.vue

@@ -1,119 +1,86 @@
 <template>
-  <CommonMarkHeader
-    :isSingleStudent="isSingleStudent"
-    :clearTasks="clearTasks"
-    showPaperAndAnswer
-    showScoreBoard
-  >
-    <span>
-      <span class="header-small-text">待处理</span>
-      <span class="highlight-text">{{ store.status.totalCount ?? "-" }}</span>
-    </span>
-    <span>
-      <span class="header-small-text">已处理</span>
-      <span class="highlight-text">{{ store.status.markedCount ?? "-" }}</span>
-    </span>
-    <template #modeControl>
-      <div class="tw-flex">
-        <!-- <a-dropdown class="header-bg-color"> -->
-
-        <a-button
-          class="header-bg-color"
-          style="
-            color: rgba(255, 255, 255, 0.5);
-            border: none;
-            display: flex;
-            align-items: center;
-          "
+  <div v-if="store.setting && store.setting.subject.name" class="mark-header">
+    <div class="mark-header-part">
+      <a-tooltip v-if="!isSingleStudent">
+        <template #title>回评</template>
+        <div
+          :class="['header-menu', { 'is-toggled': store.historyOpen }]"
+          @click="store.toggleHistory"
         >
-          <img
-            src="../../assets/trackmode.png"
-            style="
-              width: 11px;
-              height: 12px;
-              display: inline;
-              margin-right: 2px;
-            "
-          />
-          {{ modeName }}
-        </a-button>
-        <!-- </a-dropdown> -->
+          <img class="header-icon" src="@/assets/icons/icon-left-menu.svg" />
+        </div>
+      </a-tooltip>
+      <div class="header-subject" :title="store.setting.subject.name">
+        {{
+          `${store.setting.subject.code ?? ""}-${
+            store.setting.subject.name ?? ""
+          }`
+        }}
+      </div>
+      <div class="header-secret">
+        <div class="header-noun">
+          <span>编号:</span>
+          <span>
+            {{ store.currentTask?.secretNumber ?? "-" }}
+          </span>
+        </div>
+      </div>
+      <div v-if="!isSingleStudent" class="header-total">
+        <span class="header-noun">
+          <span>待处理:</span>
+          <transition-group name="count-animation" tag="span">
+            <span :key="store.status.totalCount || 0">
+              {{ store.status.totalCount }}
+            </span>
+          </transition-group>
+        </span>
+        <span class="header-noun">
+          <span>已处理:</span>
+          <transition-group name="count-animation" tag="span">
+            <span :key="store.status.markedCount || 0">
+              {{ store.status.markedCount }}
+            </span>
+          </transition-group>
+        </span>
+      </div>
+    </div>
+
+    <div class="mark-header-part">
+      <div class="header-text-btn">
+        <img src="@/assets/icons/icon-track-mode.svg" class="header-icon" />
+        {{ modeName }}
+      </div>
+      <div class="header-text-btn header-logout" @click="logout">
+        <img class="header-icon" src="@/assets/icons/icon-return.svg" />返回
       </div>
-    </template>
-  </CommonMarkHeader>
+      <a-tooltip placement="bottomRight">
+        <template #title>弹出给分板</template>
+        <div
+          :class="[
+            'header-menu',
+            { 'is-toggled': store.isScoreBoardVisible && store.currentTask },
+          ]"
+          @click="store.toggleScoreBoard"
+        >
+          <img src="@/assets/icons/icon-right-menu.svg" class="header-icon" />
+        </div>
+      </a-tooltip>
+    </div>
+  </div>
 </template>
 
 <script setup lang="ts">
 import { store } from "@/store/store";
-import { useRoute } from "vue-router";
-import { clearArbitrateTask } from "@/api/arbitratePage";
-import CommonMarkHeader from "@/components/CommonMarkHeader.vue";
+
+const { isSingleStudent = false } = defineProps<{
+  isSingleStudent?: boolean;
+}>();
+
 const modeName = $computed(() =>
   store.setting.mode === "TRACK" ? "轨迹模式" : "普通模式"
 );
 
-const exchangeModeName = $computed(() =>
-  store.setting.mode === "TRACK" ? "普通模式" : "轨迹模式"
-);
-async function toggleSettingMode() {
-  if (store.isTrackMode) {
-    store.setting.mode = "COMMON";
-  } else {
-    store.setting.mode = "TRACK";
-  }
-
-  // store.currentTask.markResult.trackList = store.currentTask.questionList
-  //   .map((q) => q.trackList)
-  //   .flat();
-  // store.currentTask.markResult.specialTagList = [
-  //   ...(store.currentTask.specialTagList ?? []),
-  // ];
-  // store.currentTask.markResult.scoreList = store.currentTask.questionList.map(
-  //   (q) => q.score
-  // );
-  sessionStorage.setItem("arbitrate_local_mode", store.setting.mode);
-
-  const body = document.querySelector("body");
-  if (body) body.innerHTML = "重新加载中...";
-  // 等待一秒后,重新加载页面
-  await new Promise((resolve) => setTimeout(resolve, 1000));
-  window.location.reload();
-}
-
-const route = useRoute();
-let isSingleStudent = !!route.query.historyId;
-const {
-  subjectCode,
-  groupNumber,
-  historyId: taskId,
-} = route.query as {
-  subjectCode: string;
-  groupNumber: string;
-  historyId: string;
+const logout = () => {
+  window.history.go(-1);
 };
-
-let clearTasks = clearArbitrateTask.bind(
-  null,
-  taskId,
-  subjectCode,
-  groupNumber
-);
 </script>
-<style>
-.header-bg-color {
-  background-color: var(--header-bg-color);
-}
-.header-bg-color.ant-btn:hover {
-  background-color: var(--app-ant-select-bg-override-color) !important;
-}
-.header-bg-color.ant-btn:focus {
-  background-color: transparent;
-}
-.dropdown-triangle {
-  background-color: #8c8d9b;
-  width: 7px;
-  height: 5px;
-  clip-path: polygon(0 0, 100% 0, 50% 100%);
-  margin-left: 4px;
-}
-</style>

+ 119 - 0
src/features/arbitrate/Markhea1.vue

@@ -0,0 +1,119 @@
+<template>
+  <CommonMarkHeader
+    :isSingleStudent="isSingleStudent"
+    :clearTasks="clearTasks"
+    showPaperAndAnswer
+    showScoreBoard
+  >
+    <span>
+      <span class="header-small-text">待处理</span>
+      <span class="highlight-text">{{ store.status.totalCount ?? "-" }}</span>
+    </span>
+    <span>
+      <span class="header-small-text">已处理</span>
+      <span class="highlight-text">{{ store.status.markedCount ?? "-" }}</span>
+    </span>
+    <template #modeControl>
+      <div class="tw-flex">
+        <!-- <a-dropdown class="header-bg-color"> -->
+
+        <a-button
+          class="header-bg-color"
+          style="
+            color: rgba(255, 255, 255, 0.5);
+            border: none;
+            display: flex;
+            align-items: center;
+          "
+        >
+          <img
+            src="../../assets/trackmode.png"
+            style="
+              width: 11px;
+              height: 12px;
+              display: inline;
+              margin-right: 2px;
+            "
+          />
+          {{ modeName }}
+        </a-button>
+        <!-- </a-dropdown> -->
+      </div>
+    </template>
+  </CommonMarkHeader>
+</template>
+
+<script setup lang="ts">
+import { store } from "@/store/store";
+import { useRoute } from "vue-router";
+import { clearArbitrateTask } from "@/api/arbitratePage";
+import CommonMarkHeader from "@/components/CommonMarkHeader.vue";
+const modeName = $computed(() =>
+  store.setting.mode === "TRACK" ? "轨迹模式" : "普通模式"
+);
+
+const exchangeModeName = $computed(() =>
+  store.setting.mode === "TRACK" ? "普通模式" : "轨迹模式"
+);
+async function toggleSettingMode() {
+  if (store.isTrackMode) {
+    store.setting.mode = "COMMON";
+  } else {
+    store.setting.mode = "TRACK";
+  }
+
+  // store.currentTask.markResult.trackList = store.currentTask.questionList
+  //   .map((q) => q.trackList)
+  //   .flat();
+  // store.currentTask.markResult.specialTagList = [
+  //   ...(store.currentTask.specialTagList ?? []),
+  // ];
+  // store.currentTask.markResult.scoreList = store.currentTask.questionList.map(
+  //   (q) => q.score
+  // );
+  sessionStorage.setItem("arbitrate_local_mode", store.setting.mode);
+
+  const body = document.querySelector("body");
+  if (body) body.innerHTML = "重新加载中...";
+  // 等待一秒后,重新加载页面
+  await new Promise((resolve) => setTimeout(resolve, 1000));
+  window.location.reload();
+}
+
+const route = useRoute();
+let isSingleStudent = !!route.query.historyId;
+const {
+  subjectCode,
+  groupNumber,
+  historyId: taskId,
+} = route.query as {
+  subjectCode: string;
+  groupNumber: string;
+  historyId: string;
+};
+
+let clearTasks = clearArbitrateTask.bind(
+  null,
+  taskId,
+  subjectCode,
+  groupNumber
+);
+</script>
+<style>
+.header-bg-color {
+  background-color: var(--header-bg-color);
+}
+.header-bg-color.ant-btn:hover {
+  background-color: var(--app-ant-select-bg-override-color) !important;
+}
+.header-bg-color.ant-btn:focus {
+  background-color: transparent;
+}
+.dropdown-triangle {
+  background-color: #8c8d9b;
+  width: 7px;
+  height: 5px;
+  clip-path: polygon(0 0, 100% 0, 50% 100%);
+  margin-left: 4px;
+}
+</style>

+ 11 - 2
src/features/check/SubjectiveAnswer.vue

@@ -64,8 +64,17 @@
         </a-tooltip>
       </div>
     </div>
-
-    <mark-tool @allZeroSubmit="allZeroSubmit" />
+    <mark-tool
+      :actions="[
+        'allPage',
+        'minimap',
+        'sizeScale',
+        'shortCut',
+        'specialTag',
+        'imgScale',
+      ]"
+      @allZeroSubmit="allZeroSubmit"
+    />
     <div class="mark-main">
       <mark-body @error="removeBrokenTask" />
       <mark-board-track v-if="store.isTrackMode" @submit="saveTaskToServer" />

+ 0 - 27
src/features/mark/MarkHeader.vue

@@ -279,30 +279,3 @@ watch(
   }
 );
 </script>
-
-<style scoped>
-.count-animation-enter-active,
-.count-animation-leave-active {
-  transition: all 1.2s ease-in-out;
-}
-
-.count-animation-enter-from,
-.count-animation-leave-to {
-  opacity: 0;
-  transform: translateY(18px);
-}
-
-.question-mark-animation {
-  animation: pluse 2s ease-in-out infinite alternate;
-}
-
-@keyframes pluse {
-  0% {
-    scale: 0.7;
-  }
-
-  100% {
-    scale: 1.3;
-  }
-}
-</style>

+ 53 - 22
src/features/mark/MarkTool.vue

@@ -1,11 +1,16 @@
 <template>
   <div class="mark-tool">
     <div>
-      <div class="mark-tool-item" @click="toAllPage">
+      <div
+        v-if="checkValid('allPage')"
+        class="mark-tool-item"
+        @click="toAllPage"
+      >
         <img src="@/assets/icons/icon-all-page.svg" />
         <p>全卷</p>
       </div>
       <div
+        v-if="checkValid('minimap')"
         :class="[
           'mark-tool-item',
           { 'is-active': store.setting.uiSetting['minimap.modal'] },
@@ -15,11 +20,15 @@
         <img src="@/assets/icons/icon-thumbnail.svg" />
         <p>缩略图</p>
       </div>
-      <div class="mark-tool-item" @click="toIssuePaper">
+      <div
+        v-if="checkValid('issuePaper')"
+        class="mark-tool-item"
+        @click="toIssuePaper"
+      >
         <img src="@/assets/icons/icon-issue-paper.svg" />
         <p>问题试卷</p>
       </div>
-      <div class="mark-tool-item tool-scale">
+      <div v-if="checkValid('sizeScale')" class="mark-tool-item tool-scale">
         <div>
           <span>Aa</span>
           <a-slider
@@ -32,6 +41,7 @@
         <p>分数/标记大小</p>
       </div>
       <div
+        v-if="checkValid('shortCut')"
         :class="[
           'mark-tool-item',
           { 'is-active': store.setting.uiSetting['shortCut.modal'] },
@@ -42,6 +52,7 @@
         <p>快捷键</p>
       </div>
       <div
+        v-if="checkValid('specialTag')"
         :class="[
           'mark-tool-item',
           { 'is-active': store.setting.uiSetting['specialTag.modal'] },
@@ -61,27 +72,29 @@
         <img src="@/assets/icons/icon-all-zero.svg" />
         <p>全部零分</p>
       </div>
-      <div
-        :class="['mark-tool-item', { 'is-active': greaterThanOneScale }]"
-        @click="toMagnify"
-      >
-        <img src="@/assets/icons/icon-magnify.svg" />
-        <p>放大</p>
-      </div>
-      <div
-        :class="['mark-tool-item', { 'is-active': lessThanOneScale }]"
-        @click="toMinify"
-      >
-        <img src="@/assets/icons/icon-minify.svg" />
-        <p>缩小</p>
-      </div>
-      <div class="mark-tool-item" @click="toOrigin">
-        <img src="@/assets/icons/icon-origin-size.svg" />
-        <p>实际大小</p>
-      </div>
+      <template v-if="checkValid('imgScale')">
+        <div
+          :class="['mark-tool-item', { 'is-active': greaterThanOneScale }]"
+          @click="toMagnify"
+        >
+          <img src="@/assets/icons/icon-magnify.svg" />
+          <p>放大</p>
+        </div>
+        <div
+          :class="['mark-tool-item', { 'is-active': lessThanOneScale }]"
+          @click="toMinify"
+        >
+          <img src="@/assets/icons/icon-minify.svg" />
+          <p>缩小</p>
+        </div>
+        <div class="mark-tool-item" @click="toOrigin">
+          <img src="@/assets/icons/icon-origin-size.svg" />
+          <p>实际大小</p>
+        </div>
+      </template>
     </div>
   </div>
-  <MarkProblemDialog ref="problemRef" />
+  <MarkProblemDialog v-if="checkValid('issuePaper')" ref="problemRef" />
 </template>
 
 <script setup lang="ts">
@@ -90,8 +103,26 @@ import { store } from "@/store/store";
 import { Modal } from "ant-design-vue";
 import MarkProblemDialog from "./MarkProblemDialog.vue";
 
+/**
+ * allPage:全卷
+ * minimap:缩略图
+ * issuePaper:问题试卷
+ * sizeScale:标记大小
+ * shortCut:快捷键
+ * specialTag:特殊标记
+ * imgScale:图片缩放
+ */
+const { actions = [] } = defineProps<{
+  actions?: string[];
+}>();
+
 type ShowModalFunc = () => void;
 
+const checkValid = (name) => {
+  if (!actions.length) return true;
+  return actions.includes(name);
+};
+
 const emit = defineEmits(["allZeroSubmit"]);
 let problemRef = $ref<InstanceType<typeof MarkProblemDialog>>();
 

+ 4 - 37
src/features/student/studentTrack/StudentTrack.vue

@@ -14,48 +14,20 @@ import { onMounted } from "vue";
 import { store } from "@/store/store";
 import MarkHeader from "./MarkHeader.vue";
 import MinimapModal from "@/features/mark/MinimapModal.vue";
-import { useRoute } from "vue-router";
 import MarkBody from "../studentInspect/MarkBody.vue";
 import CommonMarkBody from "@/features/mark/CommonMarkBody.vue";
 import { message } from "ant-design-vue";
 import { getSingleStudentTaskOfStudentTrack } from "@/api/studentTrackPage";
-import { getAdminPageSetting } from "@/api/inspectPage";
-import { getPaper } from "@/api/jsonMark";
+// import { getPaper } from "@/api/jsonMark";
 import { addFileServerPrefixToTask } from "@/utils/utils";
+import vls from "@/utils/storage";
 
-const route = useRoute();
-let studentId = route.query.studentId;
-let subjectCode = route.query.subjectCode as string;
-
-async function updateSetting() {
-  const settingRes = await getAdminPageSetting(subjectCode);
-  const {
-    examType,
-    fileServer,
-    subject,
-    userName,
-    splitConfig,
-    enableSplit,
-    doubleTrack,
-  } = settingRes.data;
-  store.initSetting({
-    examType,
-    fileServer,
-    subject,
-    userName,
-    splitConfig,
-    enableSplit,
-    doubleTrack,
-  });
-  if (store.setting.subject?.paperUrl && store.isMultiMedia) {
-    await getPaper(store);
-  }
-}
+const studentId = $ref(vls.get("check-student", ""));
 
 async function updateTask() {
   const mkey = "fetch_task_key";
   void message.info({ content: "获取任务中...", duration: 1.5, key: mkey });
-  let res = await getSingleStuTask();
+  let res = await getSingleStudentTaskOfStudentTrack(studentId);
   void message.success({
     content: res.data.studentId ? "获取成功" : "无任务",
     key: mkey,
@@ -74,14 +46,9 @@ async function fetchTask() {
 }
 
 onMounted(async () => {
-  await updateSetting();
   await fetchTask();
 });
 
-async function getSingleStuTask() {
-  return getSingleStudentTaskOfStudentTrack(studentId as string);
-}
-
 const renderError = () => {
   store.currentTask = undefined;
   store.message = "加载失败,请重新加载。";

+ 14 - 12
src/router/index.ts

@@ -4,7 +4,7 @@ import ObjectiveAnswer from "@/features/check/ObjectiveAnswer.vue";
 import SubjectiveAnswer from "@/features/check/SubjectiveAnswer.vue";
 
 const routes = [
-  { path: "/", redirect: { name: "CheckSubjectiveAnswer" } },
+  { path: "/", redirect: { name: "Arbitrate" } },
   { path: "/mark", component: Mark, name: "Mark" },
   {
     // 客观题检查
@@ -18,6 +18,19 @@ const routes = [
     name: "CheckSubjectiveAnswer",
     component: SubjectiveAnswer,
   },
+  {
+    // 成绩查询-试卷轨迹
+    path: "/track/student",
+    name: "StudentTrack",
+    component: () => import("@/features/student/studentTrack/StudentTrack.vue"),
+  },
+  {
+    // 仲裁
+    path: "/admin/exam/arbitrate/start",
+    name: "Arbitrate",
+    component: () => import("@/features/arbitrate/Arbitrate.vue"),
+  },
+  // old page
   {
     // 整卷批量复核
     path: "/admin/exam/inspected/start",
@@ -40,17 +53,6 @@ const routes = [
     path: "/admin/exam/library/inspected/start",
     component: () => import("@/features/library/inspect/LibraryInspect.vue"),
   },
-  {
-    // 仲裁:TODO:
-    path: "/admin/exam/arbitrate/start",
-    component: () => import("@/features/arbitrate/Arbitrate.vue"),
-  },
-  {
-    // 成绩查询-试卷轨迹 TODO:
-    path: "/admin/exam/track/student",
-    name: "StudentTrack",
-    component: () => import("@/features/student/studentTrack/StudentTrack.vue"),
-  },
   {
     // 质量分析
     path: "/admin/exam/quality",

+ 61 - 0
src/styles/page.less

@@ -1002,6 +1002,42 @@
   }
 }
 
+// arbitrate
+.arbitrate {
+  .arbitrate-mark {
+    .flex-static;
+    width: 260px;
+    padding: 16px;
+    overflow-y: auto;
+    background-color: #fff;
+    border-right: 1px solid #e5e5e5;
+  }
+
+  .arbitrate-mark-item {
+    background-color: #e8f3ff;
+    border-radius: 4px;
+    padding: 10px;
+    margin-bottom: 16px;
+  }
+
+  .noun-item {
+    overflow: hidden;
+
+    &-title {
+      line-height: 20px;
+      float: left;
+      width: 60px;
+      padding-right: 10px;
+      text-align: right;
+      font-weight: 600;
+    }
+    &-content {
+      margin-left: 60px;
+      line-height: 20px;
+    }
+  }
+}
+
 // common
 .mark-tooltip {
   padding-top: 0px !important;
@@ -1235,3 +1271,28 @@
     }
   }
 }
+
+.count-animation-enter-active,
+.count-animation-leave-active {
+  transition: all 1.2s ease-in-out;
+}
+
+.count-animation-enter-from,
+.count-animation-leave-to {
+  opacity: 0;
+  transform: translateY(18px);
+}
+
+.question-mark-animation {
+  animation: pluse 2s ease-in-out infinite alternate;
+}
+
+@keyframes pluse {
+  0% {
+    scale: 0.7;
+  }
+
+  100% {
+    scale: 1.3;
+  }
+}