feat(export-tracker): 增强导出任务跟踪功能 (#1958)

- 添加了对数据库导出、SQL 文件导出和数据传输任务的支持
- 引入了新的状态和错误处理机制
- 实现了任务的重叠检查,防止同时进行相同目标表的数据传输
- 更新了 API 调用以支持新的任务类型
- 增加了相应的国际化文本
- 添加了单元测试以确保新功能的正确性
This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-06-26 18:50:25 +08:00
1 parent 79c2cd8bdc
commit 10aff74072
15 files changed
+838 -208

No files matched your search

@@ -14,11 +14,13 @@ import { useToast } from "@/composables/useToast";
import { useConnectionStore } from "@/stores/connectionStore";
import { databaseOptionsForConnection } from "@/composables/useDatabaseOptions";
import { cancelSqlFileExecution, executeSqlFile, listenSqlFileProgress, listDatabases, previewSqlFile, type SqlFilePreview, type SqlFileProgress, type SqlFileStatus } from "@/lib/api";
import { useExportTracker } from "@/composables/useExportTracker";
import { Check, CheckSquare, FileCode, FolderOpen, Loader2, Play, Square, X } from "@lucide/vue";
const { t } = useI18n();
const { toast } = useToast();
const { highlight } = useSqlHighlighter();
const { addSqlFileTask, updateSqlFileTask } = useExportTracker();
const open = defineModel<boolean>("open", { default: false });
const props = defineProps<{
@@ -282,6 +284,7 @@ async function startExecution() {
terminalStatus.value = "running";
terminalError.value = "";
progress.value = null;
addSqlFileTask(id, preview.value.fileName, preview.value.filePath);
let unlisten: (() => void) | undefined;
try {
@@ -296,6 +299,7 @@ async function startExecution() {
progress.value = next;
terminalStatus.value = next.status;
terminalError.value = next.error ?? terminalError.value;
updateSqlFileTask(id, next);
if (isTerminalStatus(next.status)) {
running.value = false;
cancelling.value = false;
@@ -320,6 +324,18 @@ async function startExecution() {
});
if (!isTerminalStatus(terminalStatus.value)) {
terminalStatus.value = cancelRequested.value ? "cancelled" : "done";
const lastProgress = progress.value as SqlFileProgress | null;
updateSqlFileTask(id, {
executionId: id,
status: terminalStatus.value,
statementIndex: lastProgress?.statementIndex ?? 0,
successCount: lastProgress?.successCount ?? 0,
failureCount: lastProgress?.failureCount ?? 0,
affectedRows: lastProgress?.affectedRows ?? 0,
elapsedMs: lastProgress?.elapsedMs ?? 0,
statementSummary: lastProgress?.statementSummary ?? "",
error: lastProgress?.error ?? null,
});
if (terminalStatus.value === "done") {
await refreshTargetAfterImport();
}
@@ -327,6 +343,18 @@ async function startExecution() {
} catch (e: any) {
terminalStatus.value = cancelRequested.value ? "cancelled" : "error";
terminalError.value = e?.message || String(e);
const lastProgress = progress.value as SqlFileProgress | null;
updateSqlFileTask(id, {
executionId: id,
status: terminalStatus.value,
statementIndex: lastProgress?.statementIndex ?? 0,
successCount: lastProgress?.successCount ?? 0,
failureCount: lastProgress?.failureCount ?? 0,
affectedRows: lastProgress?.affectedRows ?? 0,
elapsedMs: lastProgress?.elapsedMs ?? 0,
statementSummary: lastProgress?.statementSummary ?? "",
error: terminalError.value,
});
if (!cancelRequested.value) {
toast(terminalError.value, 5000);
}
@@ -356,7 +384,6 @@ async function cancelExecution() {
}
function handleOpenChange(nextOpen: boolean) {
if (!nextOpen && running.value) return;
open.value = nextOpen;
}
@@ -373,6 +400,7 @@ watch(
open,
(value) => {
if (!value) return;
if (running.value) return;
resetState();
if (connectionId.value) {
loadDatabasesForConnection(connectionId.value);
@@ -543,6 +571,9 @@ watch(
<DialogFooter>
<template v-if="running">
<Button variant="outline" size="sm" @click="open = false">
{{ t("sqlFile.runInBackground") }}
</Button>
<Button variant="destructive" size="sm" :disabled="cancelling" @click="cancelExecution">
<Loader2 v-if="cancelling" class="w-3.5 h-3.5 mr-1.5 animate-spin" />
<X v-else class="w-3.5 h-3.5 mr-1.5" />