From 9fc2e25fbf1abdec40aa2fa9c6f7d52149c55749 Mon Sep 17 00:00:00 2001 From: kkk000111999 <254467556@qq.com> Date: Sun, 28 Jun 2026 14:52:25 +0800 Subject: [PATCH] feat(grid): add go-to-column toolbar action --- apps/desktop/src/components/grid/DataGrid.vue | 66 +++++++++++++++++++ apps/desktop/src/i18n/locales/en.ts | 3 + apps/desktop/src/i18n/locales/zh-CN.ts | 3 + 3 files changed, 72 insertions(+) diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index 3a1c716c2..2f6a250d2 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -63,6 +63,7 @@ import { PanelRight, TableProperties, Database, + Columns3, } from "@lucide/vue"; import { Button } from "@/components/ui/button"; import QueryLoadingState from "@/components/common/QueryLoadingState.vue"; @@ -1648,6 +1649,9 @@ const nullColumnsHidden = ref(false); const autoHiddenNullColumnIndexes = ref>(new Set()); const highlightedColumnIndex = ref(null); let highlightedColumnTimer = 0; + +const goToColumnOpen = ref(false); +const goToColumnSearch = ref(""); const columnOrderKeys = computed(() => uniqueDataGridColumnOrderKeys(props.result.columns, props.sourceColumns)); const columnLayoutScopeKey = computed(() => dataGridColumnLayoutScopeKey({ @@ -1669,6 +1673,18 @@ const displayableColumnIndexes = computed(() => .filter(({ column }) => !isHiddenGridColumn(props.databaseType, column, props.tableMeta?.primaryKeys ?? [])) .map(({ index }) => index), ); +const goToColumnItems = computed(() => + displayableColumnIndexes.value.map((index) => ({ + index, + name: props.result.columns[index] ?? `#${index + 1}`, + sourceName: props.sourceColumns?.[index], + })), +); +const filteredGoToColumns = computed(() => { + const query = goToColumnSearch.value.trim().toLocaleLowerCase(); + if (!query) return goToColumnItems.value; + return goToColumnItems.value.filter((column) => column.name.toLocaleLowerCase().includes(query) || column.sourceName?.toLocaleLowerCase().includes(query)); +}); const orderedDisplayableColumnIndexes = computed(() => orderedColumnIndexes({ availableIndexes: displayableColumnIndexes.value, @@ -1780,12 +1796,33 @@ const firstVisibleColumnIndex = computed(() => visibleColumnIndexes.value[0] ?? function actualColumnIndex(visibleColumnIndex: number): number { return visibleColumnIndexes.value[visibleColumnIndex] ?? visibleColumnIndex; } +function scrollToColumn(columnIndex: number) { + goToColumnOpen.value = false; + goToColumnSearch.value = ""; + scrollToColumnIndex(columnIndex); + gridRef.value?.focus(); +} + +function onGoToColumnKeydown(event: KeyboardEvent) { + if (event.key === "Escape") { + goToColumnOpen.value = false; + goToColumnSearch.value = ""; + } +} + +watch(goToColumnOpen, (open) => { + if (!open) goToColumnSearch.value = ""; +}); + function matchesTableInfoColumn(resultColumn: string, sourceColumn: string | undefined, columnName: string): boolean { const target = columnName.toLocaleLowerCase(); return resultColumn.toLocaleLowerCase() === target || sourceColumn?.toLocaleLowerCase() === target; } function scrollToTableInfoColumn(columnName: string) { const columnIndex = props.result.columns.findIndex((column, index) => matchesTableInfoColumn(column, props.sourceColumns?.[index], columnName)); + scrollToColumnIndex(columnIndex); +} +function scrollToColumnIndex(columnIndex: number) { if (columnIndex < 0 || !displayableColumnIndexes.value.includes(columnIndex)) return; if (hiddenColumnIndexes.value.has(columnIndex)) { @@ -7165,6 +7202,35 @@ const gridContextMenuItems = computed(() => { {{ dataGridRenderMode === "canvas" ? t("grid.canvasRenderMode") : t("grid.domRenderMode") }} · {{ t("grid.renderModeHint") }} + + + + + + + +
+ + + +
+
+ +
{{ t("grid.noColumnsFound") }}
+
+
+
+
+ {{ t("grid.goToColumn") }} +