TUI

0.3.7
Menu 菜单组件

支持垂直菜单导航,配置简单,支持嵌套子菜单与图标展示。可通过 v-model 双向绑定当前路径。

尝试使用v-model 来做受控与非受控的切换, 当v-model 没有绑定或者为空时,value 和 change 事件将作为非受控的方式来切换

基础用法
传入菜单数组,通过 v-model 绑定当前选中项。
示例代码
vue
<template>
  <TsMenu v-model="activePath" :menus="iMenus" @change="handleMenuChange" :ssg="false"/>
</template>

<script setup lang="ts">
  import { ref } from 'vue';
  const activePath = ref('/path1');

  const iMenus = [
    { path: '/path1', name: '菜单1',  children: [] },
    { path: '/path2', name: '菜单2',  children: [] },
    { path: '/path3', name: '菜单3',  children: [] },
  ];

  const handleMenuChange = (path: string) => {
    console.log('选中菜单路径:', path);
  };
</script>
图标用法
配合 #prefix 插槽可以为每一项菜单自定义图标内容,可结合业务图标组件进行渲染,例如 BussinessViconsIcon
示例代码
vue
<template>
  <div style="width: 300px">
    <TsMenu v-model="activePath" :menus="iconMenus" @change="handleMenuChange" :ssg="false">
      <template #prefix="{ data }">
        <BussinessViconsIcon :icon="data.icon" />
      </template>
    </TsMenu>
  </div>
</template>

<script setup lang="ts">
  import { ref } from 'vue';
  const activePath = ref('/path1');
  const iconMenus = [
    { path: '/path1', name: '菜单1', icon: 'HomeRound', children: [] },
    { path: '/path2', name: '菜单2', icon: 'AdminPanelSettingsFilled', children: [] },
    { path: '/path3', name: '菜单3', icon: 'DynamicFormSharp', children: [] },
  ];

  const handleMenuChange = (path: string) => {
    console.log('选中菜单路径:', path);
  };
</script>
嵌套子菜单
支持多级嵌套结构,点击展开子项。
示例代码
vue
<template>
  <TsMenu v-model="activePath2" :menus="nestedMenus" @change="handleMenuChange" :ssg="false"/>
</template>

<script setup lang="ts">
  import { computed, ref } from 'vue';
  const activePath2 = ref('/nested/path1');

  const nestedMenus = [
    {
      path: '/nested',
      name: '多级菜单',
      children: [
        { path: '/nested/path1', name: '子菜单 1',  children: [] },
        {
          path: '/nested/group',
          name: '子菜单组',
          children: [
            { path: '/nested/group/item1', name: '分组项 1',  children: [] },
            { path: '/nested/group/item2', name: '分组项 2',  children: [] },
          ],
        },
      ],
    },
  ];
</script>
菜单组(共享选中状态)
多个菜单组件包裹在 TsMenuGroup 中,将共享同一个选中路径状态( v-model ),适用于多列布局或分类菜单场景。
示例代码
vue
<template>
  <TsMenuGroup v-model="groupPath" @change="handleMenuChange" :ssg="false">
    <TsMenu :menus="iMenus" />
    <TsMenu :menus="tMenus" />
  </TsMenuGroup>
</template>

<script setup lang="ts">
  const groupPath = ref('/path2');

  const iMenus = [
    { path: '/path1', name: '菜单1',  children: [] },
    { path: '/path2', name: '菜单2',  children: [] },
    { path: '/path3', name: '菜单3',  children: [] },
  ];

  const tMenus = [
    { path: '/path5', name: '菜单5',  children: [] },
    { path: '/path6', name: '菜单6',  children: [] },
    { path: '/path7', name: '菜单7',  children: [] },
  ];

  const handleMenuChange = (path: string) => {
    console.log('选中菜单路径:', path);
  };
</script>
原生vue-router 路由适配方案

基于原生 vue-router 的路由结构适配方案,支持动态菜单渲染。通过配置菜单数据结构与路由保持一致,实现路径联动和跳转逻辑。可自定义菜单图标、子级路径跳转行为等,适用于常规页面级导航结构。

expandByPath 职责:根据路径展开对应的菜单,如展开其他子项时,并需要切换回 v-model 所在的子项展开效果, 那么请调用该方法。 v-model 仅仅在值发生改变时才会展开正确的子项。

