Skip to content

树形控件

TuiTree 用于展示具有层级关系的数据结构。 它支持多选、过滤、懒加载等丰富功能,并提供了极其灵活的节点自定义能力,可用于构建文件管理器、组织架构图或系统菜单。

基础用法

基础展示

最简单的用法,通过 data 传入树形数据,通过 props 配置选项映射(默认映射 label, children)。

html
<script setup>
import { ref } from 'vue';

const treeData = ref([
  { id: 1, label: '一级 1', children: [
    { id: 4, label: '二级 1-1' }
  ]},
  { id: 2, label: '一级 2', children: [
    { id: 5, label: '二级 2-1' },
    { id: 6, label: '二级 2-2' }
  ]}
]);

const defaultProps = { children: 'children', label: 'label' };
const handleNodeClick = (data) => console.log('Click:', data);
</script>

<template>
  <TuiTree 
    :data="treeData" 
    :props="defaultProps" 
    @node-click="handleNodeClick" 
  />
</template>

外观风格

TuiTree 内置了三种外观模式,通过 appearance 属性切换。

  • tree (默认): 标准树结构,连接线风格。
  • menu: 菜单风格,无连接线,节点间距更紧凑。
  • menu-block: 块级菜单风格,节点背景占满整行,带有悬停高亮效果。
html
<template>
  <TuiTree :data="data" appearance="menu" />

  <TuiTree :data="data" appearance="menu-block" highlight-current />
</template>

图标定制

默认图标与节点图标

  • showIcon: 是否显示图标(默认为文件夹/文件图标)。
  • icon: 设置全局默认图标。
  • 数据源定义: 在数据对象中定义 icon 字段可覆盖默认图标。
html
<script setup>
const data = [
  { label: '系统管理', icon: 'tui-icon ti-settings' }, // 自定义节点图标
  { label: '用户列表' } // 使用默认图标
];
</script>

<template>
  <TuiTree 
    showIcon 
    icon="tui-icon ti-file" 
    :data="data" 
  />
</template>

场景案例:文件管理系统

利用 Scoped Slot,您可以根据节点数据(如文件类型)动态渲染不同的图标和样式。

html
<template>
  <TuiTree :data="fileData" default-expand-all>
    <template #default="{ node, data }">
      <div class="custom-tree-node">
        <i :class="[
          'tui-icon', 
          data.type === 'folder' ? (node.expanded ? 'tis-folder-open' : 'tis-folder') : 'ti-file'
        ]"></i>
        <span>{{ data.label }}</span>
        <span v-if="data.size" class="file-size">{{ data.size }}</span>
      </div>
    </template>
  </TuiTree>
</template>

交互功能

多选 (Checkbox)

设置 show-checkbox 开启复选框。

  • default-checked-keys: 默认选中的节点 Key 数组。
  • check-strictly: 是否严格遵循父子不互相关联(父选子不选)。
html
<script setup>
import { ref } from 'vue';
const treeRef = ref(null);

const getChecked = () => {
  // 获取选中节点的 Key 数组
  console.log(treeRef.value.getCheckedKeys());
  // 获取选中节点的完整数据
  console.log(treeRef.value.getCheckedNodes());
};
</script>

<template>
  <TuiTree
    ref="treeRef"
    :data="data"
    show-checkbox
    node-key="id"
    :default-checked-keys="[5]"
  />
  <TuiButton @click="getChecked">获取选中</TuiButton>
</template>

节点过滤 (Filter)

配合 filter-node-method 属性和 filter 方法实现搜索功能。

html
<script setup>
import { ref, watch } from 'vue';

const filterText = ref('');
const treeRef = ref(null);

// 过滤逻辑
const filterNode = (value, data, node) => {
  if (!value) return true;
  return node.label.includes(value);
};

watch(filterText, (val) => {
  treeRef.value.filter(val);
});
</script>

<template>
  <TuiInput v-model="filterText" placeholder="输入关键字过滤" />
  <TuiTree
    ref="treeRef"
    :data="data"
    :filter-node-method="filterNode"
  />
