7.3 el-menu

<el-menu>。它是构建网站或应用侧边栏导航、顶部导航栏等结构化导航系统的强大工具


核心概念与组成

一个 el-menu 由以下几个关键部分构成:

  1. <el-menu>: 菜单的根容器,负责管理整体布局、模式、激活状态等。
  2. <el-menu-item>: 顶级菜单项或叶子菜单项。代表一个可点击的导航链接,通常对应一个具体的路由或功能。
  3. <el-sub-menu> : 子菜单容器。用于包裹一个菜单标题和其下的子菜单项 (el-menu-item 或嵌套的 el-sub-menu),实现多级菜单结构。
  4. <el-menu-item-group> : 菜单项分组。用于将一组相关的 el-menu-item 分组显示,通常带有分组标题。
  5. <el-menu-divider>: 菜单分隔线。用于在菜单项之间添加视觉分隔。

<el-menu> 详解 (核心容器)

  • 作用: 菜单的整体容器,定义菜单的基本行为和外观。
  • 核心属性 :
    • mode: 核心属性 。定义菜单的展示模式。
      • horizontal: 水平模式。菜单项水平排列,常用于顶部导航栏。
      • vertical: 垂直模式。菜单项垂直排列,常用于侧边栏。这是默认值。
    • default-active: 当前激活菜单的 index。通常绑定到当前路由的路径($route.path),用于高亮显示当前页面对应的菜单项。
    • default-openeds: 默认展开的子菜单的 index 数组。在垂直模式下,可以指定哪些 el-sub-menu 初始时是展开的。
    • unique-opened: 是否只保持一个子菜单的展开。设置为 true 时,展开一个子菜单后,会自动收起其他已展开的子菜单。在侧边栏中很常用。
    • menu-trigger: 子菜单的触发方式。仅在 mode="horizontal" 时有效。
      • hover: 鼠标悬停时触发(默认)。
      • click: 点击时触发。
    • collapse: 核心属性 (侧边栏) 。是否水平折叠收起菜单(仅在 mode="vertical" 时可用)。设置为 true 时,菜单收起为窄栏,通常只显示图标。常与 collapse-transition 配合使用。
    • collapse-transition: 是否开启折叠动画。默认 true
    • router: 是否启用 Vue Router 模式。设置为 true 后,el-menu-itemindex 属性会被当作路由路径,点击时会触发 router.push(index) 进行导航。这是实现菜单与路由深度集成的关键
    • background-color: 菜单的背景色。
    • text-color: 菜单的文字颜色。
    • active-text-color: 当前激活菜单的文字颜色。
    • ellipsis: 当菜单项文本过长时,是否显示省略号 (...)。默认 true
    • indent: 子菜单的缩进距离(单位 px)。默认 20
  • 核心事件 :
    • open: 当展开某个子菜单时触发。回调参数 (index: string, indexPath: string[])
    • close: 当收起某个子菜单时触发。回调参数同上。
    • select: 当菜单项被点击选中时触发。回调参数 (index: string, indexPath: string[], item: ElMenuItem, routerResult?: any)routerResultrouter.push 返回的 Promise 结果(如果启用了 router 模式)。这是处理非路由菜单项点击的主要事件
  • 插槽 (Slots) :
    • 默认插槽: 放置 el-menu-item, el-sub-menu, el-menu-item-group, el-menu-divider

<el-menu-item> 详解 (菜单项)

  • 作用: 表示一个具体的、可点击的菜单选项。
  • 核心属性 :
    • index: 必需 。唯一标识符。在 router 模式下,它作为路由路径;在非 router 模式下,它是 select 事件的参数。必须保证在同一级菜单中唯一。
    • route: Vue Router 的路由对象。如果设置了此属性,点击该项会导航到该路由,优先级高于 index。常用于需要传递参数的路由。
    • disabled: 是否禁用该项。
  • 插槽 :
    • 默认插槽: 放置菜单项的文本内容。
    • title: 自定义菜单项的标题(文本部分),可以包含 HTML 或组件。

