element menu源码

src/menu.vue

<script type="text/jsx">
  import emitter from 'element-ui/src/mixins/emitter';
  import Migrating from 'element-ui/src/mixins/migrating';
  import Menubar from 'element-ui/src/utils/menu/aria-menubar';
  import { addClass, removeClass, hasClass } from 'element-ui/src/utils/dom';

  export default {
    name: 'ElMenu',

    render (h) {
      const component = (
        <ul
          role="menubar"
          key={ +this.collapse }
          style={{ backgroundColor: this.backgroundColor || '' }}
          class={{
            'el-menu--horizontal': this.mode === 'horizontal',
            'el-menu--collapse': this.collapse,
            "el-menu": true
          }}
        >
          { this.$slots.default }
        </ul>
      );

      if (this.collapseTransition) {
        return (
          <el-menu-collapse-transition>
            { component }
          </el-menu-collapse-transition>
        );
      } else {
        return component;
      }
    },

    componentName: 'ElMenu',

    mixins: [emitter, Migrating],
    // 向子孙级注入自身
    provide() {
      return {
        rootMenu: this
      };
    },

    components: {
      // 折叠动画组件
      'el-menu-collapse-transition': {
        // 一个 函数化组件 就像这样:标记组件为 functional, 这意味它是无状态(没有 data),无实例(没有 this 上下文)。
        functional: true,
        render(createElement, context) {
          const data = {
            props: {
              mode: 'out-in'
            },
            on: {
              // 进入前
              beforeEnter(el) {
                el.style.opacity = 0.2;
              },
              // 进入中
              enter(el) {
                addClass(el, 'el-opacity-transition');
                el.style.opacity = 1;
              },
              // 进入后
              afterEnter(el) {
                removeClass(el, 'el-opacity-transition');
                el.style.opacity = '';
              },
              // 离开前
              beforeLeave(el) {
                if (!el.dataset) el.dataset = {};

                if (hasClass(el, 'el-menu--collapse')) {
                  removeClass(el, 'el-menu--collapse');
                  // 记录状态
                  el.dataset.oldOverflow = el.style.overflow;
                  el.dataset.scrollWidth = el.clientWidth;
                  addClass(el, 'el-menu--collapse');
                } else {
                  addClass(el, 'el-menu--collapse');
                  el.dataset.oldOverflow = el.style.overflow;
                  el.dataset.scrollWidth = el.clientWidth;
                  removeClass(el, 'el-menu--collapse');
                }

                el.style.width = el.scrollWidth + 'px';
                el.style.overflow = 'hidden';
              },

              leave(el) {
                addClass(el, 'horizontal-collapse-transition');
                el.style.width = el.dataset.scrollWidth + 'px';
              }
            }
          };
          return createElement('transition', data, context.children);
        }
      }
    },

    props: {
      // mode    模式    string    horizontal / vertical    vertical
      mode: {
        type: String,
        default: 'vertical'
      },
      // 当前激活菜单的 index
      defaultActive: {
        type: String,
        default: ''
      },
      // 当前打开的 sub-menu 的 index 的数组
      defaultOpeneds: Array,
      // 是否只保持一个子菜单的展开
      uniqueOpened: Boolean,
      // 是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转
      router: Boolean,
      // 子菜单打开的触发方式(只在 mode 为 horizontal 时有效)
      menuTrigger: {
        type: String,
        default: 'hover'
      },
      // 是否水平折叠收起菜单(仅在 mode 为 vertical 时可用
      collapse: Boolean,
      // 菜单的背景色(仅支持 hex 格式)
      backgroundColor: String,
      // 菜单的文字颜色(仅支持 hex 格式)
      textColor: String,
      // 当前激活菜单的文字颜色(仅支持 hex 格式)
      activeTextColor: String,
      // 是否开启折叠动画
      collapseTransition: {
        type: Boolean,
        default: true
      }
    },
    data() {
      return {
        activeIndex: this.defaultActive,//当前激活的菜单index
        openedMenus: (this.defaultOpeneds && !this.collapse) ? this.defaultOpeneds.slice(0) : [],//默认展开的菜单
        items: {},
        submenus: {}
      };
    },
    computed: {
      // 菜单hover时颜色
      hoverBackground() {
        return this.backgroundColor ? this.mixColor(this.backgroundColor, 0.2) : '';
      },
      // 是否展示hover状态下的下拉框
      isMenuPopup() {
        // 如果是水平或者垂直方向并且折叠返回true
        return this.mode === 'horizontal' || (this.mode === 'vertical' && this.collapse);
      }
    },
    watch: {
      // 监听激活菜单
      defaultActive(value){
        if(!this.items[value]){
          this.activeIndex = null
        }
        this.updateActiveIndex(value)
      },

      defaultOpeneds(value) {
        if (!this.collapse) {
          this.openedMenus = value;
        }
      },

      collapse(value) {
        if (value) this.openedMenus = [];
        this.broadcast('ElSubmenu', 'toggle-collapse', value);
      }
    },
    methods: {
      // 更新激活的菜单index
      updateActiveIndex(val) {
        const item = this.items[val] || this.items[this.activeIndex] || this.items[this.defaultActive];
        if (item) {
          this.activeIndex = item.index;
          this.initOpenedMenu();
        } else {
          this.activeIndex = null;
        }
      },

      getMigratingConfig() {
        return {
          props: {
            'theme': 'theme is removed.'
          }
        };
      },
      // 获取颜色通道
      getColorChannels(color) {
        // 去掉颜色前面#号
        color = color.replace('#', '');
        // 如果是3位简写
        if (/^[0-9a-fA-F]{3}$/.test(color)) {
          // 转成数组
          color = color.split('');
          for (let i = 2; i >= 0; i--) {
          // 用法:array.splice(start,deleteCount,item...)
          // 解释:splice方法从array中移除一个或多个数组,并用新的item替换它们。参数start是从数组array中移除元素的开始位置。
          // 参数deleteCount是要移除的元素的个数。
            color.splice(i, 0, color[i]);
          }
          // 转变为6位长度颜色值
          color = color.join('');
        }
        if (/^[0-9a-fA-F]{6}$/.test(color)) {
          return {
            // 转变为16进制数值
            red: parseInt(color.slice(0, 2), 16),
            green: parseInt(color.slice(2, 4), 16),
            blue: parseInt(color.slice(4, 6), 16)
          };
        } else {
          // 否则为白色
          return {
            red: 255,
            green: 255,
            blue: 255
          };
        }
      },
      // 获取混合后的颜色
      mixColor(color, percent) {
        let { red, green, blue } = this.getColorChannels(color);
        if (percent > 0) { // shade given color
          red *= 1 - percent;
          green *= 1 - percent;
          blue *= 1 - percent;
        } else { // tint given color
          red += (255 - red) * percent;
          green += (255 - green) * percent;
          blue += (255 - blue) * percent;
        }
        return `rgb(${ Math.round(red) }, ${ Math.round(green) }, ${ Math.round(blue) })`;
      },
      // 添加菜单
      addItem(item) {
        this.$set(this.items, item.index, item);
      },
      // 移除带单
      removeItem(item) {
        delete this.items[item.index];
      },
      // 添加子菜单
      addSubmenu(item) {
        this.$set(this.submenus, item.index, item);
      },
      // 移除子菜单
      removeSubmenu(item) {
        delete this.submenus[item.index];
      },
      // 打开菜单
      openMenu(index, indexPath) {
        let openedMenus = this.openedMenus;
        if (openedMenus.indexOf(index) !== -1) return;
        // 将不在该菜单路径下的其余菜单收起
        // collapse all menu that are not under current menu item
        // 如果设置了只保留一个子菜单展开
        if (this.uniqueOpened) {
          this.openedMenus = openedMenus.filter(index => {
            return indexPath.indexOf(index) !== -1;
          });
        }
        this.openedMenus.push(index);
      },
      // 关闭菜单
      closeMenu(index) {
        const i = this.openedMenus.indexOf(index);
        if (i !== -1) {
          this.openedMenus.splice(i, 1);
        }
      },
      // 子菜单点击事件
      handleSubmenuClick(submenu) {
        const { index, indexPath } = submenu;
        let isOpened = this.openedMenus.indexOf(index) !== -1;
        // 如果打开就关闭,否则就打开
        if (isOpened) {
          this.closeMenu(index);
          this.$emit('close', index, indexPath);
        } else {
          this.openMenu(index, indexPath);
          this.$emit('open', index, indexPath);
        }
      },
      handleItemClick(item) {
        const { index, indexPath } = item;
        const oldActiveIndex = this.activeIndex;
        const hasIndex = item.index !== null;

        if (hasIndex) {
          // 点击某个菜单,设置为激活状态
          this.activeIndex = item.index;
        }
        // 触发select事件
        this.$emit('select', index, indexPath, item);
        // 如果是水平或者折叠,清空打开的菜单
        if (this.mode === 'horizontal' || this.collapse) {
          this.openedMenus = [];
        }
        // 如果设置了vue-router 的模式并且有index
        if (this.router && hasIndex) {
          this.routeToItem(item, (error) => {
            this.activeIndex = oldActiveIndex;
            if (error) console.error(error);
          });
        }
      },
      // 初始化展开菜单
      // initialize opened menu
      initOpenedMenu() {
        const index = this.activeIndex;
        const activeItem = this.items[index];
        if (!activeItem || this.mode === 'horizontal' || this.collapse) return;
        // 保存路由跳转路径
        let indexPath = activeItem.indexPath;

        // 展开该菜单项的路径上所有子菜单
        // expand all submenus of the menu item
        indexPath.forEach(index => {
          let submenu = this.submenus[index];
          submenu && this.openMenu(index, submenu.indexPath);
        });
      },
      // 跳转
      routeToItem(item, onError) {
        let route = item.route || item.index;
        try {
          this.$router.push(route, () => {}, onError);
        } catch (e) {
          console.error(e);
        }
      },
      // sub-menu 展开的回调    index: 打开的 sub-menu 的 index, indexPath: 打开的 sub-menu 的 index path
      open(index) {
        const { indexPath } = this.submenus[index.toString()];
        indexPath.forEach(i => this.openMenu(i, indexPath));
      },
      // sub-menu 收起的回调    index: 收起的 sub-menu 的 index, indexPath: 收起的 sub-menu 的 index path
      close(index) {
        this.closeMenu(index);
      }
    },
    mounted() {
      this.initOpenedMenu();
      // 接收item-click事件,handleItemClick调用
      this.$on('item-click', this.handleItemClick);
      // 接收submenu-click事件,触发handleSubmenuClick调用
      this.$on('submenu-click', this.handleSubmenuClick);
      if (this.mode === 'horizontal') {
        new Menubar(this.$el); // eslint-disable-line
      }
      this.$watch('items', this.updateActiveIndex);
    }
  };
