早就實現了功能,但是發現點擊的時候,選中的菜單項背景色會變白,周五時候仔細觀察了一下,發現并不是調整樣式的問題,而是選項沒有被選中,于是好好研究了一下組件遞歸這塊,總結記錄一下心路歷程
一、概念
遞歸:遞歸其實說白了,就是自己調用自己,樣子就像是套娃一個套一個的,小時候玩過一個游戲漢諾塔就是利用的遞歸原理:
函數遞歸:函數利用函數名還調用自己
組件遞歸:所以組件遞歸利用的是vue組件中的name屬性來實現的
二、需求
實現可折疊動態渲染多級側邊欄導航
三、分析
1、觀察到側邊欄導航是一級一級的,第二級就相當于再重復一遍第一級 2、有一個特點,有的菜單有下級,有的沒有下一級 3、動態渲染,說明是從后臺接口獲取的樹類型數據,動態的渲染上去 四、代碼實現 1、首先先執行一下文檔里的demo試一下:
文檔:element文檔
2、改成自己需要的樣式,第一次是這么寫的
父組件SideBar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
<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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
<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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
<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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
<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子菜單使用遞歸生成時使用報錯
最后附上簡單的測試數據:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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