From 17747f8a2d6d558f6c0a6946d821b7ebebb8d4b9 Mon Sep 17 00:00:00 2001 From: LRcoding <38545827+LwClick@users.noreply.github.com> Date: Fri, 17 Jul 2026 00:11:16 +0800 Subject: [PATCH] feat(objects): search table fields by comment --- .../src/components/objects/ObjectBrowser.vue | 7 +-- .../src/lib/table/objectBrowserTableInfo.ts | 13 +++++ .../app-tests/objectBrowserTableInfo.test.ts | 48 +++++++++++++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 apps/desktop/src/lib/table/objectBrowserTableInfo.ts create mode 100644 packages/app-tests/objectBrowserTableInfo.test.ts diff --git a/apps/desktop/src/components/objects/ObjectBrowser.vue b/apps/desktop/src/components/objects/ObjectBrowser.vue index 40a145a79..13949ed99 100644 --- a/apps/desktop/src/components/objects/ObjectBrowser.vue +++ b/apps/desktop/src/components/objects/ObjectBrowser.vue @@ -94,6 +94,7 @@ import { type ObjectBrowserSortKey, } from "@/lib/table/objectBrowserRows"; import { resolveRowClickAction, shouldDeferSingleClick, type ObjectBrowserRowAction } from "@/lib/table/objectBrowserRowAction"; +import { filterObjectBrowserTableColumns } from "@/lib/table/objectBrowserTableInfo"; import { createSidePanelRequestGuard } from "@/lib/table/sidePanelRequestGuard"; import { runBatchTableTruncate } from "@/lib/table/batchTableTruncate"; import { tableColumnDefaultDisplayValue } from "@/lib/table/tableColumnDefaultPresentation"; @@ -777,11 +778,7 @@ const tableInfoTabListStyle = computed(() => ({ gridTemplateColumns: `repeat(${tableInfoTabs.value.length}, minmax(0, 1fr))`, })); -const filteredTableColumns = computed(() => { - if (!tableInfoSearchQuery.value) return tableColumns.value; - const q = tableInfoSearchQuery.value.toLowerCase(); - return tableColumns.value.filter((c) => c.name.toLowerCase().includes(q) || c.data_type.toLowerCase().includes(q)); -}); +const filteredTableColumns = computed(() => filterObjectBrowserTableColumns(tableColumns.value, tableInfoSearchQuery.value)); const filteredTableIndexes = computed(() => { if (!tableInfoSearchQuery.value) return tableIndexes.value; diff --git a/apps/desktop/src/lib/table/objectBrowserTableInfo.ts b/apps/desktop/src/lib/table/objectBrowserTableInfo.ts new file mode 100644 index 000000000..52cc7b1fe --- /dev/null +++ b/apps/desktop/src/lib/table/objectBrowserTableInfo.ts @@ -0,0 +1,13 @@ +type SearchableTableInfoColumn = { + name: string; + data_type: string; + comment?: string | null; +}; + +/** Keep table-info search aligned with the name, type, and comment text visible in the field list. */ +export function filterObjectBrowserTableColumns(columns: T[], query: string): T[] { + const normalizedQuery = query.trim().toLocaleLowerCase(); + if (!normalizedQuery) return columns; + + return columns.filter((column) => [column.name, column.data_type, column.comment].some((value) => typeof value === "string" && value.toLocaleLowerCase().includes(normalizedQuery))); +} diff --git a/packages/app-tests/objectBrowserTableInfo.test.ts b/packages/app-tests/objectBrowserTableInfo.test.ts new file mode 100644 index 000000000..7063ba3f8 --- /dev/null +++ b/packages/app-tests/objectBrowserTableInfo.test.ts @@ -0,0 +1,48 @@ +import { strict as assert } from "node:assert"; +import { test } from "vitest"; +import { filterObjectBrowserTableColumns } from "../../apps/desktop/src/lib/table/objectBrowserTableInfo.ts"; + +const columns = [ + { name: "business_id", data_type: "varchar(64)", comment: "业务ID" }, + { name: "created_at", data_type: "TIMESTAMP", comment: "Created time" }, + { name: "status", data_type: "INT", comment: null }, + { name: "note", data_type: "TEXT" }, + { name: "empty_note", data_type: "TEXT", comment: "" }, +]; + +test("filters table-info columns by case-insensitive name and trimmed type text", () => { + assert.deepEqual( + filterObjectBrowserTableColumns(columns, "BUSINESS").map((column) => column.name), + ["business_id"], + ); + assert.deepEqual( + filterObjectBrowserTableColumns(columns, " timestamp ").map((column) => column.name), + ["created_at"], + ); +}); + +test("filters table-info columns by partial localized and case-insensitive comments", () => { + assert.deepEqual( + filterObjectBrowserTableColumns(columns, "业务").map((column) => column.name), + ["business_id"], + ); + assert.deepEqual( + filterObjectBrowserTableColumns(columns, "CREATED TIME").map((column) => column.name), + ["created_at"], + ); +}); + +test("handles missing, null, and empty comments without changing no-match behavior", () => { + assert.deepEqual(filterObjectBrowserTableColumns(columns, "not present"), []); +}); + +test("returns every column for empty searches and preserves match order", () => { + assert.deepEqual( + filterObjectBrowserTableColumns(columns, " ").map((column) => column.name), + columns.map((column) => column.name), + ); + assert.deepEqual( + filterObjectBrowserTableColumns(columns, "text").map((column) => column.name), + ["note", "empty_note"], + ); +});