菜单配置
DVHA 框架提供了灵活的菜单配置系统,支持本地菜单和远程菜单,可以构建单级和多级菜单结构。
功能特点
- 📋 本地菜单 - 静态配置的菜单结构
- 🌐 远程菜单 - 从服务端动态获取的菜单
- 🏗️ 多级菜单 - 支持父子级菜单关系
- 🎯 路径自动处理 - 自动添加管理端路径前缀
- 🔍 菜单过滤 - 支持隐藏特定菜单项
- 🎨 图标支持 - 支持菜单图标配置
- 📊 排序功能 - 支持菜单项排序
菜单接口定义
typescript
interface IMenu {
label?: string // 菜单名称
name: string // 菜单唯一标识
path?: string // 菜单路径
icon?: string // 菜单图标
sort?: number // 菜单排序
parent?: string // 父级菜单标识
hidden?: boolean // 是否隐藏
loader?: string // 菜单加载器
component?: RouteComponent // 菜单组件
meta?: Record<string, any> // 菜单元数据
}
本地菜单配置
基础菜单配置
js
const adminManage = {
name: 'admin',
title: '管理后台',
// 本地菜单配置
menus: [
{
name: 'dashboard',
label: '仪表盘',
path: 'dashboard',
icon: 'dashboard',
sort: 1,
component: () => import('./pages/Dashboard.vue')
},
{
name: 'users',
label: '用户管理',
path: 'users',
icon: 'users',
sort: 2,
component: () => import('./pages/Users.vue')
}
]
}
多级菜单配置
js
menus: [
// 一级菜单
{
name: 'system',
label: '系统管理',
icon: 'system',
sort: 1
},
// 二级菜单
{
name: 'system.users',
label: '用户管理',
path: 'system/users',
icon: 'users',
parent: 'system', // 指定父级菜单
sort: 1,
component: () => import('./pages/system/Users.vue')
},
{
name: 'system.roles',
label: '角色管理',
path: 'system/roles',
icon: 'roles',
parent: 'system',
sort: 2,
component: () => import('./pages/system/Roles.vue')
},
// 另一个一级菜单
{
name: 'content',
label: '内容管理',
icon: 'content',
sort: 2
},
{
name: 'content.articles',
label: '文章管理',
path: 'content/articles',
parent: 'content',
sort: 1,
component: () => import('./pages/content/Articles.vue')
}
]
隐藏菜单配置
js
menus: [
// 正常显示的菜单
{
name: 'dashboard',
label: '仪表盘',
path: 'dashboard',
component: () => import('./pages/Dashboard.vue')
},
// 隐藏的菜单(不在菜单栏显示,但路由存在)
{
name: 'user.detail',
label: '用户详情',
path: 'users/:id',
hidden: true, // 隐藏菜单
component: () => import('./pages/UserDetail.vue')
}
]
远程菜单配置
启用远程菜单
js
const adminManage = {
name: 'admin',
title: '管理后台',
// 远程菜单API路径
apiRoutePath: '/api/admin/menus',
// 数据提供者(用于获取远程菜单)
dataProvider: myDataProvider,
// 本地菜单(会与远程菜单合并)
menus: [
{
name: 'dashboard',
label: '仪表盘',
path: 'dashboard',
component: () => import('./pages/Dashboard.vue')
}
]
}
远程菜单API响应格式
js
// GET /api/admin/menus 响应格式
{
"data": [
{
"name": "users",
"label": "用户管理",
"path": "users",
"icon": "users",
"sort": 1
},
{
"name": "system",
"label": "系统管理",
"icon": "system",
"sort": 2
},
{
"name": "system.settings",
"label": "系统设置",
"path": "system/settings",
"parent": "system",
"sort": 1
}
]
}
菜单加载器
外部链接加载器
js
{
name: 'external.docs',
label: '帮助文档',
loader: 'iframe', // 使用 iframe 加载器
path: 'https://docs.example.com',
icon: 'help'
}
外联跳转菜单
js
{
name: 'custom.link',
label: '跳转',
loader: 'link',
path: 'tools/custom',
meta: {
url: "https://www.baidu.com"
}
}
菜单元数据
权限控制
js
{
name: 'users',
label: '用户管理',
path: 'users',
component: () => import('./pages/Users.vue'),
meta: {
permissions: ['user.read', 'user.write'], // 所需权限
roles: ['admin', 'manager'] // 所需角色
}
}
页面配置
js
{
name: 'dashboard',
label: '仪表盘',
path: 'dashboard',
component: () => import('./pages/Dashboard.vue'),
meta: {
title: '控制台 - 仪表盘', // 页面标题
keepAlive: true, // 保持组件活跃
breadcrumb: false, // 不显示面包屑
layout: 'full' // 布局类型
}
}
完整配置示例
单管理端菜单
js
import { createDux } from '@duxweb/dvha-core'
const app = createDux({
defaultManage: 'admin',
manages: [
{
name: 'admin',
title: '管理后台',
menus: [
// 仪表盘
{
name: 'dashboard',
label: '仪表盘',
path: 'dashboard',
icon: 'dashboard',
sort: 1,
component: () => import('./pages/Dashboard.vue')
},
// 用户管理
{
name: 'users',
label: '用户管理',
icon: 'users',
sort: 2
},
{
name: 'users.list',
label: '用户列表',
path: 'users',
parent: 'users',
sort: 1,
component: () => import('./pages/users/List.vue')
},
{
name: 'users.detail',
label: '用户详情',
path: 'users/:id',
parent: 'users',
hidden: true, // 隐藏在菜单中
component: () => import('./pages/users/Detail.vue')
},
// 系统管理
{
name: 'system',
label: '系统管理',
icon: 'system',
sort: 3
},
{
name: 'system.settings',
label: '系统设置',
path: 'system/settings',
parent: 'system',
sort: 1,
component: () => import('./pages/system/Settings.vue'),
meta: {
permissions: ['system.admin']
}
},
// 外部链接
{
name: 'docs',
label: '帮助文档',
loader: 'iframe',
path: 'https://docs.example.com',
icon: 'help',
sort: 4
}
]
}
]
})
多管理端菜单
js
const app = createDux({
defaultManage: 'admin',
manages: [
// 系统管理端
{
name: 'admin',
title: '系统管理',
menus: [
{
name: 'dashboard',
label: '系统概览',
path: 'dashboard',
icon: 'dashboard',
component: () => import('./admin/Dashboard.vue')
},
{
name: 'users',
label: '用户管理',
path: 'users',
icon: 'users',
component: () => import('./admin/Users.vue')
}
]
},
// 用户中心
{
name: 'user',
title: '用户中心',
menus: [
{
name: 'profile',
label: '个人资料',
path: 'profile',
icon: 'profile',
component: () => import('./user/Profile.vue')
},
{
name: 'settings',
label: '账户设置',
path: 'settings',
icon: 'settings',
component: () => import('./user/Settings.vue')
}
]
}
]
})
远程菜单配置
js
const adminManage = {
name: 'admin',
title: '管理后台',
// 远程菜单配置
apiRoutePath: '/api/admin/menus',
dataProvider: {
custom: async (options, manage, auth) => {
const response = await fetch(options.path, {
headers: {
'Authorization': `Bearer ${auth.token}`
}
})
return await response.json()
}
},
// 本地基础菜单
menus: [
{
name: 'dashboard',
label: '仪表盘',
path: 'dashboard',
icon: 'dashboard',
sort: 0, // 优先显示
component: () => import('./pages/Dashboard.vue')
}
]
}
菜单处理流程
菜单初始化流程
- 本地菜单加载: 加载管理端配置中的本地菜单
- 远程菜单获取: 如果配置了
apiRoutePath
,则获取远程菜单 - 菜单合并: 将本地菜单和远程菜单合并
- 路径处理: 为菜单路径添加管理端前缀
- 路由注册: 将菜单注册为 Vue Router 路由
- 菜单排序: 根据
sort
字段对菜单进行排序
路径自动处理
js
// 配置的菜单路径
{
name: 'users',
path: 'users', // 原始路径
}
// 系统自动处理后的路径
{
name: 'users',
path: '/admin/users', // 添加了管理端前缀
}
菜单使用
获取菜单数据
vue
<script setup>
import { useMenu } from '@duxweb/dvha-core'
const { menus, getMenuTree } = useMenu()
// 获取扁平的菜单列表
console.log(menus.value)
// 获取树形菜单结构
const menuTree = getMenuTree()
console.log(menuTree)
</script>
渲染菜单
vue
<template>
<nav class="menu">
<div
v-for="menu in visibleMenus"
:key="menu.name"
class="menu-item"
>
<router-link
v-if="menu.path"
:to="menu.path"
class="menu-link"
>
<i v-if="menu.icon" :class="menu.icon"></i>
{{ menu.label }}
</router-link>
<!-- 子菜单 -->
<div v-if="menu.children" class="submenu">
<div
v-for="child in menu.children"
:key="child.name"
class="submenu-item"
>
<router-link :to="child.path">
{{ child.label }}
</router-link>
</div>
</div>
</div>
</nav>
</template>
<script setup>
import { computed } from 'vue'
import { useMenu } from '@duxweb/dvha-core'
const { getMenuTree } = useMenu()
const visibleMenus = computed(() => {
return getMenuTree().filter(menu => !menu.hidden)
})
</script>
注意事项
- 菜单名称唯一性: 确保
name
字段在管理端内唯一 - 路径规范: 菜单路径不要以
/
开头,系统会自动添加前缀 - 父子关系: 使用
parent
字段建立菜单层级关系 - 排序优先级:
sort
值越小优先级越高 - 远程菜单格式: 远程菜单API返回的数据格式要符合
IMenu
接口 - 组件懒加载: 建议使用动态导入实现组件懒加载
- 权限控制: 通过
meta
字段传递权限相关信息