此文章包含了递归生成三级菜单,刷新状态保留,只展开一个父级菜单等常见问题。
一、效果
可以看到递归生成三级菜单,刷新状态保留,只展开一个父级菜单等常见问题得以解决。我自己的电脑设备老旧,反应缓慢的问题请忽略。
二、关键的API
说实话遇到问题了不知道该怎么解决,说白了还是不懂得API的使用,对相关的API不够了解,熟悉了API怎么操作都行。下面说说用到但是又容易混淆的API。
1.defaultSelectedKeys 默认选中的key
这里的key对应的是a-menu-item上面绑定的key,如果被选中,会进行导航的跳转以及被给予高亮状态。
2.openKeys 展开的a-sub-menu的key
这里的key对应的是a-sub-menu上面绑定的key。如果绑定在a-menu上,对应的子菜单会打开。
3.selectedKeys 受控选中的key
这里的key对应的是a-menu-item上面绑定的key,如果被选中,会进行导航的跳转以及被给予高亮状态。
与defaultSelectedKeys 有什么区别:
- 这两个属性为二选一使用,如果同时使用时,defaultSelectedKeys无效,将会以selectedKeys为准。
- 如果你只是希望指定一个初始化选中的菜单项,请使用defaultSelectedKeys;
- 如果你需要每次通过传入不同的props改变Menu组件的选中项,请使用selectedKeys。
三、注意事项
此处强调一点,如果是一级以及二级菜单的生成,普通的两层for循环结合递归以及v-if生成即可。
如果是想使用a-menu生成三级或者三级以上的菜单,那么就需要定义在单文件内定义函数式组件。那为什么不推荐单文件的形式定义组件,而是使用函数式组件?
以下是官方文档的说明:
四、代码
<template> <a-layout id="components-layout-demo-custom-trigger"> <a-layout-sider theme="light" v-model="collapsed" :trigger="null" collapsible> <div > <a-menu :inlineIndent="inlineIndent" :defaultSelectedKeys="[$route.path]" :openKeys="openKeys" mode="inline" :inline-collapsed="collapsed" @openChange="onOpenChange" @click="menuClick"> <!-- 菜单遍历的开始 --> <template v-for="item in list"> <!-- 如果当前遍历项没有children,视为子菜单项,注意所有的key都是path用于路由跳转,以及当前选中记录 --> <a-menu-item v-if="!item.children" :key="item.path"> <i :class="item.icon" /> <span>{{ item.title }}</span> </a-menu-item> <!-- 否则视为子菜单,传入菜单信息并且运用下面定义的函数式组件 --> <sub-menu v-else :key="item.path" :menu-info="item" /> </template> </a-menu> </div> </a-layout-sider> <a-layout> <a-layout-content :style="{ margin: '24px 16px', padding: '24px', background: '#fff', minHeight: '280px' }"> <router-view/> </a-layout-content> </a-layout> </a-layout> </template> <script> import { Menu } from 'ant-design-vue'; // 定义函数式组件 const SubMenu = { template: ` <a-sub-menu :key="menuInfo.path" v-bind="$props" v-on="$listeners"> <span slot="title"> <i :class="menuInfo.icon" /><span>{{ menuInfo.title }}</span> </span> <template v-for="item in menuInfo.children"> <a-menu-item v-if="!item.children" :key="item.path"> <i :class="item.icon" /> <span>{{ item.title }}</span> </a-menu-item> <sub-menu v-else :key="item.path" :menu-info="item" /> </template> </a-sub-menu> `, name: 'SubMenu', // must add isSubMenu: true 此项必须被定义 isSubMenu: true, props: { // 解构a-sub-menu的属性,也就是文章开头提到的为什么使用函数式组件 ...Menu.SubMenu.props, // 接收父级传递过来的菜单信息 menuInfo: { type: Object, default: () => ({}), }, }, }; export default { data() { return { // 菜单缩进 inlineIndent:12, // 默认不折叠 collapsed: false, // 全部顶级父节点,用来控制所有父级菜单只展开其中的一项,可用遍历菜单信息进行赋值 rootSubmenuKeys: ['/infomationManage','/safeInfoManage','/qualityInfoManage'], // 展开的父菜单项 openKeys: [], // 选中的子菜单项 defaultSelectedKeys: [this.$route.path], // 菜单信息,可从后台获取 list: [ { key: '1', title: '项目信息管理', path: '/infomationManage', icon:'iconfont icon-information' }, { key: '2', title: '安全信息管理', path: '/safeInfoManage', icon:'iconfont icon-anquan', children: [ { key: '2.1', title: '安全风险管理', path: '/safeRisk', icon:'', children: [ { key: '2.1.1', title: '风险分类管理', path: '/riskClassifyManage', icon:'', }, { key: '2.1.2', title: '分类辨识', path: '/classifyIdentity', icon:'', } ], }, ], }, { key: '3', title: '质量信息管理', path: '/qualityInfoManage', icon:'iconfont icon-zhiliang', children:[ { key: '3.1', title: '质量控制点管理', path: '/controlPointManage', icon:'', } ] } ], } }, created(){ // 将从缓存中取出openKeys const openKeys = window.sessionStorage.getItem('openKeys') if(openKeys){ // 存在即赋值 this.openKeys = JSON.parse(openKeys) } }, methods: { // 点击菜单,路由跳转,注意的是当点击MenuItem才会触发此函数 menuClick({ item, key, keyPath }) { // 获取到当前的key,并且跳转 this.$router.push({ path: key }) }, onOpenChange(openKeys) { // 将当前打开的父级菜单存入缓存中 window.sessionStorage.setItem('openKeys', JSON.stringify(openKeys)) // 控制只打开一个 const latestOpenKey = openKeys.find(key => this.openKeys.indexOf(key) === -1); if (this.rootSubmenuKeys.indexOf(latestOpenKey) === -1) { this.openKeys = openKeys; } else { this.openKeys = latestOpenKey ? [latestOpenKey] : []; } }, }, // 注册局部组件 components: { 'sub-menu': SubMenu, }, }; </script> <style> </style>
以上为个人经验,希望能给大家一个参考,也希望大家多多支持本站。