用ANTD-Vue做管理页面的左侧菜单 需要在刷新后也能保持左侧菜单被选中和展开 且只能展开一个菜单
成品效果图
1.在刷新后保持菜单选中
这个比较简单
ANTD的API中提供了一个defaultSelectedKeys参数
描述:初始选中的菜单项 key 数组
类型: string[]
自己手动实验得知意思就是在数组中填入字符串 例如['key']
默认值为空
一级菜单和子菜单都有一个key属性,我后端返回的json数据中的router属性是每个菜单的路由跳转地址, 同时将这个地址赋值给菜单的key 这样点击菜单跳转后 用this.$route.path获取到的当前路由地址就和菜单的key一致了
在菜单标签中设置 defaultSelectedKeys属性指向this.$route.path即可完成在刷新后保持菜单选中
2.在刷新后如果当前选中的菜单是二级菜单则展开当前菜单的父菜单
可展开(有二级菜单)的一级菜单点击后会展开或收起 不会发生跳转
ANTD提供了两个api:
openKeys 当前展开的 SubMenu 菜单项 key 数组
defaultOpenKeys 初始展开的 SubMenu 菜单项 key 数组
两个api中的描述 一个是"当前" 另一个是"初始(默认)" ,试了半天后发现用defaultOpenKeys设置值后是只生效一次的 也就是说初始设置defaultOpenKeys的对应属性为空 等ajax请求到数据后再去设置,菜单就不会变化了,所以不能用这个
openKeys
看来只能用openKeys了,我又发现一个小问题,设置openKeys后手动点击其他没展开的菜单,菜单不会有变化 这样就得用到另一个API
openChange 展开/关闭的回调
每次点击可展开/收起的菜单时都会触发这个属性所对应的函数,设置openKeys后再配合openChange切换菜单即可解决问题
具体请看代码
VUE html代码部分(只截取了菜单部分)
<a-menu theme="dark"
:openKeys="openKeys" // 重点 当前展开的菜单
mode="inline"
@openChange="onOpenChange" // 重点 当可以展开的菜单被点击时
:defaultSelectedKeys="defaultSelectedKeys" // 默认选中的菜单
style="width: 100%"
>
<template v-for="item in menuList"> // 渲染菜单列表
<a-menu-item v-if="item.children === false" :key="item.router"> // 顶级菜单
<span>{{ item.name }}</span>
</a-menu-item>
<a-sub-menu v-else :key="item.router"> // 可以展开的二级菜单
<span slot="title"><span>{{ item.name }}</span></span>
<a-menu-item v-for="menuChildren in item.children" :key="menuChildren.router">{{ menuChildren.name }}</a-menu-item>
</a-sub-menu>
</template>
</a-menu>
后端返回的JSON数据格式:
[
{
"name": "首页",
"router": "/home/index",
"children": false
},
{
"name": "商品管理",
"router": "/shop", // 点击这个菜单不会跳转 只会展开 此处的router是为了设置key属性
"children": [
{
"name": "商品分类",
"router": "/home/shopClass"
},
{
"name": "商品列表",
"router": "/home/shopList"
}
]
},
{
"name": "个人设置",
"router": "/user",
"children": [
{
"name": "我的资料",
"router": "/home/userEdit"
},
{
"name": "登录记录",
"router": "/home/userLoginRecord"
}
]
}
]
export default {
data() {
return {
defaultSelectedKeys: [this.$route.path],
openKeys: [''],
menuList: [] // 此处为动态获取的菜单数组
}
},
async created() {
const result = await this.$http.post('home/menuList') // post后端服务器获取菜单数据
this.menuList = result // 给data里面的菜单数组赋值数据
this.handleData(result) // 处理数据
},
methods: {
// 处理数据展开应展开的菜单
handleData(result) {
function loop(list, keys = []) {
// 循环【顶级菜单数组列表】 循环的数组下标用i表示
// keys参数为当前菜单的所有上级菜单的router(用作openKeys的值)
for (let item of list) {
if (item.router === this.$route.path) {
// 如果路由path与item.router相等则直接返回当前路由的所有上级的router
return [...keys]
} else if (item.children && item.children.length) {
// 如果item.router不等于当前$router.path则递归调用loop函数,传入item.children、[...keys, item.router]
let tempResult = loop(item.children, [...keys, item.router])
if (tempResult !== false) {
return tempResult
}
}
}
return false
}
this.openKeys = loop(result)
},
onOpenChange(openKeys) { // 当菜单被展开时触发此处
/*
经测试传入的变量openKeys是数组 点击已经展开的菜单时传入的是空数组
点击未展开的菜单时传入的是[当前展开菜单的key,点击的菜单key]
下面的if判断用openKeys === [] 无效 所以我只能判断数组length是否等于0
*/
if (openKeys.length !== 0) {
this.openKeys = [openKeys[1]]
} else {
this.openKeys = ['']
}
},
clickItem(obj) {
this.$router.push(obj.key)
}
}
}