#Tree 树形控件
用于展示层级嵌套数据的树形组件,支持展开/折叠、选中、勾选等功能。
#组件特性
- 🌳 层级展示:以树形结构展示嵌套数据,支持展开/折叠。
- ✔️ 勾选模式:支持
checkable启用勾选框,内置父子联动。 - 🔗 选中模式:支持单选和多选模式控制选中节点。
- 🚫 禁用状态:支持对单个节点或整棵树设置
disabled。 - ➡️ 连接线:支持
showLine显示层级连接线。 - 🔍 节点过滤:通过
filterTreeNode过滤可见节点。 - ⚡ 虚拟滚动:启用
virtualScroll仅渲染可见区域节点。 - ✋ 拖拽排序:支持节点拖拽排序。
#基础用法
通过 data 属性传入嵌套数据,每个节点需要唯一的 key 和显示文本 label。点击箭头图标展开/折叠子节点。
<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'
const treeData = ref<TreeNodeData[]>([
{
key: '1',
label: 'Documents',
children: [
{
key: '1-1',
label: 'Work',
children: [
{ key: '1-1-1', label: 'project.pdf' },
{ key: '1-1-2', label: 'report.docx' },
],
},
{
key: '1-2',
label: 'Personal',
children: [
{ key: '1-2-1', label: 'resume.pdf' },
{ key: '1-2-2', label: 'budget.xlsx' },
],
},
],
},
{
key: '2',
label: 'Pictures',
children: [
{ key: '2-1', label: 'vacation.jpg' },
{ key: '2-2', label: 'family.png' },
],
},
{
key: '3',
label: 'Music',
isLeaf: true,
},
])
const expandedKeys = ref<(string | number)[]>(['1'])
</script>
<template>
<div class="w-80">
<BrTree
v-model:expanded-keys="expandedKeys"
:data="treeData"
/>
</div>
</template>#节点选中
使用 v-model:selectedKeys 控制选中节点。点击节点即可选中。设置 selectable 为 "multiple" 启用多选,设为 "none" 禁用选中。
<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'
const treeData = ref<TreeNodeData[]>([
{
key: '1',
label: 'Documents',
children: [
{
key: '1-1',
label: 'Work',
children: [
{ key: '1-1-1', label: 'project.pdf' },
{ key: '1-1-2', label: 'report.docx' },
],
},
{
key: '1-2',
label: 'Personal',
children: [
{ key: '1-2-1', label: 'resume.pdf' },
{ key: '1-2-2', label: 'budget.xlsx' },
],
},
],
},
{
key: '2',
label: 'Pictures',
children: [
{ key: '2-1', label: 'vacation.jpg' },
{ key: '2-2', label: 'family.png' },
],
},
])
const expandedKeys = ref<(string | number)[]>(['1'])
const selectedKeys = ref<(string | number)[]>([])
function onSelect(keys: (string | number)[], node: TreeNodeData) {
console.log('Selected:', keys, node)
}
</script>
<template>
<div class="w-80">
<BrTree
v-model:expanded-keys="expandedKeys"
v-model:selected-keys="selectedKeys"
:data="treeData"
@select="onSelect"
/>
</div>
</template>#勾选模式
设置 checkable 启用勾选框,通过 v-model:checkedKeys 控制勾选状态。内置父子联动:勾选父节点自动勾选所有子节点,子节点全选自动勾选父节点。
<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'
const treeData = ref<TreeNodeData[]>([
{
key: '1',
label: 'All Files',
children: [
{
key: '1-1',
label: 'Documents',
children: [
{ key: '1-1-1', label: 'project.pdf' },
{ key: '1-1-2', label: 'report.docx' },
{ key: '1-1-3', label: 'notes.txt' },
],
},
{
key: '1-2',
label: 'Pictures',
children: [
{ key: '1-2-1', label: 'vacation.jpg' },
{ key: '1-2-2', label: 'family.png' },
],
},
{
key: '1-3',
label: 'Music',
children: [
{ key: '1-3-1', label: 'song.mp3' },
],
},
],
},
])
const expandedKeys = ref<(string | number)[]>(['1', '1-1'])
const checkedKeys = ref<(string | number)[]>(['1-1-1', '1-1-2'])
function onCheck(keys: (string | number)[], node: TreeNodeData) {
console.log('Checked:', keys, node)
}
</script>
<template>
<div class="w-80">
<BrTree
v-model:expanded-keys="expandedKeys"
v-model:checked-keys="checkedKeys"
:data="treeData"
checkable
@check="onCheck"
/>
</div>
</template>#禁用与连接线
对单个节点或整棵树设置 disabled。使用 showLine 显示层级连接线。
<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'
const treeData = ref<TreeNodeData[]>([
{
key: '1',
label: 'Documents',
children: [
{
key: '1-1',
label: 'Work',
children: [
{ key: '1-1-1', label: 'project.pdf' },
{ key: '1-1-2', label: 'report.docx' },
],
},
{
key: '1-2',
label: 'Personal',
children: [
{ key: '1-2-1', label: 'resume.pdf' },
],
},
],
},
{
key: '2',
label: 'Disabled Folder',
disabled: true,
children: [
{ key: '2-1', label: 'locked-file.txt' },
],
},
{
key: '3',
label: 'Pictures',
children: [
{ key: '3-1', label: 'vacation.jpg' },
{ key: '3-2', label: 'disabled.png', disabled: true },
],
},
])
const expandedKeys = ref<(string | number)[]>(['1'])
</script>
<template>
<div class="w-80">
<BrTree
v-model:expanded-keys="expandedKeys"
:data="treeData"
show-line
default-expand-all
/>
</div>
</template>#节点过滤
通过 filterTreeNode 过滤可见节点。传入一个返回 true 表示匹配的函数,匹配的节点及其祖先节点会自动显示。
<script setup lang="ts">
import { ref, computed } from 'vue'
import { BrTree, BrInput } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'
const treeData = ref<TreeNodeData[]>([
{
key: '1',
label: 'Documents',
children: [
{
key: '1-1',
label: 'Work',
children: [
{ key: '1-1-1', label: 'project.pdf' },
{ key: '1-1-2', label: 'report.docx' },
{ key: '1-1-3', label: 'presentation.pptx' },
],
},
{
key: '1-2',
label: 'Personal',
children: [
{ key: '1-2-1', label: 'resume.pdf' },
{ key: '1-2-2', label: 'budget.xlsx' },
],
},
],
},
{
key: '2',
label: 'Pictures',
children: [
{
key: '2-1',
label: 'Vacation',
children: [
{ key: '2-1-1', label: 'beach.jpg' },
{ key: '2-1-2', label: 'sunset.jpg' },
],
},
{ key: '2-2', label: 'family.png' },
],
},
{
key: '3',
label: 'Music',
isLeaf: true,
},
])
const searchQuery = ref('')
const filterFn = computed(() => {
if (!searchQuery.value.trim()) return undefined
const q = searchQuery.value.toLowerCase()
return (node: TreeNodeData) =>
node.label.toLowerCase().includes(q)
})
</script>
<template>
<div class="space-y-3">
<div class="w-72">
<BrInput v-model="searchQuery" placeholder="Type to filter nodes..." size="sm" />
</div>
<div class="w-80">
<BrTree
:data="treeData"
:filter-tree-node="filterFn"
default-expand-all
show-icon
/>
</div>
</div>
</template>#虚拟滚动
当数据量较大时,启用 virtualScroll 仅渲染可见区域的节点。通过 height 设置容器高度,itemHeight 设置每行高度。
<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'
function generateLargeData(count: number, depth = 0, prefix = ''): TreeNodeData[] {
const result: TreeNodeData[] = []
for (let i = 0; i < count; i++) {
const key = `${prefix}${i}`
if (depth < 3) {
result.push({
key,
label: `Node ${key}`,
children: generateLargeData(5, depth + 1, `${key}-`),
})
} else {
result.push({ key, label: `Leaf ${key}`, isLeaf: true })
}
}
return result
}
const virtualData = ref<TreeNodeData[]>(generateLargeData(20))
</script>
<template>
<div class="w-96">
<BrTree
:data="virtualData"
virtual-scroll
:item-height="32"
:height="400"
default-expand-all
show-icon
/>
</div>
</template>#拖拽排序
设置 draggable 启用拖拽排序。监听 drop 事件处理节点的新位置。使用 allowDrop 可限制有效的放置目标。
<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData, DropInfo } from '@breezeui/vue'
const treeData = ref<TreeNodeData[]>([
{
key: 'd1',
label: 'Root A',
children: [
{ key: 'd1-1', label: 'Child A1' },
{ key: 'd1-2', label: 'Child A2' },
{ key: 'd1-3', label: 'Child A3' },
],
},
{
key: 'd2',
label: 'Root B',
children: [
{ key: 'd2-1', label: 'Child B1' },
{ key: 'd2-2', label: 'Child B2' },
],
},
{
key: 'd3',
label: 'Root C',
children: [
{ key: 'd3-1', label: 'Child C1' },
],
},
])
const expandedKeys = ref<(string | number)[]>(['d1', 'd2', 'd3'])
function handleDrop(info: DropInfo) {
console.log('Drop:', info)
}
</script>
<template>
<div class="w-80">
<BrTree
v-model:expanded-keys="expandedKeys"
:data="treeData"
draggable
show-icon
@drop="handleDrop"
/>
</div>
</template>#主题定制
可以通过 Tailwind CSS 类或覆盖 CSS 变量来自定义外观。
#Tailwind 定制
使用 class 属性添加自定义样式:
<BrTree class="border rounded-md p-2" :data="treeData" />#API
#BrTree Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| data | TreeNodeData[] | [] | 树形数据源。 |
| expandedKeys | (string | number)[] | - | 展开的节点 key(受控,v-model:expandedKeys)。 |
| selectedKeys | (string | number)[] | - | 选中的节点 key(受控,v-model:selectedKeys)。 |
| checkedKeys | (string | number)[] | - | 勾选的节点 key(受控,v-model:checkedKeys)。 |
| checkable | boolean | false | 是否显示勾选框。 |
| selectable | boolean | 'single' | 'multiple' | 'none' | true | 选中模式。 |
| showLine | boolean | false | 是否显示连接线。 |
| showIcon | boolean | true | 是否显示节点图标。 |
| defaultExpandAll | boolean | false | 是否默认展开所有节点。 |
| defaultExpandedKeys | (string | number)[] | [] | 默认展开的节点 key(非受控)。 |
| defaultSelectedKeys | (string | number)[] | [] | 默认选中的节点 key(非受控)。 |
| defaultCheckedKeys | (string | number)[] | [] | 默认勾选的节点 key(非受控)。 |
| disabled | boolean | false | 是否禁用整棵树。 |
| draggable | boolean | false | 节点是否可拖拽。 |
| loadData | (node: TreeNodeData) => Promise<TreeNodeData[]> | - | 异步加载子节点回调。 |
| filterTreeNode | (node: TreeNodeData) => boolean | - | 节点过滤函数。 |
| size | 'xs' | 'sm' | 'default' | 'md' | 'lg' | '2xl' | 'default' | 组件尺寸。 |
| indent | number | 24 | 每层缩进像素数。 |
| virtualScroll | boolean | false | 是否启用虚拟滚动(大数据量场景)。 |
| itemHeight | number | 32 | 虚拟滚动模式下每行高度(像素)。 |
| height | number | string | 400 | 虚拟滚动容器高度。 |
| allowDrop | (options: AllowDropOptions) => boolean | - | 控制允许放置位置的回调函数。 |
#TreeNodeData 数据结构
| 属性名 | 类型 | 说明 |
|---|---|---|
| key | string | number | 节点唯一标识。 |
| label | string | 显示文本。 |
| children | TreeNodeData[] | 子节点。 |
| disabled | boolean | 是否禁用该节点。 |
| isLeaf | boolean | 强制为叶子节点(不显示展开箭头)。 |
| icon | Component | 自定义图标组件。 |
| loading | boolean | 加载状态。 |
#Events 事件
| 名称 | 参数 | 说明 |
|---|---|---|
| update:expandedKeys | (string | number)[] | 展开节点变化时触发。 |
| update:selectedKeys | (string | number)[] | 选中节点变化时触发。 |
| update:checkedKeys | (string | number)[] | 勾选节点变化时触发。 |
| expand | (keys, node) | 节点展开/折叠时触发。 |
| select | (keys, node) | 节点选中时触发。 |
| check | (keys, node) | 节点勾选/取消勾选时触发。 |
| dragStart | (node, event) | 拖拽开始时触发。 |
| dragEnd | (node, event) | 拖拽结束时触发。 |
| drop | (info: DropInfo) | 节点放置时触发。DropInfo 包含 { dragNode, dropNode, dropPosition }。 |
#Slots 插槽
| 名称 | 参数 | 说明 |
|---|---|---|
| switcher | { expanded, node } | 自定义展开/折叠图标。 |
| icon | { node, expanded } | 自定义节点图标。 |
| label | { node } | 自定义节点内容。 |