Skip to content

TreeSelect 树形选择器 ​

新粗野主义风格的树形下拉选择组件,使用递归节点组件 TreeSelectNode 展示层级结构,配合 Popover 实现下拉交互。支持单选、多选、搜索过滤,适用于文件选择、分类选择、组织架构等场景。

预览 ​

Preview

单选 + 搜索

多选模式

禁用状态

安装 ​

pnpm dlx brutx-vue@latest add tree-select

用法 ​

vue
<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>

多选模式 ​

vue
<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 可禁用该节点的选择:

ts
const nodes = [
    { id: '1', label: '可选节点' },
    { id: '2', label: '禁用节点', disabled: true },
]

受控展开 ​

通过 open prop 和 update:open 事件可实现对下拉框展开状态的受控管理:

vue
<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大尺寸触发器

数据类型 ​

ts
interface TreeNode {
    id: string              // 唯一标识
    label: string           // 显示文本
    children?: TreeNode[]   // 子节点(省略则为叶节点)
    icon?: string           // 节点图标
    disabled?: boolean      // 是否禁用
    data?: unknown          // 自定义附加数据
}

TreeSelectLocale ​

组件使用 useLocale composable 支持国际化,可通过 TreeSelectLocale 自定义文本:

ts
interface TreeSelectLocale {
    placeholder: string        // 触发器占位文本
    searchPlaceholder: string  // 搜索框占位文本
    search: string             // 搜索框 ARIA 标签
    emptyText: string          // 无结果提示文本
    selectedCount: string      // 多选超出 maxDisplay 时的计数文本,支持 {count} 插值
    clear: string              // 清除按钮 ARIA 标签
}

API 参考 ​

TreeSelect

TreeSelect — 属性
名称类型默认值说明
nodes
必填
TreeNode<unknown>[]
—
树形选项数据;节点通过 id、label 与可选 children 描述,并可携带 disabled 等状态。
详情

声明类型

TreeNode<unknown>[]

关联类型

TreeNode
类型定义 — TreeNode
export 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
}

默认值详情

未声明默认值
—

来源

TreeSelect.vue:23

packages/ui/src/components/tree-select/TreeSelect.vue

modelValue
string | string[] | undefined
undefined
当前选中的节点 ID;单选为字符串,多选为字符串数组,通过 v-model 受控。
详情

声明类型

string | string[] | undefined

默认值详情

解析值
undefined

来源

TreeSelect.vue:25

packages/ui/src/components/tree-select/TreeSelect.vue

open
boolean | undefined
undefined
受控的下拉面板展开状态;与 update:open 事件配合使用 v-model:open。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
undefined

来源

TreeSelect.vue:27

packages/ui/src/components/tree-select/TreeSelect.vue

multiple
boolean | undefined
false
启用后允许同时选择多个节点,modelValue 与 select 事件返回数组。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
false

来源

TreeSelect.vue:29

packages/ui/src/components/tree-select/TreeSelect.vue

searchable
boolean | undefined
true
控制面板中是否显示用于过滤节点的搜索框。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
true

来源

TreeSelect.vue:31

packages/ui/src/components/tree-select/TreeSelect.vue

placeholder
string | undefined
undefined
未选择节点时触发器显示的文字;未设置时使用当前语言的默认文案。
详情

声明类型

string | undefined

默认值详情

解析值
undefined

来源

TreeSelect.vue:33

packages/ui/src/components/tree-select/TreeSelect.vue

searchPlaceholder
string | undefined
undefined
搜索框的占位文字;未设置时使用当前语言的默认文案。
详情

声明类型

string | undefined

默认值详情

解析值
undefined

来源

TreeSelect.vue:35

packages/ui/src/components/tree-select/TreeSelect.vue

emptyText
string | undefined
undefined
过滤结果为空时显示的提示;未设置时使用当前语言的默认文案。
详情

声明类型

string | undefined

默认值详情

解析值
undefined

来源

TreeSelect.vue:37

packages/ui/src/components/tree-select/TreeSelect.vue

clearable
boolean | undefined
false
允许通过触发器中的清除操作移除当前选择。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
false

来源

TreeSelect.vue:39

packages/ui/src/components/tree-select/TreeSelect.vue

