Browse Source

查看回评 & splitConfig绘制

Michael Wang 4 years ago
parent
commit
2a12cabd97

+ 23 - 0
src/api/markPage.ts

@@ -36,3 +36,26 @@ export async function updateUISetting(
   mode && form.append("mode", JSON.stringify(mode));
   mode && form.append("mode", JSON.stringify(mode));
   return httpApp.post("/mark/updateSetting", form);
   return httpApp.post("/mark/updateSetting", form);
 }
 }
+
+/** 获取评卷历史任务 */
+export async function getHistoryTask({
+  pageNumber = 1,
+  pageSize = 10,
+  order = "markerTime",
+  sort = "DESC",
+  secretNumber = null,
+}: {
+  pageNumber?: number; // 从1开始
+  pageSize?: number;
+  order?: "markerTime" | "markerScore";
+  sort?: "ASC" | "DESC";
+  secretNumber?: string | null;
+}) {
+  const form = new FormData();
+  form.append("pageNumber", pageNumber + "");
+  form.append("pageSize", pageSize + "");
+  form.append("order", order);
+  form.append("sort", sort);
+  secretNumber && form.append("secretNumber", secretNumber);
+  return httpApp.post("/mark/getHistory", form);
+}

+ 8 - 2
src/components/mark/Mark.vue

@@ -1,7 +1,10 @@
 <template>
 <template>
   <div class="my-container">
   <div class="my-container">
-    <mark-header></mark-header>
-    <mark-body></mark-body>
+    <mark-header />
+    <div class="flex gap-1">
+      <mark-history />
+      <mark-body />
+    </div>
   </div>
   </div>
 </template>
 </template>
 
 
@@ -19,12 +22,14 @@ import { store } from "./store";
 import MarkHeader from "./MarkHeader.vue";
 import MarkHeader from "./MarkHeader.vue";
 import MarkBody from "./MarkBody.vue";
 import MarkBody from "./MarkBody.vue";
 import { useTimers } from "@/setups/useTimers";
 import { useTimers } from "@/setups/useTimers";
+import MarkHistory from "./MarkHistory.vue";
 
 
 export default defineComponent({
 export default defineComponent({
   name: "Mark",
   name: "Mark",
   components: {
   components: {
     MarkHeader,
     MarkHeader,
     MarkBody,
     MarkBody,
+    MarkHistory,
   },
   },
   setup: () => {
   setup: () => {
     const { addInterval } = useTimers();
     const { addInterval } = useTimers();
@@ -77,6 +82,7 @@ export default defineComponent({
       },
       },
       { deep: true }
       { deep: true }
     );
     );
+
     return { store };
     return { store };
   },
   },
 });
 });

+ 68 - 14
src/components/mark/MarkBody.vue

@@ -1,11 +1,13 @@
 <template>
 <template>
-  <div class="mark-body-container" ref="container">
+  <div class="mark-body-container flex-auto" ref="container">
     <div :style="{ width: answerPaperScale }">
     <div :style="{ width: answerPaperScale }">
       <template v-for="(item, index) in imageWithStyles" :key="index">
       <template v-for="(item, index) in imageWithStyles" :key="index">
         <img :src="item.url" />
         <img :src="item.url" />
+        <hr style="border: 2px solid grey" />
       </template>
       </template>
       <!-- style="border: 1px solid black; background: black" -->
       <!-- style="border: 1px solid black; background: black" -->
     </div>
     </div>
+    <div v-if="!store.currentTask" class="text-center">暂无评卷任务</div>
   </div>
   </div>
 </template>
 </template>
 
 
