前端开发中,经常遇到需要与后端配置,前端动态渲染菜单的应用场景,而究其本质,就是菜单组件的应用,只是在不确定菜单级数的情况下,我们需要对组件做一个递归处理,让它能够适应大多数应用场景。
如上图所示,以Element-Plus组件库为例,对于一个递归型菜单,我们该如何进行封装呢?先假设后端接口返回的简单菜单信息如下,
[
{
index: "1",
path: "/index",
routeParams: {},
meta: {
title: "首页",
icon: "House",
}
},
{
index: "2",
path: "/funcs",
routeParams: {},
meta: {
title: "功能中心",
icon: "Menu",
}
},
{
index: "3",
path: "/cases",
routeParams: {},
meta: {
title: "项目案例",
icon: "Grid",
mode:"vertical",
},
children: [
{
index: "3-1",
path: "",
routeParams: {},
meta: {
title: "item one",
icon: "Orange",
},
children:[
{
index: "3-1-1",
path: "",
routeParams: {},
meta: {
title: "item one-one",
icon: "Orange",
},
}
]
},
{
index: "3-2",
path: "",
routeParams: {},
meta: {
title: "item two",
icon: "Orange",
}
},
{
index: "3-3",
path: "",
routeParams: {},
meta: {
title: "item three",
icon: "Orange",
}
}
]
}
]
实际开发中,还可能包括组件路径、菜单项显隐等相关字段信息,此处暂不考虑,我们当前的核心需求是:如何去完成这样的一个菜单组件的封装。
对于上图所示的效果,我在此处所做的组件封装示例代码如下,
<!-- 多级菜单组件抽取 -->
<template>
<el-menu :default-active="activeIndex" :class="customMenuClass" background-color="transparent" text-color="#fff"
active-text-color="#ffef40" :mode="mode" @select="handleSelect" :ellipsis="false">
<template v-for="(item) in items">
<template v-if="item.children">
<el-sub-menu :index="item.index" popper-class="el-sub-menu-popper-class">
<template #title>
<router-link :to="item.path">
<el-icon>
<component :is="item.meta.icon" :size="24"></component>
</el-icon>
<span>{{ item.meta.title }}</span>
</router-link>
</template>
<menu-list :items="item.children" :mode="item.meta.mode"></menu-list>
</el-sub-menu>
</template>
<template v-else>
<el-menu-item :index="item.index" :key="item.path">
<router-link :to="item.path">
<el-icon>
<component :is="item.meta.icon" :size="24"></component>
</el-icon>
<span>{{ item.meta.title }}</span>
</router-link>
</el-menu-item>
</template>
</template>
</el-menu>
</template>
<script>
export default {
name: "menu-list",
props: {
customMenuClass: {
type: String,
required: false,
default: "el-menu-class",
},
mode: {
type: String,
default: "horizontal",
},
items: {
type: Array,
required: true,
},
activeIndex: {
type: String,
required: false,
default: "",
}
},
data() {
return {}
},
methods: {
handleSelect(key, keyPath, menuItem) {
this.$emit("select",{key, keyPath, menuItem})
}
}
}
</script>
<style lang="scss" scoped>
.el-menu-class {
.el-menu-item:not(.is-disabled):hover {
background-color: rgba(127, 255, 212, .3);
}
}
</style>
<style lang="scss">
.el-sub-menu-popper-class {
background-color: $base-background-color !important;
}
</style>
使用示例如下,
//menuList变量对应的就是文章开头处给出的示例数据
<MenuList :items="menuList" active-index="1" mode="horizontal"/>
最终,我们就能完成这样的一个递归型菜单组件了,但是对于菜单的样式,可以根据实际需要进行定制。