feat(desktop): show batch drop progress
This commit is contained in:
11 files changed
+453
-37
No files matched your search
@@ -1879,8 +1879,17 @@ defineExpose({ focusSearch, createNewGroup, collapseAllTreeNodes });
|
||||
:close-on-confirm="false"
|
||||
@confirm="confirmSidebarDangerDialog"
|
||||
>
|
||||
<template v-if="sidebarDangerDialogRequest.option" #options>
|
||||
<label class="mb-3 flex items-start gap-2 rounded-md border bg-muted/20 px-3 py-2 text-sm">
|
||||
<template #options>
|
||||
<div v-if="sidebarDangerDialogConfirming && sidebarDangerDialogRequest.progress" class="mb-3 rounded-md border bg-muted/20 px-3 py-2.5">
|
||||
<div class="mb-1.5 flex items-center justify-between text-xs tabular-nums text-muted-foreground">
|
||||
<span>{{ sidebarDangerDialogRequest.progress.completed }} / {{ sidebarDangerDialogRequest.progress.total }}</span>
|
||||
<span>{{ Math.round((sidebarDangerDialogRequest.progress.completed / sidebarDangerDialogRequest.progress.total) * 100) }}%</span>
|
||||
</div>
|
||||
<div class="h-2 overflow-hidden rounded-full bg-muted" role="progressbar" :aria-valuemin="0" :aria-valuemax="sidebarDangerDialogRequest.progress.total" :aria-valuenow="sidebarDangerDialogRequest.progress.completed">
|
||||
<div class="h-full bg-primary transition-[width] duration-200" :style="{ width: `${Math.round((sidebarDangerDialogRequest.progress.completed / sidebarDangerDialogRequest.progress.total) * 100)}%` }" />
|
||||
</div>
|
||||
</div>
|
||||
<label v-if="sidebarDangerDialogRequest.option" class="mb-3 flex items-start gap-2 rounded-md border bg-muted/20 px-3 py-2 text-sm">
|
||||
<input :checked="sidebarDangerDialogRequest.option.checked" type="checkbox" class="mt-0.5 h-3.5 w-3.5 shrink-0 accent-primary" @change="updateSidebarDangerDialogOption" />
|
||||
<span class="grid gap-0.5">
|
||||
<span class="font-medium text-foreground">{{ sidebarDangerDialogRequest.option.label }}</span>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, watch, onBeforeUnmount, inject, reactive, shallowRef } from "vue";
|
||||
import { computed, nextTick, watch, onBeforeUnmount, inject, reactive, ref, shallowRef } from "vue";
|
||||
import { createRoutedSidebarDialogController } from "./sidebarDialogControllerRouting";
|
||||
import { useSqlHighlighter } from "@/composables/useSqlHighlighter";
|
||||
import { useSidebarDataOpenRuntime } from "@/composables/useSidebarDataOpenRuntime";
|
||||
@@ -134,6 +134,7 @@ import { connectionSupportsServerDashboard as connectionSupportsPgServerDashboar
|
||||
import { sidebarTreeContextKey } from "@/lib/sidebar/sidebarTreeContext";
|
||||
import { batchTableEmptyFeedback, runBatchTableEmpty } from "@/lib/sidebar/batchTableEmpty";
|
||||
import { runBatchTableTruncate } from "@/lib/table/batchTableTruncate";
|
||||
import { runBatchTableDrop } from "@/lib/table/batchTableDrop";
|
||||
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
|
||||
import { copyToClipboard } from "@/lib/common/clipboard";
|
||||
import { rankSavedSqlHistory, type SavedSqlHistoryScope } from "@/lib/savedSql/savedSqlHistory";
|
||||
@@ -412,6 +413,8 @@ const { isTableNotView, supportsTruncate, canDropTableCascade, canTruncateTableC
|
||||
refreshMutatedTableDataTabsForNode,
|
||||
});
|
||||
|
||||
const batchDropProgress = ref({ completed: 0, total: 0 });
|
||||
|
||||
const treeItemDialogOwner = Symbol("sidebar-tree-dialog-owner");
|
||||
|
||||
function claimTreeItemDialogOwnership() {
|
||||
@@ -1726,6 +1729,7 @@ function requestBatchDrop() {
|
||||
if (!targets.length) return;
|
||||
batchDropTargets.value = targets.slice();
|
||||
batchDropCascade.value = false;
|
||||
batchDropProgress.value = { completed: 0, total: 0 };
|
||||
void refreshBatchDropPreviewSql();
|
||||
showBatchDropConfirm.value = true;
|
||||
}
|
||||
@@ -1979,6 +1983,46 @@ async function confirmBatchDrop() {
|
||||
return;
|
||||
}
|
||||
const useCascade = batchDropCascade.value && targets.every((node) => node.type !== "table" || supportsDropTableCascade(databaseTypeForNode(node)));
|
||||
if (targets.every((node) => node.type === "table" && node.connectionId && node.database)) {
|
||||
const first = targets[0]!;
|
||||
await connectionStore.ensureConnected(first.connectionId!);
|
||||
batchDropProgress.value = { completed: 0, total: targets.length };
|
||||
const plan = await Promise.all(
|
||||
targets.map(async (target) => {
|
||||
const sql = await dropSqlForTreeNode(target, { cascade: useCascade });
|
||||
if (!sql) throw new Error("Drop table SQL is unavailable");
|
||||
return { target, sql };
|
||||
}),
|
||||
);
|
||||
const batchSql = plan.map(({ sql }) => sql).join(";\n");
|
||||
const result = await executeWithProductionSqlGuard({
|
||||
connection: connectionStore.getConfig(first.connectionId!),
|
||||
database: first.database!,
|
||||
sql: batchSql,
|
||||
source: t("production.sourceSidebar"),
|
||||
execute: () =>
|
||||
runBatchTableDrop({
|
||||
databaseType: databaseTypeForNode(first),
|
||||
plan,
|
||||
executeStatement: (sql) => api.executeQuery(first.connectionId!, first.database!, sql),
|
||||
executeBatch: (sql, onProgress) => api.executeMultiWithProgress(first.connectionId!, first.database!, sql, onProgress),
|
||||
onProgress: (progress) => {
|
||||
batchDropProgress.value = { completed: Math.min(progress.completed, targets.length), total: targets.length };
|
||||
},
|
||||
}),
|
||||
});
|
||||
if (!result) return;
|
||||
|
||||
for (const target of result.succeeded) {
|
||||
closeDroppedTableObjectTabsForNode(target);
|
||||
connectionStore.removeTreeNode(target.id);
|
||||
releaseActiveNodeReference([target.id]);
|
||||
}
|
||||
if (result.failed) throw result.failed;
|
||||
toast(t("contextMenu.batchDropSuccess", { count: result.succeeded.length }), 3000);
|
||||
showBatchDropConfirm.value = false;
|
||||
return;
|
||||
}
|
||||
for (const target of targets) {
|
||||
if (!target.connectionId || !target.database) continue;
|
||||
await connectionStore.ensureConnected(target.connectionId);
|
||||
@@ -3186,6 +3230,9 @@ routeDangerDialog(showBatchDropConfirm, () =>
|
||||
return batchDropPreviewSql.value;
|
||||
},
|
||||
confirmLabel: batchDropMenuLabel(),
|
||||
get progress() {
|
||||
return batchDropProgress.value.total > 0 ? batchDropProgress.value : undefined;
|
||||
},
|
||||
option: canBatchDropCascade.value
|
||||
? {
|
||||
checked: batchDropCascade.value,
|
||||
|
||||
Reference in new issue
Block a user