Browse Source

feat: 评卷管理打回相关调试

zhangjie 10 months ago
parent
commit
8beb81da6b

+ 7 - 0
src/assets/styles/pages.scss

@@ -152,6 +152,13 @@
     color: $--color-text-gray-2;
     margin-bottom: 10px;
   }
+
+  // task-print
+  .task-print {
+    margin-top: 20px;
+    padding-top: 20px;
+    border-top: 2px dashed #999;
+  }
 }
 // apply-audit-history
 .apply-audit-history {

+ 101 - 0
src/components/ImageWatermark.vue

@@ -0,0 +1,101 @@
+<template>
+  <div class="image-watermark">
+    <img v-if="dataUrl" :src="dataUrl" />
+  </div>
+</template>
+
+<script>
+export default {
+  name: "image-watermark",
+  props: {
+    url: String,
+    content: String,
+  },
+  data() {
+    return {
+      loading: false,
+      errorMsg: "",
+      dataUrl: "",
+    };
+  },
+  mounted() {
+    this.initData();
+  },
+  methods: {
+    async initData() {
+      if (!this.url) return;
+
+      this.loading = true;
+
+      const image = await this.loadingImg(this.url).catch(() => {});
+      if (!image) {
+        this.loading = false;
+        this.errorMsg = "图片载入失败!";
+        return;
+      }
+
+      const canvas = document.createElement("canvas");
+      const ctx = canvas.getContext("2d");
+
+      canvas.width = image.naturalWidth;
+      canvas.height = image.naturalHeight;
+      ctx.drawImage(image, 0, 0);
+
+      this.drawWatermark(ctx, { width: canvas.width, height: canvas.height });
+
+      canvas.toBlob((val) => {
+        this.loading = false;
+        if (!val) return;
+        this.dataUrl = URL.createObjectURL(val);
+      });
+    },
+    loadingImg(url) {
+      return new Promise((reslove, reject) => {
+        const img = new Image();
+        img.src = url;
+        img.onload = () => {
+          reslove(img);
+        };
+        img.onerror = (e) => {
+          reject(e);
+        };
+      });
+    },
+    drawWatermark(ctx, { width, height }) {
+      const angle = (315 * Math.PI) / 180;
+      const fontSize = 100;
+      ctx.font = `${fontSize}px serif`;
+      ctx.fillStyle = "rgba(212,212,212,0.5)";
+
+      // 获取内容的宽度
+      const cInfo = ctx.measureText(this.content);
+
+      const initWH = [50, 50];
+      const spacing = [width / 3, height / 3];
+      const spaceSize = Math.max(width, height) * 1.5;
+      const spaceWH = [cInfo.width + spacing[0], fontSize + spacing[1]];
+      const wCount = Math.ceil(spaceSize / spaceWH[0]);
+      const hCount = Math.ceil(spaceSize / spaceWH[1]);
+
+      ctx.translate(-width / 2, height / 2);
+      ctx.rotate(angle);
+
+      for (let i = 0; i < wCount; i++) {
+        for (let j = 0; j < hCount; j++) {
+          const x = initWH[0] + i * spaceWH[0];
+          const y = initWH[1] + j * spaceWH[1];
+          ctx.fillText(this.content, x, y);
+        }
+      }
+    },
+  },
+};
+</script>
+
+<style>
+.image-watermark img {
+  display: block;
+  width: 100%;
+  height: auto;
+}
+</style>

+ 90 - 0
src/components/PreviewAttachment.vue

@@ -0,0 +1,90 @@
+<template>
+  <el-dialog
+    class="page-dialog"
+    :visible.sync="modalIsShow"
+    title="预览"
+    fullscreen
+    append-to-body
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    @open="visibleChange"
+  >
+    <div class="infinite-list-wrapper" style="overflow: auto">
+      <ul
+        class="list"
+        v-infinite-scroll="load"
+        infinite-scroll-disabled="disabled"
+      >
+        <li v-for="url in imageList" :key="url" style="margin-bottom: 10px">
+          <image-watermark :content="content" :url="url"></image-watermark>
+        </li>
+      </ul>
+      <p v-if="loading">
+        <i class="el-icon-loading"></i>
+      </p>
+    </div>
+    <div slot="footer"></div>
+  </el-dialog>
+</template>
+
+<script>
+import ImageWatermark from "./ImageWatermark.vue";
+import { attachmentImages } from "@/modules/login/api";
+
+export default {
+  name: "preview-attachment",
+  components: { ImageWatermark },
+  props: {
+    attachmentId: String,
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      sources: [],
+      imageList: [],
+      content: "",
+      loading: false,
+      pageSize: 1,
+    };
+  },
+  computed: {
+    noMore() {
+      return this.sources.length === 0;
+    },
+    disabled() {
+      return this.loading || this.noMore;
+    },
+  },
+  created() {
+    const { loginName, realName } = this.$ls.get("user", {});
+    this.content = `${realName}(${loginName})`;
+  },
+  methods: {
+    visibleChange() {
+      this.load();
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    async getAttachments() {
+      if (!this.attachmentId) return;
+      const res = await attachmentImages(this.attachmentId);
+      this.sources = res || [];
+
+      if (this.sources.length) this.load();
+    },
+    load() {
+      this.loading = true;
+      const imgs = this.sources.splice(0, this.pageSize);
+      this.imageList.push(...imgs);
+
+      this.$nextTick(() => {
+        this.loading = false;
+      });
+    },
+  },
+};
+</script>

+ 1 - 1
src/modules/admin/views/ToolManage.vue

@@ -80,7 +80,7 @@ export default {
     };
   },
   mounted() {
-    // this.getInfo();
+    this.getInfo();
   },
   methods: {
     initData() {

+ 21 - 1
src/modules/exam/components/ModifyTaskPaper.vue

@@ -271,6 +271,12 @@
       ref="CardPreviewDialog"
       :card-id="curAttachment.cardId"
     ></card-preview-dialog>
+
+    <!-- PreviewAttachment -->
+    <preview-attachment
+      ref="PreviewAttachment"
+      :attachment-id="curAttachmentId"
+    ></preview-attachment>
   </div>
 </template>
 
@@ -278,6 +284,7 @@
 import UploadPaperDialog from "./UploadPaperDialog";
 import ModifyCard from "../../card/components/ModifyCard";
 import CardPreviewDialog from "../../card/components/CardPreviewDialog.vue";
+import PreviewAttachment from "@/components/PreviewAttachment.vue";
 import { taskApplyDetail, taskPaperApplyEdit, cardForSelectList } from "../api";
 import { attachmentPreview } from "../../login/api";
 import { COMMON_CARD_RULE_ID } from "@/constants/enumerate";
@@ -306,7 +313,12 @@ const initTaskApply = {
 
 export default {
   name: "modify-task-paper",
-  components: { UploadPaperDialog, ModifyCard, CardPreviewDialog },
+  components: {
+    UploadPaperDialog,
+    ModifyCard,
+    CardPreviewDialog,
+    PreviewAttachment,
+  },
   props: {
     instance: {
       type: Object,
@@ -367,6 +379,8 @@ export default {
       abc: "abcdefghijklmnopqrstuvwxyz".toUpperCase(),
       cards: [],
       user: this.$ls.get("user", {}),
+      // attachmentId preview
+      curAttachmentId: "",
     };
   },
   methods: {
@@ -475,10 +489,16 @@ export default {
       if (!attachment.attachmentId) return;
       const data = await attachmentPreview(attachment.attachmentId);
       window.open(data.url);
+      // TODO:
     },
     toViewCard(attachment) {
       this.curAttachment = { ...attachment };
       this.$refs.CardPreviewDialog.open();
+      // TODO:
+    },
+    toViewAttachment(curAttachmentId) {
+      this.curAttachmentId = curAttachmentId;
+      this.$refs.PreviewAttachment.open();
     },
     async toCopyCard(attachment) {
       this.curAttachment = { ...attachment };

+ 11 - 5
src/modules/exam/components/taskApply/ModifyTaskApply.vue

@@ -14,8 +14,8 @@
     <div v-if="dataReady" class="apply-content task-detail">
       <task-info></task-info>
       <task-paper ref="TaskPaper"></task-paper>
-      <task-print ref="TaskPrint"></task-print>
-      <task-flow v-if="showTaskPrint" ref="TaskFlow"></task-flow>
+      <task-print v-if="showTaskPrint" ref="TaskPrint"></task-print>
+      <task-flow ref="TaskFlow"></task-flow>
     </div>
 
     <div slot="footer">
@@ -160,9 +160,9 @@ export default {
       }
     },
     showTaskPrint() {
-      return (
-        this.examTask.examModel !== "IS_MODEL3" &&
-        this.curTaskApply.examTaskContent
+      return Boolean(
+        this.examTask.examModel !== "MODEL3" &&
+          this.curTaskApply.examTaskContent
       );
     },
   },
@@ -216,6 +216,12 @@ export default {
         auditContent: JSON.parse(data.auditContent || "[]"),
         includePaper: data.printContent.indexOf("PAPER") !== -1,
       });
+
+      if (data.examTaskContent) {
+        const cont = JSON.parse(data.examTaskContent);
+        curTaskApply.examTaskContent = cont.examTaskContent;
+      }
+
       this.setCurTaskApply(curTaskApply);
       this.updateStatus();
       this.dataReady = true;

+ 16 - 0
src/modules/exam/components/taskApply/TaskPaper.vue

@@ -436,6 +436,12 @@
       :presetData="cardBuildPresetData"
       @confirm="cardBuildConfirm"
     ></card-build-dialog>
+
+    <!-- PreviewAttachment -->
+    <preview-attachment
+      ref="PreviewAttachment"
+      :attachment-id="curAttachmentId"
+    ></preview-attachment>
   </div>
 </template>
 
@@ -452,6 +458,7 @@ import SelectTikuPaperDialog from "../createExamAndPrintTask/SelectTikuPaperDial
 import CardBuildDialog from "../../../card/components/CardBuildDialog.vue";
 import SimpleImagePreview from "../../../../components/SimpleImagePreview.vue";
 import ModifyCard from "../../../card/components/ModifyCard.vue";
+import PreviewAttachment from "@/components/PreviewAttachment.vue";
 
 export default {
   name: "task-paper",
@@ -461,6 +468,7 @@ export default {
     ModifyCard,
     CardBuildDialog,
     SelectTikuPaperDialog,
+    PreviewAttachment,
   },
   data() {
     return {
@@ -492,6 +500,8 @@ export default {
       cards: [],
       examTaskInstr: this.$ls.get("schoolInfo", { examTaskInstr: "" })
         .examTaskInstr,
+      // attachmentId preview
+      curAttachmentId: "",
       // card-build
       cardBuildPresetData: {},
       // image-preview
@@ -822,6 +832,7 @@ export default {
       this.addPreviewLog({ attachment, type: "paper" });
       const data = await attachmentPreview(attachment.attachmentId);
       window.open(data.url);
+      // TODO:
     },
     // select-paper
     toSelect(attachment) {
@@ -876,6 +887,11 @@ export default {
     toViewPaper(attachment) {
       if (!attachment.paperUrl) return;
       window.open(attachment.paperUrl);
+      // TODO:
+    },
+    toViewAttachment(curAttachmentId) {
+      this.curAttachmentId = curAttachmentId;
+      this.$refs.PreviewAttachment.open();
     },
     // image-preview
     toPreview(index) {

+ 1 - 1
src/modules/exam/components/taskApply/TaskPrint.vue

@@ -292,7 +292,7 @@ export default {
     };
   },
   computed: {
-    ...mapState("exam", ["editType", "examTask", "curTaskApply"]),
+    ...mapState("exam", ["editType", "examTask", "curTaskApply", "taskStatus"]),
     IS_MODEL2() {
       return this.examTask.examModel === "MODEL2";
     },

+ 3 - 0
src/modules/login/api.js

@@ -24,6 +24,9 @@ export const attachmentPreview = (id) => {
 export const attachmentDetail = (id) => {
   return $postParam("/api/admin/common/file/get_one", { id });
 };
+export const attachmentImages = (id) => {
+  return $postParam("/api/admin/common/file/get_one", { id });
+};
 export const attachmentDownload = ({ id, type }) => {
   return $postParam("/api/admin/common/file/download", { id, type });
 };

+ 7 - 10
src/modules/mark/components/markDetail/MarkDetailReject.vue

@@ -79,11 +79,12 @@
           label="教学班"
           min-width="200"
         ></el-table-column>
-        <el-table-column prop="userName" label="评卷员" min-width="100">
+        <el-table-column prop="userName" label="评卷员" min-width="200">
           <template slot-scope="scope">
-            <el-tag size="medium" type="primary">
+            <span v-if="scope.row.userName">
               {{ scope.row.userName }}({{ scope.row.loginName }})
-            </el-tag>
+            </span>
+            <span v-else>--</span>
           </template>
         </el-table-column>
         <el-table-column
@@ -101,15 +102,11 @@
             scope.row.markerTime | timestampFilter
           }}</span>
         </el-table-column>
-        <el-table-column prop="rejectUserName" label="打回人" min-width="100">
+        <el-table-column prop="rejectUserName" label="打回人" min-width="200">
           <template slot-scope="scope">
-            <el-tag
-              v-if="scope.row.rejectUserName"
-              size="medium"
-              type="primary"
-            >
+            <span v-if="scope.row.rejectUserName">
               {{ scope.row.rejectUserName }}({{ scope.row.rejectLoginName }})
-            </el-tag>
+            </span>
             <span v-else>--</span>
           </template>
         </el-table-column>

+ 9 - 12
src/modules/mark/components/markDetail/MarkDetailTask.vue

@@ -109,9 +109,10 @@
         ></el-table-column>
         <el-table-column prop="userName" label="评卷员" min-width="200">
           <template slot-scope="scope">
-            <el-tag size="medium" type="primary">
+            <span v-if="scope.row.userName">
               {{ scope.row.userName }}({{ scope.row.loginName }})
-            </el-tag>
+            </span>
+            <span v-else>--</span>
           </template>
         </el-table-column>
         <el-table-column
@@ -129,15 +130,11 @@
             scope.row.markerTime | timestampFilter
           }}</span>
         </el-table-column>
-        <el-table-column prop="rejectUserName" label="打回人" min-width="100">
+        <el-table-column prop="rejectUserName" label="打回人" min-width="200">
           <template slot-scope="scope">
-            <el-tag
-              v-if="scope.row.rejectUserName"
-              size="medium"
-              type="primary"
-            >
+            <span v-if="scope.row.rejectUserName">
               {{ scope.row.rejectUserName }}({{ scope.row.rejectLoginName }})
-            </el-tag>
+            </span>
             <span v-else>--</span>
           </template>
         </el-table-column>
@@ -189,9 +186,11 @@
 <script>
 import { markDetailTaskListPage, markGroupQuestions } from "../../api";
 import { MARK_TASK_STATUS } from "@/constants/enumerate";
+import markMinxin from "../../markMinxin";
 
 export default {
   name: "mark-detail-task",
+  mixins: [markMinxin],
   props: {
     baseInfo: {
       type: Object,
@@ -251,11 +250,9 @@ export default {
     },
     toReject(row) {
       this.toMarkReject({
+        taskId: row.id,
         groupNumber: row.groupNumber,
-        studentId: row.studentId,
         paperNumber: row.paperNumber,
-        examId: row.examId,
-        secretNumber: row.secretNumber,
         studentCode: row.studentCode,
         studentName: row.studentName,
         courseCode: this.baseInfo.courseCode,

+ 2 - 4
src/modules/mark/markMinxin.js

@@ -25,10 +25,8 @@ export default {
       this.$ls.set("check-students", { examId, paperNumber, studentIds });
       this.openPage("/mark/check/subjective-answer");
     },
-    toMarkReject(datas) {
-      // groupNumber,studentId,paperNumber,examId,secretNumber,studentCode, studentName,courseCode,courseName
-
-      this.$ls.set("reject", datas);
+    toMarkReject(data) {
+      this.$ls.set("reject", data);
       this.openPage("/mark/reject");
     },
     openPage(url) {

+ 3 - 3
src/modules/mark/views/MarkManage.vue

@@ -94,17 +94,17 @@
           <span slot-scope="scope">{{ scope.row.percent || 0 }}%</span>
         </el-table-column>
         <el-table-column
-          prop="paperType"
+          prop="totalCount"
           label="任务数"
           width="80"
         ></el-table-column>
         <el-table-column
-          prop="paperType"
+          prop="currentCount"
           label="正在评卷"
           width="100"
         ></el-table-column>
         <el-table-column
-          prop="paperType"
+          prop="problemCount"
           label="问题卷"
           width="80"
         ></el-table-column>

+ 15 - 1
src/modules/print/views/PrintTaskManage.vue

@@ -473,6 +473,12 @@
       ref="DataTaskDialog"
       task-type="PRINT_PDF_DOWNLOAD"
     ></data-task-dialog>
+
+    <!-- PreviewAttachment -->
+    <preview-attachment
+      ref="PreviewAttachment"
+      :attachment-id="curAttachmentId"
+    ></preview-attachment>
   </div>
 </template>
 
@@ -495,6 +501,7 @@ import { PRINT_TASK_STATUS } from "@/constants/enumerate";
 import pickerOptions from "@/constants/datePickerOptions";
 import { parseTimeRangeDateAndTime } from "@/plugins/utils";
 import PreviewPrintTaskTemplate from "../components/PreviewPrintTaskTemplate";
+import PreviewAttachment from "@/components/PreviewAttachment.vue";
 
 const defaultTotalInfo = {
   totalSubjects: 0,
@@ -510,7 +517,7 @@ const defaultTotalInfo = {
 
 export default {
   name: "print-task-manage",
-  components: { PreviewPrintTaskTemplate },
+  components: { PreviewPrintTaskTemplate, PreviewAttachment },
   data() {
     return {
       filter: {
@@ -538,6 +545,8 @@ export default {
       multipleSelection: [],
       PRINT_TASK_STATUS,
       loading: false,
+      // attachmentId preview
+      curAttachmentId: "",
       // view-pdf
       padViewDialogVisible: false,
       pdfList: [],
@@ -763,10 +772,15 @@ export default {
     actionPdf(item) {
       if (this.actionType === "view") {
         this.viewPdf(item);
+        // TODO:
       } else {
         this.rebuildPdf(item);
       }
     },
+    toViewAttachment(curAttachmentId) {
+      this.curAttachmentId = curAttachmentId;
+      this.$refs.PreviewAttachment.open();
+    },
     async rebuildPdf(item) {
       const action = await this.$confirm(
         `确定要重新生成该印刷任务PDF吗?`,