Browse Source

feat: 接口调试

zhangjie 10 months ago
parent
commit
ae81573ede

+ 14 - 6
src/assets/styles/common-comp.scss

@@ -182,22 +182,30 @@
 // import-file
 .cc-import-file {
   &-tips {
-    max-height: 200px;
+    min-height: 20px;
+    max-height: 160px;
     line-height: 20px;
     overflow: hidden;
   }
   &-footer {
-    padding: 10px 0;
+    padding: 0 0 10px 0;
     font-size: 14px;
-    a {
+    a,
+    .el-button--text {
       color: $--color-primary;
       &:hover {
-        opacity: 0.9;
+        opacity: 0.8;
       }
     }
   }
 
-  .el-dialog__body {
-    padding-top: 10px;
+  .el-dialog {
+    &__body {
+      background-color: $--color-background;
+      padding: 20px;
+    }
+    &__footer {
+      padding: 15px 20px;
+    }
   }
 }

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

@@ -457,3 +457,48 @@
     }
   }
 }
+// label-edit
+.label-edit {
+  min-height: 60px;
+  .label-item {
+    display: inline-block;
+    vertical-align: top;
+    border: 1px solid $--color-text-gray-6;
+    border-radius: $--border-radius;
+    padding: 5px 40px 5px 10px;
+    position: relative;
+    margin: 0 10px 10px 0;
+    line-height: 21px;
+  }
+  .label-item-content {
+    margin: 0;
+    line-height: 21px;
+    vertical-align: middle;
+  }
+  .label-item-delete {
+    position: absolute;
+    right: 10px;
+    top: 50%;
+    transform: translateY(-50%);
+    z-index: 99;
+    font-size: 16px;
+    color: $--color-text-gray-5;
+    cursor: pointer;
+    &:hover {
+      color: $--color-danger;
+    }
+  }
+  .label-add {
+    display: inline-block;
+    vertical-align: top;
+    border: 1px solid $--color-text-gray-6;
+    border-radius: $--border-radius;
+    padding: 0 10px;
+    color: $--color-text-gray-2;
+    cursor: pointer;
+    &:hover {
+      border-color: $--color-primary;
+      color: $--color-primary;
+    }
+  }
+}

+ 7 - 0
src/components/ImportFile.vue

@@ -14,6 +14,10 @@
       模板下载:
       <a :href="downloadUrl" :download="dfilename">{{ dfilename }}</a>
     </div>
+    <div :class="[`${prefixCls}-footer`]" v-else-if="downloadHandle">
+      模板下载:
+      <el-button type="text" @click="downloadHandle">{{ dfilename }}</el-button>
+    </div>
     <slot></slot>
     <div :class="[`${prefixCls}-body`]">
       <el-upload
@@ -86,6 +90,9 @@ export default {
       type: String,
       default: "",
     },
