Vue 里,多级菜单要如何设计才显得专业?
老生常谈了!
虽然我们是 Java 猿,但是写起来前端代码也不含糊!今天我想来和大家聊聊这个前端的动态菜单,要如何设计才显得专业!还是以我们的 TienChin 项目为例,大家一起来看看。
先来一张截图看看效果:
那么这样的菜单是如何设计出来的呢?
今天我也不想和大家聊过多的技术细节,就聊聊这个路由是如何设计的,一旦大家明白了路由是如何设计的,剩下的问题都是细枝末节的问题了。
1. 路由设计
有的小伙伴做过 vhr,知道 vhr 里的动态菜单实现方式,松哥和大家一样,也是在不断学习不断进步中,今天我想和大家探讨 TienChin 项目中动态菜单的实现方案,看看是否是一种更佳的解决方案。
1.1 菜单设计
先来和小伙伴们回顾下 vhr 中的方案:
在 vhr 中,权限的控制,只控制到二级菜单,也就是一级菜单和权限没关系。举个例子,现在有一级菜单 A 和 二级菜单 B,B 是 A 中的菜单,现在假设:
如果当前用户权限可以查看 B 菜单,那么 A 菜单会自动显示出来。如果当前用户权限无法查看 B 菜单,且 A 菜单中也没有其他子菜单可以展示,那么 A 菜单就不会显示出来。
换言之,A 菜单显示与否,主要看它里边有没有子菜单需要展示,如果有,A 菜单就显示,如果没有,A 菜单就不显示。
vhr 中的思路是这样的。
在 TienChin 项目中,这一块有一些变化:
如果 A 中只有一个 B,那么似乎就没有必要再做一个两级菜单了,直接把 B 展示出来不就行了?用户操作也方便!
这是第一个不一样的地方。
1.2 路由数据
基于第一点,就涉及到一个问题,就是路由接口该如何设计?最主要是接口返回的数据格式应该是什么样子的?
首先有一点小伙伴们应该知道,这里的路由是一个嵌套路由,也就是一级菜单中嵌套着二级菜单。即使这个地方在展示的时候,不存在层级关系,例如上图中的促销活动,但是底层的数据结构也应该是嵌套路由。
好啦,不卖关子了,我们来看一段路由 JSON:
[{
"name": "Monitor",
"path": "/monitor",
"hidden": false,
"redirect": "noRedirect",
"component": "Layout",
"alwaysShow": true,
"meta": {
"title": "系统监控",
"icon": "monitor",
"noCache": false,
"link": null
},
"children": [{
"name": "Online",
"path": "online",
"hidden": false,
"component": "monitor/online/index",
"meta": {
"title": "在线用户",
"icon": "online",
"noCache": false,
"link": null
}
}, {
"name": "Job",
"path": "job",
"hidden": false,
"component": "monitor/job/index",
"meta": {
"title": "定时任务",
"icon": "job",
"noCache": false,
"link": null
}
}]
}, {
"path": "/",
"hidden": false,
"component": "Layout",
"children": [{
"name": "Role",
"path": "role",
"hidden": false,
"component": "system/role/index",
"meta": {
"title": "角色管理",
"icon": "peoples",
"noCache": false,
"link": null
}
}]
}]
这里我举了两个菜单的例子,这两个例子比较具有代表性,这个菜单最终显示效果大概类似下面这样:
系统监控
在线用户
定时任务
角色管理
大概显示效果如上图。
接下来我就来说一下这里几个典型属性:
redirect:noRedirect 表示该路由在面包屑导航中不可被点击。alwaysShow:如果这个属性设置为 false,那么当当前菜单只有一个子菜单的时候,默认情况下就只会显示子菜单,而忽略父菜单(如 1.1 小节所述),但是如果将该属性设置为 true,则无论当前菜单有几个子菜单,都会将当前菜单展示出来(这就类似于 vhr 中的效果了)。每一个父菜单都有自己的 path,每一个 children 也有自己的 path,父菜单的 path 加上每一个 children 的 path,共同组成每一个 children 的路径。再来看第二个角色管理这个菜单项,由于它的父菜单中只有一个子菜单项,并且父菜单中也没有 alwaysShow 属性,所以这个菜单项在最终展示的时候,就只展示里边的角色管理,父菜单则不会展示出来(正好,生成的 JSON 中也没说父菜单的名字、图标等属性)。
当然,不是说你的 JSON 这么写就自动这么显示,JSON 中的东西只是一个标记,最终怎么显示,还要看渲染:
v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
class="nest-menu"
/