TinyVue NavMenu 导航菜单组件使用指南
本文是 TinyVue 组件库使用指南系列的第八篇,将详细介绍 NavMenu 导航菜单组件的用法、数据配置、溢出处理、跳转控制和最佳实践。
前言
导航菜单是中后台系统的"骨架",决定了用户能否快速找到目标功能。TinyVue 的 NavMenu 组件是一个顶部水平导航菜单,支持多级子菜单、数据驱动、自定义服务加载、溢出处理、跳转前拦截、Logo 和工具栏插槽等能力,适合构建企业级应用的顶部导航栏。
组件介绍
NavMenu 是一个水平导航菜单组件,通过 data 属性数据驱动渲染,支持多级嵌套子菜单。
核心特性
- 数据驱动 :通过
data属性配置菜单数据 - 自定义服务 :
fetch-menu-data异步加载菜单数据 - 字段映射 :
fields自定义数据字段名 - 溢出处理:4 种溢出显示方式(auto / retract / fixed / hidden)
- 跳转拦截 :
before-skip钩子函数控制跳转 - 默认选中 :
default-active指定当前选中菜单 - 扁平数据转树 :
parent-key将扁平数组转为树结构 - Logo / 工具栏插槽:自定义左侧 Logo 和右侧工具栏
基本用法
通过 data 属性配置菜单数据,每个菜单项包含 title(显示文本)、url(跳转地址)和 id(唯一标识),子菜单通过 children 嵌套:
vue
<template>
<tiny-nav-menu :data="menuData"></tiny-nav-menu>
</template>
<script setup>
import { ref } from 'vue'
import { TinyNavMenu } from '@opentiny/vue'
const menuData = ref([
{
title: '首页',
url: '/home',
id: '1'
},
{
title: '指南',
url: '',
id: '2',
children: [
{ title: '引入组件', url: '/guide/import', id: '2-1' },
{ title: '后端适配器', url: '/guide/adapter', id: '2-2' }
]
},
{
title: '组件',
url: '',
id: '3',
children: [
{
title: '表单组件',
url: '',
id: '3-1',
children: [
{ title: 'Datepicker 日期', url: 'datepicker', id: '3-1-1' },
{ title: 'Cascader 级联选择器', url: 'cascader', id: '3-1-2' }
]
},
{
title: '数据展示',
url: '',
id: '3-2',
children: [
{ title: 'Card 卡片', url: 'card', id: '3-2-1' },
{ title: 'Collapse 折叠面板', url: 'collapse', id: '3-2-2' }
]
}
]
},
{
title: '其他',
url: '/other',
id: '4'
}
])
</script>
数据结构
每个菜单项(IDataItem)的结构:
typescript
interface IDataItem {
title: string // 显示文本
url: string // 跳转地址
id: string // 唯一标识
children?: IDataItem[] // 子菜单(可选)
}
提示:NavMenu 支持多级嵌套,一级菜单显示在顶部导航栏,二级及以下菜单通过下拉展示。
溢出处理
当一级菜单项过多无法在容器中完全显示时,通过 overflow 属性配置溢出处理方式:
vue
<template>
<tiny-nav-menu :data="menuData" overflow="fixed"></tiny-nav-menu>
</template>
| overflow 值 | 说明 |
|---|---|
| auto | 默认。右侧显示"更多"菜单,悬浮时展示剩余菜单 |
| fixed | 左侧显示三明治折叠/展开图标,悬浮时展示所有菜单 |
| retract | 不显示任何菜单,只显示三明治图标,悬浮时展示所有菜单 |
| hidden | 剩余未展示的菜单直接隐藏 |
场景建议 :菜单项较少时用
auto;菜单项较多且需要完整展示时用fixed;移动端或极窄容器用retract。
跳转前处理
通过 before-skip 钩子函数在菜单点击跳转前执行自定义逻辑,返回 false 可阻止跳转:
vue
<template>
<tiny-nav-menu :data="menuData" :before-skip="handleSkip"></tiny-nav-menu>
</template>
<script setup>
import { ref } from 'vue'
import { TinyNavMenu, TinyModal } from '@opentiny/vue'
const menuData = ref([
{ title: '首页', url: '/home', id: '1' },
{ title: '指南', url: '/guide', id: '2' },
{ title: '组件', url: '/components', id: '3' }
])
function handleSkip({ title }) {
TinyModal.message({ message: '当前跳转的菜单为:' + title, status: 'info' })
return false // 返回 false 阻止跳转
}
</script>
钩子函数参数
before-skip 接收一个 IMenuItem 对象:
typescript
interface IMenuItem {
id: string // 菜单 ID
title: string // 菜单标题
url: string // 跳转地址
route: string // 路由地址
target: string // 打开方式
pid: string // 父级 ID
isFullUrl: boolean // 是否为完整 URL
}
配合 prevent 阻止默认服务跳转
若使用组件默认服务(/workspace/current)获取菜单数据,需要同时设置 prevent 为 true 才能阻止默认跳转:
vue
<template>
<tiny-nav-menu
:before-skip="handleSkip"
:prevent="true"
></tiny-nav-menu>
</template>
注意 :仅在使用默认服务时需要
prevent;使用自定义data时,before-skip返回false即可阻止跳转。
自定义菜单服务
通过 fetch-menu-data 属性自定义菜单数据加载服务,返回 Promise 对象:
vue
<template>
<tiny-nav-menu :fetch-menu-data="getMenuData" :fields="fields"></tiny-nav-menu>
</template>
<script setup>
import { TinyNavMenu } from '@opentiny/vue'
// 自定义字段映射
const fields = {
textField: 'label', // 将 label 字段映射为 title
urlField: 'urlField' // 将 urlField 字段映射为 url
}
function getMenuData() {
const menuData = [
{ label: '首页', urlField: '/home', id: '1' },
{
label: '指南',
urlField: '',
id: '2',
children: [
{ label: '引入组件', urlField: '/guide/import', id: '2-1' },
{ label: '后端适配器', urlField: '/guide/adapter', id: '2-2' }
]
},
{ label: '其他', urlField: '/other', id: '4' }
]
return Promise.resolve(menuData)
}
</script>
字段映射(fields)
当后端返回的数据字段名不是默认的 title 和 url 时,通过 fields 进行映射:
typescript
interface IFields {
textField: string // 文本字段名,默认 'title'
urlField: string // URL 字段名,默认 'url'
key?: string // 唯一标识字段名,默认 'id'
}
场景 :后端接口返回
label/path/menuId等字段名时,通过fields映射即可适配,无需前端转换数据。
自定义选中菜单
通过 default-active 属性指定当前选中的菜单项(需与 data 中的 id 对应):
vue
<template>
<tiny-nav-menu :data="menuData" :defaultActive="defaultActive"></tiny-nav-menu>
</template>
<script setup>
import { ref } from 'vue'
import { TinyNavMenu } from '@opentiny/vue'
// 默认选中 id 为 '3-1-1' 的菜单项
const defaultActive = ref('3-1-1')
const menuData = ref([
{ title: '首页', url: '/home', id: '1' },
{
title: '组件',
url: '',
id: '3',
children: [
{
title: '表单组件',
url: '',
id: '3-1',
children: [
{ title: 'Datepicker 日期', url: 'datepicker', id: '3-1-1' },
{ title: 'Cascader 级联选择器', url: 'cascader', id: '3-1-2' }
]
}
]
}
])
</script>
扁平数据转树结构
当后端返回的是扁平数组(通过 pid 标识父子关系)时,通过 parent-key 属性指定父级标识字段,组件会自动转换为树结构:
vue
<template>
<tiny-nav-menu :data="menuData" parent-key="pid"></tiny-nav-menu>
</template>
<script setup>
import { ref } from 'vue'
import { TinyNavMenu } from '@opentiny/vue'
// 扁平数组,通过 pid 标识父级
const menuData = ref([
{ id: 1, pid: 0, title: '首页', url: '' },
{ id: 2, pid: 0, title: '指南', url: '' },
{ id: 3, pid: 0, title: '组件', url: '' },
{ id: 9, pid: 0, title: '其他', url: 'crop' },
{ id: 13, pid: 2, title: '引入组件', url: '' },
{ id: 14, pid: 2, title: '后端适配器', url: '' },
{ id: 17, pid: 3, title: '表单组件', url: '' },
{ id: 18, pid: 3, title: '数据组件', url: '' },
{ id: 39, pid: 17, title: 'Button 按钮', url: 'button' },
{ id: 40, pid: 17, title: 'Datepicker 日期', url: 'datepicker' }
])
</script>
规则 :
pid为 0 或根级标识的项作为一级菜单,其余按pid自动归入对应父级的children。
Logo 插槽
通过 logo 插槽自定义菜单栏左侧的 Logo:
vue
<template>
<tiny-nav-menu :data="menuData">
<template #logo>
<tiny-icon-total class="icon-logo" />
</template>
</tiny-nav-menu>
</template>
<script setup>
import { ref } from 'vue'
import { TinyNavMenu } from '@opentiny/vue'
import { iconTotal } from '@opentiny/vue-icon'
const TinyIconTotal = iconTotal()
const menuData = ref([
{ title: '首页', url: '/home', id: '1' },
{ title: '指南', url: '/guide', id: '2' }
])
</script>
工具栏插槽
通过 toolbar 插槽自定义菜单栏右侧的工具栏区域,常用于放置用户头像、设置图标等:
vue
<template>
<tiny-nav-menu :data="menuData">
<template #toolbar>
<tiny-icon-administrator class="icon-administrator" />
<tiny-icon-setting class="icon-setting" />
</template>
</tiny-nav-menu>
</template>
<script setup>
import { ref } from 'vue'
import { TinyNavMenu } from '@opentiny/vue'
import { iconSetting, iconAdministrator } from '@opentiny/vue-icon'
const TinyIconSetting = iconSetting()
const TinyIconAdministrator = iconAdministrator()
const menuData = ref([
{ title: '首页', url: '/home', id: '1' },
{ title: '指南', url: '/guide', id: '2' }
])
</script>
<style scoped>
.icon-setting,
.icon-administrator {
margin-right: 20px;
fill: #808080;
font-size: 24px;
}
</style>
API 参考
Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| data | IDataItem\[\] | - | 菜单数据 |
| fetch-menu-data | () => Promise<IDataItem\[\]> | - | 自定义菜单数据加载服务 |
| fields | IFields | { textField: 'title', urlField: 'url', key: 'id' } | 自定义数据字段映射 |
| overflow | 'auto' | 'retract' | 'fixed' | 'hidden' | 'auto' | 一级菜单溢出时的显示方式 |
| before-skip | (item: IMenuItem) => boolean | - | 点击跳转前的钩子函数,返回 false 阻止跳转 |
| prevent | boolean | false | 配合 before-skip 阻止默认服务的跳转行为 |
| parent-key | string | - | 父级菜单标识字段,用于扁平数组转树结构 |
| default-active | string | - | 当前选中菜单的 id |
Slots
| 插槽名 | 说明 |
|---|---|
| logo | 菜单栏左侧 Logo 插槽 |
| toolbar | 菜单栏右侧工具栏插槽 |
Types
typescript
// 菜单项(跳转钩子参数)
interface IMenuItem {
id: string
isFullUrl: boolean
pid: string
route: string
target: string
title: string
url: string
}
// 数据项
interface IDataItem {
title: string
url: string
children?: IDataItem[]
}
// 字段映射
interface IFields {
textField: string
urlField: string
key?: string
}
最佳实践
1. SPA 应用中使用 before-skip 拦截跳转
在 SPA 中,通常需要拦截菜单点击并使用路由跳转,而非浏览器原生跳转:
vue
<template>
<tiny-nav-menu :data="menuData" :before-skip="handleSkip"></tiny-nav-menu>
</template>
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
function handleSkip({ url }) {
if (url) {
router.push(url)
}
return false // 阻止默认跳转,由路由接管
}
</script>
2. 后端数据适配使用 fields 映射
避免在前端手动转换后端数据字段名,直接使用 fields 映射:
vue
<template>
<tiny-nav-menu
:fetch-menu-data="fetchMenu"
:fields="{ textField: 'name', urlField: 'path' }"
></tiny-nav-menu>
</template>
3. 扁平数据使用 parent-key 自动转树
后端返回扁平数组时,无需前端递归转换,直接用 parent-key:
vue
<template>
<tiny-nav-menu :data="flatData" parent-key="parentId"></tiny-nav-menu>
</template>
4. 溢出处理根据场景选择
- 桌面端宽屏 :
overflow="auto"(默认),超出部分收入"更多" - 桌面端窄屏 :
overflow="fixed",三明治图标展示全部菜单 - 移动端 :
overflow="retract",只显示三明治图标
5. 工具栏放置高频操作
将用户头像、消息通知、设置入口等高频操作放在 toolbar 插槽中,与导航菜单在同一行展示,节省垂直空间。
总结
TinyVue NavMenu 是一个数据驱动的水平导航菜单组件。核心要点:data 配置菜单数据(支持多级嵌套);fetch-menu-data 异步加载菜单;fields 映射非标准字段名;overflow 处理溢出(auto/fixed/retract/hidden);before-skip 拦截跳转;default-active 指定选中项;parent-key 扁平数据转树;logo / toolbar 插槽自定义左右区域。掌握这些能力后,可以高效构建企业级应用的顶部导航栏。
OpenTiny NEXT 是一套企业智能前端开发解决方案,以生成式 UI 和 WebMCP 两大核心技术为基础,对现有传统的 TinyVue 组件库、TinyEngine 低代码引擎等产品进行智能化升级,构建出面向 Agent 应用的前端 NEXT-SDKs、AI Extension、TinyRobot智能助手、GenUI等新产品,实现AI理解用户意图自主完成任务,加速企业应用的智能化改造。