Skip to content

Commit 3313863

Browse files
committed
✨ feat(TabBar组件优化): 优化TabBar组件的状态管理逻辑,确保在页面刷新时homePath对应的tab始终存在且不可关闭;处理路径变化时的tab激活逻辑,提升用户体验和组件稳定性。
1 parent cb8750b commit 3313863

1 file changed

Lines changed: 178 additions & 98 deletions

File tree

src/components/TabBar/index.tsx

Lines changed: 178 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -65,54 +65,145 @@ const TabBar: React.FC<TabBarProps> = ({ className }) => {
6565
// 初始化标记,避免重复初始化
6666
const isInitializedRef = useRef(false);
6767

68-
// 确保 homePath 对应的 tab 永远在第一个显示且固定(仅在初始化时执行)
68+
// 页面刷新时的状态恢复逻辑
6969
React.useEffect(() => {
70-
// 只有在没有tab且有菜单数据且有homePath且未初始化时才执行
71-
if (!isInitializedRef.current && tabs.length === 0 && menus.length > 0 && homePath) {
70+
// 只有在有菜单数据且有homePath且未初始化时才执行
71+
if (!isInitializedRef.current && menus.length > 0 && homePath) {
7272
isInitializedRef.current = true;
7373

74-
// 首先创建homePath的tab(第一个位置)
75-
const homeRoute = findRouteByPath(homePath);
76-
if (homeRoute?.path) {
77-
const homeTabItem: TabItem = {
78-
key: homePath,
79-
label: homeRoute.meta?.title || homePath,
80-
icon: homeRoute.meta?.icon,
81-
path: homePath,
82-
closable: false, // 第一个tab不可关闭
83-
route: homeRoute,
84-
};
74+
// 如果当前没有tabs,说明是首次加载,需要初始化
75+
if (tabs.length === 0) {
76+
// 首先创建homePath的tab(第一个位置)
77+
const homeRoute = findRouteByPath(homePath);
78+
if (homeRoute?.path) {
79+
const homeTabItem: TabItem = {
80+
key: homePath,
81+
label: homeRoute.meta?.title || homePath,
82+
icon: homeRoute.meta?.icon,
83+
path: homePath,
84+
closable: false, // 第一个tab不可关闭
85+
route: homeRoute,
86+
};
8587

86-
// 使用头插入,激活 homePath(登录后默认激活首页
87-
addTab(homeTabItem, { insertAt: 'head', activate: true });
88-
}
88+
// 使用头插入,但不激活(根据当前路径决定激活哪个
89+
addTab(homeTabItem, { insertAt: 'head', activate: false });
90+
}
8991

90-
// 然后检查当前路径是否有效(在菜单中存在)
91-
const currentRoute = findRouteByPath(pathname);
92+
// 然后检查当前路径是否有效(在菜单中存在)
93+
const currentRoute = findRouteByPath(pathname);
9294

93-
if (currentRoute?.path && pathname !== homePath) {
94-
// 如果当前路径有效且不是homePath,创建对应的tab
95-
const currentTabItem: TabItem = {
96-
key: pathname,
97-
label: currentRoute.meta?.title || pathname,
98-
icon: currentRoute.meta?.icon,
99-
path: pathname,
100-
closable: true,
101-
route: currentRoute,
102-
};
95+
if (currentRoute?.path && pathname !== homePath) {
96+
// 如果当前路径有效且不是homePath,创建对应的tab
97+
const currentTabItem: TabItem = {
98+
key: pathname,
99+
label: currentRoute.meta?.title || pathname,
100+
icon: currentRoute.meta?.icon,
101+
path: pathname,
102+
closable: true,
103+
route: currentRoute,
104+
};
103105

104-
// 使用尾插入,激活当前页面
105-
addTab(currentTabItem, { insertAt: 'tail', activate: true });
106-
} else if (pathname !== homePath) {
107-
// 如果当前路径无效且不是homePath,跳转到homePath
108-
navigate(homePath, { replace: true });
106+
// 使用尾插入,激活当前页面
107+
addTab(currentTabItem, { insertAt: 'tail', activate: true });
108+
} else if (pathname === homePath) {
109+
// 如果当前路径就是homePath,激活homePath的tab
110+
setActiveKey(homePath);
111+
} else {
112+
// 如果当前路径无效且不是homePath,跳转到homePath
113+
navigate(homePath, { replace: true });
114+
}
115+
} else {
116+
// 页面刷新时,tabs已经存在,需要确保状态正确
117+
// 1. 确保homePath的tab存在且不可关闭
118+
const homeTab = tabs.find(tab => tab.key === homePath);
119+
if (homeTab) {
120+
// 确保homePath的tab不可关闭
121+
if (homeTab.closable) {
122+
const updatedTabs = tabs.map(tab =>
123+
tab.key === homePath ? { ...tab, closable: false } : tab
124+
);
125+
setTabs(updatedTabs, activeKey);
126+
}
127+
} else {
128+
// 如果homePath的tab不存在,需要添加
129+
const homeRoute = findRouteByPath(homePath);
130+
if (homeRoute?.path) {
131+
const homeTabItem: TabItem = {
132+
key: homePath,
133+
label: homeRoute.meta?.title || homePath,
134+
icon: homeRoute.meta?.icon,
135+
path: homePath,
136+
closable: false,
137+
route: homeRoute,
138+
};
139+
addTab(homeTabItem, { insertAt: 'head', activate: false });
140+
}
141+
}
142+
143+
// 2. 确保当前路径对应的tab存在并激活
144+
const currentTab = tabs.find(tab => tab.key === pathname);
145+
if (!currentTab && pathname !== homePath) {
146+
const currentRoute = findRouteByPath(pathname);
147+
if (currentRoute?.path) {
148+
const currentTabItem: TabItem = {
149+
key: pathname,
150+
label: currentRoute.meta?.title || pathname,
151+
icon: currentRoute.meta?.icon,
152+
path: pathname,
153+
closable: true,
154+
route: currentRoute,
155+
};
156+
addTab(currentTabItem, { insertAt: 'tail', activate: true });
157+
}
158+
} else if (currentTab) {
159+
// 如果当前路径的tab存在,激活它
160+
setActiveKey(pathname);
161+
} else if (pathname === homePath) {
162+
// 如果当前路径就是homePath,确保激活homePath的tab
163+
setActiveKey(homePath);
164+
}
165+
166+
// 3. 确保homePath的tab在第一个位置
167+
const homeTabIndex = tabs.findIndex(tab => tab.key === homePath);
168+
if (homeTabIndex > 0) {
169+
const homeTab = tabs[homeTabIndex];
170+
const otherTabs = tabs.filter(tab => tab.key !== homePath);
171+
const newTabs = [homeTab, ...otherTabs];
172+
setTabs(newTabs, activeKey);
173+
}
109174
}
110175
}
111-
}, [menus, homePath, findRouteByPath, navigate, tabs.length, pathname]); // 重新添加必要的依赖
176+
}, [menus, homePath, findRouteByPath, navigate, tabs, activeKey, addTab, setActiveKey, setTabs, pathname]);
112177

113-
// 确保 homePath 对应的 tab 始终存在(即使在其他tab被添加后)
178+
// 处理路径变化时的tab激活逻辑
114179
React.useEffect(() => {
115-
if (menus.length > 0 && homePath && tabs.length > 0) {
180+
// 只有在初始化完成后才处理路径变化
181+
if (isInitializedRef.current && menus.length > 0 && homePath && tabs.length > 0) {
182+
// 确保当前路径对应的tab被激活
183+
const currentTab = tabs.find(tab => tab.key === pathname);
184+
if (currentTab && activeKey !== pathname) {
185+
setActiveKey(pathname);
186+
} else if (!currentTab && pathname !== homePath) {
187+
// 如果当前路径的tab不存在且不是homePath,创建并激活它
188+
const currentRoute = findRouteByPath(pathname);
189+
if (currentRoute?.path) {
190+
const currentTabItem: TabItem = {
191+
key: pathname,
192+
label: currentRoute.meta?.title || pathname,
193+
icon: currentRoute.meta?.icon,
194+
path: pathname,
195+
closable: true,
196+
route: currentRoute,
197+
};
198+
addTab(currentTabItem, { insertAt: 'tail', activate: true });
199+
}
200+
}
201+
}
202+
}, [pathname, tabs, activeKey, setActiveKey, menus, homePath, findRouteByPath, addTab]);
203+
204+
// 确保 homePath 对应的 tab 始终存在且不可关闭(在tabs变化时检查)
205+
React.useEffect(() => {
206+
if (menus.length > 0 && homePath && tabs.length > 0 && isInitializedRef.current) {
116207
const homeRoute = findRouteByPath(homePath);
117208
const homeTabExists = tabs.some((tab) => tab.key === homePath);
118209

@@ -144,50 +235,34 @@ const TabBar: React.FC<TabBarProps> = ({ className }) => {
144235
}
145236
}
146237
}
147-
}, [menus, homePath, findRouteByPath]); // 移除tabs和activeKey依赖,减少不必要的重新排序
238+
}, [menus, homePath, findRouteByPath, tabs, activeKey, addTab, setTabs]);
148239