@@ -21,37 +23,40 @@ export default defineComponent({
   name: "MarkBody",
   name: "MarkBody",
   setup() {
   setup() {
     const container = ref(null);
     const container = ref(null);
-    const imageWithStyles: Array<any> = reactive([]);
+    let imageWithStyles: Array<any> = reactive([]);
     watchEffect(async () => {
     watchEffect(async () => {
-      if (store.currentTask?.sliceConfig) {
-        async function loadImage(url: string): Promise<HTMLImageElement> {
-          return new Promise((resolve, reject) => {
-            const image = new Image();
-            image.setAttribute("crossorigin", "anonymous");
-            image.src = url;
-            image.onload = () => resolve(image);
-            image.onerror = reject;
-          });
-        }
+      async function loadImage(url: string): Promise<HTMLImageElement> {
+        return new Promise((resolve, reject) => {
+          const image = new Image();
+          image.setAttribute("crossorigin", "anonymous");
+          image.src = url;
+          image.onload = () => resolve(image);
+          image.onerror = reject;
+        });
+      }
+      if (!store.currentTask?.libraryId) return;
+      imageWithStyles.splice(0);
+      if (store.currentTask.sliceConfig?.length) {
         for (const url of store.currentTask.sliceUrls) {
         for (const url of store.currentTask.sliceUrls) {
           await loadImage(filters.toCompleteUrl(url));
           await loadImage(filters.toCompleteUrl(url));
         }
         }
-        let zIndex = 500;
         for (const sliceConfig of store.currentTask.sliceConfig) {
         for (const sliceConfig of store.currentTask.sliceConfig) {
           const url = filters.toCompleteUrl(
           const url = filters.toCompleteUrl(
             store.currentTask.sliceUrls[sliceConfig.i - 1]
             store.currentTask.sliceUrls[sliceConfig.i - 1]
           );
           );
           const image = await loadImage(url);
           const image = await loadImage(url);
 
 
-          const div: HTMLDivElement = container.value;
+          const div = (container.value as unknown) as HTMLDivElement;
           const maxSliceWidth = Math.max(
           const maxSliceWidth = Math.max(
             ...store.currentTask.sliceConfig.map((v) => v.w)
             ...store.currentTask.sliceConfig.map((v) => v.w)
           );
           );
 
 
           const canvas = document.createElement("canvas");
           const canvas = document.createElement("canvas");
+          // canvas.width = sliceConfig.w;
           canvas.width = Math.max(sliceConfig.w, maxSliceWidth);
           canvas.width = Math.max(sliceConfig.w, maxSliceWidth);
           canvas.height = sliceConfig.h;
           canvas.height = sliceConfig.h;
           const ctx = canvas.getContext("2d");
           const ctx = canvas.getContext("2d");
+          // drawImage 画图软件透明色
           ctx?.drawImage(
           ctx?.drawImage(
             image,
             image,
             sliceConfig.x,
             sliceConfig.x,
@@ -69,6 +74,55 @@ export default defineComponent({
             url: canvas.toDataURL(),
             url: canvas.toDataURL(),
           });
           });
         }
         }
+      } else {
+        const images = [];
+        for (const url of store.currentTask.sheetUrls) {
+          const image = await loadImage(filters.toCompleteUrl(url));
+          images.push(image);
+        }
+
+        const newConfig = (store.setting.splitConfig
+          .map((v, index, ary) =>
+            index % 2 === 0 ? [v, ary[index + 1]] : false
+          )
+          .filter((v) => v) as unknown) as Array<[number, number]>;
+
+        const maxSplitConfig = Math.max(...store.setting.splitConfig);
+        const maxSliceWidth =
+          Math.max(...images.map((v) => v.naturalWidth)) * maxSplitConfig;
+
+        for (const url of store.currentTask.sheetUrls) {
+          const completeUrl = filters.toCompleteUrl(url);
+
+          for (const config of newConfig) {
+            const image = await loadImage(completeUrl);
+
+            const div = (container.value as unknown) as HTMLDivElement;
+
+            const width = image.naturalWidth * (config[1] - config[0]);
+            const canvas = document.createElement("canvas");
+            canvas.width = Math.max(width, maxSliceWidth);
+            canvas.height = image.naturalHeight;
+            const ctx = canvas.getContext("2d");
+            // drawImage 画图软件透明色
+            ctx?.drawImage(
+              image,
+              image.naturalWidth * config[0],
+              0,
+              image.naturalWidth * config[1],
+              image.naturalHeight,
+              0,
+              0,
+              image.naturalWidth * config[1],
+              image.naturalHeight
+            );
+            // console.log(image, canvas.height, sliceConfig, ctx);
+            // console.log(canvas.toDataURL());
+            imageWithStyles.push({
+              url: canvas.toDataURL(),
+            });
+          }
+        }
       }
       }
     });
     });
 
 

+ 41 - 2
src/components/mark/MarkHeader.vue

@@ -32,7 +32,7 @@
         <li @click="normalScale">适应</li>
         <li @click="normalScale">适应</li>
       </ul>
       </ul>
     </div>
     </div>
-    <div>回看</div>
+    <div @click="toggleHistory">回看</div>
     <div
     <div
       :title="
       :title="
         $filters.datetimeFilter(store.setting.startTime) +
         $filters.datetimeFilter(store.setting.startTime) +
@@ -49,6 +49,7 @@
 </template>
 </template>
 
 
 <script lang="ts">
 <script lang="ts">
+import { getHistoryTask } from "@/api/markPage";
 import { computed, defineComponent } from "vue";
 import { computed, defineComponent } from "vue";
 import { store } from "./store";
 import { store } from "./store";
 
 
@@ -80,7 +81,45 @@ export default defineComponent({
     const normalScale = () => {
     const normalScale = () => {
       store.setting.uiSetting["answer.paper.scale"] = 1;
       store.setting.uiSetting["answer.paper.scale"] = 1;
     };
     };
-    return { store, progress, group, upScale, downScale, normalScale };
+    const toggleHistory = () => {
+      store.historyOpen = !store.historyOpen;
+    };
+
+    async function updateHistoryTask({
+      pageNumber = 1,
+      pageSize = 10,
+      order = "markerTime",
+      sort = "DESC",
+      secretNumber = null,
+    }: {
+      pageNumber: number; // 从1开始
+      pageSize: number;
+      order: "markerTime" | "markerScore";
+      sort: "ASC" | "DESC";
+      secretNumber: string | null;
+    }) {
+      const res = await getHistoryTask({
+        pageNumber,
+        pageSize,
+        order,
+        sort,
+        secretNumber,
+      });
+      if (res.data) {
+        store.historyTasks.push(res.data);
+      }
+    }
+
+    return {
+      store,
+      progress,
+      group,
+      upScale,
+      downScale,
+      normalScale,
+      updateHistoryTask,
+      toggleHistory,
+    };
   },
   },
 });
 });
 </script>
 </script>

