瀏覽代碼

feat: 问题卷

zhangjie 3 月之前
父節點
當前提交
45b1da3973

+ 1 - 0
components.d.ts

@@ -30,6 +30,7 @@ declare module '@vue/runtime-core' {
     ATextarea: typeof import('ant-design-vue/es')['Textarea']
     ATooltip: typeof import('ant-design-vue/es')['Tooltip']
     CommonMarkHeader: typeof import('./src/components/CommonMarkHeader.vue')['default']
+    ExampleComponent: typeof import('./src/components/ExampleComponent.vue')['default']
     PageError404: typeof import('./src/components/PageError404.vue')['default']
     QmButton: typeof import('./src/components/QmButton.vue')['default']
     QmDialog: typeof import('./src/components/QmDialog.vue')['default']

+ 2 - 4
src/components/QmButton.vue

@@ -1,10 +1,8 @@
 <template>
-  <!-- FIXED: 从 vue 3.1.5 升级到 3.2.1 时这里出错了,slot也要有key -->
   <a-button v-bind="newAttrs" :loading="inInterval" @click="insideClick">
-    <template v-for="(_, slot) of $slots" #[slot]="scope" :key="slot">
-      <slot v-bind="asAny(scope)" :key="slot" :name="asAny(slot)" />
+    <template v-for="(slot, name) in $slots" :key="name" #[name]="scope">
+      <slot v-bind="asAny(scope)" :key="name" :name="name" />
     </template>
-    <!-- <slot name="default" /> -->
   </a-button>
 </template>
 

+ 1 - 0
src/features/mark/composables/useAutoChooseFirstQuestion.ts

@@ -60,6 +60,7 @@ export default function useAutoChooseFirstQuestion() {
 
   function chooseQuestion(question: Question) {
     if (!question.selfMark) return;
+
     markStore.currentQuestion = question;
     // FIXME: maybe should be an async function, temp fix for eslint
     void scrollToQuestionOfBoard(question);

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

@@ -20,6 +20,9 @@ export default function useMarkSubmit() {
     markResult.scoreList.forEach((score: number, index: number) => {
       if (!markStore.currentTask) return;
       const question = markStore.currentTask.questionList[index]!;
+      // 如果是自评或者有问题的题目,不检查分数
+      if (!question.selfMark || question.problem) return;
+
       const { maxScore, minScore, mainNumber, subNumber, questionName } =
         question;
       let error;
@@ -56,10 +59,21 @@ export default function useMarkSubmit() {
       return;
     }
 
+    const problemQs = markStore.currentTask.questionList
+      .filter((q) => q.problem)
+      .map((q) => q.mainNumber + "-" + q.subNumber);
+    const questions = markStore.currentTask.questionList.filter(
+      (q) => !q.problem
+    );
+    const scoreList = markResult.scoreList.filter(
+      (_, i) =>
+        !problemQs.includes(
+          questions[i].mainNumber + "-" + questions[i].subNumber
+        )
+    );
     if (
-      markResult.scoreList.length !==
-        markStore.currentTask.questionList.length ||
-      !markResult.scoreList.every((s) => isNumber(s))
+      scoreList.length !== questions.length ||
+      !scoreList.every((s) => isNumber(s))
     ) {
       console.error({ content: "markResult格式不正确,缺少分数" });
       return;

+ 55 - 17
src/features/mark/scoring/MarkBoardTrack.vue

@@ -69,7 +69,7 @@
               'board-question',
               {
                 'is-current': isCurrentQuestion(question),
-                'is-disabled': !question.selfMark,
+                'is-disabled': isDisabledQuestion(question),
               },
             ]"
             tabindex="0"
@@ -104,8 +104,12 @@
               </div>
             </div>
             <!-- 设置高度 避免动画跳动 -->
-            <div class="question-score">
+            <div
+              :class="['question-score', { 'is-problem': question.problem }]"
+            >
+              <span v-if="question.problem"> 问题卷 </span>
               <span
+                v-else
                 :key="markStore.currentTask?.markResult?.scoreList[index] || 0"
               >
                 <!-- 特殊的空格符号 -->
@@ -146,19 +150,25 @@
       </div>
     </div>
     <div class="board-footer">
-      <div>
-        <qm-button
-          v-if="!questionScoreDisabled"
-          class="board-goback"
-          :clickTimeout="300"
-          @click="clearLatestMarkOfCurrentQuetion"
-        >
-          <template #icon>
-            <img src="@/assets/icons/icon-goback.svg" />
-          </template>
-          回退
-        </qm-button>
-      </div>
+      <qm-button
+        v-if="!questionScoreDisabled"
+        class="board-goback"
+        :clickTimeout="300"
+        @click="clearLatestMarkOfCurrentQuetion"
+      >
+        <template #icon>
+          <img src="@/assets/icons/icon-goback.svg" />
+        </template>
+        回退
+      </qm-button>
+
+      <qm-button
+        v-if="markStore.currentQuestion.problem"
+        class="board-clear"
+        @click="cancelProblem"
+      >
+        取消问题卷
+      </qm-button>
 
       <qm-button
         class="board-clear"
@@ -256,11 +266,14 @@ EventBus.on("draw-change", (list: any) => {
 // 切换题目是清空上一题的分数
 watch(
   () => markStore.currentQuestion,
-  () => {
+  (val) => {
+    console.log("currentQuestion:", val);
     markStore.currentScore = undefined;
+    if (!val) return;
     // eslint-disable-next-line @typescript-eslint/no-floating-promises
     nextTick(() => {
-      if (!props.isCheckAnswer) chooseScore(questionScoreSteps[1]);
+      if (!props.isCheckAnswer && !val.problem)
+        chooseScore(questionScoreSteps[1]);
     });
   }
 );
@@ -330,10 +343,16 @@ function isCurrentQuestion(question: Question) {
   );
 }
 
+function isDisabledQuestion(question: Question) {
+  return !question.selfMark;
+}
+
 function isCurrentScore(score: number) {
   return markStore.currentScore === score;
 }
 function chooseScore(score: number) {
+  if (markStore.currentQuestion.problem) return;
+
   if (markStore.currentScore === score) {
     markStore.currentScore = undefined;
   } else {
@@ -343,6 +362,25 @@ function chooseScore(score: number) {
   markStore.currentSpecialTagType = undefined;
 }
 
+function cancelProblem() {
+  if (!markStore.currentQuestion) return;
+
+  markStore.currentQuestion.problem = false;
+  markStore.currentQuestion.problemType = "";
+  markStore.currentQuestion.problemRemark = "";
+
+  console.log("cancelProblem currentQuestion:", markStore.currentQuestion);
+
+  // const markResult = markStore.currentTask.markResult;
+  // markResult.questionList.forEach((item) => {
+  //   if (item.questionId === markStore.currentQuestion.questionId) {
+  //     item.problem = false;
+  //     item.problemType = "";
+  //     item.problemRemark = "";
+  //   }
+  // });
+}
+
 let keyPressTimestamp = 0;
 let keys: string[] = [];
 function numberKeyListener(event: KeyboardEvent) {

+ 31 - 36
src/features/mark/toolbar/MarkProblemDialog.vue

@@ -40,10 +40,8 @@
 </template>
 
 <script setup lang="ts">
-import { doProblemType, getStatus } from "@/api/markPage";
 import { message } from "ant-design-vue";
 import { useMarkStore } from "@/store";
-import EventBus from "@/plugins/eventBus";
 import { reactive } from "vue";
 
 const markStore = useMarkStore();
@@ -63,14 +61,9 @@ const formModel: FormModel = reactive({
   problemRemark: "",
 });
 
-async function updateStatus() {
-  const res = await getStatus();
-  markStore.status = res.data;
-}
-
-const handleOk = async () => {
-  if (!markStore.currentTask) {
-    void message.warn({ content: "没有可以标记的任务", duration: 5 });
+const handleOk = () => {
+  if (!markStore.currentQuestion) {
+    void message.warn({ content: "没有可以标记的试题" });
     return;
   }
   if (!formModel.problemType) {
@@ -81,32 +74,34 @@ const handleOk = async () => {
     void message.error({ content: "请输入原因" });
     return;
   }
-  try {
-    const res = await doProblemType({
-      problemType: formModel.problemType,
-      problemRemark:
-        formModel.problemType === "OTHER" ? formModel.problemRemark : undefined,
-    });
-    if (res?.data.success) {
-      void message.success({ content: "问题卷处理成功", duration: 3 });
-      visible = false;
-      markStore.currentTask = undefined;
-      if (markStore.historyOpen) {
-        EventBus.emit("should-reload-history");
-      } else {
-        markStore.tasks.shift();
-        markStore.currentTask = markStore.tasks[0];
-      }
-      await updateStatus();
-    } else {
-      void message.error({ content: res?.data.message || "错误", duration: 5 });
-    }
-  } catch (error) {
-    console.log("问题卷处理失败", error);
-    void message.error({ content: "网络异常", duration: 5 });
-    await new Promise((res) => setTimeout(res, 1500));
-    window.location.reload();
-  }
+
+  markStore.currentQuestion.problem = true;
+  markStore.currentQuestion.problemType = formModel.problemType;
+  markStore.currentQuestion.problemRemark = formModel.problemRemark;
+
+  const markResult = markStore.currentTask.markResult;
+  // markResult.questionList.forEach((item) => {
+  //   if (item.questionId === markStore.currentQuestion.questionId) {
+  //     item.problem = true;
+  //     item.problemType = formModel.problemType;
+  //     item.problemRemark = formModel.problemRemark;
+  //   }
+  // });
+  markStore.removeScoreTracks = markResult.markerTrackList.filter(
+    (q) =>
+      q.mainNumber === markStore.currentQuestion?.mainNumber &&
+      q.subNumber === markStore.currentQuestion?.subNumber
+  );
+  markResult.markerTrackList = markResult.markerTrackList.filter(
+    (q) =>
+      q.mainNumber !== markStore.currentQuestion?.mainNumber ||
+      q.subNumber !== markStore.currentQuestion?.subNumber
+  );
+  const { __index } = markStore.currentQuestion;
+  markResult.scoreList[__index] = null;
+  markStore.currentScore = null;
+
+  visible = false;
 };
 
 const handleCancel = () => {

+ 17 - 4
src/styles/page.less

@@ -697,6 +697,12 @@
         display: block;
         overflow: hidden;
       }
+
+      &.is-problem {
+        background-color: #fff2f0;
+        color: #f53f3f;
+        font-size: 16px;
+      }
     }
 
     &.is-current,
@@ -704,6 +710,11 @@
       border-color: #165dff;
       color: #165dff;
     }
+
+    &.is-disabled {
+      cursor: not-allowed;
+      color: #999;
+    }
   }
 
   .board-spliter {
@@ -783,13 +794,14 @@
     display: inline-block;
     vertical-align: top;
     font-size: 14px;
-    width: 34px;
+    min-width: 34px;
     height: 34px;
     line-height: 30px;
     border-radius: 4px;
     border: 1px solid #e5e5e5;
     text-align: center;
     margin: 0 4px 16px;
+    padding: 0 2px;
     cursor: pointer;
 
     &:hover {
@@ -825,8 +837,8 @@
   .board-footer {
     position: absolute;
     bottom: 16px;
-    left: 16px;
-    right: 16px;
+    left: 5px;
+    right: 5px;
     height: 36px;
     z-index: 8;
     display: flex;
@@ -835,8 +847,9 @@
     background-color: #fff;
 
     .ant-btn {
-      width: 156px;
       border-radius: 4px;
+      flex: 1;
+      margin: 0 5px;
 
       img {
         display: inline-block;

+ 8 - 1
src/types/index.ts

@@ -329,11 +329,18 @@ interface RawQuestion {
   markerTagList: Array<SpecialTag> | null;
   // 科组长特殊标记
   headerTagList: Array<SpecialTag> | null;
+  // 是否打回
+  rejected: boolean;
   // 打回原因
   rejectReason: string | null;
   // 打回前给分数据
   rejectScoreList: number[] | null;
-  rejected?: boolean;
+  // 问题卷
+  problem: boolean;
+  // 问题卷类型
+  problemType: string;
+  // 问题卷备注
+  problemRemark: string;
   questionName?: string;
 }
 export interface Question extends RawQuestion {