Appearance
Menu 导航菜单
基于 Element Plus 的 ElMenu 二次封装,支持普通菜单项、可折叠子菜单及分组三种渲染模式。
菜单节点通过 h() 直接作为 ElMenu 的默认插槽子节点渲染,避免在中间再包一层自定义组件,从而与 Element Plus 内部的注入链一致,子菜单可正常展开。
图标区域使用固定宽度的 .hx-menu__icon-slot 占位(无图标时也会占位),默认宽度为 var(--el-menu-icon-width),与各层级文案左缘对齐。可通过 iconWidth 或根节点 CSS 变量 --hx-menu-icon-slot-width 调整。
基础用法
默认垂直模式,直接传入 menu 数据和当前激活路径 active。
展示竖向和横向两种布局模式。
竖向模式(默认)
- 首页
- 用户管理
- 订单管理
横向模式
- 首页
- 用户管理
- 订单管理
<template>
<div class="demo-section">
<p class="demo-title">竖向模式(默认)</p>
<div class="menu-demo-col">
<HxMenu :menu="menu" :active="'/home'" />
</div>
<p class="demo-title menu-demo-title-next">横向模式</p>
<div class="menu-demo-row">
<HxMenu :menu="menu" mode="horizontal" :active="'/home'" />
</div>
</div>
</template>
<script setup lang="ts">
import { HxMenu } from '@whhx/ui'
import type { MenuItem } from '@whhx/ui'
/** 全部使用 iconify 图标,示例展示两种常用图标集 */
const menu: MenuItem[] = [
{ title: '首页', path: '/home', icon: { name: 'mdi:home', type: 'iconify' } },
{ title: '用户管理', path: '/user', icon: { name: 'mdi:account-group', type: 'iconify' } },
{ title: '订单管理', path: '/order', icon: { name: 'mdi:clipboard-list', type: 'iconify' } },
]
</script>
<style scoped>
.demo-section {
padding: 4px 0;
}
.demo-title {
margin: 0 0 10px;
font-size: 13px;
color: var(--el-text-color-secondary);
font-weight: 500;
}
.menu-demo-title-next {
margin-top: 28px;
}
/* 固定侧栏宽度,避免 el-menu 在预览区被拉满导致右侧大片留白 */
.menu-demo-col {
width: 240px;
max-width: 100%;
}
.menu-demo-row {
width: 100%;
min-width: 0;
}
</style>
子菜单与图标
通过 type: 'sub' 定义可展开子菜单,支持无限递归嵌套。通过 icon 属性配置图标。
展示子菜单、嵌套菜单及各类图标配置。
子菜单(sub)+ 图标
- 仪表盘
- 数据概览
- 实时监控
- 系统管理
- 用户管理
- 角色管理
- 菜单管理
- 日志审计
<template>
<div class="demo-section">
<p class="demo-title">子菜单(sub)+ 图标</p>
<div class="menu-demo-col">
<HxMenu :menu="menu" width="200px" :active="'/dashboard/overview'" />
</div>
</div>
</template>
<script setup lang="ts">
import { HxMenu } from '@whhx/ui'
import type { MenuItem } from '@whhx/ui'
/**
* iconify 图标格式:{ name: '集合:图标名', type: 'iconify' }
* 常用图标集前缀:mdi(Material Design)、lucide、carbon、tabler
* 详见 https://icon-sets.iconify.design
*/
const menu: MenuItem[] = [
{
type: 'sub',
title: '仪表盘',
icon: { name: 'mdi:view-dashboard', type: 'iconify' },
children: [
{ title: '数据概览', path: '/dashboard/overview' },
{ title: '实时监控', path: '/dashboard/monitor' },
],
},
{
type: 'sub',
title: '系统管理',
icon: { name: 'mdi:cog', type: 'iconify' },
children: [
{ title: '用户管理', path: '/system/user' },
{ title: '角色管理', path: '/system/role' },
{ title: '菜单管理', path: '/system/menu' },
],
},
{ title: '日志审计', path: '/log', icon: { name: 'mdi:clipboard-text-clock', type: 'iconify' } },
]
</script>
<style scoped>
.demo-section {
padding: 4px 0;
}
.demo-title {
margin: 0 0 10px;
font-size: 13px;
color: var(--el-text-color-secondary);
font-weight: 500;
}
.menu-demo-col {
width: 260px;
max-width: 100%;
}
</style>
分组菜单
通过 type: 'group' 将菜单项分组,每组显示标题,适用于功能模块归类。
展示分组菜单,每组有独立标题。
分组菜单(group)+ 图标
- 系统管理
- 用户管理
- 角色管理
- 菜单管理
- 日志审计
- 操作日志
- 登录日志
<template>
<div class="demo-section">
<p class="demo-title">分组菜单(group)+ 图标</p>
<div class="menu-demo-col">
<HxMenu :menu="groupMenu" />
</div>
</div>
</template>
<script setup lang="ts">
import { HxMenu } from '@whhx/ui'
import type { MenuItem } from '@whhx/ui'
const groupMenu: MenuItem[] = [
{
type: 'group',
title: '系统管理',
children: [
{ title: '用户管理', path: '/user', icon: { name: 'mdi:account', type: 'iconify' } },
{ title: '角色管理', path: '/role', icon: { name: 'mdi:shield-account', type: 'iconify' } },
{ title: '菜单管理', path: '/menu', icon: { name: 'mdi:format-list-bulleted', type: 'iconify' } },
],
},
{
type: 'group',
title: '日志审计',
children: [
{ title: '操作日志', path: '/log/operation', icon: { name: 'mdi:history', type: 'iconify' } },
{ title: '登录日志', path: '/log/login', icon: { name: 'mdi:login', type: 'iconify' } },
],
},
]
</script>
<style scoped>
.demo-section {
padding: 4px 0;
}
.demo-title {
margin: 0 0 10px;
font-size: 13px;
color: var(--el-text-color-secondary);
font-weight: 500;
}
.menu-demo-col {
width: 240px;
max-width: 100%;
}
</style>
扩展功能
外部链接
通过 url 属性配置外部链接,点击后将在新窗口打开。
展示带有外部链接的菜单项。
外部链接
- 首页
- Element Plus 文档
- Vue 官网
- GitHub
<template>
<div class="demo-section">
<p class="demo-title">外部链接</p>
<div class="menu-demo-col">
<HxMenu :menu="menu" />
</div>
</div>
</template>
<script setup lang="ts">
import { HxMenu } from '@whhx/ui'
import type { MenuItem } from '@whhx/ui'
const menu: MenuItem[] = [
{ title: '首页', path: '/home', icon: { name: 'mdi:home', type: 'iconify' } },
{ title: 'Element Plus 文档', url: 'https://element-plus.org', icon: { name: 'mdi:book-open-variant', type: 'iconify' } },
{ title: 'Vue 官网', url: 'https://vuejs.org', icon: { name: 'mdi:vuejs', type: 'iconify' } },
{ title: 'GitHub', url: 'https://github.com', icon: { name: 'mdi:github', type: 'iconify' } },
]
</script>
<style scoped>
.demo-section {
padding: 4px 0;
}
.demo-title {
margin: 0 0 10px;
font-size: 13px;
color: var(--el-text-color-secondary);
font-weight: 500;
}
.menu-demo-col {
width: 240px;
max-width: 100%;
}
</style>
禁用与徽章
通过 disabled 属性禁用菜单项,通过 badge 属性显示徽章。
展示禁用菜单项和徽章的使用。
禁用与徽章
- 首页
- 待处理5
- 消息99+
- 禁用项
- 新功能New
- 设置
<template>
<div class="demo-section">
<p class="demo-title">禁用与徽章</p>
<div class="menu-demo-col">
<HxMenu :menu="menu" />
</div>
</div>
</template>
<script setup lang="ts">
import { HxMenu } from '@whhx/ui'
import type { MenuItem } from '@whhx/ui'
const menu: MenuItem[] = [
{ title: '首页', path: '/home', icon: { name: 'mdi:home', type: 'iconify' } },
{ title: '待处理', path: '/pending', icon: { name: 'mdi:clock-outline', type: 'iconify' }, badge: 5 },
{ title: '消息', path: '/messages', icon: { name: 'mdi:message-text', type: 'iconify' }, badge: '99+' },
{ title: '禁用项', path: '/disabled', icon: { name: 'mdi:lock', type: 'iconify' }, disabled: true },
{ title: '新功能', path: '/new', icon: { name: 'mdi:star', type: 'iconify' }, badge: 'New', divider: true },
{ title: '设置', path: '/settings', icon: { name: 'mdi:cog', type: 'iconify' } },
]
</script>
<style scoped>
.demo-section {
padding: 4px 0;
}
.demo-title {
margin: 0 0 10px;
font-size: 13px;
color: var(--el-text-color-secondary);
font-weight: 500;
}
.menu-demo-col {
width: 240px;
max-width: 100%;
}
</style>
分割线
通过 divider 属性在菜单项前插入分割线。
展示分割线的使用。
分割线
- 仪表盘
- 数据概览
- 实时监控
- 首页
- 用户管理
- 订单管理
- 系统设置
<template>
<div class="demo-section">
<p class="demo-title">分割线</p>
<div class="menu-demo-col">
<HxMenu :menu="menu" />
</div>
</div>
</template>
<script setup lang="ts">
import { HxMenu } from '@whhx/ui'
import type { MenuItem } from '@whhx/ui'
const menu: MenuItem[] = [
{
type: 'sub',
title: '仪表盘',
icon: { name: 'mdi:view-dashboard', type: 'iconify' },
children: [
{ title: '数据概览', path: '/dashboard/overview' },
{ title: '实时监控', path: '/dashboard/monitor' },
],
},
{ title: '首页', path: '/home', icon: { name: 'mdi:home', type: 'iconify' }, divider: true },
{ title: '用户管理', path: '/user', icon: { name: 'mdi:account-group', type: 'iconify' } },
{ title: '订单管理', path: '/order', icon: { name: 'mdi:clipboard-list', type: 'iconify' }, divider: true },
{ title: '系统设置', path: '/settings', icon: { name: 'mdi:cog', type: 'iconify' } },
]
</script>
<style scoped>
.demo-section {
padding: 4px 0;
}
.demo-title {
margin: 0 0 10px;
font-size: 13px;
color: var(--el-text-color-secondary);
font-weight: 500;
}
.menu-demo-col {
width: 240px;
max-width: 100%;
}
</style>
API
Props
| 名称 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| menu | 菜单数据 | MenuItem[] | [] |
| mode | 布局模式 | 'horizontal' | 'vertical' | 'vertical' |
| width | 垂直模式下的菜单宽度(如 240 或 '16rem') | string | number | - |
| iconWidth | 图标占位区宽度(px),写入 --hx-menu-icon-slot-width,折叠弹出层同步 | string | number | - |
| active | 当前激活的菜单项 index | string | '' |
| defaultOpeneds | 初始展开的 SubMenu index 数组 | string[] | [] |
| openeds | 展开的 SubMenu index 数组(v-model) | string[] | - |
| uniqueOpened | 是否只保持展开一个子菜单 | boolean | false |
| menuTrigger | 子菜单触发方式 | 'hover' | 'click' | 'hover' |
| collapse | 是否折叠(vertical 模式生效) | boolean | false |
| backgroundColor | 菜单背景色 | string | - |
| textColor | 菜单文字色 | string | - |
| activeTextColor | 激活菜单项文字色 | string | - |
| closeOnClickOutside | 点击菜单外是否关闭 | boolean | false |
| collapseTransition | 折叠时是否有动画 | boolean | true |
| ellipsis | 是否显示横向菜单的省略图标 | boolean | true |
| popperOffset | popper offset | number | 6 |
| popperEffect | popper 效果 | 'dark' | 'light' | 'dark' |
| popperClass | popper 额外 class | string | - |
| popperStyle | popper 额外样式 | CSSProperties | - |
| showTimeout | 子菜单展开延迟(ms) | number | 300 |
| hideTimeout | 子菜单收起延迟(ms) | number | 300 |
| persistent | 是否持久化 popper 内容 | boolean | true |
MenuItem
| 名称 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| type | 菜单项类型 | 'item' | 'sub' | 'group' | 'item' |
| title | 菜单项显示文本(group 分组时对应标题) | string | - |
| path | 路由路径(作为菜单唯一标识,item 类型必填) | string | - |
| url | 外部链接地址,点击后在新窗口打开(优先级高于 path) | string | - |
| icon | 图标配置 | MenuIconProps | - |
| children | 子菜单 | MenuItem[] | - |
| disabled | 是否禁用 | boolean | false |
| badge | 徽章文本 | string | number | - |
| divider | 是否显示分割线 | boolean | false |
MenuIconProps
| 名称 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| type | 图标类型 | 'svg' | 'iconify' | 'svg' |
| name | 图标名称 | string | - |
| size | 图标尺寸 | number | string | - |
| color | 图标颜色 | string | - |
| rotate | 旋转角度(deg) | number | - |
| flip | 翻转方向 | 'horizontal' | 'vertical' | 'both' | - |
Events
| 事件名 | 说明 | 参数 |
|---|---|---|
| select | 菜单项选中时触发 | (index: string, indexPath: string[], item: unknown, routerResult?: unknown) |
| open | 子菜单展开时触发 | (index: string, indexPath: string[]) |
| close | 子菜单收起时触发 | (index: string, indexPath: string[]) |
| update:openeds | 展开状态变化时触发(v-model) | (openeds: string[]) |