浏览代码

原图功能

Michael Wang 4 年之前
父节点
当前提交
c6838995b9
共有 5 个文件被更改,包括 158 次插入0 次删除
  1. 3 0
      src/features/mark/Mark.vue
  2. 6 0
      src/features/mark/MarkHeader.vue
  3. 147 0
      src/features/mark/SheetViewModal.vue
  4. 1 0
      src/features/mark/store.ts
  5. 1 0
      src/types/index.ts

+ 3 - 0
src/features/mark/Mark.vue

@@ -26,6 +26,7 @@
   <MinimapModal />
 
   <AllPaperModal />
+  <SheetViewModal />
 </template>
 
 <script lang="ts">
@@ -59,6 +60,7 @@ import AnswerModal from "./AnswerModal.vue";
 import PaperModal from "./PaperModal.vue";
 import MinimapModal from "./MinimapModal.vue";
 import AllPaperModal from "./AllPaperModal.vue";
+import SheetViewModal from "./SheetViewModal.vue";
 
 export default defineComponent({
   name: "Mark",
@@ -73,6 +75,7 @@ export default defineComponent({
     PaperModal,
     MinimapModal,
     AllPaperModal,
+    SheetViewModal,
   },
   setup: () => {
     const { addInterval } = useTimers();

+ 6 - 0
src/features/mark/MarkHeader.vue

@@ -135,6 +135,12 @@
             <td>全卷</td>
             <td><a-switch v-model:checked="store.allPaperModal" /></td>
           </tr>
+          <tr v-if="store.setting.sheetView">
+            <td>原图</td>
+            <td>
+              <a-switch v-model:checked="store.sheetViewModal" />
+            </td>
+          </tr>
           <tr>
             <td>缩略图</td>
             <td>

+ 147 - 0
src/features/mark/SheetViewModal.vue

@@ -0,0 +1,147 @@
+<template>
+  <teleport to="body">
+    <div v-if="store.sheetViewModal" class="dialog-container">
+      <header ref="mouseHandler" class="tw-flex tw-place-content-between">
+        <div class="tw-text-2xl tw-cursor-move">原图</div>
+        <div class="tw-mx-8 tw-flex-grow">
+          <span
+            v-for="(u, index) in dataUrls"
+            :key="index"
+            @click="checkedIndex = index"
+            class="image-index hover:tw-bg-gray-300"
+            :class="checkedIndex === index && 'tw-bg-gray-300'"
+            >{{ index + 1 }}</span
+          >
+        </div>
+        <a-button shape="circle" @click="store.sheetViewModal = false">
+          <template #icon><CloseOutlined /></template>
+        </a-button>
+      </header>
+
+      <div>
+        <div
+          v-for="(url, index) in dataUrls"
+          :key="index"
+          style="display: none"
+          :class="index === checkedIndex && 'show-image'"
+        >
+          <img :src="url" />
+        </div>
+      </div>
+    </div>
+  </teleport>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, ref, watchEffect } from "vue";
+import { CloseOutlined } from "@ant-design/icons-vue";
+import { store } from "@/features/mark/store";
+import { loadImage } from "@/utils/utils";
+import { PictureSlice } from "@/types";
+
+export default defineComponent({
+  name: "SheetViewModal",
+  components: { CloseOutlined },
+  props: {},
+  emits: ["close"],
+  setup() {
+    const dataUrls: Array<string> = reactive([]);
+    watchEffect(async () => {
+      const urls =
+        store.currentTask?.sheetUrls.map((s) => store.setting.fileServer + s) ??
+        [];
+      const images = [];
+      for (const url of urls) {
+        images.push(await loadImage(url));
+      }
+
+      const sheetConfig = store.setting.sheetConfig;
+
+      for (let i = 0; i < images.length; i++) {
+        if (sheetConfig.length === 0) {
+          dataUrls.push(
+            getDataUrlForSheetConfig(
+              images[i],
+              i % 2 === 0
+                ? [
+                    // 通过-1来标记该用默认遮盖规则
+                    { i: -1, x: 0, y: 0, w: 0, h: 0 },
+                  ]
+                : []
+            )
+          );
+        } else {
+          const scs = sheetConfig.filter((s) => s.i - 1 === i);
+          dataUrls.push(getDataUrlForSheetConfig(images[i], scs));
+        }
+      }
+    });
+
+    const checkedIndex = ref(0);
+
+    return { store, dataUrls, checkedIndex };
+  },
+});
+
+export function getDataUrlForSheetConfig(
+  image: HTMLImageElement,
+  sliceConfigs: Array<PictureSlice>
+) {
+  const canvas = document.createElement("canvas");
+  canvas.width = image.naturalWidth;
+  canvas.height = image.naturalWidth;
+  const ctx = canvas.getContext("2d");
+  if (!ctx) {
+    console.log('canvas.getContext("2d") error');
+    return "null";
+  }
+  // drawImage 画图软件透明色
+  ctx?.drawImage(image, 0, 0);
+
+  ctx.fillStyle = "grey";
+  for (const sc of sliceConfigs) {
+    if (sc.i === -1) {
+      ctx.fillRect(0, 0, image.naturalWidth / 2, image.naturalHeight / 3);
+    } else if (sc.w === 0 && sc.h === 0) {
+      ctx.fillRect(0, 0, image.naturalWidth, image.naturalHeight);
+    } else {
+      ctx.fillRect(sc.x, sc.y, sc.w, sc.h);
+    }
+  }
+
+  const dataurl = canvas.toDataURL();
+
+  return dataurl;
+}
+</script>
+
+<style scoped>
+.dialog-container {
+  /* always top */
+  z-index: 99999;
+  position: absolute;
+  background-color: white;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 100vh;
+}
+header {
+  background-color: #eff3f6;
+}
+.image-index {
+  display: inline-block;
+  border: 1px solid grey;
+  width: 25px;
+  margin-right: 10px;
+  margin-top: 5px;
+  text-align: center;
+  border-radius: 5px;
+
+  cursor: pointer;
+}
+
+.show-image {
+  display: block !important;
+}
+</style>

+ 1 - 0
src/features/mark/store.ts

@@ -46,6 +46,7 @@ const obj = {
   maxModalZIndex: 1020,
   minimapScrollTo: 0,
   allPaperModal: false,
+  sheetViewModal: false,
 } as MarkStore;
 
 /** 如果currentTask不存在,则返回undefined; 如果currentMarkResult不存在,则创建一个对应的markResult并返回 */

+ 1 - 0
src/types/index.ts

@@ -22,6 +22,7 @@ export interface MarkStore {
   maxModalZIndex: number;
   minimapScrollTo: number; // 高度的百分比
   allPaperModal: boolean; // 是否显示全卷
+  sheetViewModal: boolean; // 是否显示原卷
 }
 
 export interface Setting {