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)获取菜单数据,需要同时设置 preventtrue 才能阻止默认跳转:

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)

当后端返回的数据字段名不是默认的 titleurl 时,通过 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:

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理解用户意图自主完成任务,加速企业应用的智能化改造。

相关推荐
Jason_chen1 小时前
Linux 3.0 串口机制深度解析:传统8250驱动与基础RS-232/485支持
linux·前端
TPBoreas1 小时前
前端面试问题打靶
前端
赵庆明老师1 小时前
JS检查提交的文件是否合规
开发语言·前端·javascript
禅思院1 小时前
前端请求取消与调度完全指南:从 AbortController 到企业级优先级架构
前端·设计模式·前端框架
颂love1 小时前
Vue的两大生态以及组件通信
前端·javascript·vue.js·typescript
甜汤圆1 小时前
Python 里**自定义数据单元**
前端
cidy_981 小时前
将 Figma 接入 Codex MCP:从 `/plugins` 到本地插件配置的完整教程
前端
vivo互联网技术1 小时前
动效开发不踩坑:几种动效实现方案对比与实战选型
前端·性能优化·动效
Csvn1 小时前
【Vue3】Composition API vs Options API —— 什么场景该选哪个
前端