树形控件
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
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| data | Array | [] | 树形结构数据。 |
| nodeKey | String | 'id' | 每个树节点用来作为唯一标识的属性名。 |
| props | Object | - | 配置选项:children, label, disabled, isLeaf, class。 |
| emptyText | String | - | 内容为空时显示的文本。 |
| renderAfterExpand | Boolean | true | 是否在第一次展开时才渲染子节点。 |
| highlightCurrent | Boolean | false | 是否高亮当前选中节点。 |
| defaultExpandAll | Boolean | false | 是否默认展开所有节点。 |
| expandOnClickNode | Boolean | true | 是否在点击节点时展开/收起。如果为 false,则只有点击箭头图标才会展开。 |
| checkOnClickNode | Boolean | false | 是否在点击节点时选中复选框。 |
| autoExpandParent | Boolean | true | 展开子节点时是否自动展开父节点。 |
| defaultExpandedKeys | Array | [] | 默认展开的节点的 key 的数组。 |
| showCheckbox | Boolean | false | 是否显示复选框。 |
| checkStrictly | Boolean | false | 在显示复选框的情况下,是否严格遵循父子不互相关联的做法。 |
| defaultCheckedKeys | Array | [] | 默认勾选的节点的 key 的数组。 |
| currentNodeKey | String/Number | - | 当前选中的节点的 key。 |
| filterNodeMethod | Function | - | 对树节点进行筛选时执行的方法,返回 true 表示显示。 |
| accordion | Boolean | false | 是否开启手风琴模式。 |
| indent | Number | 18 | 相邻级节点间的水平缩进,单位为像素。 |
| icon | String | - | 自定义树节点的图标(全局默认)。 |
| showIcon | Boolean | false | 是否显示节点图标。 |
| lazy | Boolean | false | 是否懒加载子节点。 |
| load | Function | - | 加载子树数据的方法。 |
| draggable | Boolean | false | 是否开启拖拽节点功能。 |
| allowDrag | Function | - | 判断节点是否允许被拖拽。 |
| allowDrop | Function | - | 判断节点能否被拖入。 |
| appearance | String | '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) |