From e9d58e5aef1fa6a89f117dfcb4578db876e58eb2 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Thu, 21 May 2026 22:26:10 +0800 Subject: [PATCH] feat(grid): add table info column navigation --- apps/desktop/src/components/grid/DataGrid.vue | 54 ++++++++++++++++++- .../dataGridColumnNavigation.test.ts | 20 +++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 packages/app-tests/dataGridColumnNavigation.test.ts diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index 11a036d76..9d3e6f736 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -1070,6 +1070,8 @@ const gridRef = ref(); const headerRef = ref(); const gridScrollbarGutter = ref(0); const hiddenColumnIndexes = ref>(new Set()); +const highlightedColumnIndex = ref(null); +let highlightedColumnTimer = 0; const displayableColumnIndexes = computed(() => props.result.columns .map((column, index) => ({ column, index })) @@ -1111,6 +1113,40 @@ const firstVisibleColumnIndex = computed(() => visibleColumnIndexes.value[0] ?? function actualColumnIndex(visibleColumnIndex: number): number { return visibleColumnIndexes.value[visibleColumnIndex] ?? visibleColumnIndex; } +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), + ); + if (columnIndex < 0 || !displayableColumnIndexes.value.includes(columnIndex)) return; + + if (hiddenColumnIndexes.value.has(columnIndex)) { + hiddenColumnIndexes.value.delete(columnIndex); + hiddenColumnIndexes.value = new Set(hiddenColumnIndexes.value); + } + + highlightedColumnIndex.value = columnIndex; + clearTimeout(highlightedColumnTimer); + highlightedColumnTimer = window.setTimeout(() => { + highlightedColumnIndex.value = null; + }, 1400); + + nextTick(() => { + const visibleColIdx = visibleColumnIndexes.value.indexOf(columnIndex); + const scroller = gridRef.value?.querySelector(".data-grid-scroller"); + const headerCell = headerRef.value?.querySelector(`[data-grid-column-index="${columnIndex}"]`); + if (visibleColIdx < 0 || !scroller || !headerCell) return; + + const targetLeft = Math.max(0, headerCell.offsetLeft - scroller.clientWidth / 2 + headerCell.offsetWidth / 2); + scroller.scrollLeft = targetLeft; + if (headerRef.value) { + headerRef.value.scrollLeft = scroller.scrollLeft; + } + }); +} // --- Column resize composable --- const { initColumnWidths, onResizeStart, autoFitColumn, columnVars, getIsResizing } = useDataGridColumnResize({ @@ -2817,6 +2853,7 @@ onUnmounted(() => { onDdlResizeEnd(); onDetailResizeEnd(); finishCellSelection(); + clearTimeout(highlightedColumnTimer); clearTimeout(_searchTimer); clearInterval(_loadingTimer); }); @@ -3342,7 +3379,12 @@ defineExpose({
- + diff --git a/packages/app-tests/dataGridColumnNavigation.test.ts b/packages/app-tests/dataGridColumnNavigation.test.ts new file mode 100644 index 000000000..337234bd9 --- /dev/null +++ b/packages/app-tests/dataGridColumnNavigation.test.ts @@ -0,0 +1,20 @@ +import { readFileSync } from "node:fs"; +import { strict as assert } from "node:assert"; +import test from "node:test"; + +const source = readFileSync("apps/desktop/src/components/grid/DataGrid.vue", "utf8"); + +test("table info column rows navigate the grid header", () => { + assert.match(source, /function scrollToTableInfoColumn\(columnName: string\)/); + assert.match(source, /@click="scrollToTableInfoColumn\(column\.name\)"/); + assert.match(source, /:data-grid-column-index="actualColumnIndex\(colIdx\)"/); +}); + +test("column navigation reveals hidden columns and keeps header scroll synchronized", () => { + const match = source.match(/function scrollToTableInfoColumn\(columnName: string\) \{([\s\S]*?)\n\}/); + assert.ok(match, "DataGrid should define scrollToTableInfoColumn"); + assert.match(match[1], /hiddenColumnIndexes\.value\.delete\(columnIndex\)/); + assert.match(match[1], /\.data-grid-scroller/); + assert.match(match[1], /headerRef\.value\.scrollLeft = scroller\.scrollLeft/); + assert.match(match[1], /highlightedColumnIndex\.value = columnIndex/); +});