菜单路由
# 1. 路由
项目基于 unibest (opens new window) 框架,使用约定式路由,页面文件放在 src/pages 或 src/pages-xxx 目录下,会自动生成路由配置。
官方文档
详细的路由配置和使用方式,请参考:《unibest 官方文档 —— uni 插件》 (opens new window)
# 1.1 路由配置
使用 definePage 宏配置页面信息:
<script lang="ts" setup>
definePage({
style: {
navigationStyle: 'custom', // 自定义导航栏
navigationBarTitleText: '', // 页面标题
},
excludeLoginPath: false, // 是否需要登录,默认为 false(一般情况下,不用添加)
})
</script>
# 1.2 路由跳转
① uniapp 提供了多种路由跳转方式,可见 《uniapp 官方文档 —— 页面和路由》 (opens new window) 文档。
② 项目封装了增强的返回方法 navigateBackPlus,当不存在上一页时会跳转到首页:
import { navigateBackPlus } from '@/utils'
// 返回上一页,如果不存在则跳转到首页
navigateBackPlus()
// 返回上一页,如果不存在则跳转到指定页面
navigateBackPlus('/pages-system/user/index')
# 1.3 登录拦截
项目默认在 src/router/config.ts (opens new window) 开启登录拦截,未登录时访问需要登录的页面,会自动跳转到登录页。
可通过以下两种方式,配置页面不需要登录拦截:
① 方式一:在 definePage 中配置
<script lang="ts" setup>
definePage({
excludeLoginPath: true, // 排除登录拦截
})
</script>
② 方式二:在 EXCLUDE_LOGIN_PATH_LIST 中配置
// src/router/config.ts
export const EXCLUDE_LOGIN_PATH_LIST = [
'/pages/xxx/index',
'/pages-sub/xxx/index',
]
# 2. 首页菜单
首页的菜单列表,定义在 src/pages/index/index.ts (opens new window) 文件中。

# 2.1 菜单数据结构
/** 菜单项类型 */
export interface MenuItem {
key: string // 菜单唯一标识
name: string // 菜单名称
icon: string // 菜单图标(Wot UI 图标名)
url?: string // 跳转路径
iconColor?: string // 图标颜色
enabled?: boolean // 是否启用(默认 true)
permission?: string // 权限标识
}
/** 菜单分组类型 */
export interface MenuGroup {
key: string // 分组唯一标识
name: string // 分组名称
menus: MenuItem[] // 分组下的菜单列表
}
# 2.2 菜单配置示例
const menuGroupsData: MenuGroup[] = [
{
key: 'system',
name: '系统管理',
menus: [
{
key: 'user',
name: '用户管理',
icon: 'user',
url: '/pages-system/user/index',
iconColor: '#1890ff',
permission: 'system:user:list',
},
{
key: 'role',
name: '角色管理',
icon: 'secured',
url: '/pages-system/role/index',
iconColor: '#2f54eb',
permission: 'system:role:query',
},
// ... 更多菜单
],
},
// ... 更多分组
]
疑问:首页菜单,是否支持管理后台配置?
短期来看,暂时需求量不大。
目前先通过 PC 端的管理后台 [系统管理 -> 菜单管理] 功能,里面的 permission 字段,来控制「移动端 - 首页菜单」的显示和隐藏即可。
# 2.3 仅 PC 端页面
部分功能(如代码生成、表单构建等)仅支持 PC 端访问,配置 url 为 ONLY_PC_PAGE 常量:
import { ONLY_PC_PAGE } from '@/router/config'
{
key: 'codegen',
name: '代码生成',
icon: 'code',
url: ONLY_PC_PAGE, // 点击后跳转到"仅 PC 端访问"提示页
iconColor: '#52c41a',
}
# 3. 权限控制
前端通过权限控制,隐藏用户没有权限的菜单和按钮,实现功能级别的权限。
友情提示
前端的权限控制,主要是提升用户体验,避免操作后发现没有权限。最终在请求到后端时,还是会进行一次权限的校验。
# 3.1 useAccess Hook
项目提供了 useAccess (opens new window) Hook 进行权限判断:
import { useAccess } from '@/hooks/useAccess'
const { hasAccessByCodes, hasAccessByRoles } = useAccess()
// 基于权限码判断
if (hasAccessByCodes(['system:user:create'])) {
// 有新增用户权限
}
// 基于角色判断
if (hasAccessByRoles(['admin'])) {
// 是管理员角色
}
# 3.2 权限数据来源
用户的权限数据在登录成功后,通过 /admin-api/system/auth/get-permission-info (opens new window) 接口获取,存储在 userStore 中:
import { useUserStore } from '@/store/user'
const userStore = useUserStore()
// 用户角色列表
console.log(userStore.roles) // ['admin', 'common']
// 用户权限列表
console.log(userStore.permissions) // ['system:user:list', 'system:user:create', ...]
# 3.3 菜单权限过滤
首页菜单会自动根据用户权限进行过滤,没有权限的菜单项不会显示。
实现代码:src/pages/index/index.ts (opens new window) 的 getMenuGroups 方法:
// src/pages/index/index.ts
export function getMenuGroups(): MenuGroup[] {
const { hasAccessByCodes } = useAccess()
return menuGroupsData
.map(group => ({
...group,
// 过滤掉没有权限的菜单项
menus: group.menus.filter((menu) => {
// 没有配置权限的菜单项默认展示
if (!menu.permission) {
return true
}
return hasAccessByCodes([menu.permission])
}),
}))
// 过滤掉没有菜单项的分组
.filter(group => group.menus.length > 0)
}
# 3.4 按钮权限控制
在页面中可以使用 v-if 结合 hasAccessByCodes 或 hasAccessByRoles 控制按钮显示。
实战案例:
- 列表页的新增按钮:
src/pages-system/user/index.vue(opens new window) - 详情页的编辑、删除按钮
src/pages-system/user/detail/index.vue(opens new window)
<template>
<wd-button
v-if="hasAccessByCodes(['system:user:create'])"
type="primary"
@click="handleCreate"
>
新增用户
</wd-button>
</template>
<script lang="ts" setup>
import { useAccess } from '@/hooks/useAccess'
const { hasAccessByCodes } = useAccess()
</script>