外观
路由与导航如何配合
目标:让页面可访问、菜单可发现,并在模块停用或权限变化后保持一致。路由是前端声明,导航是当前用户可用入口,两者分别配置。
三种路由入口
createUAdmin 的类型位于 packages/app/src/types.ts:
| 配置 | 用途 | 演示位置 |
|---|---|---|
modules | 可随模块启停的业务路由 | apps/demo/src/modules.ts |
routes | 布局内始终注册的页面 | apps/demo/src/routes.ts |
publicRoutes | 布局外免登录页面 | 同上 |
routes 不代表免登录;公开访问必须放进 publicRoutes。不要通过把业务页移到公开路由来绕过导航加载问题。
ts
const reportRoute = {
path: '/reports',
name: 'Reports',
component: () => import('./Reports.vue'),
meta: { title: 'reports.title', perms: ['report:read'], keepAlive: true },
}给页面使用稳定且唯一的 name,标签页清理和缓存均依赖路由身份。meta.affix 固定标签,hiddenTag 隐藏标签,hideInMenu 用于静态菜单;它们均不代替权限声明。
后端导航模式
默认 nav: 'backend' 调用 backend.nav()。快照满足 @uadmin/protocol 的 NavSnapshot:
json
{
"protocolVersion": 1,
"modules": ["reports"],
"permissions": ["report:read"],
"menu": [
{
"key": "business",
"title": "业务",
"children": [
{
"key": "reports",
"title": "报表",
"path": "/reports",
"module": "reports"
}
]
}
]
}服务端应按用户裁剪菜单,也可以返回 titleKey 交给前端翻译。内核只注册“前端声明过且快照启用”的模块,未知模块不会加载代码。快照权限会覆盖会话中的权限,确保路由和最新授权一致。
首次进入的执行顺序
packages/app/src/guard.ts 先判断登录;没有 token 时转到登录页,并保留 redirect。已有 token 但导航未就绪时,useNavStore().load() 获取快照和必要的用户资料、注册动态路由,然后重新匹配原地址,最后校验 meta.perms。
模块生命周期操作后可调用 await useNavStore().load() 重新同步。旧动态路由先移除,新路由再注册;失效的非固定、命名标签会被清理。模块开关不会删除前端源码,也不会安装后端程序。
静态导航模式
设置 nav: 'static' 后,菜单从模块路由的 meta.title、group、order、icon 等生成,全部前端模块启用,不请求导航快照。用户资料仍来自 backend.me(),路由守卫仍校验权限。
当前静态菜单生成器不按 meta.perms 裁剪菜单;需要按用户展示入口时优先使用后端模式。隐藏菜单只是呈现控制,直接输入 URL 仍由守卫判断。
深链和排障
默认 hash 路由可使用 /demo/#/tasks,静态服务器只需提供 /demo/index.html。选择 history: 'web' 后,必须配置服务器将页面深链回退到应用入口。
有菜单但 404: 核对菜单 path 与路由 path,以及模块名称是否一致。能打开但没有菜单: 检查快照树、父分组和 module 字段。权限修改未生效: 刷新快照,检查最终 permissions,而不是只看角色名称。新增页面还需登记演示的 src/views/page-nav/entries.ts。