瀏覽代碼

feat: 轨迹图修改

zhangjie 3 月之前
父節點
當前提交
da78f3869d

+ 9 - 1
src/api/studentTrackPage.ts

@@ -1,6 +1,14 @@
 import { httpApp } from "@/plugins/axiosApp";
-import { Task, CardData } from "@/types";
+import { Task, CardData, StudentTrackTask } from "@/types";
 
+/** 查看单个学生的试卷轨迹-聚合版 */
+export async function getStudentTrack(studentId: string) {
+  return httpApp.post<StudentTrackTask>(
+    "/api/admin/mark/archive/student/question/track",
+    {},
+    { params: { studentId } }
+  );
+}
 /** 查看单个学生的试卷轨迹 */
 export async function getSingleStudentTaskOfStudentTrack(studentId: string) {
   return httpApp.post<Task>(

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

@@ -64,9 +64,11 @@ export default function useMarkSubmit() {
       return;
     }
 
-    const allowNullQs = markStore.currentTask.questionList
-      .filter((q) => q.problem || !q.selfMark)
-      .map((q, i) => i);
+    // 问题卷或者不是自己评的试题的答案允许为null
+    const allowNullQs = [];
+    markStore.currentTask.questionList.forEach((q, i) => {
+      if (q.problem || !q.selfMark) allowNullQs.push(i);
+    });
     const questions = markStore.currentTask.questionList.filter(
       (q) => !q.problem && q.selfMark
     );

+ 73 - 46
src/features/track/Track.vue

@@ -44,11 +44,7 @@ import { useMarkStore } from "@/store";
 import { Task } from "@/types";
 import vls from "@/utils/storage";
 
-import {
-  getSingleStudentTaskOfStudentTrack,
-  getSingleStudentCardData,
-} from "@/api/studentTrackPage";
-import { studentObjectiveConfirmData } from "@/api/checkPage";
+import { getStudentTrack } from "@/api/studentTrackPage";
 import { doLogout } from "@/api/markPage";
 
 import MarkTool from "../mark/toolbar/MarkTool.vue";
@@ -64,49 +60,80 @@ function logout() {
 async function updateTask() {
   const mkey = "fetch_task_key";
   void message.info({ content: "获取任务中...", duration: 1.5, key: mkey });
-  let res = await getSingleStudentTaskOfStudentTrack(studentId);
-
-  if (res.data.studentId) {
-    const rawTask = res.data;
-
-    if (!res.data.sheetUrls?.length) {
-      rawTask.sheetUrls = rawTask.sheetUrls || [];
-      markStore.message = "暂无数据";
-    } else {
-      // 获取客观题选项信息
-      const objectiveRes = await studentObjectiveConfirmData(studentId);
-      const objectiveData = objectiveRes.data;
-
-      const answerMap = {} as Task["answerMap"];
-      objectiveData.answers.forEach((item) => {
-        answerMap[`${item.mainNumber}_${item.subNumber}`] = {
-          answer: item.answer,
-          totalScore: item.totalScore,
-          score: item.score,
-          isRight: item.answer === item.standardAnswer,
-        };
-      });
-
-      // 获取题卡数据
-      const cardRes = await getSingleStudentCardData(studentId);
-      const cardData = cardRes.data?.content
-        ? JSON.parse(cardRes.data.content)
-        : { pages: [] };
-
-      rawTask.answerMap = answerMap;
-      rawTask.recogDatas = objectiveData.sheetUrls.map(
-        (item) => item.recogData
-      );
-      rawTask.cardData = cardData.pages;
-    }
-
-    rawTask.sliceUrls = [...rawTask.sheetUrls];
-    markStore.currentTask = rawTask;
-    markStore.setting.doubleTrack = true;
-  } else {
-    markStore.message = res.data.message;
+  const res = await getStudentTrack(studentId);
+
+  if (!res.data?.studentId) {
+    void message.success({
+      content: "无任务",
+      key: mkey,
+    });
+    markStore.message = res.data?.message;
+    return;
   }
 
+  if (!res.data?.sheetUrls?.length) {
+    markStore.message = "暂无数据";
+    void message.success({
+      content: "无任务",
+      key: mkey,
+    });
+    return;
+  }
+
+  const taskData = res.data;
+  const rawTask: Task = {
+    examId: taskData.examId,
+    studentId: taskData.studentId,
+    secretNumber: taskData.secretNumber,
+    courseCode: taskData.courseCode,
+    courseName: taskData.courseName,
+    paperNumber: taskData.paperNumber,
+    studentCode: taskData.studentCode,
+    studentName: taskData.studentName,
+    paperType: taskData.paperType,
+    objectiveScore: taskData.objectiveScore || 0,
+    markerScore:
+      (taskData.objectiveScore || 0) + (taskData.subjectiveScore || 0),
+    sheetUrls: taskData.sheetUrls.map((item) => item.url),
+    sliceUrls: [],
+    questionList: taskData.subjectiveQuestions,
+    answerMap: {},
+    recogDatas: [],
+    cardData: [],
+    markAreas: taskData.subjectiveQuestions.map((item) => {
+      return {
+        mainNumber: item.mainNumber,
+        subNumber: item.subNumber,
+        questionType: item.questionType,
+        splitConfig: item.picList || [],
+      };
+    }),
+  };
+
+  // 获取客观题选项信息
+  const answerMap = {} as Task["answerMap"];
+  taskData.objectiveQuestions.forEach((item) => {
+    answerMap[`${item.mainNumber}_${item.subNumber}`] = {
+      answer: item.answer,
+      totalScore: item.totalScore,
+      score: item.score,
+      isRight: item.answer === item.standardAnswer,
+    };
+  });
+
+  // 获取题卡数据
+  const cardData: { pages: Task["cardData"] } = taskData.cardContent
+    ? JSON.parse(taskData.cardContent)
+    : { pages: [] };
+
+  rawTask.answerMap = answerMap;
+  rawTask.recogDatas = taskData.sheetUrls.map((item) => item.recogData);
+  rawTask.cardData = cardData.pages;
+
+  rawTask.sliceUrls = [...rawTask.sheetUrls];
+  markStore.currentTask = rawTask;
+  markStore.setting.doubleTrack = true;
+
   void message.success({
     content: res.data.studentId ? "获取成功" : "无任务",
     key: mkey,

+ 67 - 59
src/features/track/composables/useTrackTag.ts

@@ -107,55 +107,13 @@ export default function useTrackTag() {
     return answerTags;
   }
 
-  // 解析各试题答题区域
-  function parseQuestionAreas(questions: Question[]) {
-    if (!questions.length || !markStore.currentTask.cardData?.length) return [];
-
-    const pictureConfigs: QuestionArea[] = [];
-    const structs = questions.map(
-      (item) => `${item.mainNumber}_${item.subNumber}`
-    );
-    markStore.currentTask.cardData.forEach((page, pindex) => {
-      page.exchange.answer_area.forEach((area) => {
-        const [x, y, w, h] = area.area;
-        const qStruct = `${area.main_number}_${area.sub_number}`;
-
-        const pConfig: QuestionArea = {
-          i: pindex + 1,
-          x,
-          y,
-          w,
-          h,
-          qStruct,
-        };
-
-        if (typeof area.sub_number === "number") {
-          if (!structs.includes(qStruct)) return;
-          pictureConfigs.push(pConfig);
-          return;
-        }
-        // 复合区域处理,比如填空题,多个小题合并为一个区域
-        if (typeof area.sub_number === "string") {
-          const areaStructs = area.sub_number
-            .split(",")
-            .map((subNumber) => `${area.main_number}_${subNumber}`);
-          if (
-            structs.some((struct) => areaStructs.includes(struct)) &&
-            !pictureConfigs.find((item) => item.qStruct === qStruct)
-          ) {
-            pictureConfigs.push(pConfig);
-          }
-        }
-      });
-    });
-    // console.log(pictureConfigs);
-
-    // 合并相邻区域
+  // 合并评卷区
+  function combinePictureConfig(pictureConfigs: QuestionArea[]) {
     pictureConfigs.sort((a, b) => {
       return a.i - b.i || a.x - b.x || a.y - b.y;
     });
     const combinePictureConfigList: QuestionArea[] = [];
-    let prevConfig = null;
+    let prevConfig = {} as QuestionArea;
     pictureConfigs.forEach((item, index) => {
       if (!index) {
         prevConfig = { ...item };
@@ -180,26 +138,76 @@ export default function useTrackTag() {
     return combinePictureConfigList;
   }
 
-  // 获取属于填空题的试题号
-  function getFillLines() {
-    if (!markStore.currentTask.cardData?.length) return {};
+  // 解析各试题答题区域
+  function parseQuestionAreas(questions: Question[]) {
+    if (!questions.length || !markStore.currentTask.cardData?.length) return [];
 
-    const questions: Record<number, string[]> = {};
-    markStore.currentTask.cardData.forEach((page) => {
-      page.columns.forEach((column) => {
-        column.elements.forEach((element) => {
-          if (element.type !== "FILL_LINE") return;
+    if (!questions.length || !markStore.currentTask?.markAreas?.length)
+      return [];
 
-          if (!questions[element.topicNo]) questions[element.topicNo] = [];
+    const pictureConfigs: QuestionArea[] = [];
+    const structs = questions.map(
+      (item) => `${item.mainNumber}_${item.subNumber}`
+    );
 
-          for (let i = 0; i < element.questionsCount; i++) {
-            questions[element.topicNo].push(
-              `${element.topicNo}_${element.startNumber + i}`
-            );
-          }
+    markStore.currentTask.markAreas.forEach((markArea) => {
+      const qStruct = `${markArea.mainNumber}_${markArea.subNumber}`;
+      if (!structs.includes(qStruct)) return;
+
+      (markArea.splitConfig || []).forEach((area) => {
+        pictureConfigs.push({
+          i: area.i,
+          x: area.x,
+          y: area.y,
+          w: area.w,
+          h: area.h,
+          qStruct,
         });
       });
     });
+    // console.log(pictureConfigs);
+
+    // 合并相邻区域
+    const combinePictureConfigList: QuestionArea[] =
+      combinePictureConfig(pictureConfigs);
+    // console.log(combinePictureConfigList);
+    return combinePictureConfigList;
+  }
+
+  // 获取属于填空题的试题号
+  // function getFillLinesFromCard() {
+  //   if (!markStore.currentTask.cardData?.length) return {};
+
+  //   const questions: Record<number, string[]> = {};
+  //   markStore.currentTask.cardData.forEach((page) => {
+  //     page.columns.forEach((column) => {
+  //       column.elements.forEach((element) => {
+  //         if (element.type !== "FILL_LINE") return;
+
+  //         if (!questions[element.topicNo]) questions[element.topicNo] = [];
+
+  //         for (let i = 0; i < element.questionsCount; i++) {
+  //           questions[element.topicNo].push(
+  //             `${element.topicNo}_${element.startNumber + i}`
+  //           );
+  //         }
+  //       });
+  //     });
+  //   });
+  //   return questions;
+  // }
+
+  // 通过评卷区获取属于填空题的试题号
+  function getFillLines() {
+    if (!markStore.currentTask.markAreas?.length) return {};
+
+    const questions: Record<number, string[]> = {};
+    markStore.currentTask.markAreas.forEach((markArea) => {
+      const { mainNumber, subNumber, questionType } = markArea;
+      if (questionType !== 4) return;
+      if (!questions[mainNumber]) questions[mainNumber] = [];
+      questions[mainNumber].push(`${mainNumber}_${subNumber}`);
+    });
     return questions;
   }
 

File diff suppressed because it is too large
+ 19 - 0
src/features/track/data.json


+ 43 - 0
src/types/index.ts

@@ -189,6 +189,14 @@ export interface Group {
   totalCount: number;
 }
 
+export interface MarkArea {
+  mainNumber: number;
+  subNumber: number;
+  // 1: 单选 2: 多选 3: 判断 4: 填空 5: 问答
+  questionType: 1 | 2 | 3 | 4 | 5;
+  splitConfig?: SplitConfig[];
+}
+
 interface RawTask {
   // 考试id
   examId: string;
@@ -298,6 +306,7 @@ export interface Task extends RawTask {
   >;
   recogDatas?: string[];
   cardData?: Array<CardDataItem>;
+  markAreas?: MarkArea[];
 }
 
 interface RawQuestion {
@@ -701,6 +710,40 @@ export interface CardData {
   content: string;
 }
 
+export interface SubjectiveQuestion extends RawQuestion {
+  picList: SplitConfig[];
+  // 1-单选,2-多选,3-判断,4-填空,5-问答
+  questionType: 1 | 2 | 3 | 4 | 5;
+}
+export interface objectiveQuestion {
+  mainNumber: number;
+  subNumber: string;
+  answer: string;
+  exist: boolean;
+  standardAnswer: string;
+  score: number;
+  totalScore: number;
+  // 1-单选,2-多选,3-判断
+  questionType: 1 | 2 | 3;
+}
+export interface StudentTrackTask {
+  examId: string;
+  studentId: string;
+  secretNumber: string;
+  studentName: string;
+  studentCode: string;
+  courseCode: string;
+  courseName: string;
+  paperNumber: string;
+  paperType: string;
+  objectiveScore: number;
+  subjectiveScore: number;
+  sheetUrls: Array<{ index: number; url: string; recogData: string }>;
+  subjectiveQuestions: SubjectiveQuestion[];
+  objectiveQuestions: objectiveQuestion[];
+  cardContent: string;
+}
+
 // track-tag
 export interface MarkDetailUserItem {
   userId: string;

Some files were not shown because too many files changed in this diff