</script>

src/submenu.vue

<script>
  import ElCollapseTransition from 'element-ui/src/transitions/collapse-transition';
  import menuMixin from './menu-mixin';
  import Emitter from 'element-ui/src/mixins/emitter';
  import Popper from 'element-ui/src/utils/vue-popper';

  const poperMixins = {
    props: {
      transformOrigin: {
        type: [Boolean, String],
        default: false
      },
      offset: Popper.props.offset,
      boundariesPadding: Popper.props.boundariesPadding,
      popperOptions: Popper.props.popperOptions
    },
    data: Popper.data,
    methods: Popper.methods,
    beforeDestroy: Popper.beforeDestroy,
    deactivated: Popper.deactivated // keep-alive组件停用时调用
  };

  export default {
    name: 'ElSubmenu',

    componentName: 'ElSubmenu',

    mixins: [menuMixin, Emitter, poperMixins],

    components: { ElCollapseTransition },

    props: {
      // 唯一标志    string/null    —    null
      index: {
        type: String,
        required: true
      },
      // 展开 sub-menu 的延时    number    —    300
      showTimeout: {
        type: Number,
        default: 300
      },
      // 收起 sub-menu 的延时    number    —    300
      hideTimeout: {
        type: Number,
        default: 300
      },
      // 弹出菜单的自定义类名
      popperClass: String,
      // 是否禁用
      disabled: Boolean,
      // 是否将弹出菜单插入至 body 元素。在菜单的定位出现问题时,可尝试修改该属性    boolean    —    一级子菜单:true / 非一级子菜单:false
      popperAppendToBody: {
        type: Boolean,
        default: undefined
      }
    },

    data() {
      return {
        popperJS: null,
        timeout: null,
        items: {},
        submenus: {},
        mouseInChild: false
      };
    },
    watch: {
      // 是否打开
      opened(val) {
        if (this.isMenuPopup) {
          this.$nextTick(_ => {
            this.updatePopper();
          });
        }
      }
    },
    computed: {
      // popper option
      appendToBody() {
        return this.popperAppendToBody === undefined
          ? this.isFirstLevel
          : this.popperAppendToBody;
      },
      menuTransitionName() {
        return this.rootMenu.collapse ? 'el-zoom-in-left' : 'el-zoom-in-top';
      },
      // 打开
      opened() {
        return this.rootMenu.openedMenus.indexOf(this.index) > -1;
      },
      active() {
        let isActive = false;
        const submenus = this.submenus;
        const items = this.items;

        Object.keys(items).forEach(index => {
          if (items[index].active) {
            isActive = true;
          }
        });

        Object.keys(submenus).forEach(index => {
          if (submenus[index].active) {
            isActive = true;
          }
        });

        return isActive;
      },
      // 鼠标移入的背景色
      hoverBackground() {
        return this.rootMenu.hoverBackground;
      },
      // 菜单的背景色(仅支持 hex 格式)
      backgroundColor() {
        return this.rootMenu.backgroundColor || '';
      },
      // 当前激活菜单的文字颜色(仅支持 hex 格式)
      activeTextColor() {
        return this.rootMenu.activeTextColor || '';
      },
      // 菜单的文字颜色(仅支持 hex 格式)
      textColor() {
        return this.rootMenu.textColor || '';
      },
      // 模式
      mode() {
        return this.rootMenu.mode;
      },
      // 是否展示下拉框
      isMenuPopup() {
        return this.rootMenu.isMenuPopup;
      },
      // title样式
      titleStyle() {
        if (this.mode !== 'horizontal') {
          return {
            color: this.textColor
          };
        }
        return {
          borderBottomColor: this.active
            ? (this.rootMenu.activeTextColor ? this.activeTextColor : '')
            : 'transparent',
          color: this.active
            ? this.activeTextColor
            : this.textColor
        };
      },
      // 是否是第一级
      isFirstLevel() {
        let isFirstLevel = true;
        let parent = this.$parent;
        while (parent && parent !== this.rootMenu) {
          if (['ElSubmenu', 'ElMenuItemGroup'].indexOf(parent.$options.componentName) > -1) {
            isFirstLevel = false;
            break;
          } else {
            parent = parent.$parent;
          }
        }
        return isFirstLevel;
      }
    },
    methods: {
      // 开关折叠
      handleCollapseToggle(value) {
        // 如果为true
        if (value) {
          this.initPopper();
        } else {
          this.doDestroy();
        }
      },
      addItem(item) {
        this.$set(this.items, item.index, item);
      },
      removeItem(item) {
        delete this.items[item.index];
      },
      addSubmenu(item) {
        this.$set(this.submenus, item.index, item);
      },
      removeSubmenu(item) {
        delete this.submenus[item.index];
      },
      // 点击子菜单
      handleClick() {
        const { rootMenu, disabled } = this;
        if (
          (rootMenu.menuTrigger === 'hover' && rootMenu.mode === 'horizontal') ||
          (rootMenu.collapse && rootMenu.mode === 'vertical') ||
          disabled
        ) {
          return;
        }
        this.dispatch('ElMenu', 'submenu-click', this);
      },
      // 鼠标移入
      handleMouseenter(event, showTimeout = this.showTimeout) {

        if (!('ActiveXObject' in window) && event.type === 'focus' && !event.relatedTarget) {
          return;
        }
        const { rootMenu, disabled } = this;
        if (
          (rootMenu.menuTrigger === 'click' && rootMenu.mode === 'horizontal') ||
          (!rootMenu.collapse && rootMenu.mode === 'vertical') ||
          disabled
        ) {
          return;
        }
        // 寻找父级,在父组件触发
        this.dispatch('ElSubmenu', 'mouse-enter-child');
        clearTimeout(this.timeout);
        this.timeout = setTimeout(() => {
          this.rootMenu.openMenu(this.index, this.indexPath);
        }, showTimeout);
      },
      // 鼠标移出
      handleMouseleave() {
        const {rootMenu} = this;
        if (
          (rootMenu.menuTrigger === 'click' && rootMenu.mode === 'horizontal') ||
          (!rootMenu.collapse && rootMenu.mode === 'vertical')
        ) {
          return;
        }
        this.dispatch('ElSubmenu', 'mouse-leave-child');
        clearTimeout(this.timeout);
        this.timeout = setTimeout(() => {
          !this.mouseInChild && this.rootMenu.closeMenu(this.index);
        }, this.hideTimeout);
      },
      // 鼠标移入子菜单,背景色设置为父组件鼠标移入背景色
      handleTitleMouseenter() {
        if (this.mode === 'horizontal' && !this.rootMenu.backgroundColor) return;
        const title = this.$refs['submenu-title'];
        title && (title.style.backgroundColor = this.rootMenu.hoverBackground);
      },
      // 同上
      handleTitleMouseleave() {
        if (this.mode === 'horizontal' && !this.rootMenu.backgroundColor) return;
        const title = this.$refs['submenu-title'];
        title && (title.style.backgroundColor = this.rootMenu.backgroundColor || '');
      },
      // 更新位置
      updatePlacement() {
        // 如果是水平方向且是一级为底部,否则为右侧
        this.currentPlacement = this.mode === 'horizontal' && this.isFirstLevel
          ? 'bottom-start'
          : 'right-start';
      },
      // 初始化poper
      initPopper() {
        this.referenceElm = this.$el;
        this.popperElm = this.$refs.menu;
        this.updatePlacement();
      }
    },
    created() {
      // 接收toggle-collapse订阅,触发handleCollapseToggle
      this.$on('toggle-collapse', this.handleCollapseToggle);
      // 接收mouse-enter-child订阅,鼠标进入
      this.$on('mouse-enter-child', () => {
        this.mouseInChild = true;
        clearTimeout(this.timeout);
      });
      // 鼠标离开
      this.$on('mouse-leave-child', () => {
        this.mouseInChild = false;
        clearTimeout(this.timeout);
      });
    },
    mounted() {
      // 初始化,添加
      this.parentMenu.addSubmenu(this);
      this.rootMenu.addSubmenu(this);
      this.initPopper();
    },
    // 销毁前
    beforeDestroy() {
      // 移除
      this.parentMenu.removeSubmenu(this);
      this.rootMenu.removeSubmenu(this);
    },
    render(h) {
      const {
        active,
        opened,
        paddingStyle,
        titleStyle,
        backgroundColor,
        rootMenu,
        currentPlacement,
        menuTransitionName,
        mode,
        disabled,
        popperClass,
        $slots,
        isFirstLevel
      } = this;

      const popupMenu = (
        <transition name={menuTransitionName}>
          <div
            ref="menu"
            v-show={opened}
            class={[`el-menu--${mode}`, popperClass]}
            on-mouseenter={($event) => this.handleMouseenter($event, 100)}
            on-mouseleave={this.handleMouseleave}
            on-focus={($event) => this.handleMouseenter($event, 100)}>
            <ul
              role="menu"
              class={['el-menu el-menu--popup', `el-menu--popup-${currentPlacement}`]}
              style={{ backgroundColor: rootMenu.backgroundColor || '' }}>
              {$slots.default}
            </ul>
          </div>
        </transition>
      );

      const inlineMenu = (
        <el-collapse-transition>
          <ul
            role="menu"
            class="el-menu el-menu--inline"
            v-show={opened}
            style={{ backgroundColor: rootMenu.backgroundColor || '' }}>
            {$slots.default}
          </ul>
        </el-collapse-transition>
      );

      const submenuTitleIcon = (
        rootMenu.mode === 'horizontal' && isFirstLevel ||
        rootMenu.mode === 'vertical' && !rootMenu.collapse
      ) ? 'el-icon-arrow-down' : 'el-icon-arrow-right';

      return (
        <li
          class={{
            'el-submenu': true,
            'is-active': active,
            'is-opened': opened,
            'is-disabled': disabled
          }}
          role="menuitem"
          aria-haspopup="true"
          aria-expanded={opened}
          on-mouseenter={this.handleMouseenter}
          on-mouseleave={this.handleMouseleave}
          on-focus={this.handleMouseenter}
        >
          <div
            class="el-submenu__title"
            ref="submenu-title"
            on-click={this.handleClick}
            on-mouseenter={this.handleTitleMouseenter}
            on-mouseleave={this.handleTitleMouseleave}
            style={[paddingStyle, titleStyle, { backgroundColor }]}
          >
            {$slots.title}
            <i class={[ 'el-submenu__icon-arrow', submenuTitleIcon ]}></i>
          </div>
          {this.isMenuPopup ? popupMenu : inlineMenu}
        </li>
      );
    }
  };