149240

150-
// 合并路径变化处理和空tabs处理逻辑
151-
const pathAndTabsHandler = useMemo(() => {
152-
return () => {
153-
// 如果正在关闭tab,跳过执行
154-
if (isClosingTabRef.current) {
155-
isClosingTabRef.current = false;
156-
return;
157-
}
241+
// 处理路径变化时的tab管理逻辑(仅在初始化完成后执行)
242+
React.useEffect(() => {
243+
// 如果正在关闭tab,跳过执行
244+
if (isClosingTabRef.current) {
245+
isClosingTabRef.current = false;
246+
return;
247+
}
158248

159-
if (!pathname || pathname === '/login') return;
249+
// 只有在初始化完成后才处理路径变化
250+
if (!isInitializedRef.current || !menus.length || !homePath) return;
160251

161-
// 如果当前没有tabs,说明是关闭所有tabs后的情况,需要跳转到首页
162-
if (tabs.length === 0) {
163-
if (homePath && pathname !== homePath) {
164-
navigate(homePath, { replace: true });
165-
}
166-
return;
167-
}
252+
if (!pathname || pathname === '/login') return;
168253

169-
// 如果tabs存在但只有一个且是homePath,且当前路径不是homePath,则添加新tab
170-
if (tabs.length === 1 && tabs[0].key === homePath && pathname !== homePath) {
171-
const route = findRouteByPath(pathname);
172-
if (route) {
173-
const tabItem: TabItem = {
174-
key: pathname,
175-
label: route.meta?.title || pathname,
176-
icon: route.meta?.icon,
177-
path: pathname,
178-
closable: true,
179-
route,
180-
};
181-
useTabStore.getState().addTab(tabItem);
182-
}
183-
return;
254+
// 如果当前没有tabs,说明是关闭所有tabs后的情况,需要跳转到首页
255+
if (tabs.length === 0) {
256+
if (homePath && pathname !== homePath) {
257+
navigate(homePath, { replace: true });
184258
}
259+
return;
260+
}
185261

186-
// 如果tabs存在且超过1个,检查当前路径对应的tab
187-
if (tabs.length > 1) {
188-
const route = findRouteByPath(pathname);
189-
if (!route) return;
190-
262+
// 如果tabs存在但只有一个且是homePath,且当前路径不是homePath,则添加新tab
263+
if (tabs.length === 1 && tabs[0].key === homePath && pathname !== homePath) {
264+
const route = findRouteByPath(pathname);
265+
if (route) {
191266
const tabItem: TabItem = {
192267
key: pathname,
193268
label: route.meta?.title || pathname,
@@ -196,32 +271,39 @@ const TabBar: React.FC<TabBarProps> = ({ className }) => {
196271
closable: true,
197272
route,
198273
};
199-
200-
// 检查tab是否已存在
201-
const existingTab = tabs.find((tab) => tab.key === pathname);
202-
if (!existingTab) {
203-
// 新tab,添加到store
204-
useTabStore.getState().addTab(tabItem);
205-
} else {
206-
// tab已存在,只激活它
207-
setActiveKey(pathname);
208-
}
274+
useTabStore.getState().addTab(tabItem);
209275
}
210-
};
211-
}, [pathname, tabs, homePath, findRouteByPath, setActiveKey, navigate]);
276+
return;
277+
}
212278

213-
// 合并路径变化和空tabs处理
214-
React.useEffect(() => {
215-
pathAndTabsHandler();
216-
}, [pathAndTabsHandler]);
279+
// 如果tabs存在且超过1个,检查当前路径对应的tab
280+
if (tabs.length > 1) {
281+
const route = findRouteByPath(pathname);
282+
if (!route) return;
283+
284+
const tabItem: TabItem = {
285+
key: pathname,
286+
label: route.meta?.title || pathname,
287+
icon: route.meta?.icon,
288+
path: pathname,
289+
closable: true,
290+
route,
291+
};
217292

218-
// 监听用户退出登录和页面刷新事件
219-
React.useEffect(() => {
220-
const handleBeforeUnload = () => {
221-
// 页面刷新或关闭时清空所有tab
222-
resetTabs();
223-
};
293+
// 检查tab是否已存在
294+
const existingTab = tabs.find((tab) => tab.key === pathname);
295+
if (!existingTab) {
296+
// 新tab,添加到store
297+
useTabStore.getState().addTab(tabItem);
298+
} else {
299+
// tab已存在,只激活它
300+
setActiveKey(pathname);
301+
}
302+
}
303+
}, [pathname, tabs, homePath, findRouteByPath, setActiveKey, navigate, menus]);
224304

305+
// 监听用户退出登录事件(不监听页面刷新)
306+
React.useEffect(() => {
225307
const handleStorageChange = (e: StorageEvent) => {
226308
// 监听 localStorage 变化,检测用户登录状态变化
227309
if (e.key === 'user-storage') {
@@ -237,11 +319,9 @@ const TabBar: React.FC<TabBarProps> = ({ className }) => {
237319
}
238320
};
239321

240-
window.addEventListener('beforeunload', handleBeforeUnload);
241322
window.addEventListener('storage', handleStorageChange);
242323

243324
return () => {
244-
window.removeEventListener('beforeunload', handleBeforeUnload);
245325
window.removeEventListener('storage', handleStorageChange);
246326
};
247327
}, [resetTabs]);

0 commit comments

Comments
 (0)