TreeSelect 树形选择器
新粗野主义风格的树形下拉选择组件,使用递归节点组件 TreeSelectNode 展示层级结构,配合 Popover 实现下拉交互。支持单选、多选、搜索过滤,适用于文件选择、分类选择、组织架构等场景。
预览
单选 + 搜索
多选模式
禁用状态
安装
pnpm dlx brutx-vue@latest add tree-select用法
<script setup>
import { ref } from 'vue'
import { TreeSelect } from 'brutx-ui-vue'
import type { TreeNode } from 'brutx-ui-vue'
const nodes: TreeNode[] = [
{
id: 'docs',
label: '文档',
children: [
{ id: 'guide', label: '指南' },
{ id: 'api', label: 'API 参考' },
],
},
{
id: 'src',
label: '源码',
children: [
{
id: 'components',
label: '组件',
children: [
{ id: 'button', label: 'Button.vue' },
{ id: 'input', label: 'Input.vue' },
],
},
{ id: 'utils', label: '工具函数' },
],
},
]
const selected = ref(undefined)
</script>
<template>
<TreeSelect
v-model="selected"
:nodes="nodes"
placeholder="选择文件..."
searchable
clearable
/>
</template>多选模式
<script setup>
import { ref } from 'vue'
import { TreeSelect } from 'brutx-ui-vue'
const selected = ref([])
</script>
<template>
<TreeSelect
v-model="selected"
:nodes="nodes"
multiple
searchable
clearable
placeholder="选择多个文件..."
/>
</template>搜索过滤
启用 searchable 后,用户输入关键词可递归过滤树节点,匹配时保留祖先路径。
禁用节点
在 TreeNode 中设置 disabled: true 可禁用该节点的选择:
const nodes = [
{ id: '1', label: '可选节点' },
{ id: '2', label: '禁用节点', disabled: true },
]受控展开
通过 open prop 和 update:open 事件可实现对下拉框展开状态的受控管理:
<script setup>
import { ref } from 'vue'
import { TreeSelect } from 'brutx-ui-vue'
const selected = ref(undefined)
const isOpen = ref(false)
function handleClose() {
isOpen.value = false
}
</script>
<template>
<TreeSelect
v-model="selected"
v-model:open="isOpen"
:nodes="nodes"
/>
</template>任意深度支持
组件使用递归渲染,支持任意深度的树结构,不限于两层。
变体
| 变体 | 说明 |
|---|---|
single | 默认单选模式,modelValue 为 string | undefined |
multiple | 多选模式(设置 multiple prop),modelValue 为 string[] |
尺寸
| 尺寸 | 说明 |
|---|---|
sm | 小尺寸触发器 |
default | 默认尺寸触发器 |
lg | 大尺寸触发器 |
数据类型
interface TreeNode {
id: string // 唯一标识
label: string // 显示文本
children?: TreeNode[] // 子节点(省略则为叶节点)
icon?: string // 节点图标
disabled?: boolean // 是否禁用
data?: unknown // 自定义附加数据
}TreeSelectLocale
组件使用 useLocale composable 支持国际化,可通过 TreeSelectLocale 自定义文本:
interface TreeSelectLocale {
placeholder: string // 触发器占位文本
searchPlaceholder: string // 搜索框占位文本
search: string // 搜索框 ARIA 标签
emptyText: string // 无结果提示文本
selectedCount: string // 多选超出 maxDisplay 时的计数文本,支持 {count} 插值
clear: string // 清除按钮 ARIA 标签
}API 参考
TreeSelect
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
nodes必填 | TreeNode<unknown>[] | — | 树形选项数据;节点通过 id、label 与可选 children 描述,并可携带 disabled 等状态。 |
详情声明类型TreeNode<unknown>[] 关联类型 TreeNode 类型定义 — TreeNodeexport interface TreeNode<T = unknown> {
/** 节点唯一标识 */
id: string
/** 节点显示文本 */
label: string
/** 节点图标名称或自定义图标类名 */
icon?: string
/** 子节点列表 */
children?: TreeNode<T>[]
/** 自定义业务数据 */
data?: T
/** 是否禁用交互 */
disabled?: boolean
/** 是否叶子节点(懒加载时若为 true 则不展示展开图标) */
isLeaf?: boolean
/** 懒加载子节点是否已加载 */
loaded?: boolean
/** 懒加载是否处于请求中 */
loading?: boolean
/** 过滤搜索时是否隐藏 */
hidden?: boolean
}默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
modelValue | string | string[] | undefined | undefined | 当前选中的节点 ID;单选为字符串,多选为字符串数组,通过 v-model 受控。 |
详情声明类型string | string[] | undefined 默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
open | boolean | undefined | undefined | 受控的下拉面板展开状态;与 update:open 事件配合使用 v-model:open。 |
详情声明类型boolean | undefined 字面量
默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
multiple | boolean | undefined | false | 启用后允许同时选择多个节点,modelValue 与 select 事件返回数组。 |
详情声明类型boolean | undefined 字面量
默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
searchable | boolean | undefined | true | 控制面板中是否显示用于过滤节点的搜索框。 |
详情声明类型boolean | undefined 字面量
默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
placeholder | string | undefined | undefined | 未选择节点时触发器显示的文字;未设置时使用当前语言的默认文案。 |
详情声明类型string | undefined 默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
searchPlaceholder | string | undefined | undefined | 搜索框的占位文字;未设置时使用当前语言的默认文案。 |
详情声明类型string | undefined 默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
emptyText | string | undefined | undefined | 过滤结果为空时显示的提示;未设置时使用当前语言的默认文案。 |
详情声明类型string | undefined 默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
clearable | boolean | undefined | false | 允许通过触发器中的清除操作移除当前选择。 |
详情声明类型boolean | undefined 字面量
默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
disabled | boolean | undefined | false | 禁用触发器和面板交互,并阻止打开下拉面板。 |
详情声明类型boolean | undefined 字面量
默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
size | "default" | "lg" | "sm" | undefined | "default" | 设置触发器的尺寸变体。 |
详情声明类型NonNullable<"default" | "lg" | "sm" | null | undefined> | undefined 字面量
默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
ariaLabel | string | undefined | undefined | 设置触发器的无障碍标签。 |
详情声明类型string | undefined 默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
maxDisplay | number | undefined | 3 | 多选时触发器最多直接显示的已选标签数;其余选择合并为计数。 |
详情声明类型number | undefined 默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
maxHeight | string | undefined | "15rem" | 设置树形下拉列表的 CSS 最大高度。 |
详情声明类型string | undefined 默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
dropdownClass | string | undefined | undefined | 追加到下拉面板的 CSS 类。 |
详情声明类型string | undefined 默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
class | string | undefined | undefined | 追加到选择器触发器根元素的 CSS 类。 |
详情声明类型string | undefined 默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
iconSize | IconSize | undefined | "md" | 设置触发器主图标尺寸。 |
详情声明类型IconSize | undefined 字面量
关联类型 IconSize 类型定义 — IconSizeexport type IconSize = NonNullable<VariantProps<typeof iconSizeVariants>['size']> 默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
itemVariant | "default" | "primary" | "secondary" | undefined | "default" | 设置树节点选中高亮使用的视觉变体。 |
详情声明类型"default" | "primary" | "secondary" | undefined 字面量
默认值详情
来源packages/ui/src/components/tree-select/TreeSelect.vue | |||
| 名称 | 签名或类型 | 说明 |
|---|---|---|
@update:modelValue | [value: string | string[] | undefined] | 选择变化时发出新的节点 ID;清除或无有效选择时可发出 undefined。 |
详情声明类型[value: string | string[] | undefined] 来源packages/ui/src/components/tree-select/TreeSelect.vue | ||
@update:open | [value: boolean] | 下拉展开状态变化时发出新布尔值,可用于 v-model:open。 |
详情声明类型[value: boolean] 字面量
来源packages/ui/src/components/tree-select/TreeSelect.vue | ||
@open-change | [open: boolean] | 下拉面板打开或关闭后发出当前展开状态。 |
详情声明类型[open: boolean] 字面量
来源packages/ui/src/components/tree-select/TreeSelect.vue | ||
@select | [node: TreeNode<unknown> | TreeNode<unknown>[] | undefined] | 用户选择或清除节点时发出节点对象;多选模式返回节点数组。 |
详情声明类型[node: TreeNode<unknown> | TreeNode<unknown>[] | undefined] 关联类型 TreeNode 类型定义 — TreeNodeexport interface TreeNode<T = unknown> {
/** 节点唯一标识 */
id: string
/** 节点显示文本 */
label: string
/** 节点图标名称或自定义图标类名 */
icon?: string
/** 子节点列表 */
children?: TreeNode<T>[]
/** 自定义业务数据 */
data?: T
/** 是否禁用交互 */
disabled?: boolean
/** 是否叶子节点(懒加载时若为 true 则不展示展开图标) */
isLeaf?: boolean
/** 懒加载子节点是否已加载 */
loaded?: boolean
/** 懒加载是否处于请求中 */
loading?: boolean
/** 过滤搜索时是否隐藏 */
hidden?: boolean
}来源packages/ui/src/components/tree-select/TreeSelect.vue | ||
| 名称 | 签名或类型 | 说明 |
|---|---|---|
searchQuery | string | 当前搜索关键词的响应式状态。 |
详情声明类型string 来源packages/ui/src/components/tree-select/TreeSelect.vue | ||
selectedNodes | TreeNode<unknown>[] | 根据 modelValue 解析出的已选节点列表;该计算状态只读。 |
详情声明类型TreeNode<unknown>[] 关联类型 TreeNode 类型定义 — TreeNodeexport interface TreeNode<T = unknown> {
/** 节点唯一标识 */
id: string
/** 节点显示文本 */
label: string
/** 节点图标名称或自定义图标类名 */
icon?: string
/** 子节点列表 */
children?: TreeNode<T>[]
/** 自定义业务数据 */
data?: T
/** 是否禁用交互 */
disabled?: boolean
/** 是否叶子节点(懒加载时若为 true 则不展示展开图标) */
isLeaf?: boolean
/** 懒加载子节点是否已加载 */
loaded?: boolean
/** 懒加载是否处于请求中 */
loading?: boolean
/** 过滤搜索时是否隐藏 */
hidden?: boolean
}来源packages/ui/src/components/tree-select/TreeSelect.vue | ||
expandedIds | Set<string> | 当前展开节点的 ID 集合;可通过组件实例读取或替换。 |
详情声明类型Set<string> 来源packages/ui/src/components/tree-select/TreeSelect.vue | ||
focus | () => void | undefined | 将键盘焦点移至选择器触发器。 |
详情声明类型() => void | undefined 来源packages/ui/src/components/tree-select/TreeSelect.vue | ||
open | WritableComputedRef<boolean, boolean> | 下拉面板的可写展开状态;写入会更新内部或受控状态并发出 update:open。 |
详情声明类型WritableComputedRef<boolean, boolean> 字面量
来源packages/ui/src/components/tree-select/TreeSelect.vue | ||
TreeSelectNode
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
node必填 | TreeNode<unknown> | — | 当前渲染的树节点及其数据、层级关系和禁用状态。 |
详情声明类型TreeNode<unknown> 关联类型 TreeNode 类型定义 — TreeNodeexport interface TreeNode<T = unknown> {
/** 节点唯一标识 */
id: string
/** 节点显示文本 */
label: string
/** 节点图标名称或自定义图标类名 */
icon?: string
/** 子节点列表 */
children?: TreeNode<T>[]
/** 自定义业务数据 */
data?: T
/** 是否禁用交互 */
disabled?: boolean
/** 是否叶子节点(懒加载时若为 true 则不展示展开图标) */
isLeaf?: boolean
/** 懒加载子节点是否已加载 */
loaded?: boolean
/** 懒加载是否处于请求中 */
loading?: boolean
/** 过滤搜索时是否隐藏 */
hidden?: boolean
}默认值详情
来源packages/ui/src/components/tree-select/TreeSelectNode.vue | |||
selectedIds必填 | Set<string> | — | 已选节点 ID 集合,用于确定当前节点是否选中。 |
详情声明类型Set<string> 默认值详情
来源packages/ui/src/components/tree-select/TreeSelectNode.vue | |||
expandedIds必填 | Set<string> | — | 已展开节点 ID 集合,用于确定当前节点的展开状态。 |
详情声明类型Set<string> 默认值详情
来源packages/ui/src/components/tree-select/TreeSelectNode.vue | |||
depth | number | undefined | 0 | 当前节点在树中的层级深度,用于计算缩进。 |
详情声明类型number | undefined 默认值详情
来源packages/ui/src/components/tree-select/TreeSelectNode.vue | |||
multiple | boolean | undefined | false | 指示父选择器是否处于多选模式。 |
详情声明类型boolean | undefined 字面量
默认值详情
来源packages/ui/src/components/tree-select/TreeSelectNode.vue | |||
focusedId | string | undefined | undefined | 当前键盘焦点节点的 ID,用于呈现焦点状态。 |
详情声明类型string | undefined 默认值详情
来源packages/ui/src/components/tree-select/TreeSelectNode.vue | |||
variant | "default" | "primary" | "secondary" | undefined | "default" | 设置节点选中状态的视觉变体。 |
详情声明类型"default" | "primary" | "secondary" | undefined 字面量
默认值详情
来源packages/ui/src/components/tree-select/TreeSelectNode.vue | |||
| 名称 | 签名或类型 | 说明 |
|---|---|---|
@select | [node: TreeNode<unknown>] | 叶子节点被选择时发出该节点对象。 |
详情声明类型[node: TreeNode<unknown>] 关联类型 TreeNode 类型定义 — TreeNodeexport interface TreeNode<T = unknown> {
/** 节点唯一标识 */
id: string
/** 节点显示文本 */
label: string
/** 节点图标名称或自定义图标类名 */
icon?: string
/** 子节点列表 */
children?: TreeNode<T>[]
/** 自定义业务数据 */
data?: T
/** 是否禁用交互 */
disabled?: boolean
/** 是否叶子节点(懒加载时若为 true 则不展示展开图标) */
isLeaf?: boolean
/** 懒加载子节点是否已加载 */
loaded?: boolean
/** 懒加载是否处于请求中 */
loading?: boolean
/** 过滤搜索时是否隐藏 */
hidden?: boolean
}来源packages/ui/src/components/tree-select/TreeSelectNode.vue | ||
@focus | [id: string] | 当前节点获得焦点时发出节点 ID。 |
详情声明类型[id: string] 来源packages/ui/src/components/tree-select/TreeSelectNode.vue | ||
@toggle | [id: string] | 非叶子节点被展开或折叠时发出节点 ID。 |
详情声明类型[id: string] 来源packages/ui/src/components/tree-select/TreeSelectNode.vue | ||
可访问性
- 键盘操作:节点支持
ArrowUp/ArrowDown导航;非叶节点支持ArrowRight展开 /ArrowLeft折叠;Home/End键跳转至首尾节点;清除按钮支持Enter/Space操作 - ARIA 属性:触发器使用
role="combobox"和aria-expanded;下拉列表使用role="tree"和role="treeitem";多选模式下添加aria-multiselectable;禁用状态下使用aria-disabled - 焦点管理:使用 roving tabindex 管理焦点;禁用节点设置
tabindex="-1"
通过 ref 使用组件状态
通过 ref 访问组件实例后可调用以下方法:
<script setup>
import { ref } from 'vue'
import { TreeSelect } from 'brutx-ui-vue'
const treeRef = ref(null)
function expandAll() {
// 获取所有节点 ID 并展开
const allIds = getAllNodeIds(nodes)
treeRef.value.expandedIds = new Set(allIds)
}
</script>
<template>
<TreeSelect ref="treeRef" v-model="selected" :nodes="nodes" />
<button @click="expandAll">展开全部</button>
</template>常见问题
Q: 搜索过滤时为什么有些节点搜不到?
A: 搜索过滤会递归匹配所有层级的节点。匹配到子节点时,其祖先路径会自动保留以确保可见性。如果搜索不到某个节点,请检查节点的 label 是否与搜索关键词匹配。搜索默认是大小写敏感的模糊匹配。
Q: 单选和多选模式的 modelValue 类型不同,如何统一处理?
A: 单选模式下 modelValue 为 string | undefined,多选模式下为 string[]。在 TypeScript 项目中,根据是否设置了 multiple 属性来确定类型。如果需要动态切换模式,建议使用条件类型或分别定义 ref 变量。
Q: 下拉列表内容很多时如何控制显示高度?
A: 使用 maxHeight 属性控制下拉列表的最大高度,默认为 '15rem'。当树节点超出最大高度时,列表会自动出现滚动条。可以传入任意有效的 CSS 高度值,如 '200px'、'50vh' 等。