Răsfoiți Sursa

feat: 水印元素

zhangjie 3 săptămâni în urmă
părinte
comite
2fcc56d122

+ 1 - 1
package.json

@@ -1,7 +1,7 @@
 {
   "name": "teachcloud-mark-tool",
   "description": "teachcloud mark tool",
-  "version": "1.0.5",
+  "version": "1.0.6",
   "main": "./out/main/index.js",
   "author": "chulinice",
   "license": "MIT",

+ 10 - 0
src/constants/enumerate.ts

@@ -28,3 +28,13 @@ export const STUDENT_FILE_RULE = {
   CODE_NAME: '学号-姓名',
 };
 export type StudentFileRuleEnum = keyof typeof STUDENT_FILE_RULE;
+
+// 水印元素类型:总分,客观题得分,主观题得分,主观题评卷员信息及给分,主观题明细分
+export const WATERMARK_ELEMENT_TYPE = {
+  TOTAL: '总分',
+  OBJECTIVE: '客观题得分',
+  SUBJECTIVE: '主观题得分',
+  SUBJECTIVE_EVALUATOR: '主观题评卷员信息及给分',
+  SUBJECTIVE_DETAIL: '主观题明细分',
+};
+export type WatermarkElementTypeEnum = keyof typeof WATERMARK_ELEMENT_TYPE;

+ 6 - 1
src/hooks/dict-option.ts

@@ -1,10 +1,15 @@
 import { ref } from 'vue';
-import { DEFAULT_LABEL, PICTURE_TYPE } from '@/constants/enumerate';
+import {
+  DEFAULT_LABEL,
+  PICTURE_TYPE,
+  WATERMARK_ELEMENT_TYPE,
+} from '@/constants/enumerate';
 import { dictToOption } from '@/utils/utils';
 import { SelectOptions } from '@/types/global';
 
 const dicts = {
   PICTURE_TYPE,
+  WATERMARK_ELEMENT_TYPE,
 };
 
 type DictTypeType = keyof typeof dicts;

+ 7 - 0
src/store/modules/app/index.ts

@@ -13,6 +13,13 @@ const useAppStore = defineStore('app', {
       outputDirIsDefault: true,
       trackColorType: 'DEFAULT',
       studentFileRule: 'CODE',
+      watermarkElements: [
+        'TOTAL',
+        'OBJECTIVE',
+        'SUBJECTIVE',
+        'SUBJECTIVE_EVALUATOR',
+        'SUBJECTIVE_DETAIL',
+      ],
     },
     downloadTaskRunning: false,
     // auto download task

+ 2 - 0
src/store/modules/app/types.ts

@@ -2,6 +2,7 @@ import {
   PictureTypeEnum,
   TrackColorTypeEnum,
   StudentFileRuleEnum,
+  WatermarkElementTypeEnum,
 } from '@/constants/enumerate';
 
 export interface TrackConfigType {
@@ -11,6 +12,7 @@ export interface TrackConfigType {
   outputDirIsDefault: boolean;
   trackColorType: TrackColorTypeEnum;
   studentFileRule: StudentFileRuleEnum;
+  watermarkElements: WatermarkElementTypeEnum[];
 }
 
 export interface DownloadSetType {

+ 28 - 0
src/views/base/track-export/modifySet.vue

@@ -59,6 +59,18 @@
           >
         </a-radio-group>
       </a-form-item>
+      <a-form-item field="watermarkElements" label="水印元素">
+        <a-checkbox-group
+          v-model="formData.watermarkElements"
+          direction="vertical"
+        >
+          <template v-for="(option, index) in watermarkOptions" :key="index">
+            <a-checkbox :value="option.value">
+              {{ option.label }}
+            </a-checkbox>
+          </template>
+        </a-checkbox-group>
+      </a-form-item>
     </a-form>
 
     <template #footer>
@@ -87,6 +99,7 @@
     TrackColorTypeEnum,
     STUDENT_FILE_RULE,
     StudentFileRuleEnum,
+    WatermarkElementTypeEnum,
   } from '@/constants/enumerate';
 
   defineOptions({
@@ -100,6 +113,9 @@
   const appStore = useAppStore();
 
   const { optionList: pictureOptions } = useDictOption('PICTURE_TYPE');
+  const { optionList: watermarkOptions } = useDictOption(
+    'WATERMARK_ELEMENT_TYPE'
+  ); // Assuming a new dict type
   const pictureDesc: Record<PictureTypeEnum, string> = {
     track: '批阅后图片',
     origin: '学生作答原图',
@@ -113,6 +129,7 @@
     outputDirIsDefault: true,
     trackColorType: 'ALL_RED' as TrackColorTypeEnum,
     studentFileRule: 'CODE_NAME' as StudentFileRuleEnum,
+    watermarkElements: [] as WatermarkElementTypeEnum[], // Default to no elements selected
   };
 
   const emit = defineEmits(['modified']);
@@ -132,6 +149,17 @@
         message: '请选择保存目录',
       },
     ],
+    watermarkElements: [
+      {
+        required: false, // Or true, depending on requirements
+        validator: (value, callback) => {
+          if (value && value.length > 0) {
+            return callback();
+          }
+          return callback('请选择至少一个水印元素');
+        },
+      },
+    ],
   };
 
   async function toSelectDir() {

+ 95 - 63
src/views/base/track-export/useDraw.ts

@@ -144,6 +144,26 @@ export default function useDraw(drawConfig: DrawConfig) {
   const hasTrack = trackConfig.pictureType.includes('track');
   const hasPdf = trackConfig.pictureType.includes('pdf');
 
+  // 总分
+  const watermarkHasTotal = trackConfig.watermarkElements.includes('TOTAL');
+  // 客观题得分
+  const watermarkHasObjective =
+    trackConfig.watermarkElements.includes('OBJECTIVE');
+  // 主观题得分
+  const watermarkHasSubjective =
+    trackConfig.watermarkElements.includes('SUBJECTIVE');
+  // 主观题明细分
+  const watermarkHasSubjectiveDetail =
+    trackConfig.watermarkElements.includes('SUBJECTIVE_DETAIL');
+  // 主观题评卷员信息及给分
+  const watermarkHasSubjectiveEvaluator =
+    trackConfig.watermarkElements.includes('SUBJECTIVE_EVALUATOR');
+  // 主观题
+  const watermarkHasSbj =
+    watermarkHasSubjective ||
+    watermarkHasSubjectiveDetail ||
+    watermarkHasSubjectiveEvaluator;
+
   const defaultColorConfig = {
     track: ['red', 'blue', 'gray'],
     head: 'green',
@@ -368,16 +388,18 @@ export default function useDraw(drawConfig: DrawConfig) {
     for (let i = 0; i < originImgs.length; i++) {
       const img = originImgs[i];
       const drawTrackList = [] as DrawTrackItem[];
-      trackLists
-        .filter((item) => item.offsetIndex === i + 1)
-        .forEach((item) => {
-          drawTrackList.push(getDrawTrackItem(item));
-        });
-      tagLists
-        .filter((item) => item.offsetIndex === i + 1)
-        .forEach((item) => {
-          drawTrackList.push(getDrawTagTrackItem(item));
-        });
+      if (watermarkHasSubjectiveDetail) {
+        trackLists
+          .filter((item) => item.offsetIndex === i + 1)
+          .forEach((item) => {
+            drawTrackList.push(getDrawTrackItem(item));
+          });
+        tagLists
+          .filter((item) => item.offsetIndex === i + 1)
+          .forEach((item) => {
+            drawTrackList.push(getDrawTagTrackItem(item));
+          });
+      }
       const answerTags = paserRecogData(i);
       drawTrackList.push(...answerTags);
       drawTrackList.push(...(markDeailList[i] || []));
@@ -385,7 +407,7 @@ export default function useDraw(drawConfig: DrawConfig) {
         (tag) => tag.trackItem
       );
       drawTrackList.push(...oTags);
-      drawTrackList.push(getTotalTrack(img));
+      if (watermarkHasTotal) drawTrackList.push(getTotalTrack(img));
 
       trackData[i] = {
         url: img.url,
@@ -396,7 +418,7 @@ export default function useDraw(drawConfig: DrawConfig) {
       };
     }
 
-    if (!hasMarkArea && !cardData.length) {
+    if (watermarkHasSbj && !hasMarkArea && !cardData.length) {
       const summarys = parseSummaryData(originImgs[0]);
       trackData[0].drawTrackList.push(...summarys);
     }
@@ -697,41 +719,45 @@ export default function useDraw(drawConfig: DrawConfig) {
         });
       };
 
-      users.forEach((user) => {
-        offsetY += trackInfoLineHeight;
+      if (watermarkHasSubjectiveEvaluator) {
+        users.forEach((user) => {
+          offsetY += trackInfoLineHeight;
+          dataArr.push({
+            type: 'text',
+            option: {
+              x: area.x,
+              y: area.y + offsetY,
+              text: `${user.prename}:${user.userName},评分:`,
+              fontSize: trackInfoFontSize,
+              color: user.color,
+            },
+          });
+          const userScore = user.scores.map(
+            (item) => `${item.subNumber}:${item.score}分`
+          );
+          groupLineScore(userScore, user.color);
+        });
+      }
+
+      if (watermarkHasSubjective) {
+        const score = calcSumPrecision(
+          groupQuestions.map((item) => item.markerScore || 0)
+        );
+        const maxScore = calcSumPrecision(
+          groupQuestions.map((item) => item.maxScore)
+        );
+        const tCont = `得分:${score},满分:${maxScore}`;
+        const tContLen = strGbLen(tCont) / 2;
         dataArr.push({
           type: 'text',
           option: {
-            x: area.x,
-            y: area.y + offsetY,
-            text: `${user.prename}:${user.userName},评分:`,
-            fontSize: trackInfoFontSize,
-            color: user.color,
+            x: area.x + area.w - Math.ceil(tContLen * trackTextFontSize),
+            y: area.y,
+            text: tCont,
+            fontSize: trackTextFontSize,
           },
         });
-        const userScore = user.scores.map(
-          (item) => `${item.subNumber}:${item.score}分`
-        );
-        groupLineScore(userScore, user.color);
-      });
-
-      const score = calcSumPrecision(
-        groupQuestions.map((item) => item.markerScore || 0)
-      );
-      const maxScore = calcSumPrecision(
-        groupQuestions.map((item) => item.maxScore)
-      );
-      const tCont = `得分:${score},满分:${maxScore}`;
-      const tContLen = strGbLen(tCont) / 2;
-      dataArr.push({
-        type: 'text',
-        option: {
-          x: area.x + area.w - Math.ceil(tContLen * trackTextFontSize),
-          y: area.y,
-          text: tCont,
-          fontSize: trackTextFontSize,
-        },
-      });
+      }
     });
 
     // 其他试题
@@ -818,30 +844,35 @@ export default function useDraw(drawConfig: DrawConfig) {
         });
       }
 
-      users.forEach((user, index) => {
-        const content = `${user.prename}:${user.userName},评分:${user.score}`;
+      if (watermarkHasSubjectiveEvaluator) {
+        users.forEach((user, index) => {
+          const content = `${user.prename}:${user.userName},评分:${user.score}`;
+          dataArr.push({
+            type: 'text',
+            option: {
+              x: area.x,
+              y: area.y + index * trackInfoLineHeight,
+              text: content,
+              fontSize: trackInfoFontSize,
+              color: user.color,
+            },
+          });
+        });
+      }
+
+      if (watermarkHasSubjective) {
+        const tCont = `得分:${question.markerScore},满分:${question.maxScore}`;
+        const tContLen = strGbLen(tCont) / 2;
         dataArr.push({
           type: 'text',
           option: {
-            x: area.x,
-            y: area.y + index * trackInfoLineHeight,
-            text: content,
-            fontSize: trackInfoFontSize,
-            color: user.color,
+            x: area.x + area.w - Math.ceil(tContLen * trackTextFontSize),
+            y: area.y,
+            text: tCont,
+            fontSize: trackTextFontSize,
           },
         });
-      });
-      const tCont = `得分:${question.markerScore},满分:${question.maxScore}`;
-      const tContLen = strGbLen(tCont) / 2;
-      dataArr.push({
-        type: 'text',
-        option: {
-          x: area.x + area.w - Math.ceil(tContLen * trackTextFontSize),
-          y: area.y,
-          text: tCont,
-          fontSize: trackTextFontSize,
-        },
-      });
+      }
     });
 
     return dataList;
@@ -1047,7 +1078,8 @@ export default function useDraw(drawConfig: DrawConfig) {
   // answer tag ----- start->
   // 解析客观题答案展示位置
   function paserRecogData(imageIndex: number): DrawTrackItem[] {
-    if (!recogDatas.length || !recogDatas[imageIndex]) return [];
+    if (!watermarkHasObjective || !recogDatas.length || !recogDatas[imageIndex])
+      return [];
 
     const recogData: PaperRecogData = JSON.parse(
       window.atob(recogDatas[imageIndex])
@@ -1097,7 +1129,7 @@ export default function useDraw(drawConfig: DrawConfig) {
   function parseObjectiveAnswerTags(images: ImageItem[]) {
     const objectiveAnswerTags: Array<ObjectiveAnswerTagItem[]> = [];
 
-    if (!cardData?.length) return objectiveAnswerTags;
+    if (!watermarkHasObjective || !cardData?.length) return objectiveAnswerTags;
 
     cardData.forEach((page, pindex) => {
       if (!objectiveAnswerTags[pindex]) objectiveAnswerTags[pindex] = [];