|
@@ -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 和用户手动更新
|
|
|
+};
|