# el-menu

相信大家在使用 Element UIel-menu 组件的时候,都碰到过如何通过数据动态渲染的问题,而官方的文档却没有这个实例,网上找的一些博客大部分人都实现了 el-menu 的垂直模式,而水平模式却没有,并且垂直模式的 el-menu 在收缩的时候会出现问题,但是他们并没有去解决,让我很苦恼,没办法,只能自己去解决了,先给大家看看效果先:

效果图

如上所示,水平和垂直模式的 el-menu 都一实现,而且垂直的 el-menu 收缩也正常,下面给大家贴上代码:

# 水平 el-menu

水平模式的 el-menu 主要分为两个 js 文件:

  1. ch-horizonal-menu.js
  2. 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

# 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

# 使用示例

// 注册 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

# 垂直 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

# 使用示例

// 注册 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

由于垂直模式的 el-menu 只是用 Element UI原生组件进行再封装,所以相关配置请参考其文档,而水平模式的 el-menu 则是自定义的组件,因此两者没有封装到一起,用法上也有所不同,还请各位见谅!

完整代码请查看 vue-web-frame (opens new window) 导航菜单示例