Michael Wang 4 vuotta sitten
vanhempi
commit
661925ff13
3 muutettua tiedostoa jossa 163 lisäystä ja 13 poistoa
  1. 99 12
      src/components/mark/MarkBody.vue
  2. 63 0
      src/components/mark/MarkDrawTrack.vue
  3. 1 1
      src/types/index.ts

+ 99 - 12
src/components/mark/MarkBody.vue

@@ -1,10 +1,21 @@
 <template>
   <div class="mark-body-container flex-auto" ref="container">
     <div :style="{ width: answerPaperScale }">
-      <template v-for="(item, index) in imageWithStyles" :key="index">
+      <div
+        v-for="(item, index) in sliceImagesWithTrackList"
+        :key="index"
+        class="single-image-container"
+      >
         <img :src="item.url" />
+        <MarkDrawTrack
+          :track-list="item.trackList"
+          :original-image="item.originalImage"
+          :slice-image="item.sliceImage"
+          :dx="item.dx"
+          :dy="item.dy"
+        />
         <hr style="border: 2px solid grey" />
-      </template>
+      </div>
       <!-- style="border: 1px solid black; background: black" -->
     </div>
     <div v-if="!store.currentTask" class="text-center">暂无评卷任务</div>
@@ -15,15 +26,24 @@
 import { computed, defineComponent, reactive, ref, watchEffect } from "vue";
 import { store } from "./store";
 import filters from "@/filters";
+import MarkDrawTrack from "./MarkDrawTrack.vue";
+import { Track } from "@/types";
 
