elementUI 修改导航菜单源码NavMenu

一、下载elementUI源码并运行

macos 调试elementUI源码

二、修改element源码实现自己的导航栏菜单

1、实现需求
实现大概效果,鼠标悬浮到以及目录是,同时出现二级目录和三级目录
在这里插入图片描述
2、实现思路

1、 首选调整样式所有菜单都显示在父级菜单的下面
在这里插入图片描述
2、第二级菜单横着排列,其他竖向排列

在这里插入图片描述
3、为子菜单设置固定宽度和都显示出来

<style>

.el-menu li[role="menuitem"]:not(li[tabindex="0"]) {
  width:200px;
}
.el-menu li[role="menuitem"]:not(li[tabindex="0"]) .el-menu--horizontal {
  display: block !important;
}
</style>


//在saber 项目中的样式代码为
.el-menu .menu-wrapper:not(.menu-wrapper[tabindex="0"]) li[role="menuitem"] {
  width: 200px;
}

.el-menu .menu-wrapper:not(.menu-wrapper[tabindex="0"]) .el-menu--horizontal {
  display: block !important;
}


4、判断鼠标悬浮是菜单的第几级,如果是一级,则悬浮展示,其他级菜单不做任何处理

在这里插入图片描述

3、源代码 = package/menu/src/submenu

<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
};

export default {
  name: "ElSubmenu",

  componentName: "ElSubmenu",

  mixins: [menuMixin, Emitter, poperMixins],

  components: { ElCollapseTransition },

  props: {
    index: {
      type: String,
      required: true
    },
    showTimeout: {
      type: Number,
      default: 300
    },
    hideTimeout: {
      type: Number,
      default: 300
    },
    popperClass: String,
    disabled: Boolean,
    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;
    },
    backgroundColor() {
      return this.rootMenu.backgroundColor || "";
    },
    activeTextColor() {
      return this.rootMenu.activeTextColor || "";
    },
    textColor() {
      return this.rootMenu.textColor || "";
    },
    mode() {
      return this.rootMenu.mode;
    },
    isMenuPopup() {
      return this.rootMenu.isMenuPopup;
    },
    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) {
      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) {
      //menubar 说明是第一级菜单,一级菜单悬浮展示,其他菜单悬浮不做处理
      let menubar = event.target.parentElement.parentElement.outerHTML.indexOf(
        "menubar"
      );
      if (menubar == -1) return;
      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);

      if (this.appendToBody) {
        this.$parent.$el.dispatchEvent(new MouseEvent("mouseenter"));
      }
    },
    handleMouseleave(deepDispatch = false) {
      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);

      if (this.appendToBody && deepDispatch) {
        if (this.$parent.$options.name === "ElSubmenu") {
          this.$parent.handleMouseleave(true);
        }
      }
    },
    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"
          : "bottom-start";
    },
    initPopper() {
      this.referenceElm = this.$el;
      this.popperElm = this.$refs.menu;
      this.updatePlacement();
    }
  },
  created() {
    this.$on("toggle-collapse", this.handleCollapseToggle);
    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(true)}
          on-focus={$event => this.handleMouseenter($event, 100)}
        >
          <ul
            role="menu"
            class={[
              "el-menu el-menu--popup",
              `el-menu--popup-${currentPlacement}`
            ]}
            style={{
              backgroundColor: rootMenu.backgroundColor,
              display: isFirstLevel ? "flex" : "block" || ""
            }}
          >
            {$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"
        style="width:200px"
        aria-expanded={opened}
        on-mouseenter={this.handleMouseenter}
        on-mouseleave={() => this.handleMouseleave(false)}
        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]} />
        </div>
        {this.isMenuPopup ? popupMenu : inlineMenu}
      </li>
    );
  }
};
</script>
<style>
.el-menu li[tabindex="0"] {
  width: auto !important;
}
.el-menu li[role="menuitem"]:not(li[tabindex="0"]) .el-menu--horizontal {
  display: block !important;
}
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值