zhangjie 1 year ago
parent
commit
eb05b9bfa9

+ 75 - 0
src/modules/client/components/HandleInputDialog.vue

@@ -0,0 +1,75 @@
+<template>
+  <el-dialog
+    :visible.sync="modalIsShow"
+    title="手动输入"
+    top="10vh"
+    width="460px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    :show-close="false"
+    append-to-body
+    @open="visibleChange"
+  >
+    <el-form
+      ref="modalFormComp"
+      :model="modalForm"
+      :rules="rules"
+      label-width="80px"
+    >
+      <el-form-item prop="studentCode" label="学号:">
+        <el-input
+          v-model.trim="modalForm.studentCode"
+          placeholder="请输入第一张试卷学号"
+          clearable
+        ></el-input>
+      </el-form-item>
+    </el-form>
+    <div slot="footer">
+      <el-button type="primary" @click="submit">确认</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  name: "handle-input-dialog",
+  data() {
+    return {
+      modalIsShow: false,
+      modalForm: { studentCode: "" },
+      rules: {
+        studentCode: [
+          {
+            required: true,
+            message: "请输入第一张试卷学号",
+            trigger: "change",
+          },
+          {
+            message: "学号只能由数字、字母和下划线组成,长度3-30个字符",
+            pattern: /^[a-zA-Z0-9_-]{3,30}$/,
+            trigger: "change",
+          },
+        ],
+      },
+    };
+  },
+  methods: {
+    visibleChange() {
+      this.modalForm.studentCode = "";
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    async submit() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      this.$emit("confirm", this.modalForm.studentCode);
+      this.cancel();
+    },
+  },
+};
+</script>

+ 3 - 0
src/modules/client/components/OcrAreaSetDialog.vue

