刘洋 2 жил өмнө
parent
commit
35ff802dcc

+ 4 - 0
.eslintrc.js

@@ -38,6 +38,8 @@ module.exports = {
     "@typescript-eslint/restrict-template-expressions": "off",
     "@typescript-eslint/no-non-null-assertion": "off",
     "@typescript-eslint/no-unsafe-call": "off",
+    "@typescript-eslint/no-unsafe-return": "off",
+    "@typescript-eslint/no-unsafe-argument": "off",
     // 一处使用hypen和一处不使用hypen,让字符搜索变得困难
     "vue/attribute-hyphenation": ["error", "never"],
     "vue/v-on-event-hyphenation": ["error", "never", { autofix: true }],
@@ -47,6 +49,8 @@ module.exports = {
     "vue/no-setup-props-destructure": "off",
     "vue/multi-word-component-names": "off",
     "vue/no-unsafe-call": "off",
+    "vue/no-unsafe-return": "off",
+    "vue/no-unsafe-argument": "off",
   },
   ignorePatterns: [
     ".eslintrc.js",

+ 1 - 0
components.d.ts

@@ -14,6 +14,7 @@ declare module '@vue/runtime-core' {
     AForm: typeof import('ant-design-vue/es')['Form']
     AFormItem: typeof import('ant-design-vue/es')['FormItem']
     AInput: typeof import('ant-design-vue/es')['Input']
+    AInputSearch: typeof import('ant-design-vue/es')['InputSearch']
     AMenu: typeof import('ant-design-vue/es')['Menu']
     AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
     AModal: typeof import('ant-design-vue/es')['Modal']

+ 11 - 0
src/devLoginParams.ts

@@ -22,6 +22,17 @@ export const LOGIN_CONFIG = {
 };
 // export const LOGIN_CONFIG = {
 //   isAdmin: false,
+//   forceChange: true,
+//   loginName: "spj111-01",
+//   // loginName: "liuyang",
+//   password: "123456",
+//   examId: "295",
+//   markerId: "3132",
+//   // markerId: "482",
+//   // markerId: "483",
+// };
+// export const LOGIN_CONFIG = {
+//   isAdmin: false,
 //   forceChange: false,
 //   loginName: "spj111-01",
 //   // loginName: "spj432-01",

+ 29 - 24
src/features/mark/CommonMarkBody.vue

@@ -70,9 +70,11 @@
         {{ store.currentQuestion?.mainNumber }}-{{
           store.currentQuestion?.subNumber
         }}({{
-          store.currentTask?.markResult?store.currentTask?.markResult.scoreList[
-            store.currentQuestion?.__index || 0
-          ] : " "
+          store.currentTask?.markResult
+            ? store.currentTask?.markResult.scoreList[
+                store.currentQuestion?.__index || 0
+              ]
+            : " "
         }})
       </div>
       <div class="text">
@@ -99,7 +101,7 @@ import "viewerjs/dist/viewer.css";
 import Viewer from "viewerjs";
 import ZoomPaper from "@/components/ZoomPaper.vue";
 import { message } from "ant-design-vue";
-
+import EventBus from "@/plugins/eventBus";
 type MakeTrack = (
   event: MouseEvent,
   item: SliceImage,
@@ -180,6 +182,14 @@ let sliceImagesWithTrackList: SliceImage[] = reactive([]);
 let maxSliceWidth = 0; // 最大的裁切块宽度,图片容器以此为准
 let theFinalHeight = 0; // 最终宽度,用来定位轨迹在第几张图片,不包括image-seperator高度
 
+watch(
+  () => sliceImagesWithTrackList,
+  () => {
+    EventBus.emit("draw-change", sliceImagesWithTrackList);
+  },
+  { deep: true }
+);
+
 async function processSliceConfig() {
   if (!store.currentTask) return;
   let markResult = store.currentTask.markResult;
@@ -203,7 +213,7 @@ async function processSliceConfig() {
     const image = await loadImage(url);
     images[sliceConfig.i] = image;
     const { x, y, w, h } = sliceConfig;
-    
+
     if (sliceConfig.w === 0 && sliceConfig.h === 0) {
       // 选择整图时,w/h 为0
       sliceConfig.w = image.naturalWidth;
@@ -211,12 +221,11 @@ async function processSliceConfig() {
     }
 
     if (x < 1 && y < 1 && w < 1 && h < 1) {
-      sliceConfig.x = (image.naturalWidth * x);
-      sliceConfig.y = (image.naturalHeight * y);
-      sliceConfig.w = (image.naturalWidth * w);
-      sliceConfig.h = (image.naturalHeight * h);
+      sliceConfig.x = image.naturalWidth * x;
+      sliceConfig.y = image.naturalHeight * y;
+      sliceConfig.w = image.naturalWidth * w;
+      sliceConfig.h = image.naturalHeight * h;
     }
-    
   }
 
   theFinalHeight = store.currentTask.sliceConfig
@@ -257,9 +266,9 @@ async function processSliceConfig() {
       (t) => t.offsetIndex === indexInSliceUrls
     );
 
-    const sliceImageRendered = await loadImage(dataUrl!);
+    const sliceImageRendered = await loadImage(dataUrl);
     tempSliceImagesWithTrackList.push({
-      url: dataUrl!,
+      url: dataUrl,
       indexInSliceUrls: sliceConfig.i,
       // 通过positionY来定位是第几张slice的还原,并过滤出相应的track
       trackList: thisImageTrackList.filter(
@@ -405,9 +414,9 @@ async function processSplitConfig() {
       const thisImageTagList = tagLists.filter(
         (t) => t.offsetIndex === indexInSliceUrls
       );
-      const sliceImageRendered = await loadImage(dataUrl!);
+      const sliceImageRendered = await loadImage(dataUrl);
       tempSliceImagesWithTrackList.push({
-        url: dataUrl!,
+        url: dataUrl,
         indexInSliceUrls: store.currentTask.sliceUrls.indexOf(url) + 1,
         trackList: thisImageTrackList.filter(
           (t) =>
@@ -460,8 +469,8 @@ async function processSplitConfig() {
 // should not render twice at the same time
 let renderLock = false;
 const renderPaperAndMark = async () => {
-  console.log('renderPagerAndMark=>store.curTask:',store.currentTask);
-  
+  console.log("renderPagerAndMark=>store.curTask:", store.currentTask);
+
   if (!store.currentTask) return;
   if (!store.isScanImage) return;
   if (renderLock) {
@@ -515,7 +524,7 @@ const renderPaperAndMark = async () => {
 watch(
   () => store.currentTask,
   () => {
-    setTimeout(renderPaperAndMark,50)
+    setTimeout(renderPaperAndMark, 50);
   }
 );
 //#endregion : 计算裁切图和裁切图上的分数轨迹和特殊标记轨迹
@@ -525,9 +534,7 @@ const answerPaperScale = $computed(() => {
   // 放大、缩小不影响页面之前的滚动条定位
   let percentWidth = 0;
   let percentTop = 0;
-  const container = document.querySelector(
-    ".mark-body-container"
-  ) as HTMLDivElement;
+  const container = document.querySelector(".mark-body-container");
   if (container) {
     const { scrollLeft, scrollTop, scrollWidth, scrollHeight } = container;
     percentWidth = scrollLeft / scrollWidth;
@@ -657,9 +664,7 @@ function giveScoreCicle(event: KeyboardEvent) {
   // 接收currentScore间隔时间外才会进入此事件
   if (event.key === " " && typeof store.currentScore === "number") {
     // topKB--;
-    const circleElement = document.querySelector(
-      ".kb-circle"
-    ) as HTMLDivElement;
+    const circleElement = document.querySelector(".kb-circle");
     let { top, left } = circleElement.getBoundingClientRect();
     top = top + 45;
     left = left + 45;
@@ -684,7 +689,7 @@ function giveScoreCicle(event: KeyboardEvent) {
     if (eles[0].tagName == "IMG") {
       ele = eles[0];
     }
-    if (ele!) {
+    if (ele) {
       ele.dispatchEvent(me);
     }
   }

+ 11 - 11
src/features/mark/Mark.vue

@@ -233,24 +233,24 @@ watch(
 );
 
 const showRejectedReason = (task: Task) => {
-  const rejectShowedTasksString = sessionStorage.getItem("reject-task-showed");
+  // const rejectShowedTasksString = sessionStorage.getItem("reject-task-showed");
 
-  let rejectShowedTasks: string[] = [];
+  // let rejectShowedTasks: string[] = [];
 
-  if (rejectShowedTasksString) {
-    rejectShowedTasks = JSON.parse(rejectShowedTasksString);
-  }
+  // if (rejectShowedTasksString) {
+  //   rejectShowedTasks = JSON.parse(rejectShowedTasksString);
+  // }
 
   if (
-    !rejectShowedTasks.includes(task.secretNumber) &&
+    // !rejectShowedTasks.includes(task.secretNumber) &&
     task.rejected &&
     task.rejectReason
   ) {
-    rejectShowedTasks.push(task.secretNumber);
-    sessionStorage.setItem(
-      "reject-task-showed",
-      JSON.stringify(rejectShowedTasks)
-    );
+    // rejectShowedTasks.push(task.secretNumber);
+    // sessionStorage.setItem(
+    //   "reject-task-showed",
+    //   JSON.stringify(rejectShowedTasks)
+    // );
     const [reasonType, reasonDesc] = task.rejectReason.split(":");
     Modal.info({
       title: null,

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

@@ -33,9 +33,17 @@
         <div class="total-score tw-ml-5 tw-font-bold">
           <transition-group name="score-number-animation" tag="span">
             <span
-              :key="store.currentTaskEnsured.markResult?store.currentTaskEnsured.markResult.markerScore+'' :'0'"
+              :key="
+                store.currentTaskEnsured.markResult
+                  ? store.currentTaskEnsured.markResult.markerScore + ''
+                  : '0'
+              "
               class="tw-inline-block"
-              >{{ store.currentTaskEnsured.markResult?store.currentTaskEnsured.markResult.markerScore:'' }}</span
+              >{{
+                store.currentTaskEnsured.markResult
+                  ? store.currentTaskEnsured.markResult.markerScore
+                  : ""
+              }}</span
             >
           </transition-group>
         </div>
@@ -103,7 +111,10 @@
         >
           <div class="tw-flex tw-justify-between">
             <div>
-              <div>
+              <div v-if="!!question.questionName">
+                {{ question.questionName }}
+              </div>
+              <div v-else>
                 {{ question.title }} {{ question.mainNumber }}-{{
                   question.subNumber
                 }}

+ 4 - 1
src/features/mark/MarkBoardMouse.vue

@@ -100,7 +100,10 @@
       >
         <div class="question tw-rounded tw-mb-2">
           <div>
-            <div>
+            <div v-if="!!question.questionName">
+              {{ question.questionName }}
+            </div>
+            <div v-else>
               {{ question.title }} {{ question.mainNumber }}-{{
                 question.subNumber
               }}

+ 33 - 9
src/features/mark/MarkBoardTrack.vue

@@ -132,19 +132,22 @@
             outline="0"
             hidefocus="true"
             @click="chooseQuestion(question)"
-            @contextmenu="onRightClick"
+            @contextmenu="onRightClick($event, index)"
             @blur="rightBlur"
           >
             <div
               v-if="activeRightMenuItem"
-              class="tw-absolute right-menu-box"
+              class="tw-fixed right-menu-box"
               :style="tmpStyle"
             >
               <div class="right-menu-item" @click="positioning(question)">
                 定位
               </div>
             </div>
-            <div>
+            <div v-if="!!question.questionName">
+              {{ question.questionName }}
+            </div>
+            <div v-else>
               {{ question.title }} {{ question.mainNumber }}-{{
                 question.subNumber
               }}
@@ -273,7 +276,8 @@ import { store } from "@/store/store";
 import { autoChooseFirstQuestion } from "./use/autoChooseFirstQuestion";
 import { dragSplitPane } from "./use/splitPane";
 import { addFocusTrack, removeFocusTrack } from "./use/focusTracks";
-
+import EventBus from "@/plugins/eventBus";
+import { cloneDeep } from "lodash-es";
 const props = defineProps<{ modal?: boolean }>();
 const emit = defineEmits(["submit", "allZeroSubmit", "unselectiveSubmit"]);
 const { dragSpliter, topPercent } = dragSplitPane();
@@ -281,19 +285,31 @@ const activeRightMenuItem = ref<any>(null);
 const tmpStyle = reactive<any>({
   left: 0,
   top: 0,
+  zIndex: 300,
 });
-const onRightClick = (e: any) => {
+const onRightClick = (e: any, index?: any) => {
   e.preventDefault();
-  tmpStyle.left = e.layerX + "px";
-  tmpStyle.top = e.layerY + "px";
-  activeRightMenuItem.value = e;
+  if (
+    store.currentTask?.markResult.scoreList[index] ||
+    store.currentTask?.markResult.scoreList[index] === 0
+  ) {
+    tmpStyle.left = e.clientX + "px";
+    tmpStyle.top = e.clientY + "px";
+    activeRightMenuItem.value = e;
+  }
 };
 const rightBlur = () => {
   activeRightMenuItem.value = null;
   removeFocusTrack();
 };
 const positioning = (question: Question) => {
-  addFocusTrack(undefined, question.mainNumber, question.subNumber, true);
+  addFocusTrack(
+    undefined,
+    question.mainNumber,
+    question.subNumber,
+    true,
+    sliceImagesWithTrackListCopy.value
+  );
   activeRightMenuItem.value = null;
 };
 watch(topPercent, () => {
@@ -306,6 +322,14 @@ watch(topPercent, () => {
 });
 
 const { chooseQuestion } = autoChooseFirstQuestion();
+let sliceImagesWithTrackListCopy = ref([]);
+EventBus.on("draw-change", (list: any) => {
+  if (store.getMarkStatus === "正评" || store.getMarkStatus === "试评") {
+    sliceImagesWithTrackListCopy.value = cloneDeep(list);
+  } else {
+    sliceImagesWithTrackListCopy.value = [];
+  }
+});
 
 // 切换题目是清空上一题的分数
 watch(

+ 13 - 4
src/features/mark/MarkDrawTrack.vue

@@ -60,7 +60,7 @@ const computeTopAndLeft = (track: Track | SpecialTag) => {
   }
 
   return {
-    color: track.color || 'red',
+    color: track.color || "red",
     top: topInsideSliceRatio * 100 + "%",
     left: leftInsideSliceRatio * 100 + "%",
     "font-size":
@@ -70,9 +70,18 @@ const computeTopAndLeft = (track: Track | SpecialTag) => {
       "em",
   };
 };
-
+const hasMember = (track: any) => {
+  return (
+    (store.getMarkStatus === "正评" || store.getMarkStatus === "试评") &&
+    store.focusTracks.find((item: any) => {
+      return (
+        item.mainNumber == track.mainNumber && item.subNumber == track.subNumber
+      );
+    })
+  );
+};
 const focusedTrack = (track: Track) => {
-  return store.focusTracks.includes(track);
+  return store.focusTracks.includes(track) || hasMember(track);
 };
 
 watch(
@@ -99,7 +108,7 @@ watch(
     }
   },
   {
-    deep: true
+    deep: true,
   }
 );
 </script>

+ 30 - 6
src/features/mark/MarkHistory.vue

@@ -11,25 +11,27 @@
         <a-select
           ref="select"
           v-model:value="searchType"
-          style="width: 75px; margin-right: 4px"
+          style="width: 75px; margin-right: 4px; font-size: 12px"
           @change="searchTypeChange"
         >
           <a-select-option value="1">编号</a-select-option>
           <a-select-option value="2">分数</a-select-option>
         </a-select>
-        <a-input
+        <a-input-search
           v-model:value="secretNumberInput"
-          class="tw-flex-1"
+          style="font-size: 13px"
+          class="tw-flex-1 search-value"
           placeholder="查找试卷"
           allowClear
           @keyup.enter="searchHistoryTask"
           @keypress.stop=""
           @keydown.stop=""
+          @search="searchHistoryTask"
         >
-          <template #suffix>
+          <!-- <template #suffix>
             <SearchOutlined style="color: rgba(0, 0, 0, 0.45)" />
-          </template>
-        </a-input>
+          </template> -->
+        </a-input-search>
       </div>
 
       <!-- <input
@@ -166,7 +168,23 @@ const {
   getHistory: GetHistory;
 }>();
 let searchType = $ref("1");
+
 let secretNumberInput = $ref("");
+const format = (val: string, preVal: string) => {
+  const reg = /^-?\d*(\.\d*)?$/;
+
+  if ((!isNaN(+val) && reg.test(val)) || val === "" || val === "-") {
+    secretNumberInput = val;
+  } else {
+    secretNumberInput = preVal;
+  }
+};
+watch(
+  () => secretNumberInput,
+  (val, preVal) => {
+    format(val, preVal);
+  }
+);
 const searchTypeChange = (val) => {
   console.log(val);
 };
@@ -357,6 +375,12 @@ async function searchHistoryTask() {
   height: calc(100vh - 56px);
   transition: margin-left 0.5s;
 }
+.history-container .search-value :deep(.ant-input::-webkit-input-placeholder) {
+  font-size: 12px;
+}
+.history-container :deep(.ant-input-affix-wrapper) {
+  padding: 4px 6px;
+}
 .history-container.show {
   margin-left: 0;
 }

+ 23 - 3
src/features/mark/use/focusTracks.ts

@@ -7,13 +7,14 @@ export function addFocusTrack(
   groupNumber: number | undefined,
   mainNumber: number | undefined,
   subNumber: string | undefined,
-  isMark?: boolean | undefined // 是否是评卷,评卷时要考虑标记删除
+  isMark?: boolean | undefined, // 是否是评卷,评卷时要考虑标记删除
+  list?: any
 ) {
   hovering = true;
 
   timeoutId = setTimeout(() => {
     if (hovering) {
-      _addFocusTrack(groupNumber, mainNumber, subNumber, isMark);
+      _addFocusTrack(groupNumber, mainNumber, subNumber, isMark, list);
     }
   }, 200);
 }
@@ -22,9 +23,28 @@ function _addFocusTrack(
   groupNumber: number | undefined,
   mainNumber: number | undefined,
   subNumber: string | undefined,
-  isMark: boolean | undefined // 是否是评卷,评卷时要考虑标记删除
+  isMark: boolean | undefined, // 是否是评卷,评卷时要考虑标记删除
+  list?: any
 ) {
   store.focusTracks.splice(0);
+  if (list) {
+    const trackList: any = list.map((q) => q.trackList).flat();
+
+    trackList
+      .filter((t) => {
+        if (mainNumber) {
+          return t.mainNumber === mainNumber && t.subNumber === subNumber;
+        } else {
+          return false;
+        }
+      })
+      .forEach((t: any) => {
+        // 回评时,如果没被删除
+        const shouldAdd = isMark ? trackList.includes(t) : true;
+        if (shouldAdd) store.focusTracks.push(t);
+      });
+    return;
+  }
 
   if (groupNumber) {
     store.currentTask?.questionList

+ 1 - 0
src/plugins/eventBus.ts

@@ -2,6 +2,7 @@ import mitt from "mitt";
 
 type Events = {
   "should-reload-history": undefined;
+  "draw-change": any;
 };
 
 const EventBus = mitt<Events>();

+ 1 - 0
src/types/index.ts

@@ -240,6 +240,7 @@ interface RawQuestion {
   /** 选做题分组 */
   selectiveIndex: number | null;
   rejected?: boolean;
+  questionName?: string;
 }
 export interface Question extends RawQuestion {
   /** question 在 task 里面的 index ,用来对应 scoreList 的 score */

+ 1 - 0
vite.config.ts

@@ -4,6 +4,7 @@ import ViteComponents from "unplugin-vue-components/vite";
 import { AntDesignVueResolver } from "unplugin-vue-components/resolvers";
 
 const SERVER_URL = "http://192.168.10.224";
+// const SERVER_URL = "http://192.168.11.103:8090";
 
 const path = require("path");