Files
crawler-plugin/admin-frontend-vue/src/layout/menu-mapper.ts
T
huangzd1997 26edb9e6ed task-6(壳层/路由): 定义后端菜单树到侧边栏节点映射规则并落地
新增 src/layout/menu-mapper.ts:toSidebarEntries 处理分组/顶级路由叶/畸形节点,
groupKeysForActive 按当前路由展开激活分组;AdminLayout 菜单区改用映射结果,
删除旧 visibleChildren/canSee 死逻辑;8 用例覆盖空树/单叶/缺 route 过滤等。
2026-09-05 14:43:52 +08:00

47 lines
1.8 KiB
TypeScript

import type { AdminMenuNode } from '../types/admin'
export interface SidebarItem {
key: string
name: string
route: string
}
/** 侧边栏渲染项:分组(含子页面)或单页面项(含顶级路由叶节点)。 */
export type SidebarEntry =
| { kind: 'group'; key: string; name: string; children: SidebarItem[] }
| { kind: 'item'; key: string; name: string; route: string }
/**
* 后端菜单树 -> 侧边栏节点映射规则(任务 6):
* - 有可路由子节点的节点渲染为分组,只保留带 route 的子项;
* - 无子节点但有 route 的节点渲染为单页面项(不丢顶级叶菜单);
* - 既无子节点也无 route 的节点跳过(点不进去的纯占位不展示)。
*/
export function toSidebarEntries(nodes: AdminMenuNode[] | null | undefined): SidebarEntry[] {
const entries: SidebarEntry[] = []
for (const node of nodes || []) {
const name = node.name || ''
const key = node.key || ''
const children: SidebarItem[] = (node.children || [])
.filter((child): child is AdminMenuNode & { route: string } => Boolean(child.route))
.map((child) => ({ key: child.key || '', name: child.name || '', route: child.route }))
if (children.length > 0) {
entries.push({ kind: 'group', key, name, children })
} else if (node.route) {
entries.push({ kind: 'item', key, name, route: node.route })
}
}
return entries
}
/** 计算需默认展开的分组 key:分组下含当前激活路由子项时返回该分组 key。 */
export function groupKeysForActive(entries: SidebarEntry[], activeRoute: string): string[] {
const keys: string[] = []
for (const entry of entries) {
if (entry.kind === 'group' && entry.children.some((child) => child.route === activeRoute)) {
keys.push(entry.key)
}
}
return keys
}