<el-sub-menu> 详解 (子菜单)

  • 作用: 容器,用于创建可展开/收起的子菜单。
  • 核心属性 :
    • index: 必需。子菜单的唯一标识符。
    • popper-class: 自定义弹出菜单的 class
    • teleported: 是否将子菜单的 DOM 元素使用 Teleport 移动到 body 下。默认 true,有助于解决 z-indexoverflow 问题。
    • show-timeout / hide-timeout: 子菜单展开/隐藏的延迟时间(毫秒)。
  • 插槽 :
    • 默认插槽: 放置子菜单下的 el-menu-itemel-sub-menu
    • title: 必需。定义子菜单的标题内容(显示在主菜单上的部分),通常包含图标和文本。

<el-menu-item-group><el-menu-divider>

  • <el-menu-item-group> : 用于对 el-menu-item 进行逻辑分组。它有一个 title 属性来设置分组标题。
  • <el-menu-divider>: 简单的分隔线组件,没有属性,直接插入即可。

工作原理与关键点

  1. index 是灵魂 : el-menu 通过 index 属性来识别和管理每一个菜单项和子菜单。default-active 通过匹配 index 来确定哪个项是激活状态。
  2. router 模式 : 当 el-menu 设置了 router="true"el-menu-itemindex 会被当作路由路径使用 router.push 进行导航。default-active 通常绑定到 $route.path 以实现自动高亮。
  3. collapse 模式 : 在侧边栏应用中,通过一个布尔值控制 collapse 属性,可以实现菜单的展开/收起动画。收起时,通常只显示图标,需要为 el-menu-itemel-sub-menutitle 插槽设计好图标。
  4. 事件流 : select 事件由 el-menu-item 触发,被 el-menu 捕获。open/close 事件由 el-sub-menu 触发,被 el-menu 捕获。
  5. teleported : 对于复杂的布局(如菜单在 overflow: hidden 的容器内),teleported="true" 可以确保子菜单正确弹出,不受父级样式限制。

常用示例

示例 1: 基础垂直侧边栏菜单
复制代码
<template>
  <el-menu
    :default-active="$route.path"
    class="el-menu-vertical-demo"
    @open="handleOpen"
    @close="handleClose"
    @select="handleSelect"
    router <!-- 启用路由模式 -->
  >
    <el-menu-item index="/home">
      <el-icon><HomeFilled /></el-icon>
      <span>首页</span>
    </el-menu-item>
    <el-menu-item index="/user">
      <el-icon><User /></el-icon>
      <span>用户管理</span>
    </el-menu-item>
    <el-sub-menu index="article">
      <template #title>
        <el-icon><Document /></el-icon>
        <span>文章管理</span>
      </template>
      <el-menu-item index="/article/list">文章列表</el-menu-item>
      <el-menu-item index="/article/create">创建文章</el-menu-item>
    </el-sub-menu>
    <el-menu-item-group title="工具">
      <el-menu-item index="/tools/calculator">计算器</el-menu-item>
      <el-menu-item index="/tools/converter">转换器</el-menu-item>
    </el-menu-item-group>
    <el-menu-divider />
    <el-menu-item index="/settings">
      <el-icon><Setting /></el-icon>
      <span>设置</span>
    </el-menu-item>
  </el-menu>
</template>

<script setup>
import { HomeFilled, User, Document, Setting } from '@element-plus/icons-vue'
import { useRouter } from 'vue-router'

const router = useRouter()

const handleOpen = (index, indexPath) => {
  console.log('open:', index, indexPath)
}
const handleClose = (index, indexPath) => {
  console.log('close:', index, indexPath)
}
const handleSelect = (index, indexPath) => {
  console.log('select:', index, indexPath)
  // 如果没有启用 router 模式,这里可以手动导航
  // router.push(index)
}
</script>

