Element UI菜单折叠后的el-menu-item属性无法修改问题解决

要理解为什么"挂 body 上就可以",你必须知道两件事:

CSS 变量(--xxx)是"继承型属性"

CSS 变量的继承规则:

  • 子元素可以继承父元素的 CSS 变量

  • 只能继承祖先节点的变量

  • 如果祖先链上没有这个变量 → 变量无效

举例:

复制代码
<div style="--color: red">
  <span>能继承</span>
</div>

但如果结构是:

复制代码
<div style="--color: red"></div>

<body>
  <span>不能继承</span>
</body>

span 不会继承 div 的变量,因为它不是 div 的子孙节点。

折叠后 ElementUI 会把菜单弹窗挂到 body:

复制代码
<body>
  <ul class="el-menu el-menu--popup-right-start">
    <li class="el-menu-item">...</li>
  </ul>
</body>

注意:

  • popup 不在你的组件内部

  • popup 不是 .sidebar-menu 的子元素

  • popup 不在 scoped 样式作用域内

所以:

❌ 你写在组件上的 CSS 变量,popup 根本继承不到

❌ 你写在 scoped 样式里的 hover,popup 根本命不中

❌ v-bind() 在全局样式里无效

这就是你之前所有方法都失败的原因。

那为什么挂到 body 上就可以?

因为 popup 是 body 的子元素:

复制代码
body
 └── ul.el-menu--popup

所以当你这样写:

复制代码
document.body.style.setProperty('--hover-color', this.hoverColor)

popup 就能继承:

复制代码
body
 └── popup(继承 --hover-color)

然后你在全局样式里写:

复制代码
body .el-menu--popup .el-menu-item:hover {
  background-color: var(--hover-color) !important;
}

popup 就能正确使用 hoverColor。

一句话总结原理

因为 popup 不在你的组件里,所以它继承不到组件上的 CSS 变量。 但 popup 是 body 的子元素,所以把 CSS 变量挂到 body 上,它就能继承了。

再补一句你会恍然大悟的话

你之前写的:

复制代码
<div class="sidebar-menu" :style="{ '--hover-color': hoverColor }">

对 popup 来说等价于:

复制代码
一个完全无关的 div 上设置了变量
popup 根本看不到

所以当然不生效。

代码示例

复制代码
<template>
  <div class="sidebar-menu">
    <!-- 顶部 logo 区域 -->
    <div class="menu-header">
      <el-menu
        :default-active="$route.path"
        class="el-menu-vertical-demo"
        router
        :collapse="isCollapse"
        :text-color="textColor"
        :active-text-color="activeColor"
        :background-color="backgroundColor"
      >
        <el-menu-item disabled>
          <div class="logo">
            <img src="@/assets/images/horse.png" />
          </div>
          <span :style="{ color: textColor }">后台管理系统</span>
        </el-menu-item>
      </el-menu>
    </div>

    <!-- 菜单项区域 -->
    <div class="menu-scroll">
      <el-menu
        v-if="isLightOn"
        :default-active="$route.path"
        class="el-menu-vertical-demo"
        router
        :collapse="isCollapse"
        :text-color="textColor"
        :active-text-color="activeColor"
      >
        <menu-item
          v-for="item in menu_list"
          :key="item.path"
          :item="item"
          @menu-click="updateMenu"
        />
      </el-menu>
      <el-menu
        v-else
        :default-active="$route.path"
        class="el-menu-vertical-demo"
        router
        :collapse="isCollapse"
        :text-color="textColor"
        :active-text-color="activeColor"
        :background-color="backgroundColor"
      >
        <menu-item
          v-for="item in menu_list"
          :key="item.path"
          :item="item"
          @menu-click="updateMenu"
        />
      </el-menu>
    </div>
  </div>
</template>

<script>
import MenuItem from "@/components/Layout/MenuItem.vue";
import { mapActions, mapGetters } from "vuex";

