Browse Source

ocr识别

zhangjie 2 năm trước cách đây
mục cha
commit
ff76cf6f76

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

@@ -264,3 +264,124 @@
     display: none;
   }
 }
+
+/* ocr-area */
+.ocr-area {
+  height: 100%;
+  position: relative;
+
+  &-cont {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 50px;
+    z-index: 7;
+    overflow: hidden;
+    &-disabled::before {
+      content: "";
+      display: block;
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      top: 0;
+      left: 0;
+      z-index: 99;
+    }
+    &-disabled .cropper-container > .cropper-modal,
+    &-disabled .cropper-container > .cropper-crop-box {
+      display: none;
+    }
+  }
+  &-cont img {
+    display: block;
+    width: 100%;
+  }
+
+  &-action {
+    position: absolute;
+    width: 100%;
+    bottom: 0;
+    height: 50px;
+    z-index: 8;
+    background-color: #fff;
+    padding: 10px;
+    text-align: right;
+  }
+}
+
+/* .cropper-line */
+.cropper {
+  &-modal {
+    opacity: 0.7;
+  }
+  &-line {
+    background-color: $--color-cyan;
+    opacity: 1;
+    &.line-e {
+      right: -2px;
+      top: 3px;
+      bottom: 3px;
+      width: 2px;
+    }
+    &.line-n {
+      height: 2px;
+      top: -2px;
+      left: 3px;
+      right: 3px;
+    }
+    &.line-w {
+      left: -2px;
+      top: 3px;
+      bottom: 3px;
+      width: 2px;
+    }
+    &.line-s {
+      height: 2px;
+      bottom: -2px;
+      left: 3px;
+      right: 3px;
+    }
+  }
+  &-point {
+    background-color: rgba(51, 51, 51, 0.7);
+    border: 2px solid $--color-cyan;
+    border-radius: 50%;
+    opacity: 1;
+    width: 10px !important;
+    height: 10px !important;
+
+    &.point-e {
+      margin-top: -5px;
+      right: -6px;
+    }
+    &.point-w {
+      margin-top: -5px;
+      left: -6px;
+    }
+    &.point-n {
+      margin-left: -5px;
+      top: -6px;
+    }
+    &.point-s {
+      margin-left: -5px;
+      bottom: -6px;
+    }
+    &.point-ne {
+      top: -6px;
+      right: -6px;
+    }
+    &.point-nw {
+      top: -6px;
+      left: -6px;
+    }
+    &.point-sw {
+      bottom: -6px;
+      left: -6px;
+    }
+    &.point-se {
+      bottom: -6px;
+      right: -6px;
+    }
+  }
+}

+ 1 - 1
src/constants/menus-data.js

