Pārlūkot izejas kodu

试卷结构生成题卡新增整卷连续配置

zhangjie 2 gadi atpakaļ
vecāks
revīzija
486b7231ad

+ 250 - 0
src/modules/card/components/BatchBuildCardDialog.vue

@@ -0,0 +1,250 @@
+<template>
+  <el-dialog
+    custom-class="side-dialog"
+    :visible.sync="modalIsShow"
+    title="批量生成题卡"
+    width="520px"
+    :modal="false"
+    append-to-body
+    @open="visibleChange"
+  >
+    <el-upload
+      ref="UploadComp"
+      :action="uploadUrl"
+      :headers="headers"
+      :max-size="maxSize"
+      :accept="accept"
+      :format="format"
+      :data="uploadDataDict"
+      :on-error="handleError"
+      :on-success="handleSuccess"
+      :on-change="fileChange"
+      :http-request="upload"
+      :show-file-list="false"
+      :disabled="loading"
+      :auto-upload="false"
+    >
+      <el-button
+        slot="trigger"
+        size="small"
+        type="primary"
+        icon="icon icon-search-white"
+        :disabled="loading"
+      >
+        选择文件
+      </el-button>
+      <el-button
+        size="small"
+        type="primary"
+        icon="icon icon-save-white"
+        :loading="loading"
+        :disabled="!fileValid"
+        @click="submitUpload"
+      >
+        确认上传
+      </el-button>
+      <el-button
+        size="small"
+        type="primary"
+        icon="icon icon-delete-white"
+        :disabled="loading"
+        @click="removeFile"
+      >
+        清空文件
+      </el-button>
+      <el-button
+        v-if="templateUrl"
+        size="small"
+        type="primary"
+        icon="icon icon-export-white"
+        @click="exportFile"
+      >
+        下载模板
+      </el-button>
+    </el-upload>
+    <p v-if="filename" class="tips-info">{{ filename }}</p>
+    <p v-if="!res.success" class="tips-info tips-error">{{ res.message }}</p>
+    <el-form label-width="80px">
+      <el-form-item label="小题序号">
+        <el-radio-group v-model="seqMode" class="input">
+          <el-radio label="MODE3">按大题独立</el-radio>
+          <el-radio label="MODE5">整卷连续</el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+  </el-dialog>
+</template>
+
+<script>
+import { fileMD5 } from "@/plugins/md5";
+import { $httpWithMsg } from "@/plugins/axios";
+
+export default {
+  name: "BatchBuildCardDialog",
+  props: {
+    format: {
+      type: Array,
+      default() {
+        return ["xlsx", "xls"];
+      },
+    },
+    accept: {
+      type: String,
+      default: null,
+    },
+    uploadUrl: {
+      type: String,
+      required: true,
+    },
+    uploadData: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+    maxSize: {
+      type: Number,
+      default: 20 * 1024 * 1024,
+    },
+    addFilenameParam: {
+      type: String,
+      default: "filename",
+    },
+    templateUrl: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      headers: {
+        md5: "",
+      },
+      res: {},
+      loading: false,
+      uploadDataDict: {},
+      filename: "",
+      fileValid: false,
+      seqMode: "MODE3",
+    };
+  },
+  methods: {
+    visibleChange() {
+      this.res = {};
+      this.loading = false;
+      this.uploadDataDict = {};
+      this.filename = "";
+      this.fileValid = false;
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    checkFileFormat(fileType) {
+      const _file_format = fileType.split(".").pop().toLowerCase();
+      return this.format.length
+        ? this.format.some((item) => item.toLowerCase() === _file_format)
+        : true;
+    },
+    fileChange(fileObj) {
+      if (fileObj.status === "ready") {
+        this.handleBeforeUpload(fileObj.raw).catch(() => {});
+      }
+    },
+    async handleBeforeUpload(file) {
+      this.res = {};
+      this.filename = file.name;
+      this.uploadDataDict = {
+        ...this.uploadData,
+      };
+      this.uploadDataDict[this.addFilenameParam] = file.name;
+
+      if (!this.checkFileFormat(file.name)) {
+        this.handleFormatError();
+        this.fileValid = false;
+        return Promise.reject();
+      }
+
+      if (file.size > this.maxSize) {
+        this.handleExceededSize();
+        this.fileValid = false;
+        return Promise.reject();
+      }
+
+      const md5 = await fileMD5(file);
+      this.headers["md5"] = md5;
+      this.fileValid = true;
+
+      return true;
+    },
+    upload(options) {
+      if (!options.file) return Promise.reject("文件丢失");
+
+      let formData = new FormData();
+      Object.entries(options.data).forEach(([k, v]) => {
+        formData.append(k, v);
+      });
+      formData.append("file", options.file);
+
+      return $httpWithMsg.post(options.action, formData, {
+        headers: options.headers,
+      });
+    },
+    handleError(error) {
+      this.loading = false;
+      this.res = {
+        success: false,
+        message: error.response.data.desc,
+      };
+      this.uploadDataDict = {};
+      this.filename = "";
+      this.fileValid = false;
+      this.$refs.UploadComp.clearFiles();
+    },
+    handleSuccess(res) {
+      this.loading = false;
+      this.res = {
+        success: true,
+        message: "导入成功!",
+      };
+      this.cancel();
+      this.$emit("uploaded", { ...res, seqMode: this.seqMode });
+    },
+    handleFormatError() {
+      const content = "只支持文件格式为" + this.format.join("/");
+      this.res = {
+        success: false,
+        message: content,
+      };
+    },
+    handleExceededSize() {
+      const content =
+        "文件大小不能超过" + Math.floor(this.maxSize / (1024 * 1024)) + "M";
+      this.res = {
+        success: false,
+        message: content,
+      };
+    },
+    // action
+    submitUpload() {
+      if (this.loading) return;
+      this.$refs.UploadComp.submit();
+      this.loading = true;
+    },
+    removeFile() {
+      if (this.loading) return;
+      this.uploadDataDict = {};
+      this.filename = "";
+      this.fileValid = false;
+      this.res = {};
+      this.$refs.UploadComp.clearFiles();
+    },
+    exportFile() {
+      window.location.href = this.templateUrl;
+    },
+  },
+};
+</script>

