云
2 天以前 fbc58d6280a811f3c36696c530961f44c6491bb2
src/views/mes/pro/components/qr-export-task-modal.vue
@@ -2,19 +2,21 @@
  import type { QrExportTask } from '#/api/mes/pro/qrExport';
  import { onBeforeUnmount, onMounted, ref } from 'vue';
  import { downloadFileFromBlobPart } from '@vben/utils';
  import { Button, message, Modal, Progress } from 'ant-design-vue';
  import {
    cancelQrExportTask,
    downloadQrExportTask,
    getQrExportTask,
  } from '#/api/mes/pro/qrExport';
  import { downloadQrExportZip, pickQrExportSaveFile } from '../qrexport/download-qr-export';
  const storageKey = 'mes-pro-last-qr-export-task-id';
  const visible = ref(false);
  const task = ref<QrExportTask>();
  const downloading = ref(false);
  const downloadLoaded = ref(0);
  const downloadTotal = ref(0);
  let downloadController: AbortController | undefined;
  let timer: ReturnType<typeof setTimeout> | undefined;
  function stopPolling() {
@@ -56,15 +58,36 @@
  async function download() {
    if (!task.value?.downloadable || downloading.value) return;
    const currentTask = task.value;
    // 文件选择器必须由用户点击直接触发,不能等网络请求之后再调用。
    const saveHandle = await pickQrExportSaveFile(`${currentTask.taskName}.zip`);
    downloading.value = true;
    downloadLoaded.value = 0;
    downloadTotal.value = currentTask.fileSize ?? 0;
    const controller = new AbortController();
    downloadController = controller;
    try {
      const data = await downloadQrExportTask(task.value.id);
      downloadFileFromBlobPart({ fileName: `${task.value.taskName}.zip`, source: data });
      localStorage.removeItem(storageKey);
      await refresh();
      await downloadQrExportZip(currentTask, {
        saveHandle,
        signal: controller.signal,
        onProgress(loaded, total) {
          downloadLoaded.value = loaded;
          downloadTotal.value = total;
        },
      });
      message.success('ZIP 下载完成');
    } catch (error: any) {
      if (!controller.signal.aborted) {
        message.error(error?.message ?? '下载失败,请重试');
      }
    } finally {
      downloading.value = false;
      if (downloadController === controller) downloadController = undefined;
    }
  }
  function formatSize(bytes: number) {
    return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
  }
  onMounted(async () => {
@@ -81,7 +104,10 @@
      localStorage.removeItem(storageKey);
    }
  });
  onBeforeUnmount(stopPolling);
  onBeforeUnmount(() => {
    stopPolling();
    downloadController?.abort();
  });
  defineExpose({ show, showLast });
</script>
@@ -92,6 +118,9 @@
      <p>任务 #{{ task.id }} · {{ task.taskName }}</p>
      <p>已生成 {{ task.finishedCount }} / {{ task.totalCount }} 张,{{ task.statusName }}</p>
      <Progress :percent="task.progress" :status="task.status === 50 ? 'exception' : task.downloadable ? 'success' : 'active'" />
      <p v-if="downloading" class="mt-2 text-sm">
        正在下载 {{ formatSize(downloadLoaded) }} / {{ formatSize(downloadTotal) }}
      </p>
      <p v-if="task.errorMsg" class="text-red-600">{{ task.errorMsg }}</p>
      <div class="mt-4 flex justify-end gap-2">
        <Button @click="refresh">刷新</Button>