From f80b1e8bc97b536be90f2b06a4bbe01e6f80b99c Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Sun, 17 May 2026 10:54:50 +0800 Subject: [PATCH] feat(schema): add object rename actions --- src/components/objects/ObjectBrowser.vue | 100 ++++++++++++++++++++++ src/components/sidebar/TreeItem.vue | 101 +++++++++++++++++++++++ src/i18n/locales/en.ts | 4 + src/i18n/locales/zh-CN.ts | 4 + src/lib/objectRenameSql.ts | 87 +++++++++++++++++++ tests/objectRenameSql.test.ts | 99 ++++++++++++++++++++++ 6 files changed, 395 insertions(+) create mode 100644 src/lib/objectRenameSql.ts create mode 100644 tests/objectRenameSql.test.ts diff --git a/src/components/objects/ObjectBrowser.vue b/src/components/objects/ObjectBrowser.vue index 271b522cf..6a8f818c1 100644 --- a/src/components/objects/ObjectBrowser.vue +++ b/src/components/objects/ObjectBrowser.vue @@ -7,6 +7,7 @@ import { Copy, Eye, Loader2, + Pencil, PencilLine, RefreshCw, Search, @@ -34,11 +35,13 @@ import { isSchemaAware } from "@/lib/databaseCapabilities"; import { buildTableSelectSql, qualifiedTableName } from "@/lib/tableSelectSql"; import { useToast } from "@/composables/useToast"; import { buildExecutableObjectSourceSql, objectSourceSaveExecutionMode } from "@/lib/objectSourceEditor"; +import { buildRenameObjectSql, supportsObjectRename } from "@/lib/objectRenameSql"; import { useConnectionStore } from "@/stores/connectionStore"; import { useQueryStore } from "@/stores/queryStore"; import QueryEditor from "@/components/editor/QueryEditor.vue"; import type { SqlFormatDialect } from "@/lib/sqlFormatter"; import { isCancelSearchShortcut } from "@/lib/keyboardShortcuts"; +import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; type ObjectRow = { id: string; @@ -85,6 +88,10 @@ const sourceSaveError = ref(""); const error = ref(""); const showDropConfirm = ref(false); const dropTarget = ref(null); +const showRenameDialog = ref(false); +const renameTarget = ref(null); +const renameInput = ref(""); +const renameError = ref(""); let loadId = 0; const needsSchema = computed(() => isSchemaAware(props.connection.db_type)); @@ -176,6 +183,10 @@ function canOpenSource(row: ObjectRow) { return row.type === "VIEW" || row.type === "PROCEDURE" || row.type === "FUNCTION"; } +function canRename(row: ObjectRow) { + return supportsObjectRename(props.connection.db_type, row.type); +} + function sourceTitle(row: ObjectRow | null) { if (!row) return t("objects.source"); return `${row.name} ${t("objects.source")}`; @@ -241,6 +252,59 @@ function requestDrop(row: ObjectRow) { showDropConfirm.value = true; } +function requestRename(row: ObjectRow) { + renameTarget.value = row; + renameInput.value = row.name; + renameError.value = ""; + showRenameDialog.value = true; +} + +function renamePreviewSql() { + const row = renameTarget.value; + const newName = renameInput.value.trim(); + if (!row || !newName || newName === row.name) return ""; + try { + return buildRenameObjectSql({ + databaseType: props.connection.db_type, + objectType: row.type, + schema: row.schema || selectedSchema.value, + oldName: row.name, + newName, + }); + } catch { + return ""; + } +} + +async function confirmRename() { + const row = renameTarget.value; + const newName = renameInput.value.trim(); + if (!row || !newName || newName === row.name) return; + renameError.value = ""; + try { + const schema = row.schema || selectedSchema.value || props.database; + const sql = buildRenameObjectSql({ + databaseType: props.connection.db_type, + objectType: row.type, + schema, + oldName: row.name, + newName, + }); + await api.executeQuery(props.connection.id, props.database, sql, schema); + toast(t("contextMenu.renameObjectSuccess", { oldName: row.name, newName })); + showRenameDialog.value = false; + if (sourceRow.value?.id === row.id) closeSource(); + await reload(); + await connectionStore.refreshObjectListTreeNode( + props.connection.id, + props.database, + row.schema || selectedSchema.value, + ); + } catch (e: any) { + renameError.value = e?.message || String(e); + } +} + async function confirmDrop() { if (!dropTarget.value) return; const row = dropTarget.value; @@ -570,6 +634,9 @@ watch( {{ t("contextMenu.newQuery") }} + + {{ t("contextMenu.renameObject") }} + {{ t("contextMenu.dropTable") }} @@ -580,6 +647,9 @@ watch( {{ t("contextMenu.viewSource") }} + + {{ t("contextMenu.renameObject") }} + {{ t("contextMenu.dropView") }} @@ -590,6 +660,9 @@ watch( {{ t("contextMenu.viewSource") }} + + {{ t("contextMenu.renameObject") }} + @@ -694,6 +767,33 @@ watch( :confirm-label="t('dangerDialog.deleteConfirm')" @confirm="confirmDrop" /> + + + + + {{ t("contextMenu.renameObjectTitle") }} + +
+ +
{{ renamePreviewSql() }}
+

{{ renameError }}

+
+ + + + +
+