</script>

src/menu-item-group

<template>
  <li class="el-menu-item-group">
    <div class="el-menu-item-group__title" :style="{paddingLeft: levelPadding + 'px'}">
      <template v-if="!$slots.title">{{title}}</template>
      <slot v-else name="title"></slot>
    </div>
    <ul>
      <slot></slot>
    </ul>
  </li>
</template>
<script>
  export default {
    name: 'ElMenuItemGroup',

    componentName: 'ElMenuItemGroup',

    inject: ['rootMenu'],
    props: {
      title: {
        type: String
      }
    },
    data() {
      return {
        paddingLeft: 20
      };
    },
    computed: {
      levelPadding() {
        let padding = 20;
        let parent = this.$parent;
        if (this.rootMenu.collapse) return 20;
        while (parent && parent.$options.componentName !== 'ElMenu') {
          // 每层submenu + 20
          if (parent.$options.componentName === 'ElSubmenu') {
            padding += 20;
          }
          parent = parent.$parent;
        }
        return padding;
      }
    }
  };
</script>

src/menu-item.vue

<template>
  <li class="el-menu-item"
    role="menuitem"
    tabindex="-1"
    :style="[paddingStyle, itemStyle, { backgroundColor }]"
    :class="{
      'is-active': active,
      'is-disabled': disabled
    }"
    @click="handleClick"
    @mouseenter="onMouseEnter"
    @focus="onMouseEnter"
    @blur="onMouseLeave"
    @mouseleave="onMouseLeave"
  >
    <el-tooltip
      v-if="parentMenu.$options.componentName === 'ElMenu' && rootMenu.collapse && $slots.title"
      effect="dark"
      placement="right">
      <div slot="content"><slot name="title"></slot></div>
      <div style="position: absolute;left: 0;top: 0;height: 100%;width: 100%;display: inline-block;box-sizing: border-box;padding: 0 20px;">
        <slot></slot>
      </div>
    </el-tooltip>
    <template v-else>
      <slot></slot>
      <slot name="title"></slot>
    </template>
  </li>
