vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航
早就实现了功能,但是发现点击的时候,选中的菜单项背景⾊会变⽩,周五时候仔细观察了⼀下,发现并不是调整样式的问题,⽽是选项没有被选中,于是好好研究了⼀下组件递归这块,总结记录⼀下⼼路历程
⼀、概念
递归:递归其实说⽩了,就是⾃⼰调⽤⾃⼰,样⼦就像是套娃⼀个套⼀个的,⼩时候玩过⼀个游戏汉诺塔就是利⽤的递归原理:
函数递归:函数利⽤函数名还调⽤⾃⼰
组件递归:所以组件递归利⽤的是vue组件中的name属性来实现的
⼆、需求
实现可折叠动态渲染多级侧边栏导航
三、分析
1、观察到侧边栏导航是⼀级⼀级的,第⼆级就相当于再重复⼀遍第⼀级
2、有⼀个特点,有的菜单有下级,有的没有下⼀级
3、动态渲染,说明是从后台接⼝获取的树类型数据,动态的渲染上去四、代码实现 1、⾸先先执⾏⼀下⽂档⾥的demo试⼀下:
⽂档:
2、改成⾃⼰需要的样式,第⼀次是这么写的
⽗组件SideBar
<template>
<el-menu class="menu-wrap" :default-active="menuActiveName || 'home'" :active="menuActiveName || 'home'"
:collapse="sidebarFold" :collapseTransition="false" :unique-opened="true" @select="selectItem">
<el-menu-item @click="sidebarFold = !sidebarFold">
<i v-show="!sidebarFold" class="el-icon-s-fold"></i>
<i v-show="sidebarFold" class="el-icon-s-unfold"></i>
<span slot="title" class="sidebar-one">导航列表</span>
</el-menu-item>
</template>
<!--    <side-bar-item :list="menuList"></side-bar-item>-->
<template v-for="(item,index) in menuList" class="menu">
<!-- 标题 -->
<template v-if="item.children.length" >
<el-submenu :key="index" :index="item.id" class="sub-menu-item">          <template :index="item.index" slot="title">
<!--            <i :class="item.icon"></i>-->
<i class="iconfont icon-danganjianying"></i>
<span>{{item.name}}</span>
</template>
<el-menu-item-group class="menu-item-group">
<side-bar-item :list="item.children"></side-bar-item>
</el-menu-item-group>
</el-submenu>
</template>
<!-- 选项 -->
<template v-else>
<el-menu-item :key="index" :index="item.id" class="menu-item">
<!--          <i :class="item.icon"></i>-->
<i class="iconfont icon-danganjianying"></i>
<span>{{item.name}}</span>
</el-menu-item>
</template>
</template>
</el-menu>
</template>
<script>
export default {
name: 'SideBar',
components: {
SideBarItem: () => import('@/components/common/SideBarItem')
},
data () {
return {
}
},
mounted () {
},
methods: {
selectItem(name, path){
/
/ alert(name)
this.$router.push(path)
this.$storemit('common/updateMenuActiveName', name)
}
},
computed: {
menuList: {
get () {
return this.$uList
},
set (val) {
this.$storemit('common/updateMenuList', val)
}
},
menuActiveName: {
get () { return this.$uActiveName },
set (val) { this.$storemit('common/updateMenuActiveName', val) }    },
sidebarFold: {
get() {return this.$store.statemon.sidebarFold;},
set(val) {this.$storemit("common/updateSidebarFold", val);}
},
},
}
</script>
<style lang="less" scoped>
.menu-wrap{
width: 200px;
min-height: 1020px;
background: url('../../assets/img/sidebar_bg.png') no-repeat;
background-size: 100% 100%;
}
/deep/ .el-menu{
background-color: transparent !important;
.
iconfont {
font-size: 18px;
vertical-align: sub;
margin-right: 5px;
display: inline-block;
width: 20px;
text-align: center;
}
}
/deep/ .el-menu-item,
/deep/ .el-submenu__title{
color: #fff;
.iconfont{
color: #fff;
}
}
/deep/ .el-menu-item span,
/deep/ .el-submenu__title span{
padding-left: 10px;
}
/deep/ .el-menu-item.is-active {
-webkit-box-shadow: inset 5px 100px 0px -2px #0064B6;
box-shadow: inset 5px 100px 0px -2px #0064B6;
}
/deep/ .el-submenu__title:hover,
/deep/ .el-menu-item:hover{
background: #0064B6;
}
/deep/ .el-menu-item-group__title{
padding: 0;
}
⼦组件SideBarItem
<template>
<div class="menu">
<template v-for="(item,index) in list">
<!-- 标题 -->
<template v-if="item.children.length" >
<el-submenu :key="index" :index="item.id" class="sub-menu-item">
<template :index="item.index" slot="title">
<!--            <i :class="item.icon"></i>-->
<i class="iconfont icon-danganjianying"></i>
<span>{{item.name}}</span>
</template>
<el-menu-item-group class="menu-item-group">
<side-bar-item :list="item.children"></side-bar-item>
</el-menu-item-group>
</el-submenu>
</template>
<!-- 选项 -->
<template v-else>
<el-menu-item :key="index" :index="item.id" class="menu-item" @click="selectItem(item.name, item.path)"> <!--          <i :class="item.icon"></i>-->
<i class="iconfont icon-danganjianying"></i>
<span>{{item.name}}</span>
</el-menu-item>
</template>
</template>
</div>
</template>
<script>
export default {
name: 'SideBarItem',
// props: ['list'],
props: {
list: {
type: Array || ''
}
},
data () {
return {
treeData: [{
label: '某某省',
children: [{
label: '中共某某省委员会'
// children: [{
// label: '三级 1-1-1'
// }]
}, {
label: '中共某某省办公室'
}, {
label: '中共某某省组织部'
}
]
}
],
isShow: false
// menuList: []
}
},
mounted () {
this.loadSysMenu()
},
methods: {
loadSysMenu () {
// console.log('menu', uList)
},
// personManage (name) {
//  if (name === '⼈员管理') {
//    this.isShow = !this.$store.statemon.rbflag
//    // alert('111' + this.isShow)
/
/    this.$storemit('common/updateShowRbox', this.isShow)
//  }
// },
selectItem(name, path){
// alert(name)
this.$router.push(path)
this.$storemit('common/updateMenuActiveName', name)
}
},
}
</script>
<style lang="less" scoped>
.menu{
width: 100%;
.sub-menu-item /deep/ .el-submenu__title,
.menu-item{
height: 60px;
line-height: 60px;
text-align: left;
//padding-left: 30px !important;
//border-bottom: 1px solid #000;
//border-right: 1px solid #000;
color: #fff;
}
.sub-menu-item .el-menu-item{
padding-right: 0;
}
/deep/ .el-menu-item .is-active{
background-color: #0087df;
}
.menu-item:hover,
/deep/ .el-submenu__title:hover{
background-color: #0087df;
}
.menu-item span,
.sub-menu-item /deep/ .el-submenu__title>span{
font-weight: 700;
}
.menu-item-group /deep/ .el-menu-item-group__title{
padding: 0 !important;
}
.menu-item-group .menu-item{
background: url('../../assets/img/sidebar_bg.png') no-repeat;
}
.
el-menu-item-group span{
font-weight: normal;
}
}
</style>
后来发现折叠不成功,⽽且选中之后选中项样式没变,后来发现是没选中,研究发现是因为多嵌套了⼀层div,⽽且⽤了el-menu-item-group项⽬中并不需要这个,于是改进如下:⽗组件SideBar
<template>
<el-menu class="menu-wrap" :default-active="menuActiveName" :collapse="sidebarFold" :collapseTransition="false" :unique-opened="true">
<template>
<el-menu-item @click="foldSideBar">
<i v-show="!sidebarFold" class="el-icon-s-fold"></i>
<i v-show="sidebarFold" class="el-icon-s-unfold"></i>
<span slot="title" class="sidebar-one">导航列表</span>
</el-menu-item>
</template>
<side-bar-item v-for="menu in menuList" :key="menu.id" :menu="menu"></side-bar-item>
</el-menu>
</template>
<script>
export default {
name: 'SideBar',
components: {
SideBarItem: () => import('@/components/common/SideBarItem')
},
data () {
return {
}
},
mounted () {
},
methods: {
foldSideBar(){
this.sidebarFold = !this.sidebarFold
}
},
computed: {
menuList: {
get () {
return this.$uList
},
set (val) {
this.$storemit('common/updateMenuList', val)
}
},
menuActiveName: {
get () {
console.log(this.$uActiveName)
return this.$uActiveName
},
set (val) {
this.$storemit('common/updateMenuActiveName', val)
}
},
sidebarFold: {
get() {return this.$store.statemon.sidebarFold;},
set(val) {this.$storemit("common/updateSidebarFold", val);}
},
},
}
</script>
<style lang="less" scoped>
.menu-wrap{
width: 200px;
min-height: 1020px;
background: url('../../assets/img/sidebar_bg.png') no-repeat;
background-size: 100% 100%;
}
/deep/ .el-menu{
background-color: transparent !important;
.iconfont {
font-size: 18px;
vertical-align: sub;
margin-right: 5px;
display: inline-block;
width: 20px;
text-align: center;
}
}
/deep/ .el-menu-item,
/deep/ .el-submenu__title{
color: #fff;
.iconfont{
color: #fff;
}
}
/deep/ .el-menu-item span,
/
deep/ .el-submenu__title span{
padding-left: 10px;
}
/deep/ .el-menu-item.is-active {
-webkit-box-shadow: inset 5px 100px 0px -2px #0064B6;
box-shadow: inset 5px 100px 0px -2px #0064B6;
}
/deep/ .el-submenu__title:hover,
/deep/ .el-menu-item:hover{
background: #0064B6;
}
</style>
⼦组件SideBarItem
<template>
<!--    该菜单下还有⼦菜单-->
<el-submenu v-if="menu.children.length" :index="de" :popper-append-to-body=false>
<template slot="title">
<i class="iconfont icon-danganjianying"></i>
<span>{{ menu.name }}</span>
</template>
<side-bar-item v-for="item in menu.children" :key="item.id" :menu="item"></side-bar-item>
</el-submenu>
vue element admin<!--    该菜单下⽆⼦菜单-->
<el-menu-item v-else :index="de" @click="de, menu.path)">
<i class="iconfont icon-danganjianying"></i>
<span>{{ menu.name }}</span>
</el-menu-item>
</template>
<script>
export default {
name: 'SideBarItem',
// props: ['menu'],
props: {
menu: {
type: Object || {}
}
},
data () {
return {
}
},
mounted () {
},
methods: {
selectItem(code, path){
// alert(name)
console.log(code, path)
this.$router.push(path)
this.$storemit('common/updateMenuActiveName', code)
}
},
}
</script>
<style lang="less" scoped>
.menu{
width: 100%;
.menu-item{
height: 60px;
line-height: 60px;
text-align: left;
color: #fff;
}
.sub-menu-item .el-menu-item{
padding-right: 0;
}
/deep/ .el-menu-item .is-active{
background-color: #0087df;
}
.menu-item:hover{
background-color: #0087df;
}
.menu-item span{
font-weight: 700;
}
}
</style>
功能基本实现,但是出现⼀个bug,当⿏标点折叠时,会出现循环调⽤某个事件,导致栈溢出报错,查看⽂章只需对⼦菜单设置属性 :popper-append-to-body=“false” 即可
参考⽂章:
最后附上简单的测试数据:
testData: [
{"id":"34161C2E8-7348-4439-8899-9A8039AE6AE4","pid":"0","code":"HOME","name":"⾸页","path":"/home","type":null,"icon":null,"sysId":"2761C2E8-7348-4439-8899-9A8039AE6AE3","orderNo":0,"isCheck":null,"children":[]},
{"id":"703DBEBD-F92C-4347-9203-F60A73153C3F","pid":"0","code":"WD","name":"温度","path":"/temperature","type":null,"icon":null,"sysId":"2AB00274-73DF-459A-A02E-C79A4D8A8929","orderNo":0,"isCheck":null,"children":[]},
{"id":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","pid":"0","code":"BJ","name":"报警","path":"/alarm","type":null,"icon":null,"sysId":"2AB00274-73DF-459A-A02E-C79A4D8A8929","orderNo":0,"isCheck":null,
"children":[
{"id":"1C99333D-886F-4AD6-93C4-7C5244E48247","pid":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","code":"FD","name":"防盗","path":"/burg","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":null,                {"id":"1DBDF678-F51F-444A-B995-61E5D9CCA5AF","pid":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","code":"JL","name":"警铃","path":"/bell","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":null                {"id":"BFC8C2E1-0E5B-4EEE-B91D-3DABC63FF481","pid":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","code":"JS","name":"浸⽔","path":"/immersion","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCh                {"id":"BFC8C2E1-0E5B-4EEE-B91D-3DABC63FF482","pid":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","code":"MJ","name":"门禁","path":"/punch","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck"                {"id":"BFC8C2E1-0E5B-4EEE-B91D-3DABC63FF483","pid":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","code":"ZT","name":"状态","path":"/state","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":              ]
},
{"id":"34161C2E8-7348-4439-8899-9A8039AE6AE5","pid":"0","code":"GZ","name":"⼯作","path":"/work","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":null,
"children":[]
},
{"id":"0CD6B09A-AA43-4AE9-9AC7-29BC5AC83495","pid":"0","code":"SJ","name":"数据","path":"/data","type":null,"icon":null,"sysId":"2AB00274-73DF-459A-A02E-C79A4D8A8929","orderNo":0,"isCheck":null,
"children":[]
},
{"id":"049C670D-A33E-4188-9206-B3F3B5DDE77B","pid":"0","code":"SP","name":"视频","path":"/video","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":null,"children":[]},
{"id":"0A15DBB6-3241-4C7F-AAD4-5417E7BBECAA","pid":"0","code":"RZ","name":"⽇志","path":"/log","type":null,"icon":null,"sysId":"2AB00274-73DF-459A-A02E-C79A4D8A8929","orderNo":0,"isCheck":null,
"children":[]
}
]
效果如图:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。