zhangjie 11 mesiacov pred
rodič
commit
299fc17425

+ 7 - 3
src/components/ImageContain.vue

@@ -41,12 +41,12 @@
       <i class="el-icon-loading"></i>
     </div>
     <!-- action -->
-    <div v-if="showAction" :class="[`${prefixCls}-action`]">
+    <div :class="[`${prefixCls}-action`]">
       <ul>
-        <li title="合适大小" @click.stop="toOrigin">
+        <li v-if="showAction" title="合适大小" @click.stop="toOrigin">
           <i class="el-icon-rank" />
         </li>
-        <li title="旋转" @click.stop="toRotate">
+        <li v-if="showRotate" title="旋转" @click.stop="toRotate">
           <i class="el-icon-refresh-right"></i>
         </li>
       </ul>
@@ -71,6 +71,10 @@ export default {
       type: Boolean,
       default: true,
     },
+    showRotate: {
+      type: Boolean,
+      default: true,
+    },
     showGuide: {
       type: Boolean,
       default: true,

+ 52 - 21
src/components/base/FileTypeSelect.vue

@@ -1,28 +1,37 @@
 <template>
-  <el-select
-    v-model="selected"
-    class="file-type-select"
-    :placeholder="placeholder"
-    filterable
-    :clearable="clearable"
-    :disabled="disabled"
-    @change="select"
-  >
-    <el-option
-      v-for="item in optionList"
-      :key="item.id"
-      :value="item.id"
-      :label="item.name"
+  <span>
+    <el-select
+      v-model="selected"
+      class="file-type-select"
+      :placeholder="placeholder"
+      filterable
+      :clearable="clearable"
+      :disabled="disabled"
+      @change="select"
     >
-    </el-option>
-  </el-select>
+      <el-option
+        v-for="item in optionList"
+        :key="item.id"
+        :value="item.id"
+        :label="item.name"
+      >
+      </el-option>
+    </el-select>
+    <!-- ModifyFileType -->
+    <modify-file-type
+      ref="ModifyFileType"
+      @modified="fileTypeModified"
+    ></modify-file-type>
+  </span>
 </template>
 
 <script>
 import { fileTypeQuery } from "../../modules/client/api";
+import ModifyFileType from "./ModifyFileType.vue";
 
 export default {
   name: "file-type-select",
+  components: { ModifyFileType },
   props: {
     value: { type: [Number, String], default: "" },
     placeholder: { type: String, default: "请选择" },
@@ -50,14 +59,36 @@ export default {
     async search() {
       this.optionList = [];
       const res = await fileTypeQuery();
-      this.optionList = res;
+      const optionList = res || [];
+      const typeSerial = {
+        CUSTOMER: 1,
+        OTHER: 2,
+      };
+      const getSerialNo = (type) => {
+        return typeSerial[type] || 0;
+      };
+      optionList.sort((a, b) => getSerialNo(a.type) - getSerialNo(b.type));
+      this.optionList = optionList;
     },
-    select() {
+    async fileTypeModified(name) {
+      await this.search();
+      const selectOption = this.optionList.find((item) => item.name === name);
+      this.selected = selectOption.id;
+
       this.$emit("input", this.selected);
-      this.$emit(
-        "change",
-        this.optionList.find((item) => item.id === this.selected)
+      this.$emit("change", selectOption);
+    },
+    select() {
+      const selectOption = this.optionList.find(
+        (item) => item.id === this.selected
       );
+      if (selectOption.type === "OTHER") {
+        this.$refs.ModifyFileType.open();
+        return;
+      }
+
+      this.$emit("input", this.selected);
+      this.$emit("change", selectOption);
     },
   },
 };

+ 112 - 0
src/components/base/ModifyFileType.vue

@@ -0,0 +1,112 @@
+<template>
+  <el-dialog
+    class="modify-semester"
+    :visible.sync="modalIsShow"
+    :title="title"
+    top="10vh"
+    width="460px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    @open="visibleChange"
+  >
+    <el-form
+      ref="modalFormComp"
+      :model="modalForm"
+      :rules="rules"
+      label-width="100px"
+    >
+      <el-form-item prop="name" label="类型名称:">
+        <el-input
+          v-model.trim="modalForm.name"
+          placeholder="请输入类型名称"
+          clearable
+        ></el-input>
+      </el-form-item>
+    </el-form>
+    <div slot="footer">
+      <el-button type="primary" :disabled="isSubmit" @click="submit"
+        >确认</el-button
+      >
+      <el-button @click="cancel">取消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { saveFileType } from "@/modules/client/api";
+
+const initModalForm = {
+  name: "",
+  type: "CUSTOMER",
+};
+
+export default {
+  name: "modify-file-type",
+  props: {
+    instance: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  computed: {
+    isEdit() {
+      return !!this.instance.id;
+    },
+    title() {
+      return (this.isEdit ? "编辑" : "新增") + "文件类型";
+    },
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      modalForm: { ...initModalForm },
+      rules: {
+        name: [
+          {
+            required: true,
+            message: "请输入类型名称",
+            trigger: "change",
+          },
+          {
+            message: "类型名称不能超过30个字",
+            max: 30,
+            trigger: "change",
+          },
+        ],
+      },
+    };
+  },
+  methods: {
+    initData(val) {
+      this.modalForm = this.$objAssign(initModalForm, val);
+    },
+    visibleChange() {
+      this.initData(this.instance);
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    async submit() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      const res = await saveFileType(this.modalForm).catch(() => {});
+      this.isSubmit = false;
+
+      if (!res) return;
+      this.$message.success(this.title + "成功!");
+      this.$emit("modified", this.modalForm.name);
+      this.cancel();
+    },
+  },
+};
+</script>

+ 7 - 0
src/modules/client/api.js

@@ -55,3 +55,10 @@ export const getStudentInfo = (datas) => {
 export const updateStudent = (datas) => {
   return $post("/api/admin/client/student/save", datas);
 };
+
+export const saveFileType = (datas) => {
+  return $post("/api/admin/basic/filetype/save", datas);
+};
+export const fieldListQuery = () => {
+  return $postParam("/api/admin/basic/field/list", {});
+};

+ 87 - 22
src/modules/client/components/ManualBindDialog.vue

@@ -117,6 +117,23 @@
           :disabled="editDisabled"
         ></el-input>
       </el-form-item>
+
+      <!-- 自定义字段 -->
+      <template v-if="extendFields.length">
+        <el-form-item
+          v-for="field in extendFields"
+          :key="field.code"
+          :label="`${field.name}:`"
+        >
+          <el-input
+            v-model.trim="modalForm[field.code]"
+            :placeholder="`请输入${field.name}`"
+            clearable
+            :max-length="100"
+            :disabled="editDisabled"
+          ></el-input>
+        </el-form-item>
+      </template>
     </el-form>
     <div slot="footer">
       <el-button type="primary" :disabled="!canSubmit" @click="submit"
@@ -128,7 +145,7 @@
 </template>
 
 <script>
-import { getStudentInfo, updateStudent } from "../api";
+import { getStudentInfo, updateStudent, fieldListQuery } from "../api";
 
 const initModalForm = {
   id: null,
@@ -146,6 +163,7 @@ const initModalForm = {
   examRoom: "",
   score: undefined,
   remark: "",
+  extendFields: "",
 };
 
 export default {
@@ -268,6 +286,7 @@ export default {
           },
         ],
       },
+      extendFields: [],
     };
   },
   computed: {
@@ -280,27 +299,56 @@ export default {
         this.releaseStudent.studentCode === this.modalForm.studentCode
       );
     },
+    taskBaseInfo() {
+      return {
+        semesterId: this.task.semesterId,
+        examId: this.task.examId,
+        courseCode: this.task.courseCode,
+        courseName: this.task.courseName,
+      };
+    },
   },
   methods: {
-    visibleChange() {
+    async getExtendFields() {
+      const res = await fieldListQuery();
+      const fields = res || [];
+
+      this.extendFields = fields.filter(
+        (item) => !item.basicField && item.enable
+      );
+    },
+    async visibleChange() {
+      await this.getExtendFields();
+      let modalForm = {};
       if (this.datas.length === 1) {
-        this.modalForm = this.$objAssign(initModalForm, this.datas[0]);
+        modalForm = this.$objAssign(initModalForm, this.datas[0]);
         if (this.datas[0].studentName) {
-          this.releaseStudent = { ...this.modalForm };
+          this.releaseStudent = { ...modalForm };
         } else {
+          modalForm.id = null;
           this.releaseStudent = null;
         }
       } else {
-        this.modalForm = { ...initModalForm };
         this.releaseStudent = null;
+        modalForm = { ...initModalForm };
       }
 
-      if (!this.modalForm.studentName) this.modalForm.id = null;
+      modalForm = this.$objAssign(modalForm, this.taskBaseInfo);
 
-      this.modalForm.semesterId = this.task.semesterId;
-      this.modalForm.examId = this.task.examId;
-      this.modalForm.courseCode = this.task.courseCode;
-      this.modalForm.courseName = this.task.courseName;
+      if (this.extendFields.length) {
+        const extendFieldsData = this.releaseStudent?.extendFields
+          ? JSON.parse(this.releaseStudent?.extendFields)
+          : [];
+        const extendFieldDict = {};
+        extendFieldsData.forEach((field) => {
+          extendFieldDict[field.code] = field.value;
+        });
+        this.extendFields.forEach((field) => {
+          modalForm[field.code] = extendFieldDict[field.code] || "";
+        });
+      }
+
+      this.modalForm = modalForm;
 
       this.$nextTick(() => {
         this.$refs.modalFormComp.clearValidate();
@@ -332,21 +380,31 @@ export default {
         courseCode: this.task.courseCode,
         studentCode: this.modalForm.studentCode,
       }).catch(() => {});
+      let modalForm = {};
       if (res) {
         this.releaseStudent = res;
-        this.modalForm = this.$objAssign(this.modalForm, res);
+        modalForm = this.$objAssign(initModalForm, res);
       } else {
         this.releaseStudent = null;
-        this.modalForm = {
-          ...initModalForm,
-          studentCode: this.modalForm.studentCode,
-        };
-        this.modalForm.semesterId = this.task.semesterId;
-        this.modalForm.examId = this.task.examId;
-        this.modalForm.courseCode = this.task.courseCode;
-        this.modalForm.courseName = this.task.courseName;
+        modalForm = this.$objAssign(initModalForm, this.taskBaseInfo);
+        modalForm.studentCode = this.modalForm.studentCode;
+      }
+
+      if (this.extendFields.length) {
+        const extendFieldsData = this.releaseStudent?.extendFields
+          ? JSON.parse(this.releaseStudent?.extendFields)
+          : [];
+        const extendFieldDict = {};
+        extendFieldsData.forEach((field) => {
+          extendFieldDict[field.code] = field.value;
+        });
+        this.extendFields.forEach((field) => {
+          modalForm[field.code] = extendFieldDict[field.code] || "";
+        });
       }
 
+      this.modalForm = modalForm;
+
       this.$nextTick(() => {
         this.$refs.modalFormComp.clearValidate();
       });
@@ -355,14 +413,21 @@ export default {
       const valid = await this.$refs.modalFormComp.validate().catch(() => {});
       if (!valid) return;
 
+      const datas = this.$objAssign(initModalForm, this.modalForm);
       if (!this.editDisabled) {
         // 新创建的考生,先保存
-        const res = await updateStudent(this.modalForm).catch(() => {});
+        if (this.extendFields.length) {
+          const extendFieldsData = this.extendFields.map((field) => {
+            return { ...field, value: this.modalForm[field.code] };
+          });
+          datas.extendFields = JSON.stringify(extendFieldsData);
+        }
+        const res = await updateStudent(datas).catch(() => {});
         if (!res) return;
-        this.modalForm.id = res;
+        datas.id = res.id;
       }
 
-      this.$emit("confirm", this.modalForm);
+      this.$emit("confirm", datas);
       this.cancel();
     },
   },

+ 11 - 7
src/modules/client/components/ScanResultTable.vue

@@ -2,7 +2,7 @@
   <el-table
     class="scan-result-table"
     :data="datas"
-    @row-dblclick="studentClickHandle"
+    @cell-click="studentClickHandle"
   >
     <el-table-column type="expand" class-name="td-expand" width="22">
       <template slot-scope="scope">
@@ -10,7 +10,7 @@
           :data="scope.row.papers"
           :show-header="false"
           class="scan-result-expand-table"
-          @row-dblclick="paperClickHandle"
+          @cell-click="paperClickHandle"
         >
           <el-table-column width="22" class-name="td-checkbox" align="center">
             <template slot-scope="subScope">
@@ -20,7 +20,7 @@
               ></el-checkbox>
             </template>
           </el-table-column>
-          <el-table-column label="文件名">
+          <el-table-column label="文件名" prop="filename">
             <template slot-scope="subScope">
               第{{ subScope.$index + 1 }}张
             </template>
@@ -36,7 +36,7 @@
                 class="btn-danger"
                 type="text"
                 icon="el-icon-error"
-                @click="toDeletePaper(scope.row, subScope.row)"
+                @click.stop="toDeletePaper(scope.row, subScope.row)"
               >
               </el-button>
             </template>
@@ -77,7 +77,7 @@
         <el-button
           class="btn-danger"
           type="text"
-          @click="toDeleteUser(scope.row)"
+          @click.stop="toDeleteUser(scope.row)"
         >
           删除
         </el-button>
@@ -199,7 +199,9 @@ export default {
       this.updateSelectList();
       this.$emit("delete-paper", selectedFiles);
     },
-    studentClickHandle(row) {
+    studentClickHandle(row, column) {
+      if (column.property !== "studentCode") return;
+
       const curPapers = row.papers
         .map((item) => [item.frontOriginImgPath, item.versoOriginImgPath])
         .flat();
@@ -209,7 +211,9 @@ export default {
         curPapers,
       });
     },
-    paperClickHandle(paper) {
+    paperClickHandle(paper, column) {
+      if (column.property !== "filename") return;
+
       const row = this.datas.find((item) =>
         item.papers.some((elem) => elem.id === paper.id)
       );

+ 45 - 31
src/modules/client/views/ScanPaper.vue

@@ -66,7 +66,11 @@
           ></scan-result-table>
         </div>
         <div v-if="isNormalTab" class="scan-result-foot">
-          共{{ studentCount }}人,{{ paperCount }}张图片
+          <span>共</span>
+          <span class="color-primary mlr-1">{{ studentCount }}</span>
+          <span>人,</span>
+          <span class="color-primary mr-1">{{ paperCount }}</span>
+          <span>张图片</span>
         </div>
       </div>
       <div class="scan-content">
@@ -74,6 +78,7 @@
           v-if="curPaper && curPaper.url"
           ref="ImageContain"
           :image="curPaper"
+          :show-rotate="false"
           @on-prev="toPrevPaper"
           @on-next="toNextPaper"
         ></image-contain>
@@ -211,9 +216,9 @@ export default {
       this.scanStageList = [];
       this.errorStageList = [];
       this.scanStatus = "INIT";
-      this.curPagePapers = [];
-      this.curPagePaperIndex = 0;
-      this.curPagePaper = { url: "" };
+      this.curPapers = [];
+      this.curPaperIndex = 0;
+      this.curPaper = { url: "" };
       this.scanCount = 0;
     },
     clearFiles() {
@@ -358,30 +363,32 @@ export default {
           continue;
         }
 
-        const res = await getStudentInfo({
-          examId: this.task.examId,
-          courseCode: this.task.courseCode,
-          studentCode: fileInfo.studentCode,
-        }).catch(() => {});
-        if (res) {
-          fileInfo.studentName = res.studentName;
-          this.scanStageList.push({
-            id: res.id,
-            studentCode: res.studentCode,
-            studentName: res.studentName,
-            courseCode: res.courseCode,
-            courseName: res.courseName,
-            teacher: res.teacher,
-            teachClass: res.teachClass,
-            collegeName: res.collegeName,
-            majorName: res.majorName,
-            className: res.className,
-            score: res.score,
-            remark: res.remark,
-            select: false,
-            papers: [fileInfo],
-          });
-          continue;
+        if (fileInfo.studentCode) {
+          const res = await getStudentInfo({
+            examId: this.task.examId,
+            courseCode: this.task.courseCode,
+            studentCode: fileInfo.studentCode,
+          }).catch(() => {});
+          if (res) {
+            fileInfo.studentName = res.studentName;
+            this.scanStageList.push({
+              id: res.id,
+              studentCode: res.studentCode,
+              studentName: res.studentName,
+              courseCode: res.courseCode,
+              courseName: res.courseName,
+              teacher: res.teacher,
+              teachClass: res.teachClass,
+              collegeName: res.collegeName,
+              majorName: res.majorName,
+              className: res.className,
+              score: res.score,
+              remark: res.remark,
+              select: false,
+              papers: [fileInfo],
+            });
+            continue;
+          }
         }
 
         const errorStudentStage = this.errorStageList.find(
@@ -399,6 +406,13 @@ export default {
           papers: [fileInfo],
         });
       }
+
+      if (imageList.length) {
+        const lastImg = imageList.pop();
+        this.curPapers = [lastImg.frontFile, lastImg.versoFile];
+        this.curPaperIndex = 0;
+        this.setCurPaper(0);
+      }
     },
     async saveScanItem(fileInfo) {
       const ouputImageList = saveOutputImage(
@@ -607,9 +621,9 @@ export default {
         this.errorStageList = [];
       }
 
-      this.curPagePapers = [];
-      this.curPagePaperIndex = 0;
-      this.curPagePaper = { url: "" };
+      this.curPapers = [];
+      this.curPaperIndex = 0;
+      this.curPaper = { url: "" };
 
       logger.info(`99数据清空`);
       this.$message.success("数据已清空!");

+ 8 - 1
vue.config.js

@@ -12,7 +12,14 @@ module.exports = defineConfig({
   },
   pluginOptions: {
     electronBuilder: {
-      externals: ["crypto-js", "log4js", "gm", "cropperjs", "p-queue"],
+      externals: [
+        "crypto-js",
+        "log4js",
+        "gm",
+        "cropperjs",
+        "p-queue",
+        "js-md5",
+      ],
       builderOptions: {
         extraFiles: [
           "extra/scan/**",