</template>
<script>
  import Menu from './menu-mixin';
  import ElTooltip from 'element-ui/packages/tooltip';
  import Emitter from 'element-ui/src/mixins/emitter';

  export default {
    name: 'ElMenuItem',

    componentName: 'ElMenuItem',

    mixins: [Menu, Emitter],

    components: { ElTooltip },

    props: {
      index: {
        default: null,
        validator: val => typeof val === 'string' || val === null
      },
      route: [String, Object],
      disabled: Boolean
    },
    computed: {
      // 是否激活
      active() {
        return this.index === this.rootMenu.activeIndex;
      },
      // hove背景色
      hoverBackground() {
        return this.rootMenu.hoverBackground;
      },
      // 菜单背景色
      backgroundColor() {
        return this.rootMenu.backgroundColor || '';
      },
      // 激活文字背景色
      activeTextColor() {
        return this.rootMenu.activeTextColor || '';
      },
      // 文字颜色
      textColor() {
        return this.rootMenu.textColor || '';
      },
      // 模式
      mode() {
        return this.rootMenu.mode;
      },
      // item样式
      itemStyle() {
        const style = {
          color: this.active ? this.activeTextColor : this.textColor
        };
        if (this.mode === 'horizontal' && !this.isNested) {
          style.borderBottomColor = this.active
            ? (this.rootMenu.activeTextColor ? this.activeTextColor : '')
            : 'transparent';
        }
        return style;
      },
      isNested() {
        return this.parentMenu !== this.rootMenu;
      }
    },
    methods: {
      // 鼠标移入
      onMouseEnter() {
        if (this.mode === 'horizontal' && !this.rootMenu.backgroundColor) return;
        this.$el.style.backgroundColor = this.hoverBackground;
      },
      // 鼠标移出
      onMouseLeave() {
        if (this.mode === 'horizontal' && !this.rootMenu.backgroundColor) return;
        this.$el.style.backgroundColor = this.backgroundColor;
      },
      // 点击
      handleClick() {
        if (!this.disabled) {
          this.dispatch('ElMenu', 'item-click', this);
          this.$emit('click', this);
        }
      }
    },
    mounted() {
      this.parentMenu.addItem(this);
      this.rootMenu.addItem(this);
    },
    beforeDestroy() {
      this.parentMenu.removeItem(this);
      this.rootMenu.removeItem(this);
    }
  };