@@ -1197,7 +1197,7 @@ export default [
   {
     id: "15",
     name: "档案类型管理",
-    url: "RecordTypeManage",
+    url: "ArchivesTypeManage",
     type: "MENU",
     parentId: "13",
     sequence: 1,

+ 1 - 0
src/main.js

@@ -10,6 +10,7 @@ import "./plugins/filters";
 // https://github.com/RobinCK/vue-ls
 import VueLocalStorage from "vue-ls";
 import ElementUI from "element-ui";
+import "cropperjs/dist/cropper.min.css";
 import "element-ui/lib/theme-chalk/index.css";
 import "./assets/styles/index.scss";
 

+ 8 - 124
src/modules/base/api.js

@@ -192,34 +192,6 @@ export const studentCascadeQuery = datas => {
   return $postParam("/api/admin/common/basic_student/condition", datas);
 };
 
-// clazz-manage
-export const clazzListQuery = datas => {
-  return $postParam("/api/admin/basic/clazz/query", datas);
-};
-export const deleteClazz = idList => {
-  return $postParam("/api/admin/basic/clazz/delete_batch", { idList });
-};
-export const updateClazz = datas => {
-  return $post("/api/admin/basic/clazz/save", datas);
-};
-export const clazzQuery = datas => {
-  return $postParam("/api/admin/basic/clazz/datasource", datas);
-};
-
-// clazz-simple-manage
-export const clazzSimpleListPage = datas => {
-  return $postParam("/api/admin/teach/clazz/page", datas);
-};
-export const clazzTeachCourseQuery = datas => {
-  return $postParam("/api/admin/teach/clazz/find_teach_course", datas);
-};
-export const deleteClazzSimple = idList => {
-  return $postParam("/api/admin/teach/clazz/delete", { idList });
-};
-export const updateClazzSimple = datas => {
-  return $post("/api/admin/teach/clazz/edit", datas);
-};
-
 // semester-manage
 export const semesterListQuery = datas => {
   return $postParam("/api/admin/basic/semester/page", datas);
@@ -231,112 +203,24 @@ export const updateSemester = datas => {
   return $post("/api/admin/basic/semester/save", datas);
 };
 // record-type-manage
-export const recordTypeListQuery = datas => {
+export const ArchivesTypeListQuery = datas => {
   return $postParam("/api/admin/paper/archives_type/page", datas);
 };
-export const deleteRecordType = id => {
+export const deleteArchivesType = id => {
   return $postParam("/api/admin/paper/archives_type/delete", { id });
 };
-export const updateRecordType = datas => {
+export const updateArchivesType = datas => {
   return $post("/api/admin/paper/archives_type/save", datas);
 };
-// major-manage
-export const majorListQuery = datas => {
-  return $postParam("/api/admin/basic/major/query", datas);
-};
-export const deleteMajor = idList => {
-  return $postParam("/api/admin/basic/major/delete", { idList });
-};
-export const updateMajor = datas => {
-  return $post("/api/admin/basic/major/save", datas);
-};
-// college-manage
-export const collegeListQuery = datas => {
-  return $postParam("/api/admin/basic/college/query", datas);
-};
-export const deleteCollege = idList => {
-  return $postParam("/api/admin/basic/college/delete", { idList });
-};
-export const updateCollege = datas => {
-  return $post("/api/admin/basic/college/save", datas);
-};
-
-// other --------------------------------->
-// print-plan-push-manage
-export const printPlanPushListQuery = datas => {
-  return $postParam("/api/admin/exam/print_sync/list_sync", datas);
-};
-export const printPlanBatchPush = list => {
-  return $post("/api/admin/exam/print_sync/sync_data_cloud_batch", {
-    list
-  });
-};
-export const printPlanPush = ({ printPlanId, thirdRelateId }) => {
-  return $postParam("/api/admin/exam/print_sync/sync_data_cloud", {
-    printPlanId,
-    thirdRelateId
-  });
-};
-export const printPlanMergePush = datas => {
-  return $post("/api/admin/exam/print_sync/sync_data_merge", datas);
-};
-export const printPlanSyncExamList = () => {
-  return $post("/api/admin/exam/print_sync/list_relate_ids", {});
-};
 
-// sms-manage
-export const smsListQuery = datas => {
-  return $postParam("/api/admin/basic/message/list", datas);
-};
-export const smsTypes = () => {
-  return $postParam("/api/admin/basic/message/list_message_types", {});
+// system-setting
+export const systemSettingQuery = () => {
+  return $postParam("/api/admin/sys/setting/get", {});
 };
-export const resendSms = id => {
-  return $postParam("/api/admin/basic/message/resend", { id });
+export const updateSystemSetting = datas => {
+  return $post("/api/admin/sys/setting/save", datas);
 };
 
-// exam-manage
-export const examQuery = datas => {
-  return $postParam("/api/admin/basic/exam/query", datas);
-};
-export const examListQuery = datas => {
-  return $postParam("/api/admin/basic/exam/page", datas);
-};
-export const deleteExam = id => {
-  return $postParam("/api/admin/basic/exam/delete", { id });
-};
-export const updateExam = datas => {
-  return $post("/api/admin/basic/exam/save", datas);
-};
-export const ableExam = ({ id, enable }) => {
-  return $postParam("/api/admin/basic/exam/enable", { id, enable });
-};
-
-// exam-config
-export const examConfigQuery = datas => {
-  return $postParam("/api/admin/basic/print_config/page", datas);
-};
-export const deleteExamConfig = id => {
-  return $postParam("/api/admin/basic/print_config/delete", { id });
-};
-export const updateExamConfig = datas => {
-  return $post("/api/admin/basic/print_config/save", datas);
-};
-export const ableExamConfig = ({ id, enable }) => {
-  return $postParam("/api/admin/basic/print_config/enable", { id, enable });
-};
-export const examConfigByExamIdOrgId = datas => {
-  return $postParam(
-    "/api/admin/basic/print_config/get_by_exam_id_and_org_id",
-    datas
-  );
-};
-export const listOrgsByExamId = ({ id, examId }) => {
-  return $postParam("/api/admin/basic/print_config/list_org_id_by_exam_id", {
-    id,
-    examId
-  });
-};
 // common
 export const uploadFile = datas => {
   return $post("/api/admin/common/file/upload", datas);

+ 2 - 2
src/modules/base/components/ModifyRecordType.vue → src/modules/base/components/ModifyArchivesType.vue

@@ -29,7 +29,7 @@
 </template>
 
 <script>
-import { updateRecordType } from "../api";
+import { updateArchivesType } from "../api";
 
 const initModalForm = {
   id: null,
@@ -99,7 +99,7 @@ export default {
       if (this.isSubmit) return;
       this.isSubmit = true;
       let datas = { ...this.modalForm };
-      const data = await updateRecordType(datas).catch(() => {});
+      const data = await updateArchivesType(datas).catch(() => {});
       this.isSubmit = false;
 
       if (!data) return;

+ 6 - 0
src/modules/base/router.js

@@ -2,6 +2,7 @@
 import OrganizationManage from "./views/OrganizationManage.vue";
 import RoleManage from "./views/RoleManage.vue";
 import UserManage from "./views/UserManage.vue";
+import SystemSetting from "./views/SystemSetting.vue";
 
 // dict
 import StudentManage from "./views/StudentManage.vue";
@@ -24,6 +25,11 @@ export default [
     name: "UserManage",
     component: UserManage
   },
+  {
+    path: "/base/system-setting",
+    name: "SystemSetting",
+    component: SystemSetting
+  },
   {
     path: "/base/student-manage",
     name: "StudentManage",

+ 10 - 10
src/modules/base/views/ArchivesTypeManage.vue

@@ -60,21 +60,21 @@
       </div>
     </div>
 
-    <modify-record-type
+    <modify-archives-type
       :instance="curRow"
       @modified="getList"
-      ref="ModifyRecordType"
-    ></modify-record-type>
+      ref="ModifyArchivesType"
+    ></modify-archives-type>
   </div>
 </template>
 
 <script>
-import { recordTypeListQuery, deleteRecordType } from "../api";
-import ModifyRecordType from "../components/ModifyRecordType.vue";
+import { ArchivesTypeListQuery, deleteArchivesType } from "../api";
+import ModifyArchivesType from "../components/ModifyArchivesType.vue";
 
 export default {
   name: "record-type-manage",
-  components: { ModifyRecordType },
+  components: { ModifyArchivesType },
   data() {
     return {
       filter: {},
@@ -97,7 +97,7 @@ export default {
         pageNumber: this.current,
         pageSize: this.size
       };
-      const data = await recordTypeListQuery(datas);
+      const data = await ArchivesTypeListQuery(datas);
       this.dataList = data.records;
       this.total = data.total;
     },
@@ -107,11 +107,11 @@ export default {
     },
     toAdd() {
       this.curRow = {};
-      this.$refs.ModifyRecordType.open();
+      this.$refs.ModifyArchivesType.open();
     },
     toEdit(row) {
       this.curRow = row;
-      this.$refs.ModifyRecordType.open();
+      this.$refs.ModifyArchivesType.open();
     },
     async toDelete(row) {
       const result = await this.$confirm(
@@ -123,7 +123,7 @@ export default {
       ).catch(() => {});
       if (result !== "confirm") return;
 
-      await deleteRecordType(row.id);
+      await deleteArchivesType(row.id);
       this.$message.success("删除成功!");
       this.deletePageLastItem();
     }

+ 1 - 1
src/modules/base/views/OrganizationManage.vue

@@ -44,7 +44,7 @@
             </div>
             <div class="org-actions" v-if="data.type === 'SCHOOL'">
               <el-button
-                v-if="checkPrivilege('button', 'Add')"
+                v-if="checkPrivilege('link', 'Add')"
                 class="btn-primary"
                 type="text"
                 @click="() => toAdd(data)"

+ 87 - 0
src/modules/base/views/SystemSetting.vue

@@ -0,0 +1,87 @@
+<template>
+  <div class="system-setting">
+    <div class="part-box part-box-pad">
+      <el-form
+        ref="modalFormComp"
+        :model="modalForm"
+        :rules="rules"
+        label-width="150px"
+      >
+        <el-form-item prop="openOcr" label="是否开启OCR识别:">
+          <el-radio-group v-model="modalForm.openOcr">
+            <el-radio
+              v-for="item in BOOLEAN_STATUS"
+              :key="item.value"
+              :label="item.value"
+              >{{ item.label }}</el-radio
+            >
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" :disabled="isSubmit" @click="submit"
+            >确认</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </div>
+  </div>
+</template>
+
+<script>
+import { systemSettingQuery, updateSystemSetting } from "../api";
+const initModalForm = {
+  openOcr: false
+};
+
+export default {
+  name: "system-setting",
+  data() {
+    return {
+      modalForm: {},
+      isSubmit: false,
+      BOOLEAN_STATUS: [
+        {
+          label: "开启",
+          value: true
+        },
+        {
+          label: "关闭",
+          value: false
+        }
+      ],
+      rules: {
+        openOcr: [
+          {
+            required: true,
+            message: "请选择",
+            trigger: "change"
+          }
+        ]
+      }
+    };
+  },
+  mounted() {
+    this.getSysSet();
+  },
+  methods: {
+    async getSysSet() {
+      const res = await systemSettingQuery();
+      this.modalForm = this.$objAssign(initModalForm, res || {});
+      if (!this.modalForm.openOcr) this.modalForm.openOcr = false;
+    },
+    async submit() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      const data = await updateSystemSetting(this.modalForm).catch(() => {});
+      this.isSubmit = false;
+
+      if (!data) return;
+
+      this.$message.success("设置成功!");
+    }
+  }
+};
+</script>

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

@@ -69,3 +69,6 @@ export const paperBindUser = datas => {
 export const listByStudentCode = datas => {
   return $postParam("/api/admin/paper/library/list_by_student_code", datas);
 };
+export const libraryOcrResult = datas => {
+  return $postParam("/api/admin/paper/library/ocr", datas);
+};

+ 80 - 7
src/modules/record/components/LibraryDialog.vue

@@ -23,6 +23,13 @@
             <el-button size="mini" type="primary" @click="toSelectTaskStd"
               >所有任务考生信息</el-button
             >
+            <el-button
+              v-if="openOcr"
+              size="mini"
+              type="success"
+              @click="toSetOcrArea"
+              >设置OCR识别区</el-button
+            >
             <el-divider></el-divider>
             <el-form-item prop="studentId" label="绑定学生">
               <el-input
@@ -56,7 +63,7 @@
             </el-table>
           </div>
         </div>
-        <div class="data-check-content part-box">
+        <div v-show="!isSetOcrSet" class="data-check-content part-box">
           <div v-if="curPagePapers.length" class="data-check-content-page">
             <el-button
               :type="curPagePaperIndex === 0 ? 'primary' : 'default'"
@@ -75,6 +82,14 @@
             :show-guide="false"
           ></image-contain>
         </div>
+        <div v-if="isSetOcrSet" class="data-check-content part-box">
+          <ocr-area-set
+            :image-url="curPagePaper.url"
+            :setting="ocrArea"
+            @cancel="isSetOcrSet = false"
+            @confirm="ocrAreaChange"
+          ></ocr-area-set>
+        </div>
       </div>
     </el-dialog>
 
@@ -92,10 +107,13 @@ import timeMixin from "../../../mixins/timeMixin";
 import {
   paperBindUser,
   studentUnbindTaskListPage,
-  scanTaskStudentListPage
+  scanTaskStudentListPage,
+  libraryOcrResult
 } from "../api";
+import { systemSettingQuery } from "../../base/api";
 import ImageContain from "../../../components/ImageContain.vue";
 import RelateStudentDialog from "./RelateStudentDialog.vue";
+import OcrAreaSet from "./OcrAreaSet.vue";
 
 const initModalForm = {
   paperLibraryId: "",
@@ -126,7 +144,7 @@ export default {
       }
     }
   },
-  components: { ImageContain, RelateStudentDialog },
+  components: { ImageContain, RelateStudentDialog, OcrAreaSet },
   mixins: [timeMixin],
   data() {
     return {
@@ -152,7 +170,12 @@ export default {
       curPagePapers: [],
       curPagePaperIndex: 0,
       curPagePaper: { url: "" },
-      lastPaperScanTaskId: ""
+      lastPaperScanTaskId: "",
+      // ocr
+      isSetOcrSet: false,
+      openOcr: false,
+      ocrArea: { x: null, y: null, width: null, height: null },
+      ocrResult: []
     };
   },
   computed: {
@@ -160,6 +183,10 @@ export default {
       return this.model === "undo";
     }
   },
+  mounted() {
+    this.ocrArea = this.$ls.get("ocrArea") || this.ocrArea;
+    this.getSysSet();
+  },
   methods: {
     async visibleChange() {
       this.modalForm = { ...initModalForm };
@@ -191,6 +218,10 @@ export default {
     open() {
       this.modalIsShow = true;
     },
+    async getSysSet() {
+      const res = await systemSettingQuery();
+      this.openOcr = res && res.openOcr;
+    },
     nameOrNoInput() {
       this.clearSetTs();
 
@@ -241,12 +272,18 @@ export default {
     },
     getNextPaper() {
       this.curPage = this.undoPageList.shift();
-      if (this.lastPaperScanTaskId !== this.curPage.paperScanTaskId) {
-        this.getStudentList();
-      }
       this.curPagePapers = this.curPage.fileUrls;
       this.switchCurPage(this.curPagePaperIndex);
+
+      if (this.openOcr) {
+        this.getOrcResult();
+      } else {
+        if (this.lastPaperScanTaskId !== this.curPage.paperScanTaskId) {
+          this.getStudentList();
+        }
+      }
     },
+
     toBind(row) {
       this.lastPaperScanTaskId = this.curPage.paperScanTaskId;
       this.modalForm.paperScanTaskDetailId = row.paperScanTaskDetailId;
@@ -271,6 +308,42 @@ export default {
       } else {
         this.cancel();
       }
+    },
+    // ocr
+    toSetOcrArea() {
+      if (!this.openOcr) return;
+      this.isSetOcrSet = true;
+    },
+    ocrAreaChange(ocrArea) {
+      Object.keys(ocrArea).forEach(key => {
+        ocrArea[key] = Math.floor(ocrArea[key]);
+      });
+      this.ocrArea = this.$objAssign(this.ocrArea, ocrArea);
+
+      this.$ls.set("ocrArea", this.ocrArea);
+      this.isSetOcrSet = false;
+      this.getOrcResult();
+    },
+    async getOrcResult() {
+      if (this.ocrArea.x === null) {
+        this.$message.error("请设置ORC识别区");
+        return;
+      }
+
+      const res = await libraryOcrResult({
+        paperLibraryId: this.curPage.id,
+        index: this.curPagePaperIndex,
+        ...this.ocrArea
+      });
+      if (res && res.length) {
+        this.ocrResult = res;
+        if (res.length === 1) {
+          this.studentNameOrNo = res[0];
+        } else {
+          // todo:选择一个结果
+        }
+      }
+      this.getStudentList();
     }
   }
 };

+ 73 - 0
src/modules/record/components/OcrAreaSet.vue

@@ -0,0 +1,73 @@
+<template>
+  <div class="ocr-area">
+    <div class="ocr-area-cont">
+      <img :src="imageUrl" ref="editImage" />
+    </div>
+    <div class="ocr-area-action">
+      <el-button type="primary" @click="confirm">确认</el-button>
+      <el-button @click="cancel">取消</el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import Cropper from "cropperjs";
+
+export default {
+  name: "ocr-area-set",
+  props: {
+    imageUrl: {
+      type: String,
+      require: true
+    },
+    setting: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  data() {
+    return {
+      cropper: ""
+    };
+  },
+  mounted() {
+    this.initCropper();
+  },
+  methods: {
+    initCropper() {
+      const _this = this;
+      const defCodeArea = this.setting || {};
+
+      this.cropper = new Cropper(this.$refs.editImage, {
+        viewMode: 1,
+        checkCrossOrigin: false,
+        zoomable: false,
+        minCropBoxWidth: 10,
+        minCropBoxHeight: 10,
+        preview: _this.$refs.CodeAreaSpinImg,
+        ready() {
+          _this.cropper.setData(defCodeArea);
+          _this.$emit("on-ready");
+        }
+      });
+    },
+    confirm() {
+      const codeArea = {
+        ...this.cropper.getData()
+      };
+      this.$emit("confirm", codeArea);
+    },
+    cancel() {
+      this.$emit("cancel");
+    }
+  },
+  beforeDestroy() {
+    if (this.cropper) {
+      this.cropper.destroy();
+      this.cropper = false;
+    }
+  }
+};
+</script>