好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航

早就实现了功能,但是发现点击的时候,选中的菜单项背景色会变白,周五时候仔细观察了一下,发现并不是调整样式的问题,而是选项没有被选中,于是好好研究了一下组件递归这块,总结记录一下心路历程

一、概念

递归:递归其实说白了,就是自己调用自己,样子就像是套娃一个套一个的,小时候玩过一个游戏汉诺塔就是利用的递归原理:

函数递归:函数利用函数名还调用自己
组件递归:所以组件递归利用的是vue组件中的name属性来实现的

二、需求

实现可折叠动态渲染多级侧边栏导航

三、分析

1、观察到侧边栏导航是一级一级的,第二级就相当于再重复一遍第一级 2、有一个特点,有的菜单有下级,有的没有下一级 3、动态渲染,说明是从后台接口获取的树类型数据,动态的渲染上去 四、代码实现 1、首先先执行一下文档里的demo试一下:

文档:element文档

2、改成自己需要的样式,第一次是这么写的

父组件SideBar

?

<template>

   <el-menu class= "menu-wrap" : default -active= "menuActiveName || 'home'" :active= "menuActiveName || 'home'"

            :collapse= "sidebarFold" :collapseTransition= "false" :unique-opened= "true" @select= "selectItem" >

     <template>

       <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 .$store.commit( 'common/updateMenuActiveName' , name)

     }

   },

   computed: {

     menuList: {

       get () {

         return this .$store.state.common.menuList

       },

       set (val) {

         this .$store.commit( 'common/updateMenuList' , val)

       }

     },

     menuActiveName: {

       get () { return this .$store.state.common.menuActiveName },

       set (val) { this .$store.commit( 'common/updateMenuActiveName' , val) }

     },

     sidebarFold: {

       get() { return this .$store.state.common.sidebarFold;},

       set(val) { this .$store.commit( "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;

}

 

</style>

子组件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', this.menuList)

     },

     // personManage (name) {

     //   if (name === '人员管理') {

     //     this.isShow = !this.$store.state.common.rbflag

     //     // alert('111' + this.isShow)

     //     this.$store.commit('common/updateShowRbox', this.isShow)

     //   }

     // },

     selectItem(name, path){

       // alert(name)

       this .$router.push(path)

       this .$store.commit( '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

       this .menuActiveName = 'NAV'

     }

   },

   computed: {

     menuList: {

       get () {

         return this .$store.state.common.menuList

       },

       set (val) {

         this .$store.commit( 'common/updateMenuList' , val)

       }

     },

     menuActiveName: {

       get () {

         console.log( this .$store.state.common.menuActiveName)

         return this .$store.state.common.menuActiveName

       },

       set (val) {

         this .$store.commit( 'common/updateMenuActiveName' , val)

       }

     },

     sidebarFold: {

       get() { return this .$store.state.common.sidebarFold;},

       set(val) { this .$store.commit( "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= "menu.code" :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>

     <!--    该菜单下无子菜单-->

     <el-menu-item v- else :index= "menu.code" @click= "selectItem(menu.code, 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 .$store.commit( '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] 即可
参考文章: Element-ui NavMenu子菜单使用递归生成时使用报错

最后附上简单的测试数据:

?

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 , "children" :[]},

                 { "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 , "children" :[]},

                 { "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, "isCheck" : null , "children" :[]},

                 { "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" : null , "children" :[]},

                 { "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" : null , "children" :[]}

               ]

             },

             { "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" :[]

             }

           ]

效果如图:

折叠后如图:

到此这篇关于vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航的文章就介绍到这了,更多相关elementUI可折叠动态侧边栏导航内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/focusmickey/article/details/115801169

查看更多关于vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航的详细内容...

  阅读:55次