+    downloadHandle: {
+      type: Function,
+    },
     downloadFilename: String,
     format: {
       type: Array,

+ 1 - 0
src/constants/enumerate.js

@@ -37,6 +37,7 @@ export const DATA_TASK_TYPE = {
   ORG_IMPORT: "导入组织架构",
   USER_IMPORT: "导入用户",
   STUDENT_IMPORT: "导入学生",
+  EXAM_STUDENT_IMPORT: "导入考生",
   PAPER_SCAN_TASK_IMPORT: "导入任务",
   PAPER_DOWNLOAD: "批量下载",
 };

+ 19 - 7
src/modules/base/api.js

@@ -157,6 +157,18 @@ export const updateStudent = (datas) => {
 export const studentCascadeQuery = (datas) => {
   return $postParam("/api/admin/common/basic_student/condition", datas);
 };
+export const buildStudentPdf = () => {
+  return $postParam("/api/admin/exam/student/pdf/generate", {});
+};
+export const exportStudentTemplate = () => {
+  return $post(
+    "/api/admin/exam/student/download_template",
+    {},
+    {
+      responseType: "blob",
+    }
+  );
+};
 
 // semester-manage
 export const semesterListQuery = (datas) => {
@@ -200,29 +212,29 @@ export const updateSystemSetting = (datas, isSystem) => {
 
 // field-manage
 export const fieldListQuery = () => {
-  return $postParam("/api/admin/basic/exam/page", {});
+  return $postParam("/api/admin/basic/field/list", {});
 };
 export const saveFieldList = (datas) => {
-  return $post("/api/admin/basic/exam/page", datas);
+  return $post("/api/admin/basic/field/save", datas);
 };
 
 // file-type-manage
 export const fileTypeListQuery = (datas) => {
-  return $postParam("/api/admin/basic/exam/page", datas);
+  return $postParam("/api/admin/basic/filetype/page", datas);
 };
 export const saveFileType = (datas) => {
-  return $post("/api/admin/basic/exam/page", datas);
+  return $post("/api/admin/basic/filetype/save", datas);
 };
 export const deleteFileType = (id) => {
-  return $postParam("/api/admin/basic/semester/delete", { id });
+  return $postParam("/api/admin/basic/filetype/delete", { id });
 };
 
 // scan statistics
 export const scanStatisticsListQuery = (datas) => {
-  return $postParam("/api/admin/basic/exam/page", datas);
+  return $postParam("/api/admin/basic/scan/stat/page", datas);
 };
 export const exportScanStatistics = (datas) => {
-  return $post("/api/admin/auth/export/device/info", datas, {
+  return $post("/api/admin/basic/scan/stat/export", datas, {
     responseType: "blob",
   });
 };

+ 8 - 1
src/modules/base/components/DataTaskDialog.vue

@@ -63,6 +63,14 @@
                 @click="toDonwload(scope.row.resultFile)"
                 >下载文件</el-button
               >
+              <el-button
+                v-if="scope.row.errorFilePath"
+                class="btn-primary"
+                type="text"
+                :disabled="loading"
+                @click="toDonwloadErrorFile(scope.row.errorFilePath)"
+                >下载错误文件</el-button
+              >
             </template>
           </el-table-column>
         </el-table>
@@ -147,7 +155,6 @@ export default {
       };
       const data = await dataTaskList(datas);
       this.dataList = data.records.map((item) => {
-        item.reportFile = item.txtFilePath;
         item.resultFile = item.importFilePath || item.exportFilePath || "";
         return item;
       });

+ 37 - 1
src/modules/base/components/ModifyField.vue

@@ -42,11 +42,21 @@
 const initModalForm = {
   name: "",
   code: "",
-  enable: false,
+  enable: false, // 是否选择
+  disabled: false, // 是否禁用
+  basicField: false, // 基础字段
 };
 
 export default {
   name: "modify-field",
+  props: {
+    sources: {
+      type: Array,
+      default() {
+        return [];
+      },
+    },
+  },
   data() {
     return {
       modalIsShow: false,
@@ -63,6 +73,15 @@ export default {
             message: "字段名称不能超过20",
             trigger: "change",
           },
+          {
+            validator: (rule, value, callback) => {
+              if (this.names.includes(value)) {
+                return callback(new Error(`字段名称重复`));
+              }
+              return callback();
+            },
+            trigger: "change",
+          },
         ],
         code: [
           {
@@ -71,10 +90,27 @@ export default {
             message: "字段变量名只能由字母组成,长度在3-30之间",
             trigger: "change",
           },
+          {
+            validator: (rule, value, callback) => {
+              if (this.codes.includes(value)) {
+                return callback(new Error(`字段变量名重复`));
+              }
+              return callback();
+            },
+            trigger: "change",
+          },
         ],
       },
     };
   },
+  computed: {
+    names() {
+      return this.sources.map((item) => item.name);
+    },
+    codes() {
+      return this.sources.map((item) => item.code);
+    },
+  },
   methods: {
     visibleChange() {
       this.modalForm = { ...initModalForm };

+ 5 - 5
src/modules/base/components/ModifyFileType.vue

@@ -4,7 +4,7 @@
     :visible.sync="modalIsShow"
     :title="title"
     top="10vh"
-    width="700px"
+    width="460px"
     :close-on-click-modal="false"
     :close-on-press-escape="false"
     append-to-body
@@ -16,9 +16,9 @@
       :rules="rules"
       label-width="100px"
     >
-      <el-form-item prop="evaluation" label="类型名称:">
+      <el-form-item prop="name" label="类型名称:">
         <el-input
-          v-model.trim="modalForm.evaluation"
+          v-model.trim="modalForm.name"
           placeholder="请输入类型名称"
           clearable
         ></el-input>
@@ -39,7 +39,7 @@ import { saveFileType } from "../api";
 const initModalForm = {
   id: null,
   name: "",
-  type: "CUSTOM",
+  type: "CUSTOMER",
 };
 
 export default {
@@ -66,7 +66,7 @@ export default {
       isSubmit: false,
       modalForm: { ...initModalForm },
       rules: {
-        evaluation: [
+        name: [
           {
             required: true,
             message: "请输入类型名称",

+ 1 - 1
src/modules/base/router.js

@@ -60,7 +60,7 @@ export default [
   },
   {
     path: "/base/scan-statistics",
-    name: "ScanStatistics",
+    name: "ScanStatManage",
     component: ScanStatistics,
   },
 ];

+ 20 - 16
src/modules/base/views/FieldManage.vue

@@ -49,7 +49,11 @@
     </el-form>
 
     <!-- ModifyField -->
-    <modify-field ref="ModifyField" @confirm="addField"></modify-field>
+    <modify-field
+      ref="ModifyField"
+      :sources="fieldSources"
+      @confirm="addField"
+    ></modify-field>
   </div>
 </template>
 
@@ -58,7 +62,6 @@ import ModifyField from "../components/ModifyField";
 import { fieldListQuery, saveFieldList } from "../api";
 
 const initModalForm = {
-  id: null,
   requiredFields: [],
   extendFields: [],
 };
@@ -73,6 +76,7 @@ export default {
       examRule: {},
       ruleChanged: false,
       prevModalFrom: "",
+      fieldSources: [],
     };
   },
   mounted() {
@@ -81,11 +85,10 @@ export default {
   methods: {
     async getData() {
       const res = await fieldListQuery();
-      const field = res || { ...initModalForm };
-      this.modalForm.requiredFields = JSON.parse(field.requiredFields);
-      if (this.modalForm.extendFields)
-        this.modalForm.extendFields = JSON.parse(field.extendFields);
-      this.modalForm.id = field.id;
+      const fields = res || [];
+
+      this.modalForm.requiredFields = fields.filter((item) => item.basicField);
+      this.modalForm.extendFields = fields.filter((item) => !item.basicField);
 
       this.prevModalFrom = JSON.stringify(this.modalForm);
     },
@@ -93,6 +96,10 @@ export default {
       this.ruleChanged = this.prevModalFrom !== JSON.stringify(this.modalForm);
     },
     toAddField() {
+      this.fieldSources = [
+        ...this.modalForm.requiredFields,
+        ...this.modalForm.extendFields,
+      ];
       this.$refs.ModifyField.open();
     },
     addField(field) {
@@ -107,24 +114,21 @@ export default {
       this.checkRuleChange();
     },
     async submit() {
-      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
-      if (!valid) return;
-
       if (this.isSubmit) return;
       this.isSubmit = true;
-      let datas = {
-        id: this.modalForm.id,
-        requiredFields: JSON.stringify(datas.requiredFields),
-        extendFields: JSON.stringify(datas.extendFields),
-      };
+      const datas = [
+        ...this.modalForm.requiredFields,
+        ...this.modalForm.extendFields,
+      ];
+
       const data = await saveFieldList(datas).catch(() => {});
       this.isSubmit = false;
       if (!data) return;
 
-      this.modalForm.id = data;
       this.prevModalFrom = JSON.stringify(this.modalForm);
       this.checkRuleChange();
       this.$message.success("保存成功!");
+      this.getData();
     },
   },
 };

+ 4 - 4
src/modules/base/views/FileTypeManage.vue

@@ -7,13 +7,13 @@
       </div>
     </div>
     <div class="part-box part-box-pad">
-      <el-table :data="dataList">
+      <el-table ref="TableList" :data="dataList">
         <el-table-column type="index" label="序号" width="70"></el-table-column>
-        <el-table-column prop="name" label="其他文件类型"></el-table-column>
+        <el-table-column prop="name" label="文件类型"></el-table-column>
         <el-table-column class-name="action-column" label="操作" width="100px">
           <template slot-scope="scope">
             <el-button
-              v-if="scope.row.type === 'CUSTOM'"
+              v-if="scope.row.type === 'CUSTOMER'"
               class="btn-danger"
               type="text"
               @click="toDelete(scope.row)"
@@ -86,7 +86,7 @@ export default {
     },
     async toDelete(row) {
       const confirm = await this.$confirm(
-        `确定要删除其他文件类型【${row.name}】吗?`,
+        `确定要删除文件类型【${row.name}】吗?`,
         "提示",
         {
           type: "warning",

+ 12 - 24
src/modules/base/views/ScanStatistics.vue

@@ -5,22 +5,17 @@
         <template v-if="checkPrivilege('condition', 'condition')">
           <el-form-item label="日期:">
             <el-date-picker
-              v-model="createTime"
-              type="daterange"
-              :picker-options="pickerOptions"
-              range-separator="至"
-              start-placeholder="开始日期"
-              end-placeholder="结束日期"
+              v-model="filter.scanDate"
+              type="date"
               value-format="timestamp"
-              align="right"
-              unlink-panels
+              placeholder="日期选择"
             >
             </el-date-picker>
           </el-form-item>
           <el-form-item label="扫描员:">
             <el-input
               style="width: 180px"
-              v-model.trim="filter.scanUser"
+              v-model.trim="filter.scanner"
               placeholder="扫描员"
               clearable
             ></el-input>
@@ -54,16 +49,18 @@
           width="70"
           :index="indexMethod"
         ></el-table-column>
-        <el-table-column prop="scanUserName" label="扫描员"></el-table-column>
+        <el-table-column prop="scanner" label="扫描员"></el-table-column>
         <el-table-column
-          prop="scanUserName"
+          prop="pictureCount"
           label="扫描图片(张)"
         ></el-table-column>
         <el-table-column
-          prop="scanUserName"
+          prop="studentCount"
           label="扫描学生数"
         ></el-table-column>
-        <el-table-column prop="scanUserName" label="扫描日期"></el-table-column>
+        <el-table-column prop="scanDate" label="扫描日期" width="140">
+          <span slot-scope="scope">{{ scope.row.scanDate | dateFilter }}</span>
+        </el-table-column>
       </el-table>
       <div class="part-page">
         <el-pagination
@@ -83,25 +80,20 @@
 <script>
 import { scanStatisticsListQuery, exportScanStatistics } from "../api";
 import { downloadByApi } from "@/plugins/download";
-import pickerOptions from "@/constants/datePickerOptions";
 
 export default {
   name: "scan-statistics",
   data() {
     return {
       filter: {
-        startTime: "",
-        endTime: "",
-        scanUser: "",
+        scanDate: "",
+        scanner: "",
       },
       current: 1,
       size: this.GLOBAL.pageSize,
       total: 0,
       dataList: [],
       downloading: false,
-      // date-picker
-      createTime: [],
-      pickerOptions,
     };
   },
   mounted() {
@@ -116,10 +108,6 @@ export default {
         pageNumber: this.current,
         pageSize: this.size,
       };
-      if (this.createTime) {
-        datas.startTime = this.createTime[0];
-        datas.endTime = this.createTime[1];
-      }
       const data = await scanStatisticsListQuery(datas);
       this.dataList = data.records;
       this.total = data.total;

+ 146 - 41
src/modules/base/views/StudentManage.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="student-manage">
-    <div class="part-box part-box-filter part-box-flex">
+    <div class="part-box part-box-filter">
       <el-form ref="FilterForm" label-position="left" label-width="90px" inline>
         <sec-select
           v-model="filter"
@@ -24,28 +24,53 @@
           >
         </el-form-item>
       </el-form>
-      <div class="part-box-action">
-        <el-button
-          v-if="checkPrivilege('button', 'BatchDelete')"
-          type="danger"
-          icon="el-icon-delete"
-          @click="toBatchDelete"
-          >批量删除</el-button
-        >
-        <el-button
-          v-if="checkPrivilege('button', 'import')"
-          type="success"
-          icon="el-icon-upload"
-          @click="toImportStudent"
-          >导入考生</el-button
-        >
-        <el-button
-          v-if="checkPrivilege('button', 'add')"
-          type="primary"
-          icon="el-icon-circle-plus-outline"
-          @click="toAdd"
-          >新增考生</el-button
-        >
+      <div class="box-justify" style="padding-bottom: 15px">
+        <div>
+          <el-button
+            v-if="checkPrivilege('button', 'import')"
+            type="primary"
+            icon="el-icon-s-order"
+            @click="toDataTask('EXAM_STUDENT_IMPORT')"
+            >导入结果</el-button
+          >
+          <el-button
+            v-if="checkPrivilege('button', 'import')"
+            type="primary"
+            icon="el-icon-s-order"
+            @click="toDataTask('EXAM_STUDENT_IMPORT')"
+            >pdf生成结果</el-button
+          >
+        </div>
+        <div>
+          <el-button
+            v-if="checkPrivilege('button', 'BatchDelete')"
+            type="danger"
+            icon="el-icon-delete"
+            @click="toBatchDelete"
+            >批量删除</el-button
+          >
+          <el-button
+            v-if="checkPrivilege('button', 'import')"
+            type="success"
+            icon="el-icon-upload"
+            @click="toImportStudent"
+            >导入考生</el-button
+          >
+          <el-button
+            v-if="checkPrivilege('button', 'import')"
+            type="primary"
+            icon="el-icon-document"
+            @click="toBuildPdf"
+            >生成PDF</el-button
+          >
+          <el-button
+            v-if="checkPrivilege('button', 'add')"
+            type="primary"
+            icon="el-icon-circle-plus-outline"
+            @click="toAdd"
+            >新增考生</el-button
+          >
+        </div>
       </div>
     </div>
     <div class="part-box part-box-pad">
@@ -59,18 +84,62 @@
           width="55"
           align="center"
         ></el-table-column>
-        <el-table-column prop="courseName" label="课程名称"></el-table-column>
-        <el-table-column prop="courseCode" label="课程代码"></el-table-column>
-        <el-table-column prop="studentName" label="姓名"></el-table-column>
-        <el-table-column prop="studentCode" label="学号"></el-table-column>
-        <el-table-column prop="collegeName" label="学院"></el-table-column>
-        <el-table-column prop="majorName" label="专业"></el-table-column>
-        <el-table-column prop="className" label="班级"></el-table-column>
-        <el-table-column prop="teacher" label="任课老师"></el-table-column>
-        <el-table-column prop="teachClass" label="教学班"></el-table-column>
-        <el-table-column prop="score" label="成绩"></el-table-column>
-        <el-table-column prop="examRoom" label="考场"></el-table-column>
-        <el-table-column prop="remark" label="备注"></el-table-column>
+        <el-table-column
+          prop="courseName"
+          label="课程名称"
+          min-width="200"
+        ></el-table-column>
+        <el-table-column
+          prop="courseCode"
+          label="课程代码"
+          width="120"
+        ></el-table-column>
+        <el-table-column
+          prop="studentName"
+          label="姓名"
+          min-width="140"
+        ></el-table-column>
+        <el-table-column
+          prop="studentCode"
+          label="学号"
+          width="140"
+        ></el-table-column>
+        <el-table-column
+          prop="collegeName"
+          label="学院"
+          min-width="120"
+        ></el-table-column>
+        <el-table-column
+          prop="majorName"
+          label="专业"
+          min-width="120"
+        ></el-table-column>
+        <el-table-column
+          prop="className"
+          label="班级"
+          min-width="120"
+        ></el-table-column>
+        <el-table-column
+          prop="teacher"
+          label="任课老师"
+          min-width="140"
+        ></el-table-column>
+        <el-table-column
+          prop="teachClass"
+          label="教学班"
+          min-width="120"
+        ></el-table-column>
+        <el-table-column prop="score" label="成绩" width="60"></el-table-column>
+        <el-table-column
+          prop="examRoom"
+          label="考场"
+          min-width="140"
+        ></el-table-column>
+        <el-table-column
+          prop="remark"
+          label="备注"
+          width="160"
+        ></el-table-column>
         <el-table-column prop="createTime" label="创建时间" width="170">
           <span slot-scope="scope">{{
             scope.row.createTime | timestampFilter
@@ -129,22 +198,35 @@
         examId: filter.examId,
       }"
       :format="['xls', 'xlsx']"
-      :download-url="downloadUrl"
+      :download-handle="downloadTemplate"
       :download-filename="dfilename"
       :auto-upload="false"
       @upload-success="uploadSuccess"
-    ></import-file>
+    >
+    </import-file>
+    <!-- DataTaskDialog -->
+    <data-task-dialog
+      ref="DataTaskDialog"
+      :task-type="taskType"
+    ></data-task-dialog>
   </div>
 </template>
 
 <script>
-import { studentListQuery, deleteStudent } from "../api";
+import {
+  studentListQuery,
+  deleteStudent,
+  buildStudentPdf,
+  exportStudentTemplate,
+} from "../api";
 import ModifyStudent from "../components/ModifyStudent.vue";
 import ImportFile from "../../../components/ImportFile.vue";
+import DataTaskDialog from "../components/DataTaskDialog.vue";
+import { downloadByApi } from "@/plugins/download";
 
 export default {
   name: "student-manage",
-  components: { ModifyStudent, ImportFile },
+  components: { ModifyStudent, ImportFile, DataTaskDialog },
   data() {
     return {
       filter: {
@@ -159,9 +241,10 @@ export default {
       dataList: [],
       curRow: {},
       multipleSelection: [],
+      taskType: "",
       // import
+      tempDownloading: false,
       uploadUrl: "/api/admin/exam/student/import",
-      downloadUrl: "/temps/studentTemplate.xlsx",
       dfilename: "考生导入模板.xlsx",
     };
   },
@@ -230,9 +313,31 @@ export default {
         })
         .catch(() => {});
     },
+    async toBuildPdf() {
+      await buildStudentPdf();
+      this.$message.success("任务已经提交,后台正在生成pdf!");
+    },
+    toDataTask(taskType) {
+      this.taskType = taskType;
+      this.$refs.DataTaskDialog.open();
+    },
     // import
+    async downloadTemplate() {
+      if (this.tempDownloading) return;
+      this.tempDownloading = true;
+
+      const res = await downloadByApi(() => {
+        return exportStudentTemplate();
+      }).catch((e) => {
+        this.$message.error(e || "下载失败,请重新尝试!");
+      });
+      this.tempDownloading = false;
+
+      if (!res) return;
+      this.$message.success("下载成功!");
+    },
     uploadSuccess() {
-      this.$message.success("导入成功");
+      this.$message.success("文件上传成功,后台正在导入!");
       this.getList();
     },
   },

+ 1 - 0
src/modules/login/views/Login.vue

@@ -30,6 +30,7 @@
               type="password"
               v-model.trim="loginModel.password"
               placeholder="请输入密码"
+              show-password
               clearable
             >
               <i class="icon icon-password" slot="prefix"></i>

+ 1 - 1
src/modules/record/api.js

@@ -13,7 +13,7 @@ export const clearScanTaskData = (paperScanTaskId) => {
   });
 };
 export const clearStudentScanData = (datas) => {
-  return $postParam("/api/admin/paper/scan_task/clear_data", datas);
+  return $postParam("/api/admin/paper/scan_task/clear_single_data", datas);
 };
 export const unbindScanTaskUser = (paperScanTaskId) => {
   return $postParam("/api/admin/paper/scan_task/unbind_scanner", {

+ 0 - 0
src/modules/record/views/PictureManage.vue


+ 3 - 0
src/plugins/filters.js

@@ -29,6 +29,9 @@ Vue.filter("timestampFilter", function (val) {
     ? formatDate("YYYY-MM-DD HH:mm:ss", new Date(val * 1))
     : DEFAULT_FIELD;
 });
+Vue.filter("dateFilter", function (val) {
+  return val ? formatDate("YYYY-MM-DD", new Date(val * 1)) : DEFAULT_FIELD;
+});
 Vue.filter("orgTypeFilter", function (val) {
   return ORG_TYPE[val] || DEFAULT_FIELD;
 });