zoukankan      html  css  js  c++  java
  • 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%; 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' };
        }
      }
    };
  • 相关阅读:
    NOI Online 2020 提高组游记
    【HDU5840】This world need more Zhu
    CSP-S 2019 AFO记
    防错笔记
    关于Blog
    题解 【UER #6】逃跑
    动态规划杂题选记
    有趣计数题选做
    题解 [POI2012] Leveling Ground
    xioa han 带画家!
  • 原文地址:https://www.cnblogs.com/wsk1576025821/p/10969174.html
Copyright © 2011-2022 走看看