-interface ImageStyle {
-  width: number; // or string?
+interface SliceImage {
+  url: string;
+  indexInSliceUrls: number;
+  trackList: Array<Track>;
+  originalImage: HTMLImageElement;
+  sliceImage: HTMLImageElement;
+  dx: number;
+  dy: number;
 }
 export default defineComponent({
   name: "MarkBody",
+  components: { MarkDrawTrack },
   setup() {
     const container = ref(null);
-    let imageWithStyles: Array<any> = reactive([]);
+    let sliceImagesWithTrackList: Array<SliceImage> = reactive([]);
     watchEffect(async () => {
       async function loadImage(url: string): Promise<HTMLImageElement> {
         return new Promise((resolve, reject) => {
@@ -35,12 +55,25 @@ export default defineComponent({
         });
       }
       if (!store.currentTask?.libraryId) return;
-      imageWithStyles.splice(0);
+
+      // reset sliceImagesWithTrackList
+      sliceImagesWithTrackList.splice(0);
+      const allTrackList = store.currentTask.questionList.reduce(
+        (all, c) => all.concat(c.trackList),
+        [] as Array<Track>
+      );
+
       if (store.currentTask.sliceConfig?.length) {
         for (const url of store.currentTask.sliceUrls) {
           await loadImage(filters.toCompleteUrl(url));
         }
+        const theFinalheight = store.currentTask.sliceConfig
+          .map((v) => v.h)
+          .reduce((acc, v) => (acc += v));
+        let accumTopHeight = 0;
+        let accumBottomHeight = 0;
         for (const sliceConfig of store.currentTask.sliceConfig) {
+          accumBottomHeight += sliceConfig.h;
           const url = filters.toCompleteUrl(
             store.currentTask.sliceUrls[sliceConfig.i - 1]
           );
@@ -70,17 +103,38 @@ export default defineComponent({
           );
           // console.log(image, canvas.height, sliceConfig, ctx);
           // console.log(canvas.toDataURL());
-          imageWithStyles.push({
-            url: canvas.toDataURL(),
+          const thisImageTrackList = allTrackList.filter(
+            (v) => v.offsetIndex === sliceConfig.i
+          );
+
+          const dataUrl = canvas.toDataURL();
+          const sliceImage = new Image();
+          sliceImage.src = dataUrl;
+          // sliceConfig.x + sliceConfig.w
+          sliceImagesWithTrackList.push({
+            url: dataUrl,
+            indexInSliceUrls: sliceConfig.i,
+            // 通过positionY来定位是第几张slice的还原,并过滤出相应的track
+            trackList: thisImageTrackList.filter(
+              (t) =>
+                t.positionY >= accumTopHeight / theFinalheight &&
+                t.positionY < accumBottomHeight / theFinalheight
+            ),
+            originalImage: image,
+            sliceImage,
+            dx: sliceConfig.x,
+            dy: sliceConfig.y,
           });
+          accumTopHeight = accumBottomHeight;
         }
       } else {
         const images = [];
-        for (const url of store.currentTask.sheetUrls) {
+        for (const url of store.currentTask.sliceUrls) {
           const image = await loadImage(filters.toCompleteUrl(url));
           images.push(image);
         }
 
+        // TODO: add loading
         const newConfig = (store.setting.splitConfig
           .map((v, index, ary) =>
             index % 2 === 0 ? [v, ary[index + 1]] : false
@@ -91,12 +145,19 @@ export default defineComponent({
         const maxSliceWidth =
           Math.max(...images.map((v) => v.naturalWidth)) * maxSplitConfig;
 
-        for (const url of store.currentTask.sheetUrls) {
+        const theFinalheight =
+          newConfig.length *
+          images.reduce((acc, v) => (acc += v.naturalHeight), 0);
+
+        let accumTopHeight = 0;
+        let accumBottomHeight = 0;
+        for (const url of store.currentTask.sliceUrls) {
           const completeUrl = filters.toCompleteUrl(url);
 
           for (const config of newConfig) {
             const image = await loadImage(completeUrl);
 
+            accumBottomHeight += image.naturalHeight;
             const div = (container.value as unknown) as HTMLDivElement;
 
             const width = image.naturalWidth * (config[1] - config[0]);
@@ -118,9 +179,29 @@ export default defineComponent({
             );
             // console.log(image, canvas.height, sliceConfig, ctx);
             // console.log(canvas.toDataURL());
-            imageWithStyles.push({
+
+            const thisImageTrackList = allTrackList.filter(
+              (t) =>
+                t.offsetIndex === store.currentTask.sliceUrls.indexOf(url) + 1
+            );
+
+            const dataUrl = canvas.toDataURL();
+            const sliceImage = new Image();
+            sliceImage.src = dataUrl;
+            sliceImagesWithTrackList.push({
               url: canvas.toDataURL(),
+              indexInSliceUrls: store.currentTask.sliceUrls.indexOf(url),
+              trackList: thisImageTrackList.filter(
+                (t) =>
+                  t.positionY >= accumTopHeight / theFinalheight &&
+                  t.positionY < accumBottomHeight / theFinalheight
+              ),
+              originalImage: image,
+              sliceImage,
+              dx: image.naturalWidth * config[0],
+              dy: 0,
             });
+            accumTopHeight = accumBottomHeight;
           }
         }
       }
@@ -130,7 +211,7 @@ export default defineComponent({
       const scale = store.setting.uiSetting["answer.paper.scale"] || 1;
       return scale * 100 + "%";
     });
-    return { container, store, imageWithStyles, answerPaperScale };
+    return { container, store, sliceImagesWithTrackList, answerPaperScale };
   },
 });
 </script>
@@ -139,8 +220,14 @@ export default defineComponent({
 .mark-body-container {
   height: calc(100vh - 21px);
   overflow: scroll;
+  background-size: 8px 8px;
+  background-image: linear-gradient(to right, #e7e7e7 4px, transparent 4px),
+    linear-gradient(to bottom, transparent 4px, #e7e7e7 4px);
 }
 .mark-body-container img {
   width: 100%;
 }
+.single-image-container {
+  position: relative;
+}
 </style>

+ 63 - 0
src/components/mark/MarkDrawTrack.vue

@@ -0,0 +1,63 @@
+<template>
+  <template v-for="(track, index) in trackList" :key="index">
+    <div class="score-container" :style="computeTopAndLeft(track)">
+      {{ track.score }}
+    </div>
+  </template>
+</template>
+
+<script lang="ts">
+import { Track } from "@/types";
+import { defineComponent, PropType } from "vue";
+import { store } from "./store";
+
+export default defineComponent({
+  name: "MarkDrawTrack",
+  props: {
+    trackList: {
+      type: Array as PropType<Array<Track>>,
+    },
+    originalImage: {
+      type: Object as PropType<HTMLImageElement>,
+    },
+    sliceImage: {
+      type: Object as PropType<HTMLImageElement>,
+      required: true,
+    },
+    dx: { type: Number, required: true },
+    dy: { type: Number, required: true },
+  },
+  setup({ trackList, originalImage, sliceImage, dx, dy }) {
+    const computeTopAndLeft = (track: Track) => {
+      const topInsideSlice = track.offsetY - dy;
+      const leftInsideSlice = track.offsetX - dx;
+      // console.log({
+      //   topInsideSlice,
+      //   leftInsideSlice,
+      //   offx: track.offsetX,
+      //   offy: track.offsetY,
+      //   dx,
+      //   dy,
+      // });
+      return {
+        top: (topInsideSlice / sliceImage.naturalHeight) * 100 - 2.6 + "%",
+        left: (leftInsideSlice / sliceImage.naturalWidth) * 100 - 1 + "%",
+        "font-size": store.setting.uiSetting["answer.paper.scale"] * 2.2 + "em",
+      };
+    };
+    // const
+    // const trackList: Array<Track> = reactive([]);
+    // return { trackList };
+
+    return { store, computeTopAndLeft };
+  },
+});
+</script>
+
+<style scoped>
+.score-container {
+  display: inline-block;
+  position: absolute;
+  color: red;
+}
+</style>

+ 1 - 1
src/types/index.ts

@@ -102,7 +102,7 @@ interface Question {
   score: number; //得分;null的值时是为打回时可以被修改的;null也是从未评分过的情况,要通过rejected来判断
 }
 
-interface Track {
+export interface Track {
   mainNumber: number; // 大题号
   subNumber: string; // 小题号,当前api中只有number // 特殊标记中没有
   offsetIndex: number; // 第几张图