Skip to content

Commit 48c01eb

Browse files
committed
refactor: integrate getMenuKey for consistent menu item handling
- Introduced the getMenuKey utility to standardize menu item keys, ensuring compatibility with dynamic routing and menu state management. - Updated menu item construction in buildMenuItems to utilize the new key, enhancing the accuracy of selected and open menu states. - Adjusted resolveMenuSelection to align with the new key structure, improving the reliability of menu expansion after refresh.
1 parent d437612 commit 48c01eb

2 files changed

Lines changed: 32 additions & 14 deletions

File tree

src/layouts/LeftMenu/component/menu-utils.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { Key, ReactNode } from 'react';
44

55
import type { RouteItem } from '@/types/route';
66
import { getIcon } from '@/utils/optimized-icons';
7-
import { type MenuCaches, matchPathname, matchRoutePath } from '@/utils/utils';
7+
import { getMenuKey, type MenuCaches, matchPathname, matchRoutePath } from '@/utils/utils';
88

99
export type MenuItem = Required<MenuProps>['items'][number];
1010

@@ -178,12 +178,13 @@ export const buildMenuItems = (menuList: RouteItem[], t: TFunction): MenuItem[]
178178
continue;
179179
}
180180

181+
const key = getMenuKey(item);
181182
if (!item?.children?.length) {
182-
result.push(getItem(t, item.meta?.title, item.path, getIcon(item.meta?.icon)));
183+
result.push(getItem(t, item.meta?.title, key, getIcon(item.meta?.icon)));
183184
continue;
184185
}
185186

186-
result.push(getItem(t, item.meta?.title, item.path, getIcon(item.meta?.icon), buildMenuItems(item.children, t)));
187+
result.push(getItem(t, item.meta?.title, key, getIcon(item.meta?.icon), buildMenuItems(item.children, t)));
187188
}
188189

189190
return result;
@@ -231,6 +232,7 @@ export const hasRoutePath = (routes: RouteItem[] | undefined, targetPath: string
231232
* 根据 pathname 决定选中/展开的菜单 path。
232233
* 若命中纯路由节点,则退回最近的可见菜单。
233234
* 若完全未命中,则尝试做动态匹配。
235+
* openKeys 使用 getMenuKey 与侧栏菜单项 key 一致,保证刷新后父级能正确展开。
234236
*/
235237
export function resolveMenuSelection(
236238
pathname: string,
@@ -255,7 +257,9 @@ export function resolveMenuSelection(
255257
return { selectedPath: null, openKeys: [] };
256258
}
257259

258-
// 纯路由回退到最近的可见菜单 path
260+
const menuKey = getMenuKey(entity);
261+
262+
// 纯路由回退到最近的可见菜单(fallback 为父级的 menuKey)
259263
if (entity.meta?.menuType === 2 || entity.hidden) {
260264
const fallback = routeToMenuPathMap.get(targetPath);
261265
if (!fallback) {
@@ -268,7 +272,7 @@ export function resolveMenuSelection(
268272
}
269273

270274
return {
271-
selectedPath: entity.path,
272-
openKeys: ancestorsMap.get(entity.path) ?? [],
275+
selectedPath: menuKey,
276+
openKeys: ancestorsMap.get(menuKey) ?? [],
273277
};
274278
}

src/utils/utils.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ export type MenuCaches = {
1515
routeToMenuPathMap: Map<string, string>;
1616
};
1717

18+
/**
19+
* 菜单项在侧栏中使用的稳定 key(与 path 可能为空的一级菜单兼容)
20+
* 用于 ancestorsMap、Menu openKeys 与菜单项 key 一致,保证刷新后父级能正确展开
21+
*/
22+
export function getMenuKey(node: { path?: string; id?: string; redirect?: string }): string {
23+
const path = node.path?.trim();
24+
if (path) return path;
25+
if (node.id) return String(node.id);
26+
const redirect = node.redirect?.trim();
27+
if (redirect) return redirect;
28+
return String(node.id ?? '');
29+
}
30+
1831
/**
1932
* Add the object as a parameter to the URL
2033
* @param baseUrl url
@@ -201,25 +214,26 @@ export function buildMenuCaches(menuList: MenuEntity[]): MenuCaches {
201214

202215
const dfs = (node: MenuEntity, parentVisibleAncestors: string[]) => {
203216
const isPureRoute = node.meta?.menuType === 2;
217+
const menuKey = getMenuKey(node);
204218

205-
// 可见菜单:自身 path 是 key;隐藏/纯路由:仅记录 pathMap 方便匹配
206-
pathMap.set(node.path, node);
219+
// pathMap 仅用 path 注册,供 pathname 匹配;有 path 的节点才参与路径查找
220+
if (node.path?.trim()) {
221+
pathMap.set(node.path.trim(), node);
222+
}
207223

208224
if (!isPureRoute && !node.hidden) {
209-
ancestorsMap.set(node.path, [...parentVisibleAncestors]);
225+
ancestorsMap.set(menuKey, [...parentVisibleAncestors]);
210226
}
211227

212228
if (isPureRoute) {
213-
// 路由节点指向最近的可见菜单
214229
const nearestVisible = parentVisibleAncestors[parentVisibleAncestors.length - 1];
215-
if (nearestVisible) {
216-
routeToMenuPathMap.set(node.path, nearestVisible);
230+
if (nearestVisible && node.path?.trim()) {
231+
routeToMenuPathMap.set(node.path.trim(), nearestVisible);
217232
}
218233
}
219234

220-
// 计算下一层可见菜单的父链
221235
const nextVisibleAncestors =
222-
isPureRoute || node.hidden ? parentVisibleAncestors : [...parentVisibleAncestors, node.path];
236+
isPureRoute || node.hidden ? parentVisibleAncestors : [...parentVisibleAncestors, menuKey];
223237

224238
node.children?.forEach((child) => {
225239
dfs(child, nextVisibleAncestors);

0 commit comments

Comments
 (0)