@@ -138,6 +138,9 @@ export default {
       this.close();
     },
     dialogClosed() {
+      this.curImageUrl = "";
+      this.spinStyle = {};
+      this.rotate = 0;
       if (this.cropper) {
         this.cropper.destroy();
         this.cropper = false;

+ 197 - 88
src/modules/client/components/ScanTaskProcessDialog.vue

@@ -18,7 +18,7 @@
           >
         </h3>
         <div
-          v-if="scanStatus === 'INIT'"
+          v-if="scanStatus === 'INIT' || (scanStatus === 'FINISH' && !saving)"
           class="scan-head-btn cont-link"
           @click="close"
         >
@@ -28,28 +28,42 @@
 
       <div class="scan-image-list">
         <div
-          v-for="item in scanHistoryList"
-          :key="item.url"
+          v-for="item in scanStageList"
+          :key="item.frontOriginImgPath"
           class="scan-image-item"
         >
           <el-image
-            :src="item.url"
+            :src="`file://${item.frontOriginImgPath}`"
             fit="contain"
             lazy
             @click="toViewPaper(item)"
           ></el-image>
-          <p v-if="item.name" :title="item.name">{{ item.name }}</p>
+          <p v-if="item.studentCode" :title="item.studentCode">
+            {{ item.studentCode }}
+          </p>
         </div>
       </div>
 
-      <div class="text-center" slot="footer">
+      <div class="box-justify" slot="footer">
+        <el-button :disabled="!canSave" type="danger" @click="clearStage"
+          >清空数据</el-button
+        >
         <el-button
           type="primary"
-          :loading="scanStatus !== 'INIT'"
+          :loading="scanStatus === 'SCAN'"
+          :disabled="scanStatus === 'FINISH'"
           @click="startTask"
         >
           {{ statusDesc[scanStatus] }}
         </el-button>
+
+        <el-button
+          :disabled="!canSave"
+          :loading="saving"
+          type="primary"
+          @click="saveScanImage"
+          >保存</el-button
+        >
       </div>
     </el-dialog>
 
@@ -60,6 +74,11 @@
       @on-next="toNextPaper"
       ref="SimpleImagePreview"
     ></simple-image-preview>
+    <!-- handle-input -->
+    <handle-input-dialog
+      ref="HandleInputDialog"
+      @confirm="handleConfirm"
+    ></handle-input-dialog>
   </div>
 </template>
 
@@ -74,6 +93,7 @@ import {
 import db from "../../../plugins/db";
 import { evokeScanner } from "../../../plugins/scanner";
 import SimpleImagePreview from "@/components/SimpleImagePreview.vue";
+import HandleInputDialog from "./HandleInputDialog.vue";
 import log4js from "@/plugins/logger";
 const logger = log4js.getLogger("scan");
 
@@ -81,6 +101,7 @@ export default {
   name: "scan-task-dialog",
   components: {
     SimpleImagePreview,
+    HandleInputDialog,
   },
   props: {
     task: {
@@ -94,32 +115,45 @@ export default {
     return {
       modalIsShow: false,
       scanStatus: "INIT",
-      realScanCount: 0,
-      scanHistoryList: [],
+      scanStageList: [],
       curPaper: {},
       statusDesc: {
         INIT: "开始扫描",
         SCAN: "扫描中",
-        SAVING: "保存数据中",
+        FINISH: "扫描完成",
       },
       user: this.$ls.get("user", {}),
-      lastStudentCode: "",
+      scanHistoryList: [],
+      saving: false,
     };
   },
   computed: {
     ...mapState("client", ["ocrArea"]),
+    canSave() {
+      return this.scanStatus === "FINISH" && this.scanStageList.length;
+    },
   },
   methods: {
     initData() {
+      this.scanStageList = [];
       this.scanStatus = "INIT";
-      this.realScanCount = 0;
-      this.getScanHistory();
     },
     dialogOpen() {
       logger.info(`00进入扫描`);
       this.initData();
     },
-    close() {
+    async close() {
+      if (this.scanStageList.length) {
+        const res = await this.$confirm(
+          `当前存在未保存的扫描数据,确定要退出吗?`,
+          "警告",
+          {
+            type: "warning",
+          }
+        ).catch(() => {});
+        if (res !== "confirm") return;
+      }
+
       this.modalIsShow = false;
       this.$emit("on-close");
       logger.info(`99退出扫描`);
@@ -129,12 +163,13 @@ export default {
     },
     startTask() {
       logger.info(`01开始扫描`);
+      this.clearData();
+      this.scanStageList = [];
       this.scanStatus = "SCAN";
       this.evokeScanExe();
     },
     async evokeScanExe() {
       logger.info("02唤起扫描仪");
-      // console.log(commandStr);
       await evokeScanner(this.GLOBAL.input).catch((error) => {
         console.error(error);
       });
@@ -145,66 +180,26 @@ export default {
         logger.error(`03扫描仪停止,故障:${res.errorMsg}`);
         this.$message.error(res.errorMsg);
         this.scanStatus = "INIT";
-        clearDir(this.GLOBAL.input);
         return;
       }
       logger.info(`03扫描仪停止,扫描数:${res.data.length}`);
-      this.scanStatus = "SAVING";
-      await this.saveScanImage(res.data).catch((e) => {
-        console.dir(e);
-        this.$message.error("保存数据失败,请重新扫描!");
-        logger.error(`保存数据失败,请重新扫描!`);
-      });
-      clearDir(this.GLOBAL.input);
-      this.scanStatus = "INIT";
-      await this.getScanHistory();
-
-      this.curPaper = {};
-      this.$refs.SimpleImagePreview.cancel();
-    },
-    // scan relate
-    async getScanHistory() {
-      const res = await db
-        .searchHistoryList({
-          isFormal: this.task.isFormal ? 1 : 0,
-          taskId: this.task.id,
-        })
-        .catch((e) => {
-          console.log(e);
-        });
-      if (!res) return;
-
-      this.scanHistoryList = [];
-      res.forEach((item) => {
-        this.scanHistoryList.push({
-          url: "file:///" + item.frontOriginImgPath,
-          name: `${item.studentCode}-1`,
-        });
-        this.scanHistoryList.push({
-          url: "file:///" + item.versoOriginImgPath,
-          name: `${item.studentCode}-2`,
-        });
-      });
+      this.scanStatus = "FINISH";
+      this.stageScanImage(res.data);
+      if (this.task.isFormal) {
+        await this.checkFirstHasCode();
+      }
     },
-    async saveScanImage(imageList) {
-      logger.info(`04-1开始保存数据`);
-      for (let i = 0, len = imageList.length; i < len; i++) {
-        const files = imageList[i];
-        const ouputImageList = saveOutputImage(
-          [files.frontFile, files.versoFile],
-          {
-            courseCode: this.task.courseCode,
-          }
-        );
-        const fileInfo = {
+    stageScanImage(imageList) {
+      this.scanStageList = imageList.map((item) => {
+        return {
           taskId: this.task.id,
           schoolId: this.task.schoolId,
           semesterId: this.task.semesterId,
           examId: this.task.examId,
           courseCode: this.task.courseCode,
           courseName: this.task.courseName,
-          frontOriginImgPath: ouputImageList[0],
-          versoOriginImgPath: ouputImageList[1],
+          frontOriginImgPath: item.frontFile,
+          versoOriginImgPath: item.versoFile,
           isFormal: this.task.isFormal ? 1 : 0,
           studentCode: "",
           ocrArea: "",
@@ -212,51 +207,165 @@ export default {
           clientUsername: this.user.loginName,
           clientUserLoginTime: this.user.loginTime,
         };
+      });
+    },
+    async checkFirstHasCode() {
+      const fileInfo = this.scanStageList[0];
+      if (!fileInfo) return;
 
-        if (this.task.isFormal) {
-          const num = await decodeImageCode(
-            fileInfo.frontOriginImgPath,
-            this.ocrArea
-          ).catch((err) => {
-            console.error(err);
-            logger.error(`04-2条码解析失败,${err}`);
-          });
-          fileInfo.studentCode = num || this.lastStudentCode;
-          fileInfo.ocrArea = JSON.stringify(this.ocrArea);
+      const code = await decodeImageCode(
+        fileInfo.frontOriginImgPath,
+        this.ocrArea
+      ).catch((err) => {
+        console.error(err);
+        logger.error(`04-2条码解析失败,${err}`);
+      });
 
-          if (fileInfo.studentCode) {
-            this.lastStudentCode = fileInfo.studentCode;
-          }
+      if (code) {
+        fileInfo.studentCode = code;
+        this.updateStudentCode();
+      } else {
+        this.$refs.HandleInputDialog.open();
+      }
+    },
+    handleConfirm(code) {
+      this.scanStageList[0].studentCode = code;
+      this.updateStudentCode();
+    },
+    async updateStudentCode() {
+      if (!this.task.isFormal) return;
+
+      let lastStudentCode = this.scanStageList[0].studentCode;
+      const ocrAreaContent = JSON.stringify(this.ocrArea);
+      for (let i = 0, len = this.scanStageList.length; i < len; i++) {
+        const fileInfo = this.scanStageList[i];
+        if (!i) fileInfo.ocrArea = ocrAreaContent;
+
+        const code = await decodeImageCode(
+          fileInfo.frontOriginImgPath,
+          this.ocrArea
+        ).catch((err) => {
+          console.error(err);
+          logger.error(`03条码解析失败,${err}`);
+        });
+        fileInfo.studentCode = code || lastStudentCode;
+        fileInfo.ocrArea = ocrAreaContent;
+
+        if (fileInfo.studentCode) {
+          lastStudentCode = fileInfo.studentCode;
         }
+      }
+    },
+    // scan relate
+    async clearStage() {
+      const res = await this.$confirm(`确定要清空数据吗?`, "警告", {
+        type: "warning",
+      }).catch(() => {});
+      if (res !== "confirm") return;
+
+      this.scanStageList = [];
+      this.scanStatus = "INIT";
+      this.clearData();
+      logger.info(`99数据清空`);
+      this.$message.success("数据已清空!");
+    },
+    async saveScanImage() {
+      if (!this.scanStageList.length) {
+        this.$message.error("当前无要保存的数据!");
+        return;
+      }
+
+      if (
+        this.task.isFormal &&
+        this.scanStageList.some((item) => !item.studentCode)
+      ) {
+        this.$message.error("有试卷未绑定学号");
+        return;
+      }
+
+      if (this.saving) return;
+      this.saving = true;
+
+      logger.info(`04-1开始保存数据`);
+      for (let i = 0, len = this.scanStageList.length; i < len; i++) {
+        const fileInfo = this.scanStageList[i];
+        const ouputImageList = saveOutputImage(
+          [fileInfo.frontOriginImgPath, fileInfo.versoOriginImgPath],
+          {
+            courseCode: this.task.courseCode,
+          }
+        );
+
+        fileInfo.frontOriginImgPath = ouputImageList[0];
+        fileInfo.versoOriginImgPath = ouputImageList[0];
+
         let res = true;
         await db.saveUploadInfo(fileInfo).catch((err) => {
           res = false;
           console.error(err);
           logger.error(`04-1保存数据错误,${err}`);
         });
-        if (!res) return Promise.reject();
+        if (!res) {
+          this.saving = false;
+          this.$message.error("保存数据错误,请重新尝试!");
+          return Promise.reject();
+        }
       }
 
-      logger.info(`04-2保存数据结束`);
+      this.$message.success("保存成功!");
+      this.saving = false;
+      logger.info(`04-2保存数据成功`);
+      this.clearData();
+      this.scanStatus = "INIT";
+      this.scanStageList = [];
+    },
+    clearData() {
+      clearDir(this.GLOBAL.input);
+    },
+    async getScanHistory() {
+      const res = await db
+        .searchHistoryList({
+          isFormal: this.task.isFormal ? 1 : 0,
+          taskId: this.task.id,
+        })
+        .catch((e) => {
+          console.log(e);
+        });
+      if (!res) return;
+
+      this.scanHistoryList = [];
+      res.forEach((item) => {
+        this.scanHistoryList.push({
+          url: "file:///" + item.frontOriginImgPath,
+          name: `${item.studentCode}-1`,
+        });
+      });
     },
     // image-preview
     toViewPaper(imgItem) {
-      this.curPaper = { ...imgItem };
+      this.setCurPaper(imgItem);
       this.$refs.SimpleImagePreview.open();
     },
+    setCurPaper(imgItem) {
+      this.curPaper = {
+        ...imgItem,
+        url: "file://" + imgItem.frontOriginImgPath,
+        filename: imgItem.studentCode,
+      };
+    },
     toPrevPaper() {
-      const curPaperIndex = this.scanHistoryList.findIndex(
-        (item) => item.url === this.curPaper.url
+      const curPaperIndex = this.scanStageList.findIndex(
+        (item) => item.frontOriginImgPath === this.curPaper.frontOriginImgPath
       );
       if (curPaperIndex <= 0) return;
-      this.curPaper = this.scanHistoryList[curPaperIndex - 1];
+      this.setCurPaper(this.scanStageList[curPaperIndex - 1]);
     },
     toNextPaper() {
-      const curPaperIndex = this.scanHistoryList.findIndex(
-        (item) => item.url === this.curPaper.url
+      const curPaperIndex = this.scanStageList.findIndex(
+        (item) => item.frontOriginImgPath === this.curPaper.frontOriginImgPath
       );
-      if (curPaperIndex >= this.scanHistoryList.length - 1) return;
-      this.curPaper = this.scanHistoryList[curPaperIndex + 1];
+      if (curPaperIndex >= this.scanStageList.length - 1) return;
+      this.setCurPaper(this.scanStageList[curPaperIndex + 1]);
     },
   },
 };

+ 1 - 1
vue.config.js

@@ -5,7 +5,7 @@ module.exports = defineConfig({
   transpileDependencies: true,
   // publicPath: './',
   devServer: {
-    port: 8066,
+    port: 8059,
     client: {
       overlay: false,
     },