♻️ refactor(tab-lifecycle): 统一连接与数据库关闭时的标签回收逻辑

- 下沉批量关页逻辑到 store,减少组件重复过滤代码
- Sidebar 仅负责触发动作,状态回收由 store 原子处理
- 优化标签生命周期一致性与可维护性
This commit is contained in:
Syngnat
2026-02-11 10:23:54 +08:00
parent da5708b5bc
commit ab92e94bf8
2 changed files with 60 additions and 2 deletions

View File

@@ -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: <DisconnectOutlined />,
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("已关闭数据库");
}
},
{

View File

@@ -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<AppState>()(
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 }),