disabled
boolean | undefined
false
禁用触发器和面板交互,并阻止打开下拉面板。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
false

来源

TreeSelect.vue:41

packages/ui/src/components/tree-select/TreeSelect.vue

size
"default" | "lg" | "sm" | undefined
"default"
设置触发器的尺寸变体。
详情

声明类型

NonNullable<"default" | "lg" | "sm" | null | undefined> | undefined

字面量

  • "default"
  • "lg"
  • "sm"

默认值详情

解析值
"default"

来源

TreeSelect.vue:43

packages/ui/src/components/tree-select/TreeSelect.vue

ariaLabel
string | undefined
undefined
设置触发器的无障碍标签。
详情

声明类型

string | undefined

默认值详情

解析值
undefined

来源

TreeSelect.vue:45

packages/ui/src/components/tree-select/TreeSelect.vue

maxDisplay
number | undefined
3
多选时触发器最多直接显示的已选标签数;其余选择合并为计数。
详情

声明类型

number | undefined

默认值详情

解析值
3

来源

TreeSelect.vue:47

packages/ui/src/components/tree-select/TreeSelect.vue

maxHeight
string | undefined
"15rem"
设置树形下拉列表的 CSS 最大高度。
详情

声明类型

string | undefined

默认值详情

解析值
"15rem"

来源

TreeSelect.vue:49

packages/ui/src/components/tree-select/TreeSelect.vue

dropdownClass
string | undefined
undefined
追加到下拉面板的 CSS 类。
详情

声明类型

string | undefined

默认值详情

解析值
undefined

来源

TreeSelect.vue:51

packages/ui/src/components/tree-select/TreeSelect.vue

class
string | undefined
undefined
追加到选择器触发器根元素的 CSS 类。
详情

声明类型

string | undefined

默认值详情

解析值
undefined

来源

TreeSelect.vue:53

packages/ui/src/components/tree-select/TreeSelect.vue

iconSize
IconSize | undefined
"md"
设置触发器主图标尺寸。
详情

声明类型

IconSize | undefined

字面量

  • "2xl"
  • "lg"
  • "md"
  • "sm"
  • "xl"
  • "xs"

关联类型

IconSize
类型定义 — IconSize
export type IconSize = NonNullable<VariantProps<typeof iconSizeVariants>['size']>

默认值详情

解析值
"md"

来源

TreeSelect.vue:55

packages/ui/src/components/tree-select/TreeSelect.vue

itemVariant
"default" | "primary" | "secondary" | undefined
"default"
设置树节点选中高亮使用的视觉变体。
详情

声明类型

"default" | "primary" | "secondary" | undefined

字面量

  • "default"
  • "primary"
  • "secondary"

默认值详情

解析值
"default"

来源

TreeSelect.vue:57

packages/ui/src/components/tree-select/TreeSelect.vue

TreeSelect — 事件
名称签名或类型说明
@update:modelValue
[value: string | string[] | undefined]
选择变化时发出新的节点 ID;清除或无有效选择时可发出 undefined。
详情

声明类型

[value: string | string[] | undefined]

来源

TreeSelect.vue

packages/ui/src/components/tree-select/TreeSelect.vue

@update:open
[value: boolean]
下拉展开状态变化时发出新布尔值,可用于 v-model:open。
详情

声明类型

[value: boolean]

字面量

  • false
  • true

来源

TreeSelect.vue

packages/ui/src/components/tree-select/TreeSelect.vue

@open-change
[open: boolean]
下拉面板打开或关闭后发出当前展开状态。
详情

声明类型

[open: boolean]

字面量

  • false
  • true

来源

TreeSelect.vue

packages/ui/src/components/tree-select/TreeSelect.vue

@select
[node: TreeNode<unknown> | TreeNode<unknown>[] | undefined]
用户选择或清除节点时发出节点对象;多选模式返回节点数组。
详情

声明类型

[node: TreeNode<unknown> | TreeNode<unknown>[] | undefined]

关联类型

TreeNode
类型定义 — TreeNode
export 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
}

来源

TreeSelect.vue

packages/ui/src/components/tree-select/TreeSelect.vue

TreeSelect — 暴露成员
名称签名或类型说明
searchQuery
string
当前搜索关键词的响应式状态。
详情

声明类型

string

