antd——自定义菜单——基础积累
最近在用antd的框架,框架的具体样式如下:

由于此菜单比较常规,现有设计图如下:

给出的菜单触发要求:
左菜单实现规则:显示:
鼠标点击相应的一级菜单,右边浮动面板出现
注:鼠标hover其他一级菜单,右边浮动面板
不会切换关闭:
1、鼠标点击其他一级菜单,右边浮动面板
切换到相应的内容
2、点击面板右上角关闭图标也可消失跳转:
点击三级菜单跳转到相应页面
因此需要对antd框架的菜单部分进行相应的改动。
1.antd的菜单组件位置——src/components/menu/SideMenu.vue

2.原有菜单使用的是menu.js——是dom拼接的结构
原有部分的内容:
2.1 html部分内容
<i-menu:theme="theme":collapsed="collapsed":options="menuData"@select="onSelect"class="menu"/>
2.2 script部分内容
import IMenu from "./menu";
export default{name:'SideMenu',components: { IMenu },
}
3.分析菜单中的参数——theme collapsed menuData
1. theme:主题:跟随系统的主题改变而改变
2. collapsed:菜单是否折叠,改变的是内容区距离浏览器左边的位置
3. menuData:菜单的数据,是路由的数组形式
menuData菜单的过滤:将meta中的invisible为true的菜单要隐藏掉。由于可以有多级菜单,因此需要通过递归的方式来处理。
created(){this.menuData.map((menu) => {if (menu.meta && menu.meta.invisible) {return;} else if (menu.children && menu.children.length) {this.firstMenu.push({...menu,children: this.filterMenu(menu.children),});} else {this.firstMenu.push(menu);}});console.log("firstMenu", this.firstMenu);
},
methods:{filterMenu(arr) {var list =arr &&arr.filter((menu) => {if (menu.meta && menu.meta.invisible) {return;} else if (menu.children && menu.children.length) {return this.filterMenu(menu.children);} else {return menu;}});return list;},
}
4.自定义菜单
菜单的布局如下:

4.1 一级菜单可以通过a-menu组件来搭建

注意这个组件的用法:
如果没有二级目录,则直接使用a-menu-item,如果有二级目录,则需要用a-sub-menu标签包裹,里面再使用a-menu-item等标签
<a-menu mode="vertical" :theme="theme"><template v-for="(menu, mindex) in firstMenu"><a-menu-item@click="turnTo(menu)":key="menu.path"v-if="!menu.children || menu.children.length == 0":style="{color:currentMenu.indexOf(menu.fullPath || menu.path) > -1? '#f90': '#666',}"><a-icon:type="menu.meta && menu.meta.icon"v-if="menu.meta && menu.meta.icon"/><span>{{ menu.name }}</span></a-menu-item><a-sub-menu:key="menu.path":ref="'subRef' + mindex":style="{color:currentMenu.indexOf(menu.fullPath || menu.path) > -1? '#f90': '#666',}"v-else@titleClick="mouseover($event, menu, mindex)"overflowedIndicator=' '><span slot="title"><a-icon:type="menu.meta && menu.meta.icon"v-if="menu.meta && menu.meta.icon"/><span>{{ menu.name }}</span></span></a-sub-menu></template>
</a-menu>
分析一下上面的代码:
-
theme:主题色,组件的主题色跟框架本身的主题色保持一致

-
mode:模式,菜单类型,现在支持垂直、水平、和内嵌模式三种,
string: vertical vertical-right horizontal inline,影响到菜单的整体布局。

根据设计图,我们可以看到菜单要求是向右的箭头,则可以使用mode="vertical"的垂直菜单方式。 -
图标与菜单标题展示:
key要取唯一值,唯一值可以是menu.path路由路径 -
如果没有二级菜单和有二级菜单使用的组件不同,则需要根据是否有
children参数来判断层级。
一级菜单如果也要有图标,则需要添加a-icon组件,单独的一级菜单,则需要监听click点击事件。
<a-menu-item@click="turnTo(menu)":key="menu.path"v-if="!menu.children || menu.children.length == 0":style="{color:currentMenu.indexOf(menu.fullPath || menu.path) > -1? '#f90': '#666',}"
><a-icon:type="menu.meta && menu.meta.icon"v-if="menu.meta && menu.meta.icon"/><span>{{ menu.name }}</span>
</a-menu-item>
单独的一级菜单的点击事件——也就是上面代码的turnTo方法如下
turnTo(menu){this.visibleFlag = false;this.$router.push({ path: menu.fullPath });
}

更改后的函数如下:
turnTo(menu){this.visibleFlag = false;if (this.currentMenu != menu.fullPath) {this.$router.push({ path: menu.fullPath });}
}
4.2 二级菜单需要自定义dom
html部分的代码:
<a-sub-menu:key="menu.path":ref="'subRef' + mindex":style="{color:currentMenu.indexOf(menu.fullPath || menu.path) > -1? '#f90': '#666',}"v-else@titleClick="mouseover($event, menu, mindex)"
><span slot="title"><a-icon:type="menu.meta && menu.meta.icon"v-if="menu.meta && menu.meta.icon"/><span>{{ menu.name }}</span></span>
</a-sub-menu>
二级菜单需要实现以下几个功能:
4.2.1 根据点击内容,右侧相应位置出现弹层
这个功能最主要就是获取点击元素对应的一级菜单的位置,可以通过给一级菜单绑定ref属性来进行处理。