<style scoped>
/* 可以在这里自定义样式 */
</style>
示例 2: 可折叠的侧边栏
复制代码
<template>
  <div class="sidebar-container">
    <!-- 折叠按钮 -->
    <el-button @click="toggleCollapse" style="margin: 10px;">
      {{ isCollapse ? '展开' : '收起' }}
    </el-button>
    <!-- 菜单 -->
    <el-menu
      :default-active="$route.path"
      class="el-menu-vertical-demo"
      :collapse="isCollapse"
      :collapse-transition="true"
      router
      unique-opened
    >
      <el-menu-item index="/dashboard">
        <el-icon><HomeFilled /></el-icon>
        <template #title>仪表盘</template> <!-- #title 等同于 v-slot:title -->
      </el-menu-item>
      <el-sub-menu index="system">
        <template #title>
          <el-icon><Setting /></el-icon>
          <span>系统管理</span>
        </template>
        <el-menu-item index="/system/user">用户管理</el-menu-item>
        <el-menu-item index="/system/role">角色管理</el-menu-item>
      </el-sub-menu>
    </el-menu>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { HomeFilled, Setting } from '@element-plus/icons-vue'

const isCollapse = ref(false) // 控制折叠状态

const toggleCollapse = () => {
  isCollapse.value = !isCollapse.value
}
</script>

<style scoped>
.sidebar-container {
  width: 200px; /* 或根据 isCollapse 动态调整 */
  border-right: 1px solid #e6e6e6;
}
/* 当菜单收起时,隐藏文字 */
.el-menu--collapse span {
  height: 0;
  width: 0;
  overflow: hidden;
  visibility: hidden;
  display: inline-block;
}
/* 也可以通过 CSS 类控制图标居中 */
.el-menu--collapse .el-menu-item .el-icon,
.el-menu--collapse .el-sub-menu__title .el-icon {
  margin-right: 0;
  display: block;
  margin: 0 auto;
}
</style>
示例 3: 水平顶部导航
复制代码
<template>
  <el-menu
    :default-active="$route.path"
    mode="horizontal"
    :ellipsis="false" <!-- 顶部导航通常不省略 -->
    router
  >
    <el-menu-item index="/">首页</el-menu-item>
    <el-menu-item index="/products">产品中心</el-menu-item>
    <el-sub-menu index="services">
      <template #title>服务支持</template>
      <el-menu-item index="/services/faq">常见问题</el-menu-item>
      <el-menu-item index="/services/contact">联系我们</el-menu-item>
    </el-sub-menu>
    <el-menu-item index="/about">关于我们</el-menu-item>
  </el-menu>
</template>
示例 4: 使用 route 属性 (传递参数)
复制代码
<template>
  <el-menu :default-active="$route.path" router>
    <el-menu-item :route="{ name: 'UserProfile', params: { id: 123 } }" index="profile">
      我的资料
    </el-menu-item>
  </el-menu>
</template>
  • 解析 : 点击 "我的资料",会导航到名为 UserProfile 的路由,并传递参数 { id: 123 }index="profile" 在这里主要用于 default-active 的匹配。

使用建议与最佳实践

  1. router 模式优先 : 如果你的应用使用 Vue Router,强烈建议启用 router="true" 并利用 index 作为路径,这能极大简化导航逻辑和激活状态管理。
  2. default-active 绑定路由 : 将 default-active 绑定到 $route.path$route.fullPath,实现页面切换时菜单的自动高亮。
  3. unique-opened : 在侧边栏中,通常设置 unique-opened="true" 以提供更清晰的导航体验。
  4. collapse 的 CSS : 当菜单收起时,需要通过 CSS 隐藏文本 (visibility: hidden) 并可能调整图标位置,确保窄栏美观。
  5. teleported : 除非有特殊需求,保持 teleported="true" 以避免布局问题。
  6. 性能: 对于非常庞大的菜单,考虑虚拟滚动或分组加载。
  7. 无障碍性 (a11y): 确保菜单结构清晰,使用语义化标签。
  8. 主题定制 : 通过 background-color, text-color, active-text-color 或 SCSS 变量深度定制菜单外观。
  9. menu-trigger : 在移动端或需要精确控制的场景,可以将水平菜单的触发方式改为 click
  10. 事件处理 : select 事件是处理非路由导航或额外逻辑的主要入口。

el-menu 是构建现代 Web 应用导航结构的基石。掌握其模式、路由集成和折叠功能,可以高效地创建出专业、用户体验良好的导航系统。