</template>

手风琴模式 (Accordion)

设置 accordion 属性,使得同级节点每次只能展开一个。

html
<TuiTree :data="data" accordion />

示例:可编辑节点

html
<template>
  <TuiTree :data="data">
    <template #default="{ node, data }">
      <div class="custom-node">
        <template v-if="data.isEditing">
           <TuiInput v-model="data.label" size="small" />
           <TuiButton link @click="save(data)">保存</TuiButton>
        </template>
        <template v-else>
           <span>{{ data.label }}</span>
           <TuiButton link @click="data.isEditing = true">编辑</TuiButton>
        </template>
      </div>
    </template>
  </TuiTree>
</template>

示例:组织架构图

结合 Flex 布局和自定义样式,展示部门人数等信息。

html
<template #default="{ data }">
  <div class="org-node">
    <span class="org-icon">{{ data.icon }}</span>
    <span class="org-name">{{ data.label }}</span>
    <TuiTag size="small">{{ data.count }}人</TuiTag>
  </div>
</template>

异步数据加载

对于海量数据,可以使用懒加载模式。设置 lazy 并通过 load 方法按需加载子节点。

基础懒加载

html
<script setup>
const loadNode = (node, resolve) => {
  // level 0 代表根节点(如果 props 中未提供 data)
  if (node.level === 0) {
    return resolve([{ name: 'Root 1' }, { name: 'Root 2' }]);
  }
  
  // 模拟异步请求
  setTimeout(() => {
    const data = [
      { name: 'leaf', leaf: true }, // leaf: true 标记为叶子节点
      { name: 'zone' }
    ];
    resolve(data);
  }, 500);
};
</script>

<template>
  <TuiTree lazy :load="loadNode" :props="{ label: 'name', isLeaf: 'leaf' }" />
</template>

高级定制

TuiTree 提供了两个核心插槽,满足不同粒度的定制需求。