export default {
  name: "SidebarMenu",
  components: { MenuItem },
  props: ["isCollapse"],
  computed: {
    ...mapGetters("PermissionModule", ["menuPermissions"]),
    ...mapGetters("LightModule", [
      "isLightOn",
      "backgroundColor",
      "textColor",
      "activeColor",
      "hoverColor",
    ]),
    menu_list() {
      return this.menuPermissions || [];
    },
  },
  mounted() {
    document.body.style.setProperty("--hover-color", this.hoverColor);
    this.updateMenuFromRoute(this.$route);
  },
  watch: {
    hoverColor(val) {
      document.body.style.setProperty("--hover-color", val);
    },
  },
  methods: {
    ...mapActions("MenuStateModule", ["setCurrentMenuaddTabMenu"]),
    updateMenuFromRoute(route) {
      this.setCurrentMenuaddTabMenu(route.path);
    },
    updateMenu(path) {
      this.setCurrentMenuaddTabMenu(path);
    },
  },
};
</script>

<style lang="less" scoped>
/* ===========================
   外层统一背景(主题切换核心)
   =========================== */
.sidebar-menu {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background-color: v-bind(backgroundColor);

  /* 顶部区域同步背景 */
  .menu-header {
    flex-shrink: 0;
    position: sticky;
    top: 0;
    z-index: 10;
    background-color: v-bind(backgroundColor);

    ::v-deep .el-menu-item.is-disabled {
      opacity: 1;
      cursor: pointer;
      background-color: transparent !important;
      color: inherit !important;

      &:hover {
        background-color: transparent !important;
      }
    }
  }

  /* ===========================
     菜单滚动区域
     =========================== */
  .menu-scroll {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    background-color: v-bind(backgroundColor);

    ::v-deep .el-menu,
    ::v-deep .el-submenu__title,
    ::v-deep .el-menu-item {
      background-color: transparent !important;
    }

    ::v-deep .el-menu-item:hover,
    ::v-deep .el-submenu__title:hover {
      background-color: v-bind(hoverColor) !important;
    }

    &::-webkit-scrollbar {
      width: 6px;
    }
    &::-webkit-scrollbar-thumb {
      background-color: #ccc;
      border-radius: 3px;
    }
    &::-webkit-scrollbar-track {
      background-color: #f5f5f5;
    }
  }
}

/* logo 样式 */
.logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: white;
  border-radius: 50%;
  margin-right: 8px;

  img {
    width: 30px;
    height: 30px;
    object-fit: cover;
  }
}

.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 200px;
}
</style>

<style lang="less">
/* popup 根节点背景 */
// body .el-menu--popup,
// body .el-menu--popup-right-start {
//   background-color: var(--bg-color) !important;
// }

/* popup hover(关键) */
/* 折叠 popup hover */
body .el-menu--popup .el-menu-item:hover,
body .el-menu--popup-right-start .el-menu-item:hover {
  background-color: var(--hover-color) !important;
  // color: var(--active-color) !important;
}
</style>
相关推荐
duanyuehuan2 小时前
vueRouter重置路由
前端·javascript·vue.js
Misnearch2 小时前
npm包-serve包使用
前端·npm·node.js
IT_陈寒2 小时前
React 18 性能优化实战:5个被低估的Hooks用法让你的应用快30%
前端·人工智能·后端
阿珊和她的猫2 小时前
页面停留时长埋点实现技术详解
开发语言·前端·javascript·ecmascript
chilavert3182 小时前
技术演进中的开发沉思-275 AJax : Slider
前端·javascript·ajax·交互
梦6502 小时前
基于 Vue3 + TypeScript 封装 UEditor 富文本编辑器组件
前端·vue.js·typescript
沛沛老爹2 小时前
从Web开发到AI应用——用FastGPT构建实时问答系统
前端·人工智能·langchain·rag·advanced-rag
锥锋骚年2 小时前
Vue 3 Vben Admin 框架的Mention提及组件
前端·javascript·vue.js
QT 小鲜肉2 小时前
【Linux命令大全】001.文件管理之mlabel命令(实操篇)
linux·运维·服务器·前端·笔记