示例代码
vue
<template>
  <div style="display: flex; column-gap: 12px; margin-bottom: 24px">
    <TsButton @click="activePath3 = '/home'">changeTo: '/home'</TsButton>
    <TsButton @click="activePath3 = '/setting/menu'">changeTo: '/setting/menu'</TsButton>
    <TsButton @click="handleExpandPath('/setting/menu')">expand: '/setting/menu'</TsButton>
  </div>
  <div style="width: 300px">
    <TsMenu ref="expandMenuRef" v-model="activePath3" :menus="adaptationMenus" @change="handleMenuChange" :ssg="false">
      <template #prefix="{ data }">
        <BussinessViconsIcon icon="HomeRound" v-if="data.meta.icon" />
        <div style="margin-left: 28px" v-else></div>
      </template>
    </TsMenu>
  </div>
</template>

<script setup lang="ts">
  const expandMenuRef = ref<InstanceType<typeof TsMenu> | null>(null);
  const activePath = ref('/setting/user');

  const handleExpandPath = (path: string) => {
    expandMenuRef.value?.expandByPath(path);
  };

  const menuRouteData = [
    { path: '/home', name: 'home', meta: { title: '首页', icon: 'House' }, children: [] },
    {
      path: '/setting',
      component: null,
      meta: { title: '权限管理', icon: 'Setting' },
      children: [
        { path: '/setting/user', name: 'user', meta: { title: '用户管理' } },
        { path: '/setting/menu', name: 'menu', meta: { title: '菜单管理' } },
      ],
    },
    {
      path: '/editor',
      component: null,
      meta: { title: '编辑器', icon: 'ChatDotRound' },
      children: [{ path: '/editor/index', meta: { title: '编辑器' } }],
    },
    {
      path: '/img',
      component: null,
      meta: { title: '图片裁剪', icon: 'PictureFilled' },
      children: [{ path: '/img/index', meta: { title: '图片裁剪' } }],
    },
    {
      path: '/code',
      component: null,
      meta: { title: '代码管理', icon: 'NoSmoking' },
      children: [{ path: '/code/index', meta: { title: '代码管理' } }],
    },
  ];

  const adaptationMenus = computed(() => {
    return menuRouteData.map((item: any) => {
      item.name = item.meta.title;
      item.children.map((child: any) => {
        child.name = child.meta.title;
        return child;
      });
      return item;
    });
  });

  const handleMenuChange = (path: string) => {
    console.log('选中菜单路径:', path);
  };
</script>
非受控用法
通过 value 传入菜单路径 和 change 事件 绑定当前选中项。
示例代码
vue
<template>
  <TsMenu :value="activeUncontrolledPath" :menus="iMenus" @change="handleUncontrolledPath" :ssg="false" />
</template>

<script setup lang="ts">
  const activeUncontrolledPath = ref('/path1');

  const iMenus = [
    { path: '/path1', name: '菜单1',  children: [] },
    { path: '/path2', name: '菜单2',  children: [] },
    { path: '/path3', name: '菜单3',  children: [] },
  ];

  const handleUncontrolledPath = (path: string) => {
    activeUncontrolledPath.value = path;
  };
</script>
属性 Props
属性名
说明
类型
可选值
默认值
版本说明
v-model
绑定当前选中路径,支持双向绑定
string
value
绑定当前选中路径,建议在非受控的场景下使用
string
0.3.5 新增
menus
菜单列表数据
Array<MenuItem>
variant
外观风格,控制 tab 是卡片型还是文本型
string
card | text
card
emitOnParentWithChildren
是否在当有子节点时,父项点击触发 change 事件
boolean
false
ssg
是否开启服务端渲染
boolean
false
事件 Events
事件名
说明
类型
change
值变化时触发
方法 Functions
方法名
说明
参数
返回值
expandByPath
展开指定路径
path: string, 默认展开 v-model 绑定的当前路径
-
插槽 Slots
插槽名
说明
prefix
自定义前缀
badge
自定义徽标
MenuItem 类型
属性名
说明
类型
可选值
默认值
name
菜单显示名称
string
path
菜单路径(用于匹配 v-model)
string
icon
图标名称,可选
string
children
子菜单项
Array<MenuItem>
[]