</script>

src/menu-mixin.js

// mixis混入
export default {
  // 接收
  inject: ['rootMenu'],
  computed: {
    // 路径
    indexPath () {
      const path = [this.index];
      let parent = this.$parent;
      while (parent.$options.componentName !== 'ElMenu') {
        if (parent.index) {
          path.unshift(parent.index);
        }
        parent = parent.$parent;
      }
      return path;
    },
    // 获取elMenu或者elSubmenu
    parentMenu () {
      let parent = this.$parent;
      while (
        parent &&
        ['ElMenu', 'ElSubmenu'].indexOf(parent.$options.componentName) === -1
      ) {
        parent = parent.$parent;
      }
      return parent;
    },
    // 样式
    paddingStyle () {
      // 不是垂直返回
      if (this.rootMenu.mode !== 'vertical') return {};

      let padding = 20;
      let parent = this.$parent;

      if (this.rootMenu.collapse) {
        padding = 20;
      } else {
        while (parent && parent.$options.componentName !== 'ElMenu') {
          // 每次遇到ElSubmenu增加20
          if (parent.$options.componentName === 'ElSubmenu') {
            padding += 20;
          }
          parent = parent.$parent;
        }
      }
      return { paddingLeft: padding + 'px' };
    }
  }
};

猜你喜欢

转载自www.cnblogs.com/wsk1576025821/p/10969174.html
今日推荐