内容定制 (#default)

最常用的插槽。用于自定义节点整行的内容。参数为 { node, data }

示例:文件管理系统

html
<template>
  <TuiTree :data="fileData" default-expand-all>
    <template #default="{ node, data }">
      <div class="custom-tree-node file-node">
        <span class="file-icon">
          <i :class="['tui-icon', data.type === 'file' ? 'ti-file' : 'ti-folder']"></i>
        </span>
        
        <span class="file-name">{{ data.label }}</span>
        <span v-if="data.size" class="file-size">{{ data.size }}</span>
        
        <div class="actions" v-if="data.type === 'file'">
          <TuiButton link size="small" @click.stop="download(data)">下载</TuiButton>
        </div>
      </div>
    </template>
  </TuiTree>
</template>

示例:组织架构图

html
<template #default="{ data }">
  <div class="org-node">
    <span class="org-icon">{{ data.icon }}</span>
    <span class="org-name">{{ data.label }}</span>
    <TuiTag size="small">{{ data.count }}人</TuiTag>
  </div>
</template>

图标定制 (#icon)

新增特性。如果您只想自定义展开/收起箭头或文件图标,而不影响节点文本的默认渲染逻辑,可以使用 #icon 插槽。

html
<TuiTree :data="data" show-icon>
  <template #icon="{ node, data }">
    <i v-if="node.loading" class="tui-icon ti-loader spin"></i>
    <i v-else-if="node.isLeaf" class="tui-icon ti-file"></i>
    <i v-else class="tui-icon ti-folder"></i>
  </template>
</TuiTree>

API 参考

Props

属性名类型默认值说明
dataArray[]树形结构数据。
nodeKeyString'id'每个树节点用来作为唯一标识的属性名。
propsObject-配置选项:children, label, disabled, isLeaf, class
emptyTextString-内容为空时显示的文本。
renderAfterExpandBooleantrue是否在第一次展开时才渲染子节点。
highlightCurrentBooleanfalse是否高亮当前选中节点。
defaultExpandAllBooleanfalse是否默认展开所有节点。
expandOnClickNodeBooleantrue是否在点击节点时展开/收起。如果为 false,则只有点击箭头图标才会展开。
checkOnClickNodeBooleanfalse是否在点击节点时选中复选框。
autoExpandParentBooleantrue展开子节点时是否自动展开父节点。
defaultExpandedKeysArray[]默认展开的节点的 key 的数组。
showCheckboxBooleanfalse是否显示复选框。
checkStrictlyBooleanfalse在显示复选框的情况下,是否严格遵循父子不互相关联的做法。
defaultCheckedKeysArray[]默认勾选的节点的 key 的数组。
currentNodeKeyString/Number-当前选中的节点的 key。
filterNodeMethodFunction-对树节点进行筛选时执行的方法,返回 true 表示显示。
accordionBooleanfalse是否开启手风琴模式。
indentNumber18相邻级节点间的水平缩进,单位为像素。
iconString-自定义树节点的图标(全局默认)。
showIconBooleanfalse是否显示节点图标。
lazyBooleanfalse是否懒加载子节点。
loadFunction-加载子树数据的方法。
draggableBooleanfalse是否开启拖拽节点功能。
allowDragFunction-判断节点是否允许被拖拽。
allowDropFunction-判断节点能否被拖入。
appearanceString'tree'外观。可选:tree, menu, menu-block

Events

事件名说明回调参数
node-click节点被点击时的回调。(data, node, treeNodeEvent)
node-contextmenu当某一节点被鼠标右键点击时会触发该事件。(event, data, node, treeNodeEvent)
check-change节点选中状态发生变化时的回调。(data, checked, indeterminate)
check点击节点复选框之后触发。(data, { checkedNodes, checkedKeys, ... })
current-change当前选中节点变化时触发。(data, node)
node-expand节点被展开时触发。(data, node)
node-collapse节点被收起时触发。(data, node)
node-drag-start节点开始拖拽时触发。(node, event)
node-drag-enter拖拽进入其他节点时触发。(draggingNode, dropNode, event)
node-drag-leave拖拽离开某个节点时触发。(draggingNode, dropNode, event)
node-drag-over在拖拽节点时触发(类似 mouseover)。(draggingNode, dropNode, event)
node-drag-end拖拽结束时(可能未成功找到目标节点)触发。(draggingNode, dropNode, dropType, event)
node-drop拖拽成功完成时触发。(draggingNode, dropNode, dropType, event)

Slots

插槽名说明参数
default内容插槽。自定义节点内容,覆盖默认的 label 显示。{ node, data }
icon图标插槽。自定义节点左侧的图标区域(需开启 showIcon)。{ node, data }

Exposed Methods

方法名说明参数
filter对树节点进行筛选操作。(value)
updateKeyChildren通过 keys 设置节点子元素。(key, data)
getCheckedNodes返回目前被选中的节点所组成的数组。(leafOnly, includeHalfChecked)
setCheckedNodes设置目前勾选的节点。(nodes)
getCheckedKeys返回目前被选中的节点的 key 所组成的数组。(leafOnly)
setCheckedKeys通过 keys 设置目前勾选的节点。(keys, leafOnly)
setChecked通过 key / data 设置某个节点的勾选状态。(key/data, checked, deep)
getHalfCheckedNodes返回目前半选中的节点所组成的数组。-
getHalfCheckedKeys返回目前半选中的节点的 key 所组成的数组。-
getCurrentKey获取当前被选中节点的 key。-
getCurrentNode获取当前被选中节点的 data。-
setCurrentKey通过 key 设置某个节点的当前选中状态。(key)
setCurrentNode通过 node 设置某个节点的当前选中状态。(node)
getNode根据 data 或者 key 拿到 Tree-node component。(data)
remove删除某个节点。(data)
append为某个节点添加子节点。(data, parentNode)
insertBefore在指定节点前插入节点。(data, refNode)
insertAfter在指定节点后插入节点。(data, refNode)