vue+elementUI組件遞歸實(shí)現(xiàn)可折疊動(dòng)態(tài)渲染多級(jí)側(cè)邊欄導(dǎo)航
早就實(shí)現(xiàn)了功能,但是發(fā)現(xiàn)點(diǎn)擊的時(shí)候,選中的菜單項(xiàng)背景色會(huì)變白,周五時(shí)候仔細(xì)觀察了一下,發(fā)現(xiàn)并不是調(diào)整樣式的問(wèn)題,而是選項(xiàng)沒(méi)有被選中,于是好好研究了一下組件遞歸這塊,總結(jié)記錄一下心路歷程
一、概念
遞歸:遞歸其實(shí)說(shuō)白了,就是自己調(diào)用自己,樣子就像是套娃一個(gè)套一個(gè)的,小時(shí)候玩過(guò)一個(gè)游戲漢諾塔就是利用的遞歸原理:
函數(shù)遞歸:函數(shù)利用函數(shù)名還調(diào)用自己
組件遞歸:所以組件遞歸利用的是vue組件中的name屬性來(lái)實(shí)現(xiàn)的
二、需求
實(shí)現(xiàn)可折疊動(dòng)態(tài)渲染多級(jí)側(cè)邊欄導(dǎo)航
三、分析
1、觀察到側(cè)邊欄導(dǎo)航是一級(jí)一級(jí)的,第二級(jí)就相當(dāng)于再重復(fù)一遍第一級(jí) 2、有一個(gè)特點(diǎn),有的菜單有下級(jí),有的沒(méi)有下一級(jí) 3、動(dòng)態(tài)渲染,說(shuō)明是從后臺(tái)接口獲取的樹(shù)類型數(shù)據(jù),動(dòng)態(tài)的渲染上去 四、代碼實(shí)現(xiàn) 1、首先先執(zhí)行一下文檔里的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">導(dǎo)航列表</span> </el-menu-item> </template> <!-- <side-bar-item :list="menuList"></side-bar-item>--> <template v-for="(item,index) in menuList" class="menu"> <!-- 標(biāo)題 --> <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> <!-- 選項(xiàng) --> <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"> <!-- 標(biāo)題 --> <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> <!-- 選項(xiàng) --> <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: '中共某某省委員會(huì)' // children: [{ // label: '三級(jí) 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>
后來(lái)發(fā)現(xiàn)折疊不成功,而且選中之后選中項(xiàng)樣式?jīng)]變,后來(lái)發(fā)現(xiàn)是沒(méi)選中,研究發(fā)現(xiàn)是因?yàn)槎嗲短琢艘粚觗iv,而且用了el-menu-item-group項(xiàng)目中并不需要這個(gè),于是改進(jìn)如下:
父組件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">導(dǎo)航列表</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> <!-- 該菜單下無(wú)子菜單--> <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>
功能基本實(shí)現(xiàn),但是出現(xiàn)一個(gè)bug,當(dāng)鼠標(biāo)點(diǎn)折疊時(shí),會(huì)出現(xiàn)循環(huán)調(diào)用某個(gè)事件,導(dǎo)致棧溢出報(bào)錯(cuò),查看文章只需對(duì)子菜單設(shè)置屬性 :popper-append-to-body=“false” 即可
參考文章:Element-ui NavMenu子菜單使用遞歸生成時(shí)使用報(bào)錯(cuò)
最后附上簡(jiǎn)單的測(cè)試數(shù)據(jù):
testData: [ {"id":"34161C2E8-7348-4439-8899-9A8039AE6AE4","pid":"0","code":"HOME","name":"首頁(yè)","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":"報(bào)警","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":"狀態(tài)","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":"數(shù)據(jù)","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":[] } ]
效果如圖:
折疊后如圖:
到此這篇關(guān)于vue+elementUI組件遞歸實(shí)現(xiàn)可折疊動(dòng)態(tài)渲染多級(jí)側(cè)邊欄導(dǎo)航的文章就介紹到這了,更多相關(guān)elementUI可折疊動(dòng)態(tài)側(cè)邊欄導(dǎo)航內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VUE常見(jiàn)知識(shí)疑點(diǎn)問(wèn)題總結(jié)
這篇文章主要介紹了VUE常見(jiàn)知識(shí)疑點(diǎn)問(wèn)題總結(jié),詳細(xì)介紹了vue.js @click和v-on:click有什么區(qū)別,v-on和v-bind的區(qū)別,通過(guò)本文可以認(rèn)識(shí)Vue的export、export default、import的詳細(xì)介紹,感興趣的朋友一起看看吧2024-02-02關(guān)于iview和elementUI組件樣式覆蓋無(wú)效問(wèn)題及解決
這篇文章主要介紹了關(guān)于iview和elementUI組件樣式覆蓋無(wú)效問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09vue項(xiàng)目netWork地址無(wú)法訪問(wèn)的問(wèn)題及解決
這篇文章主要介紹了vue項(xiàng)目netWork地址無(wú)法訪問(wèn)的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09vue自定義table表如何實(shí)現(xiàn)內(nèi)容上下循環(huán)滾動(dòng)
這篇文章主要介紹了vue自定義table表如何實(shí)現(xiàn)內(nèi)容上下循環(huán)滾動(dòng)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10使用jenkins一鍵打包發(fā)布vue項(xiàng)目的實(shí)現(xiàn)
這篇文章主要介紹了使用jenkins一鍵打包發(fā)布vue項(xiàng)目的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05Vue全局監(jiān)測(cè)錯(cuò)誤并生成錯(cuò)誤日志實(shí)現(xiàn)方法介紹
在做完一個(gè)項(xiàng)目后,之后的維護(hù)尤為重要。這時(shí),如果項(xiàng)目配置了錯(cuò)誤日志記錄,這樣能大大減少維護(hù)難度。雖然不一定能捕獲到全部的錯(cuò)誤,但是一般的錯(cuò)誤還是可以監(jiān)測(cè)到的。這樣就不用測(cè)試人員去一遍一遍復(fù)現(xiàn)bug了2022-10-10解決Element-ui radio單選框label布爾/數(shù)值的一個(gè)坑
這篇文章主要介紹了解決Element-ui radio單選框label布爾/數(shù)值的一個(gè)坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04淺談Vuex@2.3.0 中的 state 支持函數(shù)申明
這篇文章主要介紹了淺談Vuex@2.3.0 中的 state 支持函數(shù)申明,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11