subMenu组件有个titleClick的点击事件,可以获取到当前元素的位置。
右侧弹层是否显示是通过visibleFlag参数来控制的。
secondMenu右侧菜单数据根据menu.children来展示。
右侧弹层的位置,特别是top值,需要根据this.$refs[ "subRef" + index][0].$el.getBoundingClientRect().top来实现,这行代码花了我好久的时间。。。
根据点击区域获取菜单的top—this.$refs["subRef" + index][0].$el.getBoundingClientRect().top
通过ref属性获取的dom元素,打印出来是vueComponent的类数组形式,则需要通过[0]选取数组的第一项,然后通过其中的$el.getBoundingClientRect().top的方式来获取top值
vue根据ref获取dom的top值的方法!!!
还有就是右侧弹层的宽度,我这边的基数是170px,根据二级菜单的数量来换列展示,因此宽度跟菜单的数量相关。
mouseover(e,menu,index){this.visibleFlag = true;this.secondMenu = menu.children;this.top = this.$refs[ "subRef" + index][0].$el.getBoundingClientRect().top;// console.log("top", this.top);var n = this.secondMenu.length / 5;this.width = Math.ceil(n) * 170;
}
4.2.2 右侧弹层的内容根据点击的内容展示
<div class="menuBgCls" :style="{ left: left + 'px' }" v-if="visibleFlag" @click="turnToPage"><divclass="secondMenuCls"@click.stop:style="{ top: top + 'px', width: width + 'px' }"><div class="item" v-for="(item, index) in secondMenu" :key="index"><div class="routerCls" @click="turnToPage"><router-link :to="item.fullPath">{{ item.name }}</router-link><a-icon@click.stoptype="star"class="starCls":theme="index % 2 == 0 ? 'filled' : 'outlined'"/></div></div><div class="closeCls" @click="closeSecondMenu"><a-icon type="close" /></div></div>
</div>
分析一下上面的代码:
首先要知道,右侧部分有两部分,底层是一个半透明的蒙层,上面是菜单展示区域。
- visibleFlag:控制弹层的显示与隐藏
- click:点击蒙层时,弹层部分要隐藏
- 蒙层的left:根据折叠与非折叠状态,left值不同。
- 菜单展示区域的left和top值:根据点击区域获取left和top值,主要是top值,left值还是跟折叠与非折叠有关系。
- 点击右上角的X,也可以关闭弹层部分
4.2.3 右侧弹层的样式
.menuBgCls {width: 100vw;height: 100vh;background: rgba(0, 0, 0, 0.3);position: fixed;right: 0;top: 0;
}
.secondMenuCls {position: absolute;left: 5px;padding: 20px 40px 10px 5px;background: #fff;z-index: 11111;min-width: 170px;width: auto;max-height: 240px;border-radius: 10px;box-shadow: 0 0 80px 5px rgba(0, 0, 0, 0.08);display: flex;// flex-wrap: wrap;flex-direction: column;flex-flow: column wrap;.closeCls {position: absolute;right: 2px;top: 8px;padding: 0 5px;cursor: pointer;i {font-size: 12px;&:hover {color: #f90;}}}.item {padding: 5px 0px;// border-right: 1px solid #f90;&:last-child {border: none;}.routerCls {display: flex;align-items: center;padding: 2px 15px;box-sizing: border-box;&:hover {background: #fff4e7;// a {// color: #f90;// }}a {color: #666;cursor: pointer;padding: 2px 10px 2px 0;width: 100px;display: inline-block;overflow: hidden;text-overflow: -o-ellipsis-lastline;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 1;line-clamp: 1;-webkit-box-orient: vertical;}.starCls {flex-shrink: 0;color: #f90;}}}
}
5.监听路由改变,右侧内容区域与左侧菜单相关联
5.1 路由参数如下:
{path: 'messageManage',name: '消息管理',component: PageView,meta: {icon: 'message'},children: [{path: 'message',name: '消息列表',meta: {icon: 'message',authority: 'Message.Look'},component: () =>import ('@/pages/message/message')}]
}
消息列表对应的路由是:/messageManage/message,则右侧如果是消息列表的页面,则左侧需要指定的是消息管理的菜单,比如颜色是主题色,表示当前是此菜单。也就是需要监听当前页面的路由,然后判断是否含有当前页面的路由参数。
监听当前页面的路由——立即监听且深度监听
watch: {$route: {handler: function(val, oldVal) {this.currentMenu = val.fullPath || val.path;},deep: true,immediate: true,},
},
currentMenu就是当前内容区域的路由路径。
currentMenu.indexOf(menu.fullPath || menu.path) > -1:如果满足条件,则表示是当前一级菜单里面的菜单,否则颜色不改变。
:style="{color:currentMenu.indexOf(menu.fullPath || menu.path) > -1? '#f90': '#666'}"
完成!!!
上效果图:
菜单展开

菜单展开时弹层显示

菜单折叠

菜单折叠时弹层展示

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
