/** 模块侧栏菜单树 — 路径规范化与校验辅助(Host / SDK 共用) */ export interface MenuTreeNodeLike { key: string; path?: string; module?: string; version?: string; children?: MenuTreeNodeLike[]; } /** 规范化模块内 path(?path= 查询参数) */ export function normalizeModulePath(path: string | undefined | null): string { if (!path || path === '/') return '/'; return path.startsWith('/') ? path : `/${path}`; } /** 从 Host 菜单 key 推导模块内 path(兼容未显式配置 path 的旧数据) */ export function modulePathFromMenuKey(moduleName: string, menuKey: string): string | null { const prefix = `${moduleName}/`; if (!menuKey.startsWith(prefix)) return null; const suffix = menuKey.slice(prefix.length); return suffix ? `/${suffix}` : '/'; } /** 解析菜单项对应的模块内导航 path */ export function resolveModuleMenuPath( moduleName: string, item: Pick, ): string { if (item.path) return normalizeModulePath(item.path); return modulePathFromMenuKey(moduleName, item.key) ?? '/'; } /** * 在菜单树中按模块名 + 模块内 path 查找选中项,并返回需展开的祖先 key。 */ export function findModuleMenuSelection( nodes: MenuTreeNodeLike[], moduleName: string, moduleSubPath: string, ancestorKeys: string[] = [], ): { selectedKey: string; openKeys: string[] } | null { const normalizedTarget = normalizeModulePath(moduleSubPath); for (const node of nodes) { const chain = [...ancestorKeys, node.key]; if (node.module === moduleName) { const itemPath = resolveModuleMenuPath(moduleName, node); const isLeaf = !node.children?.length; if (isLeaf && itemPath === normalizedTarget) { return { selectedKey: node.key, openKeys: ancestorKeys }; } } if (node.children?.length) { const nested = findModuleMenuSelection(node.children, moduleName, normalizedTarget, chain); if (nested) return nested; } } return null; } /** 按 Host pathname 在管理类菜单(带静态 path)中查找选中项 */ export function findStaticMenuSelection( nodes: MenuTreeNodeLike[], pathname: string, ancestorKeys: string[] = [], ): { selectedKey: string; openKeys: string[] } | null { for (const node of nodes) { const chain = [...ancestorKeys, node.key]; if (node.path === pathname) { return { selectedKey: node.key, openKeys: ancestorKeys }; } if (node.children?.length) { const nested = findStaticMenuSelection(node.children, pathname, chain); if (nested) return nested; } } return null; }