Jelajahi Sumber

试题上传

zhangjie 2 tahun lalu
induk
melakukan
011204e2d4

+ 244 - 0
src/components/ImportFile.vue

@@ -0,0 +1,244 @@
+<template>
+  <div class="import-file">
+    <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>
+  </div>
+</template>
+
+<script>
+import { fileMD5 } from "@/plugins/md5";
+import { $httpWithMsg } from "@/plugins/axios";
+
+export default {
+  name: "ImportFile",
+  props: {
+    format: {
+      type: Array,
+      default() {
+        return ["xlsx", "xls"];
+      },
+    },
+    accept: {
+      type: String,
+      default: null,
+    },
+    onlyFetchFile: {
+      type: Boolean,
+      default: false,
+    },
+    uploadUrl: {
+      type: String,
+      default: "",
+    },
+    uploadData: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+    maxSize: {
+      type: Number,
+      default: 20 * 1024 * 1024,
+    },
+    addFilenameParam: {
+      type: String,
+      default: "filename",
+    },
+    templateUrl: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      headers: {
+        md5: "",
+      },
+      res: {},
+      loading: false,
+      uploadDataDict: {},
+      filename: "",
+      fileValid: false,
+    };
+  },
+  methods: {
+    initData() {
+      this.res = {};
+      this.loading = false;
+      this.uploadDataDict = {};
+      this.filename = "";
+      this.fileValid = false;
+    },
+    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;
+
+      if (this.onlyFetchFile) {
+        this.$emit("file-change", {
+          file,
+          md5,
+        });
+      }
+
+      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);
+    },
+    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.onlyFetchFile) {
+        this.$emit("confirm");
+        return;
+      }
+      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;
+    },
+    setLoading(loading) {
+      this.loading = loading;
+    },
+  },
+};
+</script>

+ 5 - 0
src/modules/question/api.js

@@ -43,3 +43,8 @@ export function copyQuestionApi(questionId) {
     params: { questionId },
   });
 }
+export function importQuestionApi(data, headData) {
+  return $httpWithMsg.post(`${QUESTION_API}/paper/copyQuestion/`, data, {
+    headers: headData,
+  });
+}

+ 154 - 0
src/modules/question/components/QuestionImportDialog.vue

@@ -0,0 +1,154 @@
+<template>
+  <el-dialog
+    custom-class="side-dialog"
+    :visible.sync="modalIsShow"
+    title="试题导入"
+    width="700px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    destroy-on-close
+    @open="visibleChange"
+  >
+    <el-form
+      ref="modalFormComp"
+      :model="modalForm"
+      :rules="rules"
+      label-width="120px"
+    >
+      <el-form-item label="导入类型">
+        <el-radio-group v-model="modalForm.importType">
+          <el-radio label="word">word</el-radio>
+          <el-radio label="zip">zip</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item prop="courseId" label="导入类型">
+        <course-select v-model="modalForm.courseId"> </course-select>
+      </el-form-item>
+      <el-form-item label="是否使用原卷">
+        <el-radio-group v-model="modalForm.userPaper">
+          <el-radio :label="true">是</el-radio>
+          <el-radio :label="false">否</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="总分校验">
+        <el-radio-group v-model="modalForm.scoreCheck">
+          <el-radio :label="true">开启</el-radio>
+          <el-radio :label="false">关闭</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item prop="file">
+        <import-file
+          ref="ImportFile"
+          :format="[modalForm.importType]"
+          :template-url="templateUrl"
+          only-fetch-file
+          @file-change="fileChange"
+          @confirm="confirm"
+        ></import-file>
+      </el-form-item>
+    </el-form>
+
+    <div slot="footer"></div>
+  </el-dialog>
+</template>
+
+<script>
+import ImportFile from "@/components/ImportFile.vue";
+import { importQuestionApi } from "../api";
+import { QUESTION_API } from "@/constants/constants";
+import { mapState } from "vuex";
+
+const initModalForm = {
+  importType: "word",
+  courseId: "",
+  userPaper: false,
+  scoreCheck: false,
+};
+
+export default {
+  name: "QuestionImportDialog",
+  components: { ImportFile },
+  data() {
+    return {
+      modalIsShow: false,
+      modalForm: {
+        ...initModalForm,
+      },
+      rules: {
+        courseId: [
+          {
+            required: true,
+            message: "请选择课程",
+            trigger: "change",
+          },
+        ],
+        file: [
+          {
+            validate: (rule, value, callback) => {
+              if (!this.fileData.file) {
+                return callback(new Error(`请输入选择文件`));
+              }
+              callback();
+            },
+            trigger: "change",
+          },
+        ],
+      },
+      fileData: {},
+      templateUrl: "",
+      loading: false,
+    };
+  },
+  computed: {
+    ...mapState({ user: (state) => state.user }),
+  },
+  mounted() {
+    this.templateUrl = `${QUESTION_API}/import/paper/template?$key=${this.user.key}&$token=${this.user.token}`;
+  },
+  methods: {
+    visibleChange() {
+      this.modalForm = { ...initModalForm };
+      this.fileData = {};
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    fileChange(fileData) {
+      this.fileData = fileData;
+      this.$refs.modalFormComp.validateField("file", (err) => {
+        console.log(err);
+      });
+    },
+    async confirm() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      if (this.loading) return;
+      this.loading = true;
+      this.$refs.ImportFile.setLoading(true);
+
+      let formData = new FormData();
+      Object.entries(this.modalForm).forEach(([key, val]) => {
+        formData.append(key, val);
+      });
+      formData.append("file", this.fileData.file);
+
+      const res = await importQuestionApi(formData, {
+        md5: this.fileData.md5,
+      }).catch(() => {});
+      this.loading = false;
+      this.$refs.ImportFile.setLoading(false);
+
+      if (!res) return;
+
+      this.$message.success("导入成功!");
+      this.$emit("modified");
+      this.cancel();
+    },
+  },
+};
+</script>

+ 10 - 1
src/modules/question/views/QuestionManage.vue

@@ -209,6 +209,11 @@
       :folder-id="curQuestionFolderId"
       @selected="folderSelected"
     ></question-folder-dialog>
+    <!-- QuestionImportDialog -->
+    <question-import-dialog
+      ref="QuestionImportDialog"
+      @modified="toPage(1)"
+    ></question-import-dialog>
   </div>
 </template>
 
@@ -222,6 +227,7 @@ import {
 import QuestionStatisticsDialog from "../components/QuestionStatisticsDialog.vue";
 import QuestionSafetySetDialog from "../components/QuestionSafetySetDialog.vue";
 import QuestionFolderDialog from "../components/QuestionFolderDialog.vue";
+import QuestionImportDialog from "../components/QuestionImportDialog.vue";
 
 export default {
   name: "QuestionMamage",
@@ -229,6 +235,7 @@ export default {
     QuestionStatisticsDialog,
     QuestionSafetySetDialog,
     QuestionFolderDialog,
+    QuestionImportDialog,
   },
   data() {
     return {
@@ -283,7 +290,9 @@ export default {
       this.$refs.QuestionFolderDialog.open();
     },
     toCreateQuestion() {},
-    toImportQuestion() {},
+    toImportQuestion() {
+      this.$refs.QuestionImportDialog.open();
+    },
     toView(row) {
       console.log(row);
     },