+ 13 - 15
src/modules/card/views/CardEdit.vue

@@ -28,10 +28,7 @@ import {
   saveCardByPaper,
 } from "../api";
 import CardDesign from "../components/CardDesign";
-import {
-  getPaperJsonSimpleStructInfo,
-  getPaperStructSimpleStructInfo,
-} from "../autoBuild/paperStruct";
+import { getPaperJsonSimpleStructInfo } from "../autoBuild/paperStruct";
 
 export default {
   name: "CardEdit",
@@ -120,17 +117,18 @@ export default {
       }
 
       this.cardContent = JSON.parse(detDataRes.data.content);
-      const curPaperSimpleStruct = getPaperStructSimpleStructInfo(
-        detDataRes.data.structure || { details: [] }
-      );
-      if (curPaperSimpleStruct !== this.cardContent.paperSimpleStruct) {
-        this.$message.warning("试卷结构有变化,已重新生成题卡");
-        this.cardContent = {
-          pages: [],
-          cardConfig: this.cardContent.cardConfig,
-          paperSimpleStruct: curPaperSimpleStruct,
-        };
-      }
+      // 通过单纯试卷结构生成的题卡,试卷结构是不会变得,这里暂时不做自动更新处理。
+      // const curPaperSimpleStruct = getPaperStructSimpleStructInfo(
+      //   detDataRes.data.structure || { details: [] }
+      // );
+      // if (curPaperSimpleStruct !== this.cardContent.paperSimpleStruct) {
+      //   this.$message.warning("试卷结构有变化,已重新生成题卡");
+      //   this.cardContent = {
+      //     pages: [],
+      //     cardConfig: this.cardContent.cardConfig,
+      //     paperSimpleStruct: curPaperSimpleStruct,
+      //   };
+      // }
     },
     async initCardContent() {
       const cardConfig = await this.getCardConfig();

+ 18 - 8
src/modules/card/views/CardManage.vue

@@ -55,7 +55,7 @@
             plain
             icon="icon icon-edit"
             @click="toExportPaperStruct"
-            >导入试卷结构
+            >批量生成题卡
           </el-button>
           <el-button
             type="danger"
@@ -212,14 +212,14 @@
       </div>
     </div>
 
-    <!-- ImportFileDialog -->
-    <import-file-dialog
-      ref="ImportFileDialog"
+    <!-- BatchBuildCardDialog -->
+    <batch-build-card-dialog
+      ref="BatchBuildCardDialog"
       dialog-title="导入试卷结构"
       :upload-url="uploadUrl"
       :template-url="templateUrl"
       @uploaded="batchAutoBuildCard"
-    ></import-file-dialog>
+    ></batch-build-card-dialog>
     <!-- ModifyCard -->
     <modify-card
       ref="ModifyCard"
@@ -248,7 +248,8 @@ import {
   cardDownloadApi,
   cardPackageDownloadApi,
 } from "../api";
-import ImportFileDialog from "@/components/ImportFileDialog.vue";
+// import ImportFileDialog from "@/components/ImportFileDialog.vue";
+import BatchBuildCardDialog from "../components/BatchBuildCardDialog.vue";
 import ModifyCard from "../components/ModifyCard.vue";
 import ProgressDialog from "@/components/ProgressDialog.vue";
 import { QUESTION_API } from "@/constants/constants.js";
@@ -258,7 +259,7 @@ import { downloadByApi } from "@/plugins/download";
 
 export default {
   name: "CardManage",
-  components: { ImportFileDialog, ModifyCard, ProgressDialog },
+  components: { BatchBuildCardDialog, ModifyCard, ProgressDialog },
   data() {
     return {
       courseLoading4Search: false,
@@ -361,7 +362,7 @@ export default {
       this.$router.push({ name: "CardHeadManage" });
     },
     toExportPaperStruct() {
-      this.$refs.ImportFileDialog.open();
+      this.$refs.BatchBuildCardDialog.open();
     },
     async batchAutoBuildCard(resData) {
       if (!resData.data || !resData.data.length) return;
@@ -370,8 +371,17 @@ export default {
       this.taskList = [];
 
       const cardConfig = await this.getCardConfig();
+      const subnumModeIsPackage = resData.seqMode === "MODE5";
 
       this.taskList = resData.data.map((item) => {
+        if (subnumModeIsPackage) {
+          let serialNo = 1;
+          item.structure.details.forEach((detail) => {
+            detail.units.forEach((question) => {
+              question.number = serialNo++;
+            });
+          });
+        }
         return {
           cardId: item.id,
           cardConfig,