以前使用 Ant-design-vue 的单文件方式递归生成菜单时,发现遍历出来的菜单并不是自己想要的效果,而是渲染不出来。
官方的解释是:
Before v2.0, 因组件内部会动态更改a-sub-menu的属性,如果拆分成单文件,无法将属性挂载到a-sub-menu上,你需要自行声明属性并挂载。为了方便,避免属性的声明,我们推荐使用函数式组件。
并且给出了一个 demo,但会运行也显示不正常,那就自己来实现吧,直接根据路由配置来遍历生成菜单。
1、路由配置如下:
[
{
path: '/',
redirect: '/state',
component: Home,
children: [
{
path: '/state',
name: 'vehicleStatus',
component: VehicleStatus,
meta: { title: '车道状态', icon: 'build' }
}
]
},
{
path: '/log',
redirect: '/log/index',
component: Home,
children: [
{
path: 'index',
name: 'historyLog',
component: HistoryLog,
meta: { title: '日志记录', icon: 'history' }
}
]
},
{
path: '/system',
redirect: '/system/info',
component: Home,
meta: { title: '系统设置', icon: 'setting' },
children: [
{
path: 'info',
name: 'systemInfo',
component: SystemInfo,
meta: { title: '系统信息' }
},
{
path: 'settings',
name: 'basicSettings',
component: BasicSettings,
meta: { title: '基本设置', roles: ['editor', 'admin'] }
},
{
path: 'upgrade',
name: 'upgrade',
component: Upgrade,
meta: { title: '系统升级', roles: ['admin'] }
}
]
},
{
path: '/users',
redirect: '/users/index',
component: Home,
children: [
{
path: 'index',
name: 'userSettings',
component: UserSettings,
meta: { title: '用户管理', icon: 'user', roles: ['admin'] }
}
]
}
]
其中 title
是菜单项名称,然后 icon
是菜单项前面的小图标,roles
是一个数组,用于根据当前 登录用户的权限 来动态生成路由菜单。
2、菜单组件目录:
index.vue:
<template>
<div class="sider-bar">
<a-menu
theme="dark"
mode="inline"
:default-selected-keys="activeMenu"
:open-keys.sync="openKeys"
@click="handleClick"
>
<template v-for="item in permissionRoutes">
<a-menu-item
v-if="item.children && item.children.length === 1"
:key="item.redirect"
>
<a-icon :type="item.children[0].meta.icon" />
<span>{{ item.children[0].meta.title }}</span>
</a-menu-item>
<sub-menu v-else :menu-item="item" :key="item.path" />
</template>
</a-menu>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import SubMenu from './SubMenu.vue'
export default {
components: { SubMenu },
name: 'SiderBar',
data () {
return {
openKeys: [] // 当前展开的 SubMenu 菜单项 key 数组
}
},
computed: {
...mapGetters([
'permissionRoutes' // 权限路由配置数组
]),
activeMenu () { // 当前高亮菜单项
return [this.$router.currentRoute.path]
}
},
created () {
this.setOpenKeys()
},
methods: {
handleClick (e) {
this.$router.push(e.key)
},
setOpenKeys () { // 设置展开项
this.permissionRoutes.forEach(item => {
if (item.children && item.children.length > 1 && this.activeMenu[0].indexOf(item.path) > -1) { // 子项个数大于 1 才会设置展开项
this.openKeys = [item.path]
}
})
}
}
}
</script>
SubMenu.vue:
<template functional>
<a-sub-menu v-if="!props.menuItem.hidden" :key="props.menuItem.path">
<span slot="title">
<a-icon v-if="props.menuItem.meta.icon" :type="props.menuItem.meta.icon" />
<span>{{ props.menuItem.meta.title }}</span>
</span>
<template v-for="item in props.menuItem.children">
<a-menu-item
v-if="!item.children"
:key="`${props.menuItem.path}/${item.path}`"
>
{{ item.meta.title }}
</a-menu-item>
<sub-menu v-else :menu-item="item" :key="item.path" />
</template>
</a-sub-menu>
</template>
SubMenu组件声明了 functional
属性,使用函数式组件,并在组件里递归自身,即可支持二级以上菜单的遍历展示,但侧边栏菜单推荐不超过三个层级,因为层级过多体验反而不好,也难维护,一般情况下二级菜单已经够用了。
最后生成效果如下: