瀏覽代碼

feat: 导出异步任务

zhangjie 6 月之前
父節點
當前提交
950501ea79

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

@@ -851,6 +851,12 @@
     display: none;
   }
 }
+// task-progress-dialog
+.task-progress-dialog {
+  .el-dialog__footer {
+    display: none;
+  }
+}
 // preview-card-template
 .preview-card-template {
   .el-dialog {

+ 129 - 0
src/components/TaskProgressDialog.vue

@@ -0,0 +1,129 @@
+<template>
+  <el-dialog
+    custom-class="task-progress-dialog"
+    :visible.sync="modalIsShow"
+    :title="title"
+    top="10vh"
+    width="300px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    :show-close="false"
+    append-to-body
+    @open="openHandle"
+    @close="closeHandle"
+  >
+    <div class="progress-body">
+      <p>{{ tips }}</p>
+      <el-progress :percentage="curProgress"></el-progress>
+    </div>
+
+    <div slot="footer"></div>
+  </el-dialog>
+</template>
+
+<script>
+import useLoop from "../plugins/useLoop";
+import { asyncTaskProgressApi } from "../modules/question/api";
+
+export default {
+  name: "TaskProgressDialog",
+  props: {
+    task: {
+      type: Object,
+      default() {
+        return {
+          id: "",
+          name: "",
+        };
+      },
+    },
+    downloadHandle: {
+      type: Function,
+      default: null,
+    },
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      curProgress: 0,
+      startLoop: null,
+      stopLoop: null,
+    };
+  },
+  computed: {
+    title() {
+      return `${this.task.name}进度`;
+    },
+    tips() {
+      if (this.curProgress < 100) {
+        return `${this.task.name}中……`;
+      }
+
+      if (this.downloadHandle) {
+        return `文件生成完毕,正在下载……`;
+      }
+
+      return `任务执行成功!`;
+    },
+  },
+  mounted() {
+    const { start, stop } = useLoop(this.getProgress, 1000);
+    this.startLoop = start;
+    this.stopLoop = stop;
+  },
+  methods: {
+    openHandle() {
+      this.curProgress = 0;
+
+      if (!this.task.id) {
+        this.$message.error("任务丢失!");
+        return;
+      }
+
+      this.startLoop();
+    },
+    closeHandle() {
+      this.stopLoop();
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    delayClose() {
+      setTimeout(() => {
+        this.modalIsShow = false;
+      }, 500);
+    },
+    async getProgress() {
+      const res = await asyncTaskProgressApi(this.task.id).catch(() => {});
+      const result = res?.data;
+
+      if (!result) {
+        this.stopLoop();
+        this.delayClose();
+        return;
+      }
+      if (result.status === "FAILED") {
+        this.$message.error(result.errMsg);
+        this.stopLoop();
+        this.delayClose();
+        return;
+      }
+      this.curProgress = Math.ceil(Number(result.progress || 0));
+
+      // 文件生成成功,开始下载
+      if (result.status === "SUCCESS") {
+        this.stopLoop();
+        if (this.downloadHandle) {
+          await this.downloadHandle();
+        }
+
+        this.$emit("finished");
+        this.delayClose();
+      }
+    },
+  },
+};
+</script>

+ 31 - 4
src/modules/question/api.js

@@ -90,10 +90,13 @@ export function importQuestionApi(data, headData) {
   });
 }
 export const exportQuestionApi = (data) => {
-  return $httpWithMsg.post(`${QUESTION_API}/export/paper`, {
-    params: data,
-    responseType: "blob",
-  });
+  return $httpWithMsg.post(
+    `${QUESTION_API}/question/export/docx`,
+    {},
+    {
+      params: data,
+    }
+  );
 };
 
 export function questionEditLogsApi(data) {
@@ -388,3 +391,27 @@ export function gptTaskDetailApi({ taskId, courseId }) {
     { params: { taskId, courseId } }
   );
 }
