先上效果图:
需求
在顶部导航栏中点击首页,左侧导航栏中展示所有菜单。点击顶部其他菜单,左侧导航栏展示其子节点。
根据框架源码修改代码
看过antdv源码的同学应该都清楚,针对需求我们要对框架中的 GlobalLayout.vue 和 GlobalHeader.vue 两个文件中进行切入操作。
(这里展示的是比较笨的办法。最简单的是使用框架自带的vuex,在点击顶部一级导航时通过dispatch->commit保存子节点内容,然后在GlobalLayout中“computed”vuex子节点内容,watch页面展示变量,从而更新侧边导航内容)
确保layout模式
首先要明确的是,我们是在导航模式为“侧边栏导航”的选项下增加新功能。需要确保“src/views”目录下的 defaultSetting.js 中layout配置为: “layout: 'sidemenu'” 。
修改GlobalHeader.vue
打开 src\components\page\GlobalHeader.vue 文件,在 "mode === 'sidemenu'" 的div区域中增加 “顶部导航栏” 这里用到的还是框架自带的组件,但是会进行一些自定义改动。
图片中代码块1的组件TopMenu是这次要添加的顶部导航栏,代码块2是TopMenu参考的源组件。
记得在 “components” 属性 中引入 TopMenu 组件。
<top-menu
mode="horizontal"
:menu="menus"
:theme="theme"
@updateMenuTitle="handleUpdateMenuTitle"
@topMenuItemInfo="topMenuItemInfo"
/>
在TopMenu组件中新增的 “topMenuItemInfo” 方法,用于将一级菜单的节点信息由 GlobalHeader 传递到 GlobalLayout 中的侧边导航栏。
// 将菜单信息自TopMenu组件经GlobalHeader向GlobalLayout传递。
topMenuItemInfo (val) {
this.$emit('topMenuItemInfoToGlobalLayout', val);
}
- TopMenu组件是根据SMenu组件自定义修改而来。(文件目录自己定义,这里仅记录代码部分)
新建TopMenu.js
我们直接复制SMenu.js文件内容并生成新文件命名名为TopMenu.js。(记得修改参数name为“TopMenu”)
因为顶部导航栏只显示一级导航,所以仅对Menu组件中渲染MenuItem子节点做修改即可。使其能够只渲染一级导航。
修改TopMenu.js --> renderItem 方法,使其不需要对二三级子节点渲染。
renderItem (menu) {
if (!menu.hidden) {
return this.renderMenuItem(menu);
}
return null
}
接下来修改 renderMenuItem 方法,因为具体渲染逻辑都在这里。
首先涉及到重复判断赋值,修改const常量tag为let变量。因为要根据导航属性判断是否拥有子节点,如果拥有子节点则渲染为a标签。
修改为a标签后,需要对我们的子节点内容做操作。因为组件所在的GlobalHeader还需要将子节点内容都传递给GlobalLayout中的侧边导航栏用于展示。所以我们还需要重写该方法的返回代码。
新增如下代码。这里 $emit:topMenuItemInfo 的内容就是向上发送所点击节点的详情信息。
renderMenuItem 方法 完整代码如下:
renderMenuItem(menu) {
const target = menu.meta.target || null
let tag = (target && 'a') || 'router-link'
if (menu.children && !menu.alwaysShow) {
tag = 'a';
}
let props = { to: { name: menu.name } }
if (menu.route && menu.route === '0') {
props = { to: { path: menu.path } }
}
const attrs = { href: menu.path, target: menu.meta.target }
if (menu.children && menu.alwaysShow) {
// 把有子菜单的 并且 父菜单是要隐藏子菜单的
// 都给子菜单增加一个 hidden 属性
// 用来给刷新页面时, selectedKeys 做控制用
menu.children.forEach((item) => {
item.meta = Object.assign(item.meta, { hidden: true })
})
}
const on = {
click: () => {
this.$emit('topMenuItemInfo', menu);
},
}
let tempComponent;
if (menu.children && !menu.alwaysShow) {
tempComponent = (
<Item {...{ key: menu.path }}>
<tag {...{ on: on }} style="color: #fff;">
{this.renderIcon(menu.meta.icon)}
<span>{menu.meta.title}</span>
</tag>
</Item>
);
} else {
tempComponent = (
<Item {...{ key: menu.path }} {...{on: on }}>
<tag {...{ props, attrs }} style="color: #fff;">
{this.renderIcon(menu.meta.icon)}
<span>{menu.meta.title}</span>
</tag>
</Item>
);
}
return ( tempComponent )
}
主体render的修改
根据需求,还需在主体render函数中增加自定义样式。
这里设置的width会隐藏超出显示区域的节点。以"..."代替,并采用SubMenu的方式进行展示。可能会遇到背景与文字颜色冲突的情况(全部都是同色),解决办法是重写Menu组件样式即可。
return (
<Menu
style="display: inline-block; background: #1890FF; border: 0px; width: 790px; color: #fff;"
vModel={this.selectedKeys}
{...{ props, on: on }}
>
{menuTree}
</Menu>
)
// 超出隐藏部分SubMenu样式
.ant-menu-submenu > .ant-menu {
background-color: #1890FF;
}
目前,已经完成了对顶部导航仅显示一级菜单的工作。
修改GlobalLayout.vue
打开 src\components\page\GlobalLayout.vue 文件。此文件为总layout布局文件,其中包含GlobalHeader组件。
在GlobalHeader组件中新增方法 “@topMenuItemInfoToGlobalLayout="topMenuItemInfo" ”,用于接收组件内容。并在data中增加 “topMenus” 属性,初始值为空数组。
接下来完善 topMenuItemInfo 方法,方法中明确展示逻辑。侧边导航是通过menus来展示的。
// 将指定的菜单子节点写入到左侧菜单栏中
topMenuItemInfo (menuItem) {
// 这里的判断结构自行处理
if (menuItem.meta.title !== '首页') {
this.menus = menuItem.children; // 顶部一级节点的子节点
} else {
this.menus = this.permissionMenuList; // 展示最初的初始化路由
}
}
注意:上图中GlobalHeader组件的menus参数为topMenus,而topMenuItemInfo方法中赋值的却是menus。
我们要将顶部导航和侧边导航源数据分开,避免因为赋值导致展示错乱。
到这里就完事了!
topMenus 的导航数据就自己处理吧!
希望大家抽空仔细阅读框架源码,提升编码水平和架构能力。