From ad79642502de493e7f88b2be22caa8560db8bbb1 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Sat, 9 May 2026 00:51:51 +0800 Subject: [PATCH] feat: auto-fit column widths based on header and data content Scan column names and first 50 rows to calculate optimal column width instead of using a fixed 150px default. Double-click column separator to auto-fit individual columns. Min 60px, max 400px. --- src/components/grid/DataGrid.vue | 44 ++++++++++++++++++++++++++++- src/components/layout/AppTabBar.vue | 2 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/components/grid/DataGrid.vue b/src/components/grid/DataGrid.vue index 77daed87c0..00b9ced145 100644 --- a/src/components/grid/DataGrid.vue +++ b/src/components/grid/DataGrid.vue @@ -361,9 +361,33 @@ const gridRef = ref(); const headerRef = ref(); const { width: gridWidth } = useElementSize(gridRef); +const COL_MIN_WIDTH = 60; +const COL_MAX_WIDTH = 400; +const COL_CHAR_WIDTH = 8; +const COL_HEADER_PADDING = 48; +const COL_CELL_PADDING = 28; +const COL_SAMPLE_ROWS = 50; + +function estimateTextWidth(text: string, padding: number): number { + return text.length * COL_CHAR_WIDTH + padding; +} + function initColumnWidths() { if (columnWidths.value.length !== props.result.columns.length) { - columnWidths.value = props.result.columns.map(() => 150); + const rows = props.result.rows; + const sampleCount = Math.min(rows.length, COL_SAMPLE_ROWS); + columnWidths.value = props.result.columns.map((colName, colIdx) => { + let maxWidth = estimateTextWidth(colName, COL_HEADER_PADDING); + for (let i = 0; i < sampleCount; i++) { + const val = rows[i]?.[colIdx]; + if (val == null) continue; + const text = typeof val === "object" ? JSON.stringify(val) : String(val); + const displayLen = Math.min(text.length, 60); + const w = displayLen * COL_CHAR_WIDTH + COL_CELL_PADDING; + if (w > maxWidth) maxWidth = w; + } + return Math.max(COL_MIN_WIDTH, Math.min(COL_MAX_WIDTH, Math.round(maxWidth))); + }); } } @@ -394,6 +418,23 @@ function onResizeStart(colIdx: number, event: MouseEvent) { document.addEventListener("mouseup", onUp); } +function autoFitColumn(colIdx: number) { + const colName = props.result.columns[colIdx]; + if (!colName) return; + const rows = props.result.rows; + const sampleCount = Math.min(rows.length, COL_SAMPLE_ROWS); + let maxWidth = estimateTextWidth(colName, COL_HEADER_PADDING); + for (let i = 0; i < sampleCount; i++) { + const val = rows[i]?.[colIdx]; + if (val == null) continue; + const text = typeof val === "object" ? JSON.stringify(val) : String(val); + const displayLen = Math.min(text.length, 60); + const w = displayLen * COL_CHAR_WIDTH + COL_CELL_PADDING; + if (w > maxWidth) maxWidth = w; + } + columnWidths.value[colIdx] = Math.max(COL_MIN_WIDTH, Math.min(COL_MAX_WIDTH, Math.round(maxWidth))); +} + const ROW_NUM_WIDTH = 48; const baseTotalWidth = computed(() => columnWidths.value.reduce((a, b) => a + b, 0)); const renderedColumnWidths = computed(() => { @@ -1552,6 +1593,7 @@ defineExpose({
diff --git a/src/components/layout/AppTabBar.vue b/src/components/layout/AppTabBar.vue index 88b5ac4f7c..2d9ab1e3da 100644 --- a/src/components/layout/AppTabBar.vue +++ b/src/components/layout/AppTabBar.vue @@ -12,7 +12,7 @@ import { import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"; import { useQueryStore } from "@/stores/queryStore"; import { useTabScroll } from "@/composables/useTabScroll"; -import { connectionColor, tabDisplayTitle, tabTooltipLines } from "@/lib/tabPresentation"; +import { tabDisplayTitle, tabTooltipLines } from "@/lib/tabPresentation"; const { t } = useI18n(); const queryStore = useQueryStore();