来源

TreeSelect.vue:357

packages/ui/src/components/tree-select/TreeSelect.vue

selectedNodes
TreeNode<unknown>[]
根据 modelValue 解析出的已选节点列表;该计算状态只读。
详情

声明类型

TreeNode<unknown>[]

关联类型

TreeNode
类型定义 — TreeNode
export 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
}

来源

TreeSelect.vue:358

packages/ui/src/components/tree-select/TreeSelect.vue

expandedIds
Set<string>
当前展开节点的 ID 集合;可通过组件实例读取或替换。
详情

声明类型

Set<string>

来源

TreeSelect.vue:359

packages/ui/src/components/tree-select/TreeSelect.vue

focus
() => void | undefined
将键盘焦点移至选择器触发器。
详情

声明类型

() => void | undefined

来源

TreeSelect.vue:360

packages/ui/src/components/tree-select/TreeSelect.vue

open
WritableComputedRef<boolean, boolean>
下拉面板的可写展开状态;写入会更新内部或受控状态并发出 update:open。
详情

声明类型

WritableComputedRef<boolean, boolean>

字面量

  • false
  • true

来源

TreeSelect.vue

packages/ui/src/components/tree-select/TreeSelect.vue

TreeSelectNode

TreeSelectNode — 属性
名称类型默认值说明
node
必填
TreeNode<unknown>
—
当前渲染的树节点及其数据、层级关系和禁用状态。
详情

声明类型

TreeNode<unknown>

关联类型

TreeNode
类型定义 — TreeNode
export 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
}

默认值详情

未声明默认值
—

来源

TreeSelectNode.vue:16

packages/ui/src/components/tree-select/TreeSelectNode.vue

selectedIds
必填
Set<string>
—
已选节点 ID 集合,用于确定当前节点是否选中。
详情

声明类型

Set<string>

默认值详情

未声明默认值
—

来源

TreeSelectNode.vue:17

packages/ui/src/components/tree-select/TreeSelectNode.vue

expandedIds
必填
Set<string>
—
已展开节点 ID 集合,用于确定当前节点的展开状态。
详情

声明类型

Set<string>

默认值详情

未声明默认值
—

来源

TreeSelectNode.vue:18

packages/ui/src/components/tree-select/TreeSelectNode.vue

depth
number | undefined
0
当前节点在树中的层级深度,用于计算缩进。
详情

声明类型

number | undefined

默认值详情

解析值
0

来源

TreeSelectNode.vue:19

packages/ui/src/components/tree-select/TreeSelectNode.vue

multiple
boolean | undefined
false
指示父选择器是否处于多选模式。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
false

来源

TreeSelectNode.vue:20

packages/ui/src/components/tree-select/TreeSelectNode.vue

focusedId
string | undefined
undefined
当前键盘焦点节点的 ID,用于呈现焦点状态。
详情

声明类型

string | undefined

默认值详情

解析值
undefined

来源

TreeSelectNode.vue:21

packages/ui/src/components/tree-select/TreeSelectNode.vue

variant
"default" | "primary" | "secondary" | undefined
"default"
设置节点选中状态的视觉变体。
详情

声明类型

"default" | "primary" | "secondary" | undefined

字面量

  • "default"
  • "primary"
  • "secondary"

默认值详情

解析值
"default"

来源

TreeSelectNode.vue:22

packages/ui/src/components/tree-select/TreeSelectNode.vue

TreeSelectNode — 事件
名称签名或类型说明
@select
[node: TreeNode<unknown>]
叶子节点被选择时发出该节点对象。
详情

声明类型

[node: TreeNode<unknown>]

关联类型

TreeNode
类型定义 — TreeNode
export 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
}

来源

TreeSelectNode.vue

packages/ui/src/components/tree-select/TreeSelectNode.vue

@focus
[id: string]
当前节点获得焦点时发出节点 ID。
详情

声明类型

[id: string]

来源

TreeSelectNode.vue

packages/ui/src/components/tree-select/TreeSelectNode.vue

@toggle
[id: string]
非叶子节点被展开或折叠时发出节点 ID。
详情

声明类型

[id: string]

来源

TreeSelectNode.vue

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 访问组件实例后可调用以下方法:

vue
<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' 等。

蛮力铸就。