+
+// async task
+export function asyncTaskProgressApi(taskId) {
+  return $httpWithMsg.post(
+    `${QUESTION_API}/async-task/progress`,
+    {},
+    { params: { taskId } }
+  );
+  /**
+   *   
+    progress 进度百分数
+    status 状态 RUNNING("处理中"), SUCCESS("处理成功"), FAILED("处理失败");
+    errMsg 失败时错误信息
+   */
+}
+export const asyncTaskDownloadApi = (taskId) => {
+  return $httpWithMsg.post(
+    `${QUESTION_API}/async-task/down`,
+    {},
+    {
+      params: { taskId },
+    }
+  );
+};

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

@@ -361,6 +361,12 @@
       }"
       @modified="getList"
     ></gpt-question-dialog>
+    <!-- TaskProgressDialog -->
+    <task-progress-dialog
+      ref="TaskProgressDialog"
+      :task="curTask"
+      :download-handle="taskFinished"
+    ></task-progress-dialog>
     <router-view></router-view>
   </div>
 </template>
@@ -377,6 +383,7 @@ import {
   classifyQuestionPageListApi,
   getAiNums,
   checkOptionRepeatApi,
+  asyncTaskDownloadApi,
 } from "../api";
 import QuestionStatisticsDialog from "../components/QuestionStatisticsDialog.vue";
 import QuestionSafetySetDialog from "../components/QuestionSafetySetDialog.vue";
@@ -388,6 +395,7 @@ import FolderQuestionManageDialog from "../components/FolderQuestionManageDialog
 import PropertyTreeSelect from "../components/PropertyTreeSelect.vue";
 import QuestionImportEdit from "../components/QuestionImportEdit.vue";
 import GptQuestionDialog from "../components/GptQuestionDialog.vue";
+import TaskProgressDialog from "@/components/TaskProgressDialog.vue";
 import { mapActions, mapGetters, mapMutations } from "vuex";
 import { USER_SIGNIN } from "../../portal/store/user";
 import QuestionFolder from "@/modules/question/components/QuestionFolder.vue";
@@ -407,6 +415,7 @@ export default {
     QuestionImportEdit,
     GptQuestionDialog,
     QuestionFolder,
+    TaskProgressDialog,
   },
   data() {
     return {
@@ -444,6 +453,8 @@ export default {
       curActionQids: [],
       aiWarningMsg: "",
       downloading: false,
+      // async task
+      curTask: {},
     };
   },
   computed: {
@@ -594,8 +605,22 @@ export default {
       if (this.downloading) return;
       this.downloading = true;
 
+      const res = await exportQuestionApi(this.filter).catch(() => {});
+      this.downloading = false;
+
+      if (!res) return;
+      this.curTask = {
+        id: res.data.taskId,
+        name: "试题导出",
+      };
+      this.$refs.TaskProgressDialog.open();
+    },
+    async taskFinished() {
+      if (this.downloading) return;
+      this.downloading = true;
+
       const res = await downloadByApi(() => {
-        return exportQuestionApi(this.filter);
+        return asyncTaskDownloadApi(this.curTask.id);
       }).catch((e) => {
         this.$message.error(e || "导出失败,请重新尝试!");
       });

+ 44 - 0
src/plugins/useLoop.js

@@ -0,0 +1,44 @@
+export default function useLoop(action, interval) {
+  let sts = [];
+  let stoped = true;
+
+  async function run() {
+    clear();
+    if (stoped) return;
+    try {
+      if (action instanceof Promise) {
+        await action();
+      } else {
+        action();
+      }
+    } catch (error) {
+      console.log(error);
+    }
+
+    if (stoped) return;
+    sts.push(setTimeout(run, interval));
+  }
+
+  function start() {
+    stoped = false;
+    run();
+  }
+
+  function clear() {
+    sts.forEach((st) => {
+      clearTimeout(st);
+    });
+    sts = [];
+  }
+
+  function stop() {
+    stoped = true;
+    clear();
+  }
+
+  return {
+    stoped,
+    stop,
+    start,
+  };
+}