+ 78 - 0
src/components/mark/MarkHistory.vue

@@ -0,0 +1,78 @@
+<template>
+  <div
+    :style="{ display: store.historyOpen ? 'block' : 'none' }"
+    style="width: 300px"
+  >
+    <div>
+      <input
+        v-model="secretNumberInput"
+        type="text"
+        placeholder="查找试卷"
+        style="border: 1px solid grey; width: 100%"
+        @keyup.enter="updateHistoryTask({ secretNumber: secretNumberInput })"
+      />
+    </div>
+    <div v-for="(task, index) of store.historyTasks" :key="index">
+      <div @click="replaceCurrentTask(task)">
+        {{ task.secretNumber }} {{ task.markTime }} {{ task.markerScore }}
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { getHistoryTask } from "@/api/markPage";
+import { Task } from "@/types";
+import { defineComponent, ref, watchEffect } from "vue";
+import { store } from "./store";
+
+export default defineComponent({
+  name: "MarkHistory",
+  setup() {
+    watchEffect(async () => {
+      if (store.historyOpen) {
+        await updateHistoryTask({});
+      } else {
+        replaceCurrentTask(store.tasks[0]);
+      }
+    });
+
+    const secretNumberInput = ref(null);
+    async function updateHistoryTask({
+      pageNumber = 1,
+      pageSize = 10,
+      order = "markerTime",
+      sort = "DESC",
+      secretNumber = null,
+    }: {
+      pageNumber?: number; // 从1开始
+      pageSize?: number;
+      order?: "markerTime" | "markerScore";
+      sort?: "ASC" | "DESC";
+      secretNumber?: string | null;
+    }) {
+      const res = await getHistoryTask({
+        pageNumber,
+        pageSize,
+        order,
+        sort,
+        secretNumber,
+      });
+      if (res.data) {
+        store.historyTasks = res.data;
+      }
+    }
+
+    function replaceCurrentTask(task: Task) {
+      store.currentTask = task;
+    }
+
+    return {
+      store,
+      secretNumberInput,
+      updateHistoryTask,
+      replaceCurrentTask,
+    };
+  },
+});
+</script>

+ 4 - 2
src/components/mark/store.ts

@@ -1,4 +1,4 @@
-import { ModeEnum, Setting, MarkStore } from "@/types";
+import { ModeEnum, Setting, MarkStore, Task } from "@/types";
 import { reactive } from "vue";
 import { reactive } from "vue";
 
 
 const obj = {
 const obj = {
@@ -26,8 +26,10 @@ const obj = {
   status: <MarkStore["status"]>{},
   status: <MarkStore["status"]>{},
   groups: [],
   groups: [],
   tasks: [],
   tasks: [],
-  currentTask: <MarkStore["currentTask"]>{},
+  currentTask: <Task>{},
   markResults: [],
   markResults: [],
+  historyOpen: false,
+  historyTasks: [],
 } as MarkStore;
 } as MarkStore;
 
 
 /** 保存setting和task */
 /** 保存setting和task */

+ 3 - 1
src/types/index.ts

@@ -11,6 +11,8 @@ export interface MarkStore {
   tasks: Array<Task>; // 保持数量为3
   tasks: Array<Task>; // 保持数量为3
   currentTask: Task; // 用来切换task,还有回看
   currentTask: Task; // 用来切换task,还有回看
   markResults: Array<MarkResult>;
   markResults: Array<MarkResult>;
+  historyOpen: boolean; // 是否打开回评侧边栏
+  historyTasks: Array<Task>;
 }
 }
 
 
 export interface Setting {
 export interface Setting {
@@ -62,7 +64,7 @@ interface Group {
   totalCount: number; //总数量
   totalCount: number; //总数量
 }
 }
 
 
-interface Task {
+export interface Task {
   libraryId: number;
   libraryId: number;
   studentId: number;
   studentId: number;
   secretNumber: string;
   secretNumber: string;