diff --git a/frontend/src/components/Sidebar.locate-toolbar.test.tsx b/frontend/src/components/Sidebar.locate-toolbar.test.tsx
index 926608a7..b5b1a076 100644
--- a/frontend/src/components/Sidebar.locate-toolbar.test.tsx
+++ b/frontend/src/components/Sidebar.locate-toolbar.test.tsx
@@ -804,6 +804,12 @@ describe('Sidebar locate toolbar', () => {
expect(markup).toContain('data-sidebar-create-group-action="true"');
expect(markup).toContain('data-sidebar-batch-table-action="true"');
expect(markup).toContain('data-sidebar-batch-database-action="true"');
+ expect(source).toContain('openBatchTableExport: () => openBatchOperationModal(),');
+ expect(source).toContain('openBatchDatabaseExport: () => openBatchDatabaseModal(),');
+ expect(source).toContain('onClick={() => openBatchOperationModal()}');
+ expect(source).toContain('onClick={() => openBatchDatabaseModal()}');
+ expect(source).not.toContain('openBatchTableExport: () => openBatchTableExportWorkbench()');
+ expect(source).not.toContain('openBatchDatabaseExport: () => openBatchDatabaseExportWorkbench()');
expect(markup).toContain('data-sidebar-open-external-sql-file-action="true"');
expect(markup).toContain('data-sidebar-locate-current-tab-action="true"');
expect(markup).toContain('data-gonavi-new-query-action="true"');
diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx
index 5af46376..58c673c3 100644
--- a/frontend/src/components/Sidebar.tsx
+++ b/frontend/src/components/Sidebar.tsx
@@ -773,12 +773,14 @@ const Sidebar: React.FC<{
handleDatabaseChange,
handleBatchExport,
handleBatchClear,
+ handleBatchDeleteTables,
handleCheckAll,
handleInvertSelection,
openBatchDatabaseModal,
openBatchDatabaseExportWorkbench,
handleDbConnectionChange,
handleBatchDbExport,
+ handleBatchDbDelete,
handleCheckAllDb,
handleInvertSelectionDb,
} = useSidebarBatchExport({
@@ -2618,8 +2620,8 @@ const Sidebar: React.FC<{
},
handlers: {
openCreateTagModal: () => { setRenameViewTarget(null); createTagForm.resetFields(); setIsCreateTagModalOpen(true); },
- openBatchTableExport: () => openBatchTableExportWorkbench(),
- openBatchDatabaseExport: () => openBatchDatabaseExportWorkbench(),
+ openBatchTableExport: () => openBatchOperationModal(),
+ openBatchDatabaseExport: () => openBatchDatabaseModal(),
openExternalSqlFile: handleOpenSQLFileFromToolbar,
locateActiveTab: handleLocateActiveTabInSidebar,
toggleAI: onToggleAI ?? (() => {}),
@@ -2857,7 +2859,7 @@ const Sidebar: React.FC<{
icon={}
aria-label={t('sidebar.action.batch_tables')}
data-sidebar-batch-table-action="true"
- onClick={() => openBatchTableExportWorkbench()}
+ onClick={() => openBatchOperationModal()}
style={{ color: legacyToolbarButtonColor }}
/>
@@ -2870,7 +2872,7 @@ const Sidebar: React.FC<{
icon={}
aria-label={t('sidebar.action.batch_databases')}
data-sidebar-batch-database-action="true"
- onClick={() => openBatchDatabaseExportWorkbench()}
+ onClick={() => openBatchDatabaseModal()}
style={{ color: legacyToolbarButtonColor }}
/>
@@ -3097,6 +3099,7 @@ const Sidebar: React.FC<{
handleConnectionChange={handleConnectionChange}
handleDatabaseChange={handleDatabaseChange}
handleBatchClear={handleBatchClear}
+ handleBatchDeleteTables={handleBatchDeleteTables}
handleBatchExport={handleBatchExport}
handleCheckAll={handleCheckAll}
handleInvertSelection={handleInvertSelection}
@@ -3108,6 +3111,7 @@ const Sidebar: React.FC<{
setCheckedDbKeys={setCheckedDbKeys}
handleDbConnectionChange={handleDbConnectionChange}
handleBatchDbExport={handleBatchDbExport}
+ handleBatchDbDelete={handleBatchDbDelete}
handleCheckAllDb={handleCheckAllDb}
handleInvertSelectionDb={handleInvertSelectionDb}
/>
diff --git a/frontend/src/components/SidebarBatchActions.i18n.test.ts b/frontend/src/components/SidebarBatchActions.i18n.test.ts
index d54d052a..15aff00d 100644
--- a/frontend/src/components/SidebarBatchActions.i18n.test.ts
+++ b/frontend/src/components/SidebarBatchActions.i18n.test.ts
@@ -11,6 +11,8 @@ const requiredKeys = [
'sidebar.action.batch_tables',
'sidebar.action.batch_databases',
'sidebar.action.clear_tables',
+ 'sidebar.action.delete_tables',
+ 'sidebar.action.delete_database_count',
'sidebar.action.export_schema',
'sidebar.action.export_data_only',
'sidebar.action.backup_schema_data',
@@ -63,6 +65,7 @@ describe('Sidebar batch actions i18n', () => {
[
'清空表',
+ '删除表',
'导出结构',
'仅数据(INSERT)',
'备份(结构+数据)',
@@ -118,6 +121,8 @@ describe('Sidebar batch actions i18n', () => {
[
'sidebar.action.clear_tables',
+ 'sidebar.action.delete_tables',
+ 'sidebar.action.delete_database_count',
'sidebar.action.export_schema',
'sidebar.action.export_data_only',
'sidebar.action.backup_schema_data',
@@ -148,6 +153,10 @@ describe('Sidebar batch actions i18n', () => {
expect(batchModalSource, key).toContain(`t('${key}'`);
});
+ expect(batchModalSource).toContain('handleBatchDeleteTables');
+ expect(batchModalSource).toContain('handleBatchDbDelete');
+ expect(batchModalSource).toContain("item.objectType === 'table'");
+
[
'sidebar.tab.batch_export_objects',
'sidebar.tab.batch_export_objects_database',
@@ -170,6 +179,8 @@ describe('Sidebar batch actions i18n', () => {
requiredKeys.forEach((key) => {
expect(catalog[key], `${locale}:${key}`).toBeTruthy();
});
+ expect(placeholders(catalog['sidebar.action.delete_database_count'])).toEqual(['count']);
+ expect(placeholders(catalog['sidebar.action.delete_tables'])).toEqual([]);
expect(placeholders(catalog['sidebar.tab.batch_export_objects'])).toEqual([]);
expect(placeholders(catalog['sidebar.tab.batch_export_objects_database'])).toEqual(['database']);
expect(placeholders(catalog['sidebar.tab.batch_export_databases'])).toEqual([]);
diff --git a/frontend/src/components/SidebarBatchClearFeedback.i18n.test.ts b/frontend/src/components/SidebarBatchClearFeedback.i18n.test.ts
index 4c9b669c..4c81c985 100644
--- a/frontend/src/components/SidebarBatchClearFeedback.i18n.test.ts
+++ b/frontend/src/components/SidebarBatchClearFeedback.i18n.test.ts
@@ -12,10 +12,24 @@ const requiredKeys = [
'sidebar.message.clearing_selected_tables',
'sidebar.message.clear_success',
'sidebar.message.clear_failed',
+ 'sidebar.message.select_table_required',
+ 'sidebar.modal.confirm_delete_selected_tables.title',
+ 'sidebar.modal.confirm_delete_selected_tables.content',
+ 'sidebar.message.deleting_selected_tables',
+ 'sidebar.message.delete_tables_success',
+ 'sidebar.message.delete_tables_failed',
] as const;
const extractHandleBatchClearBlock = (): string => {
const start = source.indexOf('const handleBatchClear = async');
+ const end = source.indexOf('const handleBatchDeleteTables = async', start);
+ expect(start).toBeGreaterThanOrEqual(0);
+ expect(end).toBeGreaterThan(start);
+ return source.slice(start, end);
+};
+
+const extractHandleBatchDeleteTablesBlock = (): string => {
+ const start = source.indexOf('const handleBatchDeleteTables = async');
const end = source.indexOf('const handleCheckAll = (checked: boolean)', start);
expect(start).toBeGreaterThanOrEqual(0);
expect(end).toBeGreaterThan(start);
@@ -55,6 +69,28 @@ describe('Sidebar batch clear feedback i18n', () => {
expect(block).toContain("res.message !== '已取消'");
});
+ it('localizes handleBatchDeleteTables validation, confirmation, loading, success, and failure wrappers', () => {
+ const block = extractHandleBatchDeleteTablesBlock();
+
+ expect(block).toContain('DropTable');
+ expect(block).toContain("item.objectType === 'table'");
+ expect(block).toContain("t('sidebar.message.select_table_required')");
+ expect(block).toContain("t('sidebar.modal.confirm_delete_selected_tables.title')");
+ expect(block).toContain("t('sidebar.modal.confirm_delete_selected_tables.content'");
+ expect(block).toContain("t('sidebar.action.delete')");
+ expect(block).toContain('okButtonProps: { danger: true }');
+ expect(block).toContain("t('sidebar.action.cancel')");
+ expect(block).toContain("t('sidebar.message.deleting_selected_tables'");
+ expect(block).toContain("t('sidebar.message.delete_tables_success'");
+ expect(block).toContain("t('sidebar.message.delete_tables_failed'");
+ expect(block).toContain('connection: conn.name');
+ expect(block).toContain('database: dbName');
+ expect(block).toContain('count: tableNames.length');
+ expect(block).toContain('count: successKeys.length');
+ expect(block).toContain('table: failed.table');
+ expect(block).toContain('error: failed.error');
+ });
+
it('keeps batch clear feedback keys available with stable placeholders', () => {
locales.forEach((locale) => {
const catalog = JSON.parse(readFileSync(new URL(`../../../shared/i18n/${locale}.json`, import.meta.url), 'utf8')) as Record;
@@ -69,6 +105,12 @@ describe('Sidebar batch clear feedback i18n', () => {
expect(placeholders(catalog['sidebar.message.clearing_selected_tables'])).toEqual(['count']);
expect(placeholders(catalog['sidebar.message.clear_success'])).toEqual([]);
expect(placeholders(catalog['sidebar.message.clear_failed'])).toEqual(['error']);
+ expect(placeholders(catalog['sidebar.message.select_table_required'])).toEqual([]);
+ expect(placeholders(catalog['sidebar.modal.confirm_delete_selected_tables.title'])).toEqual([]);
+ expect(placeholders(catalog['sidebar.modal.confirm_delete_selected_tables.content'])).toEqual(['connection', 'count', 'database']);
+ expect(placeholders(catalog['sidebar.message.deleting_selected_tables'])).toEqual(['count']);
+ expect(placeholders(catalog['sidebar.message.delete_tables_success'])).toEqual(['count']);
+ expect(placeholders(catalog['sidebar.message.delete_tables_failed'])).toEqual(['error', 'table']);
});
});
});
diff --git a/frontend/src/components/SidebarBatchDatabaseExportFeedback.i18n.test.ts b/frontend/src/components/SidebarBatchDatabaseExportFeedback.i18n.test.ts
index 320c19c6..c08aa6af 100644
--- a/frontend/src/components/SidebarBatchDatabaseExportFeedback.i18n.test.ts
+++ b/frontend/src/components/SidebarBatchDatabaseExportFeedback.i18n.test.ts
@@ -10,6 +10,11 @@ const requiredKeys = [
'sidebar.message.exporting_database_backup',
'sidebar.message.database_export_success',
'sidebar.message.database_export_failed',
+ 'sidebar.modal.confirm_delete_selected_databases.title',
+ 'sidebar.modal.confirm_delete_selected_databases.content',
+ 'sidebar.message.deleting_selected_databases',
+ 'sidebar.message.delete_databases_success',
+ 'sidebar.message.delete_databases_failed',
] as const;
const extractBatchDatabaseExportBlock = (): string => {
@@ -20,6 +25,14 @@ const extractBatchDatabaseExportBlock = (): string => {
return source.slice(start, end);
};
+const extractHandleBatchDbDeleteBlock = (): string => {
+ const start = source.indexOf('const handleBatchDbDelete = async');
+ const end = source.indexOf('const handleCheckAllDb = (checked: boolean)', start);
+ expect(start).toBeGreaterThanOrEqual(0);
+ expect(end).toBeGreaterThan(start);
+ return source.slice(start, end);
+};
+
const placeholders = (value: string): string[] => [...value.matchAll(/\{\{(\w+)\}\}/g)]
.map((match) => match[1])
.sort();
@@ -47,6 +60,26 @@ describe('Sidebar batch database export feedback i18n', () => {
expect(block).toContain("res.message !== '已取消'");
});
+ it('localizes handleBatchDbDelete confirmation, loading, success, and failure wrappers', () => {
+ const block = extractHandleBatchDbDeleteBlock();
+
+ expect(block).toContain('DropDatabase');
+ expect(block).toContain("t('sidebar.message.select_database_required')");
+ expect(block).toContain("t('sidebar.modal.confirm_delete_selected_databases.title')");
+ expect(block).toContain("t('sidebar.modal.confirm_delete_selected_databases.content'");
+ expect(block).toContain("t('sidebar.action.delete')");
+ expect(block).toContain('okButtonProps: { danger: true }');
+ expect(block).toContain("t('sidebar.action.cancel')");
+ expect(block).toContain("t('sidebar.message.deleting_selected_databases'");
+ expect(block).toContain("t('sidebar.message.delete_databases_success'");
+ expect(block).toContain("t('sidebar.message.delete_databases_failed'");
+ expect(block).toContain('connection: batchConnContext.name');
+ expect(block).toContain('count: selectedDbs.length');
+ expect(block).toContain('count: successKeys.length');
+ expect(block).toContain('database: failed.database');
+ expect(block).toContain('error: failed.error');
+ });
+
it('keeps batch database export feedback keys available with stable placeholders', () => {
locales.forEach((locale) => {
const catalog = JSON.parse(readFileSync(new URL(`../../../shared/i18n/${locale}.json`, import.meta.url), 'utf8')) as Record;
@@ -59,6 +92,11 @@ describe('Sidebar batch database export feedback i18n', () => {
expect(placeholders(catalog['sidebar.message.exporting_database_backup'])).toEqual(['database']);
expect(placeholders(catalog['sidebar.message.database_export_success'])).toEqual(['database']);
expect(placeholders(catalog['sidebar.message.database_export_failed'])).toEqual(['database', 'error']);
+ expect(placeholders(catalog['sidebar.modal.confirm_delete_selected_databases.title'])).toEqual([]);
+ expect(placeholders(catalog['sidebar.modal.confirm_delete_selected_databases.content'])).toEqual(['connection', 'count']);
+ expect(placeholders(catalog['sidebar.message.deleting_selected_databases'])).toEqual(['count']);
+ expect(placeholders(catalog['sidebar.message.delete_databases_success'])).toEqual(['count']);
+ expect(placeholders(catalog['sidebar.message.delete_databases_failed'])).toEqual(['database', 'error']);
});
});
});
diff --git a/frontend/src/components/sidebar/SidebarBatchExportModals.tsx b/frontend/src/components/sidebar/SidebarBatchExportModals.tsx
index d57a4707..96026936 100644
--- a/frontend/src/components/sidebar/SidebarBatchExportModals.tsx
+++ b/frontend/src/components/sidebar/SidebarBatchExportModals.tsx
@@ -13,16 +13,11 @@ import Modal from '../common/ResizableDraggableModal';
import type { SavedConnection } from '../../types';
import { t } from '../../i18n';
import type {
+ BatchObjectItem,
BatchObjectFilterType,
BatchSelectionScope,
} from './useSidebarBatchExport';
-type BatchObjectItem = {
- key: string;
- dbName?: string;
- title: React.ReactNode;
-};
-
type SidebarBatchExportModalsProps = {
connections: SavedConnection[];
modalPanelStyle: React.CSSProperties;
@@ -55,6 +50,7 @@ type SidebarBatchExportModalsProps = {
handleConnectionChange: (connectionId: string) => void;
handleDatabaseChange: (databaseName: string) => void;
handleBatchClear: () => void;
+ handleBatchDeleteTables: () => void;
handleBatchExport: (mode: 'schema' | 'dataOnly' | 'backup') => void;
handleCheckAll: (checked: boolean) => void;
handleInvertSelection: () => void;
@@ -66,6 +62,7 @@ type SidebarBatchExportModalsProps = {
setCheckedDbKeys: (keys: string[]) => void;
handleDbConnectionChange: (connectionId: string) => void;
handleBatchDbExport: (includeData: boolean) => void;
+ handleBatchDbDelete: () => void;
handleCheckAllDb: (checked: boolean) => void;
handleInvertSelectionDb: () => void;
};
@@ -102,6 +99,7 @@ export const SidebarBatchExportModals: React.FC =
handleConnectionChange,
handleDatabaseChange,
handleBatchClear,
+ handleBatchDeleteTables,
handleBatchExport,
handleCheckAll,
handleInvertSelection,
@@ -113,9 +111,13 @@ export const SidebarBatchExportModals: React.FC =
setCheckedDbKeys,
handleDbConnectionChange,
handleBatchDbExport,
+ handleBatchDbDelete,
handleCheckAllDb,
handleInvertSelectionDb,
-}) => (
+}) => {
+ const selectedTableCount = batchTables.filter(item => checkedTableKeys.includes(item.key) && item.objectType === 'table').length;
+
+ return (
<>
=
{t('sidebar.action.cancel')}
+ }
+ onClick={() => handleBatchDeleteTables()}
+ disabled={selectedTableCount === 0}
+ >
+ {t('sidebar.action.delete_tables')}
+
,
}
@@ -430,4 +450,5 @@ export const SidebarBatchExportModals: React.FC =
)}
>
-);
+ );
+};
diff --git a/frontend/src/components/sidebar/useSidebarBatchExport.ts b/frontend/src/components/sidebar/useSidebarBatchExport.ts
index f233585e..6c7d0dcd 100644
--- a/frontend/src/components/sidebar/useSidebarBatchExport.ts
+++ b/frontend/src/components/sidebar/useSidebarBatchExport.ts
@@ -1,7 +1,7 @@
import { useEffect, useMemo, useState, type MutableRefObject } from 'react';
import { Modal, message } from 'antd';
-import { DBGetDatabases, DBGetTables } from '../../../wailsjs/go/app/App';
+import { DBGetDatabases, DBGetTables, DropDatabase, DropTable } from '../../../wailsjs/go/app/App';
import type { SavedConnection } from '../../types';
import { t } from '../../i18n';
import { buildRpcConnectionConfig } from '../../utils/connectionRpcConfig';
@@ -25,6 +25,7 @@ export type BatchSelectionScope = 'filtered' | 'all';
export interface BatchObjectItem {
title: string;
key: string;
+ dbName?: string;
objectName: string;
objectType: BatchObjectType;
dataRef: any;
@@ -510,6 +511,86 @@ export const useSidebarBatchExport = ({
}
};
+ const handleBatchDeleteTables = async () => {
+ const selectedTables = batchTables.filter(item => checkedTableKeys.includes(item.key) && item.objectType === 'table');
+ if (selectedTables.length === 0) {
+ message.warning(t('sidebar.message.select_table_required'));
+ return;
+ }
+
+ const { conn, dbName } = batchDbContext || {};
+ if (!conn || !dbName) {
+ message.warning(t('sidebar.message.select_connection_or_database_first'));
+ return;
+ }
+ const tableNames = selectedTables.map(t => t.objectName);
+
+ const ok = await new Promise((resolve) => {
+ Modal.confirm({
+ title: t('sidebar.modal.confirm_delete_selected_tables.title'),
+ content: t('sidebar.modal.confirm_delete_selected_tables.content', {
+ connection: conn.name,
+ database: dbName,
+ count: tableNames.length,
+ }),
+ okText: t('sidebar.action.delete'),
+ okButtonProps: { danger: true },
+ cancelText: t('sidebar.action.cancel'),
+ onOk: () => resolve(true),
+ onCancel: () => resolve(false),
+ });
+ });
+ if (!ok) return;
+
+ setIsBatchModalOpen(false);
+ const hide = message.loading(t('sidebar.message.deleting_selected_tables', { count: tableNames.length }), 0);
+ const startTime = Date.now();
+ const successKeys: string[] = [];
+ let failed: { table: string; error: string } | null = null;
+ try {
+ for (const item of selectedTables) {
+ const res = await DropTable(normalizeConnConfig(conn.config) as any, dbName, item.objectName);
+ if (!res.success) {
+ failed = { table: item.objectName, error: res.message || t('common.unknown') };
+ break;
+ }
+ successKeys.push(item.key);
+ }
+ } catch (e: any) {
+ failed = { table: tableNames[successKeys.length] || tableNames[0], error: e?.message || String(e) };
+ } finally {
+ hide();
+ }
+
+ if (successKeys.length > 0) {
+ const successKeySet = new Set(successKeys);
+ setBatchTables(prev => prev.filter(item => !successKeySet.has(item.key)));
+ setCheckedTableKeys(prev => prev.filter(key => !successKeySet.has(key)));
+ }
+
+ const duration = Date.now() - startTime;
+ const logSql = `/* Drop Tables (${tableNames.length} tables) */\n${tableNames.map(name => `DROP TABLE ${name}`).join(';\n')};`;
+ addSqlLog({
+ id: Date.now().toString(),
+ timestamp: Date.now(),
+ sql: logSql,
+ status: failed ? 'error' : 'success',
+ duration,
+ message: failed ? failed.error : t('sidebar.message.delete_tables_success', { count: successKeys.length }),
+ dbName,
+ affectedRows: successKeys.length,
+ });
+
+ if (failed) {
+ message.error(t('sidebar.message.delete_tables_failed', {
+ table: failed.table,
+ error: failed.error,
+ }));
+ return;
+ }
+ message.success(t('sidebar.message.delete_tables_success', { count: successKeys.length }));
+ };
+
const handleCheckAll = (checked: boolean) => {
if (batchSelectionScope === 'all') {
setCheckedTableKeys(checked ? allBatchObjectKeys : []);
@@ -678,6 +759,81 @@ export const useSidebarBatchExport = ({
}
};
+ const handleBatchDbDelete = async () => {
+ const selectedDbs = batchDatabases.filter(db => checkedDbKeys.includes(db.key));
+ if (selectedDbs.length === 0) {
+ message.warning(t('sidebar.message.select_database_required'));
+ return;
+ }
+ if (!batchConnContext?.config) {
+ message.warning(t('sidebar.message.select_connection_or_database_first'));
+ return;
+ }
+
+ const ok = await new Promise((resolve) => {
+ Modal.confirm({
+ title: t('sidebar.modal.confirm_delete_selected_databases.title'),
+ content: t('sidebar.modal.confirm_delete_selected_databases.content', {
+ connection: batchConnContext.name,
+ count: selectedDbs.length,
+ }),
+ okText: t('sidebar.action.delete'),
+ okButtonProps: { danger: true },
+ cancelText: t('sidebar.action.cancel'),
+ onOk: () => resolve(true),
+ onCancel: () => resolve(false),
+ });
+ });
+ if (!ok) return;
+
+ setIsBatchDbModalOpen(false);
+ const hide = message.loading(t('sidebar.message.deleting_selected_databases', { count: selectedDbs.length }), 0);
+ const startTime = Date.now();
+ const successKeys: string[] = [];
+ let failed: { database: string; error: string } | null = null;
+ try {
+ for (const dbItem of selectedDbs) {
+ const res = await DropDatabase(normalizeConnConfig(batchConnContext.config) as any, dbItem.dbName);
+ if (!res.success) {
+ failed = { database: dbItem.dbName, error: res.message || t('common.unknown') };
+ break;
+ }
+ successKeys.push(dbItem.key);
+ }
+ } catch (e: any) {
+ failed = { database: selectedDbs[successKeys.length]?.dbName || selectedDbs[0]?.dbName || '', error: e?.message || String(e) };
+ } finally {
+ hide();
+ }
+
+ if (successKeys.length > 0) {
+ const successKeySet = new Set(successKeys);
+ setBatchDatabases(prev => prev.filter(item => !successKeySet.has(item.key)));
+ setCheckedDbKeys(prev => prev.filter(key => !successKeySet.has(key)));
+ }
+
+ const duration = Date.now() - startTime;
+ const dbNames = selectedDbs.map(db => db.dbName);
+ addSqlLog({
+ id: Date.now().toString(),
+ timestamp: Date.now(),
+ sql: `/* Drop Databases (${dbNames.length} databases) */\n${dbNames.map(name => `DROP DATABASE ${name}`).join(';\n')};`,
+ status: failed ? 'error' : 'success',
+ duration,
+ message: failed ? failed.error : t('sidebar.message.delete_databases_success', { count: successKeys.length }),
+ affectedRows: successKeys.length,
+ });
+
+ if (failed) {
+ message.error(t('sidebar.message.delete_databases_failed', {
+ database: failed.database,
+ error: failed.error,
+ }));
+ return;
+ }
+ message.success(t('sidebar.message.delete_databases_success', { count: successKeys.length }));
+ };
+
const handleCheckAllDb = (checked: boolean) => {
if (checked) {
setCheckedDbKeys(batchDatabases.map(db => db.key));
@@ -724,12 +880,14 @@ export const useSidebarBatchExport = ({
handleDatabaseChange,
handleBatchExport,
handleBatchClear,
+ handleBatchDeleteTables,
handleCheckAll,
handleInvertSelection,
openBatchDatabaseModal,
openBatchDatabaseExportWorkbench,
handleDbConnectionChange,
handleBatchDbExport,
+ handleBatchDbDelete,
handleCheckAllDb,
handleInvertSelectionDb,
};
diff --git a/shared/i18n/de-DE.json b/shared/i18n/de-DE.json
index f560eac2..b10711c6 100644
--- a/shared/i18n/de-DE.json
+++ b/shared/i18n/de-DE.json
@@ -6594,6 +6594,8 @@
"sidebar.action.close": "Schließen",
"sidebar.action.continue": "Fortfahren",
"sidebar.action.delete": "Löschen",
+ "sidebar.action.delete_database_count": "Datenbanken löschen ({{count}})",
+ "sidebar.action.delete_tables": "Tabellen löschen",
"sidebar.action.export_data_only": "Nur Daten (INSERT)",
"sidebar.action.export_database_schema_count": "Datenbankstrukturen exportieren ({{count}})",
"sidebar.action.export_schema": "Struktur exportieren",
@@ -6828,9 +6830,15 @@
"sidebar.message.database_renamed": "Datenbank umbenannt.",
"sidebar.message.delete_connection_backend_unavailable": "Verbindungen können in diesem Build nicht gelöscht werden.",
"sidebar.message.delete_connection_failed": "Verbindung konnte nicht gelöscht werden.",
+ "sidebar.message.delete_databases_failed": "Datenbank {{database}} konnte nicht gelöscht werden: {{error}}",
+ "sidebar.message.delete_databases_success": "{{count}} Datenbanken gelöscht.",
"sidebar.message.delete_failed": "Löschen fehlgeschlagen: {{error}}",
"sidebar.message.delete_sql_directory_failed": "SQL-Verzeichnis konnte nicht gelöscht werden: {{error}}",
"sidebar.message.delete_sql_file_failed": "SQL-Datei konnte nicht gelöscht werden: {{error}}",
+ "sidebar.message.delete_tables_failed": "Tabelle {{table}} konnte nicht gelöscht werden: {{error}}",
+ "sidebar.message.delete_tables_success": "{{count}} Tabellen gelöscht.",
+ "sidebar.message.deleting_selected_databases": "{{count}} ausgewählte Datenbanken werden gelöscht...",
+ "sidebar.message.deleting_selected_tables": "{{count}} ausgewählte Tabellen werden gelöscht...",
"sidebar.message.disconnected": "Getrennt.",
"sidebar.message.duplicate_backend_unavailable": "Das Duplizieren von Verbindungen ist in diesem Build nicht verfügbar.",
"sidebar.message.duplicate_empty_result": "Die duplizierte Verbindung wurde nicht zurückgegeben.",
@@ -6914,6 +6922,7 @@
"sidebar.message.select_database_required": "Wählen Sie mindestens eine Datenbank aus.",
"sidebar.message.select_host_before_large_sql_file": "Wählen Sie einen Host aus, bevor Sie eine große SQL-Datei ausführen.",
"sidebar.message.select_object_required": "Wählen Sie mindestens ein Objekt aus.",
+ "sidebar.message.select_table_required": "Wählen Sie mindestens eine Tabelle aus.",
"sidebar.message.select_sql_directory_failed": "SQL-Verzeichnis konnte nicht ausgewählt werden: {{error}}",
"sidebar.message.sidebar_filter_reset": "Seitenleistenfilter zurückgesetzt.",
"sidebar.message.sidebar_filter_sync_disabled": "Synchronisierung des linken Filters deaktiviert.",
@@ -6954,6 +6963,10 @@
"sidebar.modal.batch_tables.title": "Stapeloperationen für Objekte",
"sidebar.modal.confirm_clear_selected_tables.content": "Daten der ausgewählten Objekte unter {{connection}} / {{database}} leeren? Dies kann nicht rückgängig gemacht werden.",
"sidebar.modal.confirm_clear_selected_tables.title": "Ausgewählte Tabellen leeren",
+ "sidebar.modal.confirm_delete_selected_databases.content": "{{count}} ausgewählte Datenbanken unter {{connection}} löschen? Dies kann nicht rückgängig gemacht werden.",
+ "sidebar.modal.confirm_delete_selected_databases.title": "Ausgewählte Datenbanken löschen",
+ "sidebar.modal.confirm_delete_selected_tables.content": "{{count}} ausgewählte Tabellen unter {{connection}} / {{database}} löschen? Dies kann nicht rückgängig gemacht werden.",
+ "sidebar.modal.confirm_delete_selected_tables.title": "Ausgewählte Tabellen löschen",
"sidebar.modal.confirm_delete_connection.content": "{{name}} löschen?",
"sidebar.modal.confirm_delete_database.content": "{{name}} löschen? Dies kann nicht rückgängig gemacht werden.",
"sidebar.modal.confirm_delete_database.title": "Datenbank löschen",
diff --git a/shared/i18n/en-US.json b/shared/i18n/en-US.json
index c6033c37..01208013 100644
--- a/shared/i18n/en-US.json
+++ b/shared/i18n/en-US.json
@@ -6594,6 +6594,8 @@
"sidebar.action.close": "Close",
"sidebar.action.continue": "Continue",
"sidebar.action.delete": "Delete",
+ "sidebar.action.delete_database_count": "Delete databases ({{count}})",
+ "sidebar.action.delete_tables": "Delete tables",
"sidebar.action.export_data_only": "Data only (INSERT)",
"sidebar.action.export_database_schema_count": "Export database schemas ({{count}})",
"sidebar.action.export_schema": "Export schema",
@@ -6828,9 +6830,15 @@
"sidebar.message.database_renamed": "Database renamed.",
"sidebar.message.delete_connection_backend_unavailable": "Delete connection is not available in this build.",
"sidebar.message.delete_connection_failed": "Failed to delete connection.",
+ "sidebar.message.delete_databases_failed": "Failed to delete database {{database}}: {{error}}",
+ "sidebar.message.delete_databases_success": "Deleted {{count}} databases.",
"sidebar.message.delete_failed": "Delete failed: {{error}}",
"sidebar.message.delete_sql_directory_failed": "Failed to delete SQL directory: {{error}}",
"sidebar.message.delete_sql_file_failed": "Failed to delete SQL file: {{error}}",
+ "sidebar.message.delete_tables_failed": "Failed to delete table {{table}}: {{error}}",
+ "sidebar.message.delete_tables_success": "Deleted {{count}} tables.",
+ "sidebar.message.deleting_selected_databases": "Deleting {{count}} selected databases...",
+ "sidebar.message.deleting_selected_tables": "Deleting {{count}} selected tables...",
"sidebar.message.disconnected": "Disconnected.",
"sidebar.message.duplicate_backend_unavailable": "Duplicate connection is not available in this build.",
"sidebar.message.duplicate_empty_result": "The duplicated connection was not returned.",
@@ -6914,6 +6922,7 @@
"sidebar.message.select_database_required": "Select at least one database.",
"sidebar.message.select_host_before_large_sql_file": "Select a Host before running a large SQL file.",
"sidebar.message.select_object_required": "Select at least one object.",
+ "sidebar.message.select_table_required": "Select at least one table.",
"sidebar.message.select_sql_directory_failed": "Failed to select SQL directory: {{error}}",
"sidebar.message.sidebar_filter_reset": "Sidebar filter reset.",
"sidebar.message.sidebar_filter_sync_disabled": "Left-side filter sync disabled.",
@@ -6954,6 +6963,10 @@
"sidebar.modal.batch_tables.title": "Batch object operations",
"sidebar.modal.confirm_clear_selected_tables.content": "Clear data in selected objects under {{connection}} / {{database}}? This cannot be undone.",
"sidebar.modal.confirm_clear_selected_tables.title": "Confirm clear selected tables",
+ "sidebar.modal.confirm_delete_selected_databases.content": "Delete {{count}} selected databases under {{connection}}? This cannot be undone.",
+ "sidebar.modal.confirm_delete_selected_databases.title": "Confirm delete selected databases",
+ "sidebar.modal.confirm_delete_selected_tables.content": "Delete {{count}} selected tables under {{connection}} / {{database}}? This cannot be undone.",
+ "sidebar.modal.confirm_delete_selected_tables.title": "Confirm delete selected tables",
"sidebar.modal.confirm_delete_connection.content": "Delete {{name}}?",
"sidebar.modal.confirm_delete_database.content": "Delete {{name}}? This cannot be undone.",
"sidebar.modal.confirm_delete_database.title": "Delete database",
diff --git a/shared/i18n/ja-JP.json b/shared/i18n/ja-JP.json
index 804c2843..490516bb 100644
--- a/shared/i18n/ja-JP.json
+++ b/shared/i18n/ja-JP.json
@@ -6594,6 +6594,8 @@
"sidebar.action.close": "閉じる",
"sidebar.action.continue": "続行",
"sidebar.action.delete": "削除",
+ "sidebar.action.delete_database_count": "データベースを削除({{count}})",
+ "sidebar.action.delete_tables": "テーブルを削除",
"sidebar.action.export_data_only": "データのみ(INSERT)",
"sidebar.action.export_database_schema_count": "データベース構造をエクスポート({{count}})",
"sidebar.action.export_schema": "構造をエクスポート",
@@ -6828,9 +6830,15 @@
"sidebar.message.database_renamed": "データベース名を変更しました。",
"sidebar.message.delete_connection_backend_unavailable": "このビルドでは接続の削除を利用できません。",
"sidebar.message.delete_connection_failed": "接続の削除に失敗しました。",
+ "sidebar.message.delete_databases_failed": "データベース {{database}} の削除に失敗しました: {{error}}",
+ "sidebar.message.delete_databases_success": "{{count}} 件のデータベースを削除しました。",
"sidebar.message.delete_failed": "削除に失敗しました: {{error}}",
"sidebar.message.delete_sql_directory_failed": "SQL ディレクトリの削除に失敗しました:{{error}}",
"sidebar.message.delete_sql_file_failed": "SQL ファイルの削除に失敗しました:{{error}}",
+ "sidebar.message.delete_tables_failed": "テーブル {{table}} の削除に失敗しました: {{error}}",
+ "sidebar.message.delete_tables_success": "{{count}} 件のテーブルを削除しました。",
+ "sidebar.message.deleting_selected_databases": "選択した {{count}} 件のデータベースを削除しています...",
+ "sidebar.message.deleting_selected_tables": "選択した {{count}} 件のテーブルを削除しています...",
"sidebar.message.disconnected": "切断しました。",
"sidebar.message.duplicate_backend_unavailable": "このビルドでは接続の複製を利用できません。",
"sidebar.message.duplicate_empty_result": "複製された接続が返されませんでした。",
@@ -6914,6 +6922,7 @@
"sidebar.message.select_database_required": "データベースを 1 件以上選択してください。",
"sidebar.message.select_host_before_large_sql_file": "大きな SQL ファイルを実行する前に Host を選択してください。",
"sidebar.message.select_object_required": "オブジェクトを 1 件以上選択してください。",
+ "sidebar.message.select_table_required": "テーブルを 1 件以上選択してください。",
"sidebar.message.select_sql_directory_failed": "SQL ディレクトリの選択に失敗しました: {{error}}",
"sidebar.message.sidebar_filter_reset": "サイドバーフィルターをリセットしました。",
"sidebar.message.sidebar_filter_sync_disabled": "左側フィルターの同期を無効にしました。",
@@ -6954,6 +6963,10 @@
"sidebar.modal.batch_tables.title": "オブジェクト一括操作",
"sidebar.modal.confirm_clear_selected_tables.content": "{{connection}} / {{database}} の選択したオブジェクトのデータをクリアしますか?この操作は元に戻せません。",
"sidebar.modal.confirm_clear_selected_tables.title": "選択したテーブルのクリアを確認",
+ "sidebar.modal.confirm_delete_selected_databases.content": "{{connection}} 配下の選択した {{count}} 件のデータベースを削除しますか?この操作は元に戻せません。",
+ "sidebar.modal.confirm_delete_selected_databases.title": "選択したデータベースの削除を確認",
+ "sidebar.modal.confirm_delete_selected_tables.content": "{{connection}} / {{database}} 配下の選択した {{count}} 件のテーブルを削除しますか?この操作は元に戻せません。",
+ "sidebar.modal.confirm_delete_selected_tables.title": "選択したテーブルの削除を確認",
"sidebar.modal.confirm_delete_connection.content": "接続 {{name}} を削除しますか?",
"sidebar.modal.confirm_delete_database.content": "{{name}} を削除しますか?この操作は元に戻せません。",
"sidebar.modal.confirm_delete_database.title": "データベースを削除",
diff --git a/shared/i18n/ru-RU.json b/shared/i18n/ru-RU.json
index 3738a4cd..23212316 100644
--- a/shared/i18n/ru-RU.json
+++ b/shared/i18n/ru-RU.json
@@ -6594,6 +6594,8 @@
"sidebar.action.close": "Закрыть",
"sidebar.action.continue": "Продолжить",
"sidebar.action.delete": "Удалить",
+ "sidebar.action.delete_database_count": "Удалить базы данных ({{count}})",
+ "sidebar.action.delete_tables": "Удалить таблицы",
"sidebar.action.export_data_only": "Только данные (INSERT)",
"sidebar.action.export_database_schema_count": "Экспортировать структуры баз данных ({{count}})",
"sidebar.action.export_schema": "Экспортировать структуру",
@@ -6828,9 +6830,15 @@
"sidebar.message.database_renamed": "База данных переименована.",
"sidebar.message.delete_connection_backend_unavailable": "Удаление подключений недоступно в этой сборке.",
"sidebar.message.delete_connection_failed": "Не удалось удалить подключение.",
+ "sidebar.message.delete_databases_failed": "Не удалось удалить базу данных {{database}}: {{error}}",
+ "sidebar.message.delete_databases_success": "Удалено баз данных: {{count}}.",
"sidebar.message.delete_failed": "Не удалось удалить: {{error}}",
"sidebar.message.delete_sql_directory_failed": "Не удалось удалить SQL-каталог: {{error}}",
"sidebar.message.delete_sql_file_failed": "Не удалось удалить SQL-файл: {{error}}",
+ "sidebar.message.delete_tables_failed": "Не удалось удалить таблицу {{table}}: {{error}}",
+ "sidebar.message.delete_tables_success": "Удалено таблиц: {{count}}.",
+ "sidebar.message.deleting_selected_databases": "Удаляются выбранные базы данных: {{count}}...",
+ "sidebar.message.deleting_selected_tables": "Удаляются выбранные таблицы: {{count}}...",
"sidebar.message.disconnected": "Отключено.",
"sidebar.message.duplicate_backend_unavailable": "Дублирование подключений недоступно в этой сборке.",
"sidebar.message.duplicate_empty_result": "Дублированное подключение не было возвращено.",
@@ -6914,6 +6922,7 @@
"sidebar.message.select_database_required": "Выберите хотя бы одну базу данных.",
"sidebar.message.select_host_before_large_sql_file": "Выберите Host перед запуском большого SQL-файла.",
"sidebar.message.select_object_required": "Выберите хотя бы один объект.",
+ "sidebar.message.select_table_required": "Выберите хотя бы одну таблицу.",
"sidebar.message.select_sql_directory_failed": "Не удалось выбрать SQL-каталог: {{error}}",
"sidebar.message.sidebar_filter_reset": "Фильтр боковой панели сброшен.",
"sidebar.message.sidebar_filter_sync_disabled": "Синхронизация левого фильтра отключена.",
@@ -6954,6 +6963,10 @@
"sidebar.modal.batch_tables.title": "Пакетные операции с объектами",
"sidebar.modal.confirm_clear_selected_tables.content": "Очистить данные выбранных объектов в {{connection}} / {{database}}? Это действие нельзя отменить.",
"sidebar.modal.confirm_clear_selected_tables.title": "Подтвердите очистку выбранных таблиц",
+ "sidebar.modal.confirm_delete_selected_databases.content": "Удалить выбранные базы данных ({{count}}) в {{connection}}? Это действие нельзя отменить.",
+ "sidebar.modal.confirm_delete_selected_databases.title": "Подтвердите удаление выбранных баз данных",
+ "sidebar.modal.confirm_delete_selected_tables.content": "Удалить выбранные таблицы ({{count}}) в {{connection}} / {{database}}? Это действие нельзя отменить.",
+ "sidebar.modal.confirm_delete_selected_tables.title": "Подтвердите удаление выбранных таблиц",
"sidebar.modal.confirm_delete_connection.content": "Удалить {{name}}?",
"sidebar.modal.confirm_delete_database.content": "Удалить {{name}}? Это действие нельзя отменить.",
"sidebar.modal.confirm_delete_database.title": "Удалить базу данных",
diff --git a/shared/i18n/zh-CN.json b/shared/i18n/zh-CN.json
index 99e78f85..3c120667 100644
--- a/shared/i18n/zh-CN.json
+++ b/shared/i18n/zh-CN.json
@@ -6594,6 +6594,8 @@
"sidebar.action.close": "关闭",
"sidebar.action.continue": "继续",
"sidebar.action.delete": "删除",
+ "sidebar.action.delete_database_count": "删除库({{count}})",
+ "sidebar.action.delete_tables": "删除表",
"sidebar.action.export_data_only": "仅数据(INSERT)",
"sidebar.action.export_database_schema_count": "导出库结构({{count}})",
"sidebar.action.export_schema": "导出结构",
@@ -6828,9 +6830,15 @@
"sidebar.message.database_renamed": "数据库已重命名。",
"sidebar.message.delete_connection_backend_unavailable": "删除连接后端不可用。",
"sidebar.message.delete_connection_failed": "删除连接失败。",
+ "sidebar.message.delete_databases_failed": "删除库 {{database}} 失败:{{error}}",
+ "sidebar.message.delete_databases_success": "已删除 {{count}} 个库。",
"sidebar.message.delete_failed": "删除失败:{{error}}",
"sidebar.message.delete_sql_directory_failed": "删除 SQL 目录失败:{{error}}",
"sidebar.message.delete_sql_file_failed": "删除 SQL 文件失败:{{error}}",
+ "sidebar.message.delete_tables_failed": "删除表 {{table}} 失败:{{error}}",
+ "sidebar.message.delete_tables_success": "已删除 {{count}} 张表。",
+ "sidebar.message.deleting_selected_databases": "正在删除 {{count}} 个已选库...",
+ "sidebar.message.deleting_selected_tables": "正在删除 {{count}} 张已选表...",
"sidebar.message.disconnected": "已断开。",
"sidebar.message.duplicate_backend_unavailable": "当前版本不支持复制连接。",
"sidebar.message.duplicate_empty_result": "未返回复制后的连接。",
@@ -6914,6 +6922,7 @@
"sidebar.message.select_database_required": "请至少选择一个数据库。",
"sidebar.message.select_host_before_large_sql_file": "请先选择一个 Host 后再执行大 SQL 文件。",
"sidebar.message.select_object_required": "请至少选择一个对象。",
+ "sidebar.message.select_table_required": "请至少选择一张表。",
"sidebar.message.select_sql_directory_failed": "选择 SQL 目录失败:{{error}}",
"sidebar.message.sidebar_filter_reset": "已重置侧栏筛选。",
"sidebar.message.sidebar_filter_sync_disabled": "已关闭左侧筛选同步。",
@@ -6954,6 +6963,10 @@
"sidebar.modal.batch_tables.title": "批量操作表",
"sidebar.modal.confirm_clear_selected_tables.content": "清空 {{connection}} / {{database}} 下已选对象的数据?此操作不可撤销。",
"sidebar.modal.confirm_clear_selected_tables.title": "确认清空已选表",
+ "sidebar.modal.confirm_delete_selected_databases.content": "删除 {{connection}} 下 {{count}} 个已选库?此操作不可撤销。",
+ "sidebar.modal.confirm_delete_selected_databases.title": "确认删除已选库",
+ "sidebar.modal.confirm_delete_selected_tables.content": "删除 {{connection}} / {{database}} 下 {{count}} 张已选表?此操作不可撤销。",
+ "sidebar.modal.confirm_delete_selected_tables.title": "确认删除已选表",
"sidebar.modal.confirm_delete_connection.content": "删除连接 {{name}}?",
"sidebar.modal.confirm_delete_database.content": "删除 {{name}}?此操作不可撤销。",
"sidebar.modal.confirm_delete_database.title": "删除数据库",
diff --git a/shared/i18n/zh-TW.json b/shared/i18n/zh-TW.json
index 1d770d7d..e54cb58b 100644
--- a/shared/i18n/zh-TW.json
+++ b/shared/i18n/zh-TW.json
@@ -6594,6 +6594,8 @@
"sidebar.action.close": "關閉",
"sidebar.action.continue": "繼續",
"sidebar.action.delete": "刪除",
+ "sidebar.action.delete_database_count": "刪除資料庫({{count}})",
+ "sidebar.action.delete_tables": "刪除資料表",
"sidebar.action.export_data_only": "僅資料(INSERT)",
"sidebar.action.export_database_schema_count": "匯出資料庫結構({{count}})",
"sidebar.action.export_schema": "匯出結構",
@@ -6828,9 +6830,15 @@
"sidebar.message.database_renamed": "資料庫已重新命名。",
"sidebar.message.delete_connection_backend_unavailable": "刪除連線後端不可用。",
"sidebar.message.delete_connection_failed": "刪除連線失敗。",
+ "sidebar.message.delete_databases_failed": "刪除資料庫 {{database}} 失敗:{{error}}",
+ "sidebar.message.delete_databases_success": "已刪除 {{count}} 個資料庫。",
"sidebar.message.delete_failed": "刪除失敗:{{error}}",
"sidebar.message.delete_sql_directory_failed": "刪除 SQL 目錄失敗:{{error}}",
"sidebar.message.delete_sql_file_failed": "刪除 SQL 檔案失敗:{{error}}",
+ "sidebar.message.delete_tables_failed": "刪除資料表 {{table}} 失敗:{{error}}",
+ "sidebar.message.delete_tables_success": "已刪除 {{count}} 張資料表。",
+ "sidebar.message.deleting_selected_databases": "正在刪除 {{count}} 個已選資料庫...",
+ "sidebar.message.deleting_selected_tables": "正在刪除 {{count}} 張已選資料表...",
"sidebar.message.disconnected": "已中斷。",
"sidebar.message.duplicate_backend_unavailable": "目前版本不支援複製連線。",
"sidebar.message.duplicate_empty_result": "未傳回複製後的連線。",
@@ -6914,6 +6922,7 @@
"sidebar.message.select_database_required": "請至少選擇一個資料庫。",
"sidebar.message.select_host_before_large_sql_file": "請先選擇 Host,再執行大型 SQL 檔案。",
"sidebar.message.select_object_required": "請至少選擇一個物件。",
+ "sidebar.message.select_table_required": "請至少選擇一張資料表。",
"sidebar.message.select_sql_directory_failed": "選擇 SQL 目錄失敗:{{error}}",
"sidebar.message.sidebar_filter_reset": "已重設側欄篩選。",
"sidebar.message.sidebar_filter_sync_disabled": "已關閉左側篩選同步。",
@@ -6954,6 +6963,10 @@
"sidebar.modal.batch_tables.title": "批次操作資料表",
"sidebar.modal.confirm_clear_selected_tables.content": "清除 {{connection}} / {{database}} 下已選物件的資料?此操作無法復原。",
"sidebar.modal.confirm_clear_selected_tables.title": "確認清除已選資料表",
+ "sidebar.modal.confirm_delete_selected_databases.content": "刪除 {{connection}} 下 {{count}} 個已選資料庫?此操作無法復原。",
+ "sidebar.modal.confirm_delete_selected_databases.title": "確認刪除已選資料庫",
+ "sidebar.modal.confirm_delete_selected_tables.content": "刪除 {{connection}} / {{database}} 下 {{count}} 張已選資料表?此操作無法復原。",
+ "sidebar.modal.confirm_delete_selected_tables.title": "確認刪除已選資料表",
"sidebar.modal.confirm_delete_connection.content": "刪除連線 {{name}}?",
"sidebar.modal.confirm_delete_database.content": "刪除 {{name}}?此操作無法復原。",
"sidebar.modal.confirm_delete_database.title": "刪除資料庫",