# el-menu
相信大家在使用 Element UI 中 el-menu 组件的时候,都碰到过如何通过数据动态渲染的问题,而官方的文档却没有这个实例,网上找的一些博客大部分人都实现了 el-menu 的垂直模式,而水平模式却没有,并且垂直模式的 el-menu 在收缩的时候会出现问题,但是他们并没有去解决,让我很苦恼,没办法,只能自己去解决了,先给大家看看效果先:
如上所示,水平和垂直模式的 el-menu 都一实现,而且垂直的 el-menu 收缩也正常,下面给大家贴上代码:
# 水平 el-menu
水平模式的 el-menu 主要分为两个 js 文件:
- ch-horizonal-menu.js
- ch-horizonal-menu-item.js
# ch-horizonal-menu.js
(function (root, factory) {
if (typeof exports === "object") {
module.exports = exports = factory();
} else if (typeof define === "function" && define.amd) {
define([], factory());
} else {
root.chHorizonalMenu = factory();
}
})(this, function () {
let chHorizonalMenu = {
template:
'<ul class="ch-horizonal-menu" :style="{backgroundColor:backgroundColor}">' +
"<ch-horizonal-menu-item " +
':menus="data" ' +
':active-text-color="activeTextColor"' +
':background-color="backgroundColor"' +
':activeId="activeId"' +
'@item-click="itemClick">' +
"</ch-horizonal-menu-item>" +
"</ul>",
props: {
data: {
type: Array,
default: function () {
return [];
},
},
backgroundColor: {
type: String,
default: "rgb(46, 50, 61)",
},
activeTextColor: {
type: String,
default: "rgb(255, 208, 75)",
},
},
data: function () {
return {
activeId: null,
submenuIds: [],
};
},
methods: {
itemClick: function (params) {
this.activeId = params.id;
this.removeActive();
this.$emit("item-click", params);
},
removeActive: function () {
var _this = this;
var activeNodes = this.$el.getElementsByClassName("is_active");
[].slice.call(activeNodes).forEach(function (el) {
el.className = el.className.replace(/ is_active/, "");
el.style.color = "#fff";
});
},
},
};
return {
name: "ch-horizonal-menu",
template: chHorizonalMenu,
};
});
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
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
# ch-horizonal-menu-item.js
(function (root, factory) {
if (typeof exports === "object") {
module.exports = exports = factory();
} else if (typeof define === "function" && define.amd) {
define([], factory());
} else {
root.chHorizonalMenuItem = factory();
}
})(this, function () {
let chHorizonalMenuItem = {
template:
"<div>" +
'<template v-for = "menu in menus">' +
'<li v-if="menu.children && menu.children.length" ' +
'@mouseenter="onMouseEnter($event , menu.id)"' +
'@mouseleave="onMouseLeave($event)"' +
'class="ch-horizonal-menu-item ch-horizonal-submenu">' +
'<div class="ch-horizonal-submenu__title">' +
"<span>{{menu.name}}</span>" +
'<i :class=\'[ floor == 1 ? "el-icon-arrow-down" : "el-icon-arrow-right", "ch-horizonal-submenu__icon-arrow"]\'></i>' +
"</div>" +
'<div class="ch-horizonal-submenu_inner" v-show="current_id == menu.id">' +
'<ul class="ch-horizonal-menu__inner" ' +
':style="{backgroundColor:backgroundColor}">' +
"<ch-horizonal-menu-item " +
':menus="menu.children" ' +
':active-text-color="activeTextColor"' +
':background-color="backgroundColor"' +
':activeId="activeId"' +
':level="floor" ' +
'@item-click="itemClick">' +
"</ch-horizonal-menu-item>" +
"</ul>" +
"</div>" +
"</li>" +
"<li v-else " +
":style=\"{color:activeId == menu.id ? activeTextColor :'#fff' }\"" +
'class="ch-horizonal-menu-item"' +
'@click="itemClick(menu , $event)">{{menu.name}}</li>' +
"</template>" +
"</div>",
props: {
menus: {
type: Array,
default: function () {
return [];
},
},
level: {
type: Number,
default: 0,
},
activeId: {
type: [Number, String],
default: "",
},
activeTextColor: String,
backgroundColor: String,
},
data: function () {
return {
show: false,
floor: this.level,
timeId: 0,
current_id: -1,
isActive: false,
};
},
created: function () {
this.floor++;
},
methods: {
onMouseEnter: function (event, id) {
clearTimeout(this.timeId);
this.current_id = id;
this.$nextTick(function () {
if (this.floor > 1) {
var innerMenu = event.target.querySelector(
".ch-horizonal-menu__inner"
);
var innerMenuItem = event.target.querySelector(
".ch-horizonal-menu-item"
);
innerMenu.style.left = innerMenuItem.offsetWidth - 20 + 5 + "px";
innerMenu.style.top = -innerMenuItem.offsetHeight + "px";
}
});
},
onMouseLeave: function (event) {
this.timeId = setTimeout(
function () {
this.current_id = -1;
}.bind(this),
200
);
},
itemClick: function (menu, event) {
this.current_id = -1;
this.$emit("item-click", menu);
if (event)
this.setParentNodeActive(
event.target.parentNode,
"ch-horizonal-submenu"
);
},
setParentNodeActive: function (el, cls) {
var classList = el.className.split(" ");
if (classList.indexOf("ch-horizonal-menu") == -1) {
if (classList.indexOf(cls) != -1) {
el.style.color = this.activeTextColor;
el.className = el.className.concat(" is_active");
}
this.setParentNodeActive(el.parentNode, cls);
}
},
},
};
return {
name: "ch-horizonal-menu-item",
template: chHorizonalMenuItem,
};
});
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
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
# 使用示例
// 注册 chHorizonalMenuItem 必须在 chHorizonalMenu 前注册
Vue.component(chHorizonalMenuItem.name , chHorizonalMenuItem.template);
Vue.component(chHorizonalMenu.name , chHorizonalMenu.template);
<ch-horizonal-menu
:data="menus"
background-color="#1B75D5"
@item-click="onHorizonalMenuClick"
>
</ch-horizonal-menu>
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 垂直 el-menu
# ch-vertical-menu-item.js
(function (root, factory) {
if (typeof exports === "object") {
module.exports = exports = factory();
} else if (typeof define === "function" && define.amd) {
define([], factory());
} else {
root.chVerticalMenuItem = factory();
}
})(this, function () {
var chVerticalMenuItem = {
template:
'<div class="ch-menu-wrapper">' +
'<template v-for="menu in data">' +
'<el-menu-item v-if="!menu.children" :index="menu.id" @click="handleMenuItem(menu)"> ' +
'<i v-if="menu.icon" :class="menu.icon"></i>' +
'<span slot="title">{{menu.name}}</span>' +
"</el-menu-item>" +
'<el-submenu v-else :index="menu.id">' +
'<template slot="title">' +
'<i v-if="menu.icon" :class="menu.icon"></i>' +
"<span>{{menu.name}}</span>" +
"</template>" +
'<ch-vertical-menu-item :data="menu.children" @item-click="onMenuClick"></ch-vertical-menu-item>' +
"</el-submenu>" +
"</template>" +
"</div> ",
props: {
data: {
type: Array,
default: function () {
return [];
},
},
},
methods: {
handleMenuItem: function (menu) {
this.$emit("item-click", menu);
},
onMenuClick: function (menu) {
this.$emit("item-click", menu);
},
},
};
return {
name: "ch-vertical-menu-item",
template: chVerticalMenuItem,
};
});
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
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
# 使用示例
// 注册 Vue.component(chVerticalMenuItem.name , chVerticalMenuItem.template);
<el-menu
:default-active="activeMenuId"
:background-color="themeColor"
text-color="#fff"
:collapse="isCollapse"
class="el-menu-vertical-left"
active-text-color="#ffd04b"
>
<ch-vertical-menu-item
:data="menus"
@item-click="onCHMenuClick"
></ch-vertical-menu-item>
</el-menu>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
由于垂直模式的 el-menu 只是用 Element UI原生组件进行再封装,所以相关配置请参考其文档,而水平模式的 el-menu 则是自定义的组件,因此两者没有封装到一起,用法上也有所不同,还请各位见谅!
完整代码请查看 vue-web-frame (opens new window) 导航菜单示例