diff --git a/frontend/src/components/Sidebar.locate-toolbar.test.tsx b/frontend/src/components/Sidebar.locate-toolbar.test.tsx index 134216e..15c96f3 100644 --- a/frontend/src/components/Sidebar.locate-toolbar.test.tsx +++ b/frontend/src/components/Sidebar.locate-toolbar.test.tsx @@ -1452,5 +1452,7 @@ describe('Sidebar locate toolbar', () => { expect(source).toContain('const SIDEBAR_LOCATE_LOAD_WAIT_ATTEMPTS = 160;'); expect(source).toContain('attempt < SIDEBAR_LOCATE_LOAD_WAIT_ATTEMPTS'); expect(source).toContain('window.setTimeout(resolve, SIDEBAR_LOCATE_LOAD_WAIT_INTERVAL_MS)'); + expect(source).toContain('return !loadingNodesRef.current.has(loadKey);'); + expect(source).toContain('对象仍在加载中'); }); }); diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 861afbb..7136b20 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -2445,10 +2445,11 @@ const Sidebar: React.FC<{ const locateObjectInSidebarRef = useRef<(detail: unknown) => Promise>(async () => {}); - const waitForSidebarLoadKey = async (loadKey: string) => { + const waitForSidebarLoadKey = async (loadKey: string): Promise => { for (let attempt = 0; attempt < SIDEBAR_LOCATE_LOAD_WAIT_ATTEMPTS && loadingNodesRef.current.has(loadKey); attempt += 1) { await new Promise(resolve => window.setTimeout(resolve, SIDEBAR_LOCATE_LOAD_WAIT_INTERVAL_MS)); } + return !loadingNodesRef.current.has(loadKey); }; const locateObjectInSidebar = async (detail: unknown) => { @@ -2511,7 +2512,11 @@ const Sidebar: React.FC<{ return; } if (loadingNodesRef.current.has(dbLoadKey)) { - await waitForSidebarLoadKey(dbLoadKey); + const loaded = await waitForSidebarLoadKey(dbLoadKey); + if (!loaded) { + message.info(`数据库节点仍在加载中:${request.dbName},请稍后再试`); + return; + } } else { await loadDatabases(connectionNode); } @@ -2526,7 +2531,11 @@ const Sidebar: React.FC<{ path = findSidebarNodePathForLocate(treeDataRef.current as SidebarLocateTreeNodeLike[], target); if (!path) { if (loadingNodesRef.current.has(tableLoadKey)) { - await waitForSidebarLoadKey(tableLoadKey); + const loaded = await waitForSidebarLoadKey(tableLoadKey); + if (!loaded) { + message.info(`${objectLabel}所在数据库对象仍在加载中:${request.dbName},请稍后再试`); + return; + } } else { await loadTables(dbNode); } diff --git a/frontend/src/utils/sidebarLocate.test.ts b/frontend/src/utils/sidebarLocate.test.ts index 8f29a55..38a5eb7 100644 --- a/frontend/src/utils/sidebarLocate.test.ts +++ b/frontend/src/utils/sidebarLocate.test.ts @@ -660,6 +660,47 @@ describe('sidebarLocate', () => { ]); }); + it('falls back to a visual table-like node with a table-prefixed key for a view request', () => { + const target = resolveSidebarLocateTarget({ + tabId: 'stale-view-tab-id', + connectionId: 'conn-1', + dbName: 'SYSDBA', + tableName: 'V_ACCOUNT', + objectGroup: 'views', + }, { groupBySchema: false }); + + const tree = [ + { + key: 'conn-1', + children: [ + { + key: 'conn-1-SYSDBA', + dataRef: { id: 'conn-1', dbName: 'SYSDBA' }, + children: [ + { + key: 'conn-1-SYSDBA-tables', + children: [ + { + key: 'conn-1-SYSDBA-table-V_ACCOUNT', + type: 'table', + dataRef: {}, + }, + ], + }, + ], + }, + ], + }, + ]; + + expect(findSidebarNodePathForLocate(tree, target)).toEqual([ + 'conn-1', + 'conn-1-SYSDBA', + 'conn-1-SYSDBA-tables', + 'conn-1-SYSDBA-table-V_ACCOUNT', + ]); + }); + it('finds a view node by title when the tree node is missing object metadata', () => { const target = resolveSidebarLocateTarget({ tabId: 'stale-view-tab-id', diff --git a/frontend/src/utils/sidebarLocate.ts b/frontend/src/utils/sidebarLocate.ts index c5d497f..699d5de 100644 --- a/frontend/src/utils/sidebarLocate.ts +++ b/frontend/src/utils/sidebarLocate.ts @@ -402,7 +402,7 @@ const getVisualNodeObjectName = ( ? [`${target.databaseKey}-trigger-`] : target.objectGroup === 'routines' ? [`${target.databaseKey}-routine-`] - : [`${target.databaseKey}-`]; + : [`${target.databaseKey}-table-`, `${target.databaseKey}-`]; const matchedPrefix = keyPrefixes.find((prefix) => nodeKey.startsWith(prefix)); return matchedPrefix ? nodeKey.slice(matchedPrefix.length) : '';