From ab92e94bf84ba08dc1ff07135ffc17b8cf721bfb Mon Sep 17 00:00:00 2001 From: Syngnat Date: Wed, 11 Feb 2026 10:23:54 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(tab-lifecycle):?= =?UTF-8?q?=20=E7=BB=9F=E4=B8=80=E8=BF=9E=E6=8E=A5=E4=B8=8E=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E5=85=B3=E9=97=AD=E6=97=B6=E7=9A=84=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E5=9B=9E=E6=94=B6=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 下沉批量关页逻辑到 store,减少组件重复过滤代码 - Sidebar 仅负责触发动作,状态回收由 store 原子处理 - 优化标签生命周期一致性与可维护性 --- frontend/src/components/Sidebar.tsx | 21 +++++++++++++-- frontend/src/store.ts | 41 +++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 95944ed..778f871 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -53,6 +53,8 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }> const addTab = useStore(state => state.addTab); const setActiveContext = useStore(state => state.setActiveContext); const removeConnection = useStore(state => state.removeConnection); + const closeTabsByConnection = useStore(state => state.closeTabsByConnection); + const closeTabsByDatabase = useStore(state => state.closeTabsByDatabase); const theme = useStore(state => state.theme); const appearance = useStore(state => state.appearance); const tableAccessCount = useStore(state => state.tableAccessCount); @@ -1592,6 +1594,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }> const res = await DropDatabase(config as any, dbName); if (res.success) { message.success("数据库删除成功"); + closeTabsByDatabase(conn.id, dbName); setExpandedKeys(prev => prev.filter(k => !k.toString().startsWith(`${conn.id}-${dbName}`))); setLoadedKeys(prev => prev.filter(k => !k.toString().startsWith(`${conn.id}-${dbName}`))); await loadDatabases(getConnectionNodeRef(conn)); @@ -2095,6 +2098,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }> setExpandedKeys(prev => prev.filter(k => k !== node.key && !k.toString().startsWith(`${node.key}-`))); setLoadedKeys(prev => prev.filter(k => k !== node.key && !k.toString().startsWith(`${node.key}-`))); setTreeData(origin => updateTreeData(origin, node.key, undefined)); + closeTabsByConnection(String(node.key)); message.success("已断开连接"); } }, @@ -2107,7 +2111,10 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }> Modal.confirm({ title: '确认删除', content: `确定要删除连接 "${node.title}" 吗?`, - onOk: () => removeConnection(node.key) + onOk: () => { + closeTabsByConnection(String(node.key)); + removeConnection(node.key); + } }); } } @@ -2177,6 +2184,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }> setLoadedKeys(prev => prev.filter(k => k !== node.key && !k.toString().startsWith(`${node.key}-`))); // Clear children (undefined to trigger reload) setTreeData(origin => updateTreeData(origin, node.key, undefined)); + closeTabsByConnection(String(node.key)); message.success("已断开连接"); } }, @@ -2189,7 +2197,10 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }> Modal.confirm({ title: '确认删除', content: `确定要删除连接 "${node.title}" 吗?`, - onOk: () => removeConnection(node.key) + onOk: () => { + closeTabsByConnection(String(node.key)); + removeConnection(node.key); + } }); } } @@ -2276,6 +2287,8 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }> label: '关闭数据库', icon: , onClick: () => { + const dbConnId = String(node.dataRef?.id || ''); + const dbName = String(node.dataRef?.dbName || node.title || '').trim(); setConnectionStates(prev => { const next = { ...prev }; delete next[node.key]; @@ -2284,6 +2297,10 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }> setExpandedKeys(prev => prev.filter(k => k !== node.key && !k.toString().startsWith(`${node.key}-`))); setLoadedKeys(prev => prev.filter(k => k !== node.key && !k.toString().startsWith(`${node.key}-`))); setTreeData(origin => updateTreeData(origin, node.key, undefined)); + if (dbConnId && dbName) { + closeTabsByDatabase(dbConnId, dbName); + } + message.success("已关闭数据库"); } }, { diff --git a/frontend/src/store.ts b/frontend/src/store.ts index 9d96935..161dfca 100644 --- a/frontend/src/store.ts +++ b/frontend/src/store.ts @@ -269,6 +269,8 @@ interface AppState { closeOtherTabs: (id: string) => void; closeTabsToLeft: (id: string) => void; closeTabsToRight: (id: string) => void; + closeTabsByConnection: (connectionId: string) => void; + closeTabsByDatabase: (connectionId: string, dbName: string) => void; closeAllTabs: () => void; setActiveTab: (id: string) => void; setActiveContext: (context: { connectionId: string; dbName: string } | null) => void; @@ -428,6 +430,45 @@ export const useStore = create()( return { tabs: newTabs, activeTabId: activeStillExists ? state.activeTabId : id }; }), + closeTabsByConnection: (connectionId) => set((state) => { + const targetConnectionId = String(connectionId || '').trim(); + if (!targetConnectionId) return state; + const newTabs = state.tabs.filter(t => String(t.connectionId || '').trim() !== targetConnectionId); + const activeStillExists = state.activeTabId ? newTabs.some(t => t.id === state.activeTabId) : false; + const nextActiveTabId = activeStillExists + ? state.activeTabId + : (newTabs.length > 0 ? newTabs[newTabs.length - 1].id : null); + const nextActiveContext = state.activeContext?.connectionId === targetConnectionId ? null : state.activeContext; + return { + tabs: newTabs, + activeTabId: nextActiveTabId, + activeContext: nextActiveContext, + }; + }), + + closeTabsByDatabase: (connectionId, dbName) => set((state) => { + const targetConnectionId = String(connectionId || '').trim(); + const targetDbName = String(dbName || '').trim(); + if (!targetConnectionId || !targetDbName) return state; + const newTabs = state.tabs.filter((tab) => { + const sameConnection = String(tab.connectionId || '').trim() === targetConnectionId; + const sameDb = String(tab.dbName || '').trim() === targetDbName; + return !(sameConnection && sameDb); + }); + const activeStillExists = state.activeTabId ? newTabs.some(t => t.id === state.activeTabId) : false; + const nextActiveTabId = activeStillExists + ? state.activeTabId + : (newTabs.length > 0 ? newTabs[newTabs.length - 1].id : null); + const sameActiveContext = state.activeContext + && state.activeContext.connectionId === targetConnectionId + && state.activeContext.dbName === targetDbName; + return { + tabs: newTabs, + activeTabId: nextActiveTabId, + activeContext: sameActiveContext ? null : state.activeContext, + }; + }), + closeAllTabs: () => set(() => ({ tabs: [], activeTabId: null })), setActiveTab: (id) => set({ activeTabId: id }),