递归element-ui el-menu 实现无限级子菜单

发布于:2025-05-08 ⋅ 阅读:(27) ⋅ 点赞:(0)

recursionMenu.vue

<template>
  <!-- 有子菜单------------------------------------------>
  <el-submenu
    v-if="(item.children || []).length"
    :popper-class="popper_class"
    :show-timeout="show_timeout"
    :hide-timeout="hide_timeout"
    :key="item.path || item.label"
    :index="item.path || item.label"
    :divide="item.divide"
  >
    <template slot="title">
      <!-- 主菜单图标 -->
      <i :class="item.icon" :style="item.style" />
      <!-- 主菜单文字 -->
      <span :style="item.style">{{ item.label }}</span>
      <el-badge
        v-if="item.badge"
        :is-dot="!item.badge.value"
        :value="item.badge.value"
        :type="item.badge ? item.badge.type : `danger`"
      />
    </template>
    <recursionMenu
      v-for="(item, i) in item.children"
      :key="item.path || item.label"
      :data="{
        item,
        show_timeout: show_timeout,
        hide_timeout: hide_timeout,
        popper_class: popper_class,
      }"
    />
  </el-submenu>

  <!-- 没有子菜单------------------------------------------>
  <el-menu-item
    v-else
    :key="`leaf-${item.path || item.label}`"
    :index="item.path || item.label"
    :divide="item.divide"
  >
    <!-- 主菜单图标 -->
    <i :class="item.icon" :style="item.style" />
    <!-- 主菜单文字 -->
    <span :style="item.style">{{ item.label }}</span>
    <el-badge
      v-if="item.badge"
      :is-dot="!item.badge.value"
      :value="item.badge.value"
      :type="item.badge ? item.badge.type : `danger`"
    />
  </el-menu-item>
</template>
<script>
export default {
  name: `recursionMenu`,
  components: {},
  data() {
    return {
      item: {},
      popper_class: ``,
      show_timeout: 300,
      hide_timeout: 300,
    };
  },
  props: ["data"],
  computed: {},
  watch: {
    data: {
      handler(newValue, oldValue) {
        // console.log(`深度监听${this.$options.name}:`, newValue, oldValue);
        if (Object.keys(newValue || {}).length) {
          this.form = JSON.parse(JSON.stringify(newValue));
          this.$g.convertForm2ComponentParam(`item`, this);
          this.$g.convertForm2ComponentParam(`popper_class`, this);
          this.$g.convertForm2ComponentParam(`show_timeout`, this);
          this.$g.convertForm2ComponentParam(`hide_timeout`, this);
        }
      },
      deep: true, //深度监听
      immediate: true, //立即执行
    },
  },
  created() {},
  mounted() {
    // 将该行代码放到能够顺利执行的地方就可以,本示例中放到了mounted中
    // 由于这种错误只发生在template下只有一个el-submenu这种情况,因此this.$children[0]就是该el-submenu
    this.$children[0].$parent = this.$parent;
  },
  beforeDestroy() {},
  methods: {},
};
</script>
<style lang="scss" scoped>
.recursionMenu {
}
</style>

demo

<template>
  <div :class="$options.name">
    <el-menu
      :class="$options.name"
      :collapse="collapse"
      :default-active="default_active"
      :default-openeds="default_openeds"
      :mode="mode"
      @select="select"
    >
      <recursionMenu
        v-for="(item, i) in menu"
        :key="i"
        :data="{
          item,
          show_timeout: show_timeout,
          hide_timeout: hide_timeout,
          popper_class: `admin_v2-top-submenu${collapse ? `-collapse` : ``}`,
        }"
    /></el-menu>
  </div>
