소스 검색

feat: 裁切图编辑

zhangjie 8 달 전
부모
커밋
204dcf016d
4개의 변경된 파일118개의 추가작업 그리고 11개의 파일을 삭제
  1. 11 0
      src/render/ap/base.ts
  2. 52 0
      src/render/ap/types/base.ts
  3. 11 6
      src/render/views/DataCheck/SliceImage/CutImageDialog.vue
  4. 44 5
      src/render/views/DataCheck/SliceImage/index.vue

+ 11 - 0
src/render/ap/base.ts

@@ -7,6 +7,8 @@ import {
   UploadSheetParams,
   UploadSliceParams,
   UploadFileResult,
+  CardInfoParams,
+  CardInfoResult,
 } from "./types/base";
 
 export const getExamList = (data: ExamListParams): Promise<ExamListResult> =>
@@ -74,3 +76,12 @@ export const uploadSlice = (
     data: formData,
   });
 };
+
+// 获取卡格式
+export const cardInfo = (data: CardInfoParams): Promise<CardInfoResult> => {
+  return request({
+    url: "/api/card/info",
+    method: "post",
+    data,
+  });
+};

+ 52 - 0
src/render/ap/types/base.ts

@@ -41,3 +41,55 @@ export interface ConfigInfo {
   scannerAssignedMaxCount: string | number;
   scannerAssignedVerifyPassword: string;
 }
+
+export interface CardInfoParams {
+  examId: number;
+  number: number;
+}
+
+export interface CardInfoResult {
+  number: number;
+  code: string;
+  // 若是通卡则为空,单一卡对应一个科目,混扫对应多个科目
+  subjectList: Array<{
+    subjectCode: string;
+    subjectName: string;
+  }>;
+  //预留字段
+  parameter: string | null;
+  remark: string;
+  //是否单页模式
+  singlePage: boolean;
+  //总张数
+  paperCount: number;
+  //卡格式来源,CLIENT表示本地程序制作,WEB表示在线工具制作
+  source: "CLIENT" | "WEB";
+  //是否需要本地适配,目前只有WEB来源的强制需要适配
+  needAdapte: boolean;
+  uri: string;
+  md5: string;
+  dpi: number;
+  updateTime: number;
+  adapteFile: Array<{
+    device: string;
+    role: string;
+    uri: string;
+    md5: string;
+    dpi: string;
+  }>;
+}
+
+export interface CardPage {
+  exchange: {
+    // 裁切图信息
+    answer_area: Array<{
+      area: [number, number, number, number];
+      main_number: number;
+      sub_number: string;
+    }>;
+  };
+}
+
+export interface CardContent {
+  pages: CardPage[];
+}

+ 11 - 6
src/render/views/DataCheck/SliceImage/CutImageDialog.vue

@@ -9,7 +9,12 @@
   >
     <div ref="imgContainRef" class="cut-image">
       <div v-if="visible" class="cut-image-body" :style="imageStyle">
-        <img ref="imgRef" :src="sheetUrl" alt="原图" @load="initImageSize" />
+        <img
+          ref="imgRef"
+          :src="getFileUrl(sheetUrl)"
+          alt="原图"
+          @load="initImageSize"
+        />
 
         <element-resize
           v-if="selection.w"
@@ -33,7 +38,7 @@
 import { computed, ref, watch } from "vue";
 import { SaveOutlined, CloseOutlined } from "@ant-design/icons-vue";
 import useModal from "@/hooks/useModal";
-import { objAssign } from "@/utils/tool";
+import { getFileUrl, objAssign } from "@/utils/tool";
 
 import ElementResize from "@/components/ElementResize/index.vue";
 
@@ -113,10 +118,10 @@ function initImageSize() {
   if (!props.sliceSelection) return;
   const rate = imgDom.naturalWidth / imageSize.value.width;
   selection.value = {
-    x: props.sliceSelection.x / rate,
-    y: props.sliceSelection.y / rate,
-    w: props.sliceSelection.w / rate,
-    h: props.sliceSelection.h / rate,
+    x: (props.sliceSelection.x * imgDom.naturalWidth) / rate,
+    y: (props.sliceSelection.y * imgDom.naturalHeight) / rate,
+    w: (props.sliceSelection.w * imgDom.naturalWidth) / rate,
+    h: (props.sliceSelection.h * imgDom.naturalHeight) / rate,
   };
 }
 

+ 44 - 5
src/render/views/DataCheck/SliceImage/index.vue

@@ -53,22 +53,25 @@
 </template>
 
 <script setup lang="ts">
-import { computed, ref } from "vue";
+import { computed, ref, watch } from "vue";
 import { NumberOutlined, PictureFilled } from "@ant-design/icons-vue";
 import { message } from "ant-design-vue";
 
-import CutImageDialog from "./CutImageDialog.vue";
-import { uploadSlice } from "@/ap/base";
+import { uploadSlice, cardInfo } from "@/ap/base";
+import { CardContent, CardPage } from "@/ap/types/base";
 import { getFileMD5 } from "@/utils/crypto";
-import { useDataCheckStore } from "@/store";
+import { useDataCheckStore, useUserStore } from "@/store";
 import { getFileUrl } from "@/utils/tool";
 
+import CutImageDialog from "./CutImageDialog.vue";
 import ImportBtn from "@/components/ImportBtn/index.vue";
+import axios from "axios";
 
 defineOptions({
   name: "SliceImage",
 });
 
+const userStore = useUserStore();
 const dataCheckStore = useDataCheckStore();
 
 const curStudent = computed(() => {
@@ -84,6 +87,8 @@ const curSliceInfo = ref({
   sheetUri: "",
 });
 
+const cardData = ref<CardPage[]>([]);
+
 function getPageTitle(paperNumber, pageIndex) {
   return `卡${paperNumber}${pageIndex === 0 ? "正面" : "反面"}`;
 }
@@ -101,7 +106,19 @@ function onEditSlice(paperNumber: number, pageIndex: number, index: number) {
     index: index + 1,
     sheetUri: paper.pages[pageIndex].sheetUri,
   };
-  curSliceSelection.value = undefined;
+  const paperPageIndex = (paperNumber - 1) * 2 + pageIndex;
+  const area = cardData.value[paperPageIndex]?.exchange.answer_area[index].area;
+  if (area) {
+    curSliceSelection.value = {
+      x: area[0],
+      y: area[1],
+      w: area[2],
+      h: area[3],
+    };
+  } else {
+    curSliceSelection.value = undefined;
+  }
+
   cutImageDialogRef.value?.open();
 }
 async function cutImageModified(file: File) {
@@ -149,6 +166,28 @@ function updateSliceSuccess(data: { url: string }) {
   });
   message.success("上传成功!");
 }
+
+// 卡格式
+async function getCardInfo() {
+  if (!dataCheckStore.curStudent) return;
+
+  const res = await cardInfo({
+    examId: userStore.curExam.id,
+    number: dataCheckStore.curStudent.cardNumber,
+  });
+  const uri = getFileUrl(res.uri);
+  const cardRes = await axios.get(uri);
+  cardData.value = (cardRes.data as CardContent).pages;
+}
+
+watch(
+  () => dataCheckStore.curStudent?.id,
+  (val) => {
+    if (!val) return;
+    getCardInfo();
+  },
+  { immediate: true }
+);
 </script>
 
 <style lang="less" scoped>