</template>
<script>
import recursionMenu from "@/vue/admin/recursionMenu";
export default {
  name: `topMenu`,
  components: { recursionMenu },
  data() {
    return {
      collapse: null,
      default_active: null,
      default_openeds: [], //默认展开的菜单index数组
      menu: [],
      mode: `horizontal`,
      select: null,
      show_timeout: 300,
      hide_timeout: 300,
    };
  },
  props: ["data"],
  computed: {},
  watch: {
    data: {
      handler(newValue, oldValue) {
        // console.log(`深度监听${this.$options.name}:`, newValue, oldValue);
        if (Object.keys(newValue || {}).length) {
          let select = newValue.select;
          this.select = select;
          delete newValue.select;
          this.form = JSON.parse(JSON.stringify(newValue));
          this.$g.convertForm2ComponentParam(`isSubMenu`, this);
          this.$g.convertForm2ComponentParam(`collapse`, this);
          this.$g.convertForm2ComponentParam(`default_active`, this);
          this.$g.convertForm2ComponentParam(`default_openeds`, this);
          this.$g.convertForm2ComponentParam(`menu`, this);
          this.$g.convertForm2ComponentParam(`mode`, this);
          this.$g.convertForm2ComponentParam(`show_timeout`, this);
          this.$g.convertForm2ComponentParam(`hide_timeout`, this);
        }
      },
      deep: true, //深度监听
      immediate: true, //立即执行
    },
    navList: {
      handler(newValue, oldValue) {
        //console.log(`深度监听${this.$options.name}:`, newValue, oldValue);
        if (Object.keys(newValue || {}).length) {
          this.collapse_navList = [
            {
              label: `菜单`,
              icon: "el-icon-menu",
              //   ID: "MENU-ROOT",
              // "PID": "SRManagePermissions",
              // "leaf": false,
              children: this.navList,
            },
          ];
        }
      },
      deep: true, //深度监听
      immediate: true, //立即执行
    },
  },
  created() {},
  mounted() {},
  beforeDestroy() {},
  methods: {},
};
</script>
<style lang="scss" scoped>
.topMenu {
  >>> .el-menu {
    background-color: transparent;
    $menu-height: 50px;

    .el-icon-menu {
      color: white;
    }

    $active-background: radial-gradient(100% 80% at 50% 100%, #ffffff66, transparent 50%);
    .el-menu-item,
    .el-submenu__title {
      height: $menu-height;
      line-height: revert;
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 0 10px;
      color: white !important;
      border-bottom: none !important;
      transition: none;
      // 选中菜单、聚焦菜单、移入菜单
      &.is-active,
      &:focus,
      &:hover {
        background: $active-background;
      }
    }
    // 有子菜单的
    .el-submenu__title {
      cursor: default;
    }

    .el-submenu {
      &.is-active {
        & > .el-submenu__title {
          background: $active-background;
        }
      }
    }

    .el-submenu__icon-arrow {
      display: none;
    }
  }
}
</style>
<style lang="scss">
.admin_v2-top-submenu,
.admin_v2-top-submenu-collapse {
  transition: none !important; //去掉下拉菜单动画
  transform: translateX(-10px);
  // 有子菜单的
  .el-submenu__title {
    cursor: default;
  }
  .el-menu-item {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 0 20px !important;
    transition: none;
    &.is-active {
      pointer-events: none;
      background-color: #409eff !important;
      color: white !important;
    }
    &:hover {
      background-color: #ecf5ff;
      color: #66b1ff !important;
    }
  }
}
.admin_v2-top-submenu-collapse {
  .el-menu-item {
    justify-content: flex-start;
  }
  .el-submenu {
    padding: 0 20px;
    .el-menu-item {
      min-width: revert;
    }
    .el-submenu__title {
      display: flex;
      align-items: center;
      padding: 0 !important;
      .el-submenu__icon-arrow {
        position: static;
        margin-top: revert;
        margin-left: 10px;
      }
    }
    &.is-active {
      background-color: #409eff;
      & > .el-submenu__title {
        background-color: transparent;
        & > * {
          color: white;
        }
      }
    }
  }
}
</style>

解决报错el-menu子菜单鼠标移入报“Maximum call stack size exceeded.“错误原因及解决方法-CSDN博客文章浏览阅读25次。【代码】el-menu子菜单鼠标移入报“Maximum call stack size exceeded.“错误原因及解决方法。 https://blog.csdn.net/qq_37860634/article/details/147758875


网站公告

今日签到

点亮在社区的每一天
去签到