Appearance
TreeSelect 树选择器
用于在层级数据中选择单个、多个或勾选一批节点,适合地区、组织架构、分类目录、指标维度等字段。
何时使用
候选项存在父子层级,并希望像 Select 一样在下拉里完成选择时使用 TreeSelect;地区、部门、目录、指标树都是典型场景。平铺枚举用 Select,独立的多列级联用 Cascader;需要直接浏览树结构时使用独立的 Tree 组件。
基础用法
单选点击叶子节点后关闭面板并回显标题;点击分支节点只展开 / 收起。allowClear 提供清空按钮;禁用节点不可点击,也不会在清空时被误删。
杭州已选:hz
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData, TreeSelectValue } from 'x-next';
const value = ref<string | number | undefined>('hz');
const feedback = ref('已选:hz');
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
children: [
{ key: 'hz', title: '杭州' },
{ key: 'nb', title: '宁波' },
{ key: 'wz', title: '温州(暂停服务)', disabled: true },
],
},
];
function changed(value: TreeSelectValue | TreeSelectValue[] | undefined) {
feedback.value = value ? `已选:${String(value)}` : '已清空';
}
</script>
<template>
<TreeSelect
v-model="value"
:data="data"
default-expand-all
allow-clear
placeholder="请选择城市"
@change="changed"
/>
<p>{{ feedback }}</p>
</template>多选
multiple 开启多选,值始终是数组,面板保持展开直到点击外部或按 Escape。maxTagCount 只折叠展示,不限制数量;tagNowrap 让标签不换行。多选 / 勾选模式下点击节点会清空搜索词(与 Select 一致),需要保留搜索词时用 allowSearch: { retainInputValue: true }。
杭州宁波点击标签 × 试试
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData, TreeSelectValue } from 'x-next';
const value = ref<(string | number)[]>(['hz', 'nb']);
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
children: [
{ key: 'hz', title: '杭州' },
{ key: 'nb', title: '宁波' },
{ key: 'wz', title: '温州(暂停服务)', disabled: true },
],
},
];
function removed(value: TreeSelectValue | undefined) {
console.log('移除:', value);
}
</script>
<template>
<TreeSelect
v-model="value"
:data="data"
multiple
default-expand-all
allow-clear
:max-tag-count="2"
tag-nowrap
placeholder="可多选城市"
@remove="removed"
/>
</template>勾选模式与父子联动
treeCheckable 开启复选框。勾选叶子后父节点自动变为半选;半选节点不出现在值里。默认 checkedStrategy="parent",勾选整个分支时只回填父节点 key。禁用和 disableCheckbox 节点不参与联动,但仍可被父节点联动勾选。
杭州勾选任意节点观察父子联动
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData, TreeSelectValue } from 'x-next';
const value = ref<(string | number)[]>(['hz']);
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
children: [
{ key: 'hz', title: '杭州' },
{ key: 'nb', title: '宁波' },
],
},
];
function changed(value: TreeSelectValue | TreeSelectValue[] | undefined, info: Record<string, unknown>) {
console.log('当前值:', value, '节点:', info.node);
}
</script>
<template>
<TreeSelect
v-model="value"
:data="data"
tree-checkable
default-expand-all
placeholder="勾选城市"
@change="changed"
/>
</template>勾选策略
checkedStrategy 控制回填值的粒度:parent(默认)保留最高层已选节点,child 只保留叶子,all 返回全部勾选节点。切换策略只影响后续回填值,规范业务数据时用 parent 更省心。
浙江省当前值:["zj"]
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect, RadioGroup, Radio } from 'x-next';
import type { TreeSelectNodeData } from 'x-next';
const value = ref<(string | number)[]>(['zj']);
const strategy = ref<'parent' | 'child' | 'all'>('parent');
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
children: [
{ key: 'hz', title: '杭州' },
{ key: 'nb', title: '宁波' },
],
},
];
</script>
<template>
<RadioGroup v-model="strategy">
<Radio value="parent">parent</Radio>
<Radio value="child">child</Radio>
<Radio value="all">all</Radio>
</RadioGroup>
<TreeSelect
v-model="value"
:data="data"
tree-checkable
default-expand-all
:checked-strategy="strategy"
/>
<p>当前值:{{ value }}</p>
</template>严格勾选模式
treeCheckStrictly 取消父子联动,勾选状态完全由业务控制;值为 ['zj'] 时子节点不会被自动勾选。适合「父级只作为分组标题、勾选互相独立」的场景。
浙江省当前值:["zj"]
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData } from 'x-next';
const value = ref<(string | number)[]>(['zj']);
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
children: [
{ key: 'hz', title: '杭州' },
{ key: 'nb', title: '宁波' },
],
},
];
</script>
<template>
<TreeSelect v-model="value" :data="data" tree-checkable tree-check-strictly default-expand-all />
</template>搜索与自定义过滤
allowSearch 开启搜索框。默认按标题忽略大小写匹配,命中节点的祖先会保留展示,命中的文字自动高亮。filterTreeNode 可换成按编码等业务字段匹配;search 事件在 searchDelay(默认 500ms)后触发,适合远程查询。
搜索事件:尚未触发
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData } from 'x-next';
const value = ref<string | number | undefined>('');
const input = ref('');
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
code: '330000',
children: [{ key: 'hz', title: '杭州', code: '330100' }],
},
];
const filterByCodeOrTitle = (input: string, node: TreeSelectNodeData) =>
String(node.code ?? '').includes(input) || String(node.title ?? '').includes(input);
</script>
<template>
<TreeSelect
v-model="value"
v-model:input-value="input"
:data="data"
allow-search
:filter-tree-node="filterByCodeOrTitle"
placeholder="输入城市名或行政区划代码"
@search="query => console.log('搜索:', query)"
/>
</template>异步加载子节点
给节点的 isLeaf: false,并传入 loadMore,展开时先请求子级、成功后再展开;加载中的节点显示转圈图标。适合目录、组织架构等大数据量场景,配合 search 事件还能实现远程搜索。
展开节点时加载子级
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData } from 'x-next';
const value = ref<string | number | undefined>('');
const data = ref<TreeSelectNodeData[]>([
{ key: 'region-east', title: '华东大区', isLeaf: false },
]);
const loadRegion = (node: TreeSelectNodeData): Promise<void> =>
new Promise((resolve) => {
setTimeout(() => {
node.children = [
{ key: `${node.key}-a`, title: `${node.title} · 一组` },
{ key: `${node.key}-b`, title: `${node.title} · 二组` },
];
resolve();
}, 600);
});
</script>
<template>
<TreeSelect v-model="value" :data="data" :load-more="loadRegion" placeholder="展开大区加载子级" />
</template>展开控制
default-expand-all 首次渲染展开全部节点;v-model:expanded-keys 受控展开状态;default-expanded-keys 只指定部分节点;auto-expand-parent(默认开启)会在指定节点参与展开时自动补全祖先。
默认展开 zj
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData } from 'x-next';
const value = ref<string | number | undefined>('');
const expandedKeys = ref<(string | number)[]>(['zj']);
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
children: [{ key: 'hz', title: '杭州' }],
},
];
</script>
<template>
<TreeSelect
v-model="value"
v-model:expanded-keys="expandedKeys"
:data="data"
@expand="keys => console.log('展开:', keys)"
/>
</template>自定义字段名
fieldNames 把 key、title、children、disabled 等字段映射到业务数据上,无需在进入组件前转换结构。checkable、disableCheckbox、selectable、isLeaf、icon 同样可映射。
设计部当前值:dept-design
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
const value = ref<string | number | undefined>('dept-design');
const data = [
{
id: 'dept',
name: '产品技术中心',
sub: [
{ id: 'dept-design', name: '设计部' },
{ id: 'dept-fe', name: '前端组' },
],
},
];
</script>
<template>
<TreeSelect
v-model="value"
:data="data"
:field-names="{ key: 'id', title: 'name', children: 'sub' }"
default-expand-all
placeholder="选择部门"
/>
</template>值与标题
labelInValue 让值变成 { value, label, raw } 对象,提交表单或保存草稿时无需再反查标题;数据中已不存在的选中值会保留并在标签里回显,fallbackOption 关闭后则直接丢弃。
杭州change 载荷:{"value":"hz","label":"杭州"}
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData, TreeSelectValue } from 'x-next';
const value = ref<{ value: string | number; label: string } | undefined>({ value: 'hz', label: '杭州' });
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
children: [{ key: 'hz', title: '杭州' }],
},
];
function changed(value: TreeSelectValue | TreeSelectValue[] | undefined) {
console.log('当前值:', value);
}
</script>
<template>
<TreeSelect v-model="value" :data="data" label-in-value default-expand-all allow-clear @change="changed" />
</template>自定义节点
title 插槽拿到节点状态(checked / halfChecked / selected / expanded / level),node-icon 与 switcher-icon 分别替换节点前缀图标和展开箭头;数据里的 icon 字段也可以直接给节点配置图标函数。
杭州集群当前值:hz
vue
<template>
<TreeSelect v-model="value" :data="data" default-expand-all>
<template #title="{ node, checked }">
<span>
{{ node.title }}
<i v-if="node.status">{{ node.status === 'healthy' ? '●' : '▲' }}</i>
</span>
</template>
</TreeSelect>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData } from 'x-next';
const value = ref<string | number | undefined>('hz');
const data: TreeSelectNodeData[] = [
{
key: 'cloud',
title: '云平台',
children: [
{ key: 'hz', title: '杭州集群', status: 'healthy' },
{ key: 'nb', title: '宁波集群', status: 'warning' },
],
},
];
</script>连接线与整行节点
showLine 显示层级连接线,最后一个子节点的线止于自身行中;blockNode 让节点占据整行、悬停与选中背景铺满,适合表格筛选器里的窄宽度选择框。两者可以组合。
太仓当前值:sz-tc;节点模式:blockNode
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect, Switch } from 'x-next';
import type { TreeSelectNodeData } from 'x-next';
const value = ref<string | number | undefined>('sz-tc');
const block = ref(true);
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
children: [
{ key: 'hz', title: '杭州' },
{ key: 'nb', title: '宁波' },
],
},
];
</script>
<template>
<Switch v-model="block" checked-text="整行" unchecked-text="缩进" />
<TreeSelect
v-model="value"
:data="data"
show-line
:block-node="block"
default-expand-all
allow-clear
placeholder="带连接线的树"
/>
</template>禁用、错误、加载与尺寸
disabled 关闭交互,error 标记校验失败,loading 展示加载图标并在面板内显示加载态,size 支持 mini / small / medium / large。这些状态与 Select 保持一致,可直接放进 FormItem。
可选节点 A
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect, Button } from 'x-next';
const value = ref<string | number | undefined>('a');
const loading = ref(false);
const data = [
{ key: 'a', title: '可选节点 A' },
{ key: 'b', title: '可选节点 B' },
];
</script>
<template>
<TreeSelect :data="data" size="mini" placeholder="mini" />
<TreeSelect :data="data" size="small" placeholder="small" />
<TreeSelect :data="data" size="medium" placeholder="medium" />
<TreeSelect :data="data" size="large" placeholder="large" />
<TreeSelect :data="data" disabled placeholder="禁用" />
<TreeSelect v-model="value" :data="data" error placeholder="错误" />
<TreeSelect :data="data" :loading="loading" placeholder="加载" />
<Button size="small" @click="loading = !loading">{{ loading ? '结束加载' : '模拟加载' }}</Button>
</template>缺失值回显
当值对应的节点不在当前 data(数据权限、分页、远程未命中)时,默认保留该值并以原始 key 回显;fallbackOption 传函数可自定义回显内容,传 false 则丢弃不存在节点。下拉里也会把这类值显示为额外节点,方便用户主动清除。
归档节点(archived-node)选中的节点暂不在 data 中
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
const value = ref<string | number | undefined>('archived-node');
const data = [
{ key: 'a', title: '可选节点 A' },
{ key: 'b', title: '可选节点 B' },
];
const fallback = (value: string | number) => ({
key: value,
title: `归档节点(${String(value)})`,
});
</script>
<template>
<TreeSelect v-model="value" :data="data" :fallback-option="fallback" allow-clear />
</template>虚拟列表
数据量大时传入 virtualListProps 开启虚拟滚动,height 控制可视区高度;关闭 scrollbar 可退回原生滚动条。搜索命中的节点同样按扁平结果虚拟化,不会因为层级展开而失效。
当前值:
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData } from 'x-next';
const value = ref<string | number | undefined>('');
const data: TreeSelectNodeData[] = Array.from({ length: 240 }, (_, index) => ({
key: `node-${index + 1}`,
title: `数据节点 ${index + 1}`,
isLeaf: true,
}));
</script>
<template>
<TreeSelect
v-model="value"
:data="data"
:virtual-list-props="{ height: 220 }"
allow-search
allow-clear
placeholder="240 个扁平节点"
/>
</template>下拉定制与批量操作
header / footer 插槽承载批量操作,show-header-on-empty / show-footer-on-empty 决定空态时是否保留;empty 替换空态。配合实例方法 checkAll() 与 expandAll() 可以做出「全选 / 展开全部」的工具条。
使用页头批量操作
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect, Button } from 'x-next';
import type { TreeSelectInstance, TreeSelectNodeData } from 'x-next';
const value = ref<(string | number)[]>([]);
const select = ref<TreeSelectInstance>();
const data: TreeSelectNodeData[] = [
{
key: 'east',
title: '华东区',
children: [
{ key: 'east-hz', title: '杭州' },
{ key: 'east-nb', title: '宁波' },
],
},
];
</script>
<template>
<TreeSelect ref="select" v-model="value" :data="data" tree-checkable default-expand-all>
<template #header>
<Button size="mini" @click="select?.checkAll(true)">全选</Button>
<Button size="mini" @click="select?.checkAll(false)">清空</Button>
<Button size="mini" @click="select?.expandAll(true)">展开全部</Button>
</template>
<template #footer>已选 {{ value.length }} 项</template>
</TreeSelect>
</template>自定义触发器与实例方法
默认触发器承载输入与标签;如需完全自定义,使用 trigger 插槽并绑定 onKeydown,同时补齐 role="combobox" 与 ARIA。实例方法提供 focus() / blur() / expandAll() / checkAll()。
当前值:未选择;面板:关闭
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData } from 'x-next';
const value = ref<string | number | undefined>('');
const popup = ref(false);
const data: TreeSelectNodeData[] = [
{
key: 'east',
title: '华东区',
children: [{ key: 'east-hz', title: '杭州' }],
},
];
</script>
<template>
<TreeSelect v-model="value" v-model:popup-visible="popup" :data="data" default-expand-all>
<template #trigger="{ popupVisible, disabled, treeId, activeDescendant, onKeydown }">
<button
type="button" role="combobox" :disabled="disabled" aria-label="自定义地区选择"
aria-haspopup="tree" :aria-expanded="popupVisible" :aria-controls="treeId"
:aria-activedescendant="activeDescendant" @keydown="onKeydown"
>{{ value || '选择地区' }}</button>
</template>
</TreeSelect>
</template>在 Form 中校验
放在 FormItem 中即可继承尺寸、禁用与校验状态;值变化会触发 validateTrigger。必填校验使用 FormItem 的规则,不要依赖原生表单控件。
点击校验查看结果
vue
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { Form, FormItem, TreeSelect, Button, Space } from 'x-next';
import type { FieldRule, FormInstance, TreeSelectNodeData } from 'x-next';
const state = reactive<{ region: string | number | undefined }>({ region: undefined });
const rules: Record<string, FieldRule[]> = { region: [{ required: true, message: '请选择归属地区' }] };
const form = ref<FormInstance>();
const result = ref('');
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
children: [{ key: 'hz', title: '杭州' }],
},
];
const validate = async () => {
const errors = await form.value!.validate();
result.value = errors ? '校验失败' : '校验通过';
};
</script>
<template>
<Form ref="form" :model="state" :rules="rules">
<FormItem field="region" label="归属地区" required>
<TreeSelect v-model="state.region" :data="data" default-expand-all allow-clear />
</FormItem>
<Button type="primary" @click="validate">校验</Button>
<p>{{ result }}</p>
</Form>
</template>键盘与无障碍
面板打开后:↑ / ↓ 在可见节点间移动,→ 展开当前节点或进入第一个子节点,← 收起或回到父节点,Enter 选中节点,勾选模式下 空格 切换勾选,Escape 关闭面板,Tab 离开并关闭。Home / End 在搜索框为空时跳到首 / 末节点。触发器是 role="combobox" 并关联 aria-controls / aria-activedescendant,树容器是 role="tree",节点带 aria-level、aria-selected、aria-checked(半选为 mixed)。
当前值:;焦点:不在
vue
<script setup lang="ts">
import { ref } from 'vue';
import { TreeSelect } from 'x-next';
import type { TreeSelectNodeData } from 'x-next';
const value = ref<string | number | undefined>('');
const popup = ref(false);
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
children: [{ key: 'hz', title: '杭州' }],
},
];
</script>
<template>
<TreeSelect
v-model="value"
v-model:popup-visible="popup"
:data="data"
default-expand-all
aria-label="键盘选择地区"
placeholder="方向键选择,Enter 确认"
/>
</template>按需导入
ts
import { TreeSelect } from 'x-next';推荐公开样式入口,自动包含 base、SelectView、Checkbox、Scrollbar、Spin、Trigger 等内部依赖:
ts
import 'x-next/style/tree-select';直接使用 .css 时须自行补齐上述依赖;可读取包内 dist/style/dependencies.json 核对。
TreeSelect Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
modelValue | 绑定值,勾选 / 多选为数组 | TreeSelectKey | TreeSelectLabelValue | Array | undefined |
defaultValue | 默认值(非受控) | 同上 | 单选 undefined,多选 / 勾选 [] |
multiple | 是否多选 | boolean | false |
treeCheckable | 是否开启勾选模式,值始终为数组 | boolean | false |
treeCheckStrictly | 勾选模式是否取消父子联动 | boolean | false |
checkedStrategy | 勾选回填策略 | 'all' | 'parent' | 'child' | 'parent' |
data | 树数据 | TreeSelectNodeData[] | [] |
fieldNames | 自定义节点字段名 | object | - |
labelInValue | 值是否包含标题 | boolean | false |
selectable | 节点可选规则,支持 'leaf' 或方法 | boolean | 'leaf' | function | true |
allowSearch | 是否允许搜索 | boolean | { retainInputValue?: boolean } | 多选 / 勾选 true,单选 false |
allowClear | 是否允许清空 | boolean | false |
filterTreeNode | 过滤节点方法,false 关闭过滤 | boolean | function | true |
searchDelay | search 事件触发延迟 | number | 500 |
inputValue | 搜索输入值(v-model) | string | - |
defaultInputValue | 默认搜索输入值 | string | '' |
size | 尺寸 | 'mini' | 'small' | 'medium' | 'large' | 跟随全局配置 |
placeholder | 占位提示 | string | - |
loading | 是否加载中 | boolean | false |
disabled | 是否禁用 | boolean | false |
error | 是否错误状态 | boolean | false |
bordered | 是否显示边框 | boolean | true |
maxTagCount | 最多展示标签数,0 不限制 | number | 0 |
tagNowrap | 标签是否不换行 | boolean | false |
popupVisible | 面板显示状态(v-model) | boolean | undefined |
defaultPopupVisible | 面板默认显示状态 | boolean | false |
unmountOnClose | 关闭时销毁面板 | boolean | false |
popupContainer | 弹出层挂载容器 | string | HTMLElement | - |
triggerProps | 触发器配置 | object | - |
defaultExpandAll | 默认展开全部节点 | boolean | false |
expandedKeys | 展开节点(v-model) | Array<string | number> | undefined |
defaultExpandedKeys | 默认展开节点 | Array<string | number> | [] |
autoExpandParent | 是否自动展开父节点 | boolean | true |
showLine | 是否显示连接线 | boolean | false |
blockNode | 节点占据整行 | boolean | false |
virtualListProps | 虚拟列表配置,传入即开启 | object | - |
scrollbar | 是否开启滚动条或滚动条配置 | boolean | object | true |
maxHeight | 下拉列表最大高度 | number | string | 300 |
loadMore | 异步加载子节点 | (node) => Promise<void> | - |
fallbackOption | 自定义缺失值显示,false 丢弃 | boolean | (key) => node | true |
formatLabel | 格式化显示内容 | (data) => string | - |
showHeaderOnEmpty | 空态是否显示页头 | boolean | false |
showFooterOnEmpty | 空态是否显示页脚 | boolean | false |
TreeSelect Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
update:modelValue | 值更新时触发 | value |
update:inputValue | 搜索输入更新时触发 | inputValue |
update:popupVisible | 面板显隐更新时触发 | visible |
update:expandedKeys | 展开节点更新时触发 | keys |
change | 值变化时触发 | value, info,info 含 node / checked / selected |
input-value-change | 搜索输入变化时触发 | inputValue |
search | 用户搜索时触发(searchDelay 后) | inputValue |
popup-visible-change | 面板显隐变化时触发 | visible |
clear | 点击清空按钮时触发 | event |
remove | 关闭多选标签时触发 | removed |
expand | 展开状态变化时触发 | keys, info,info 含 node / expanded |
dropdown-scroll | 下拉滚动时触发 | event |
node-click | 点击(勾选)节点时触发 | value, info |
focus | 默认触发器聚焦 | FocusEvent |
blur | 默认触发器失焦 | FocusEvent |
TreeSelect Slots
| 插槽名 | 说明 |
|---|---|
title | 自定义节点标题;参数 { node, value, title, level, checked, halfChecked, selected, expanded, disabled, isLeaf } |
node-icon | 节点前缀图标;参数 { node } |
switcher-icon | 展开 / 收起图标;参数 { node, expanded, loading } |
label | 选择框显示内容;参数 { data },data 为 { value, label, raw } |
header | 下拉页头 |
footer | 下拉页脚 |
empty | 空状态内容 |
prefix | 选择框前缀元素 |
arrow-icon | 箭头图标 |
loading-icon | 加载中图标 |
search-icon | 搜索图标 |
trigger | 自定义触发元素;参数 { popupVisible, disabled, treeId, activeDescendant, onKeydown } |
TreeSelect Methods
使用组件 ref,类型可导入 TreeSelectInstance。
| 方法 | 说明 | 类型 |
|---|---|---|
focus | 聚焦默认触发器 | () => void |
blur | 失焦默认触发器 | () => void |
expandAll | 展开 / 收起全部可展开节点,默认展开 | (expanded?: boolean) => void |
checkAll | 全选 / 清空勾选,仅勾选模式生效 | (checked?: boolean) => void |
节点数据
ts
import type { TreeSelectNodeData } from 'x-next';
const data: TreeSelectNodeData[] = [
{
key: 'zj',
title: '浙江省',
disabled: false,
checkable: true,
disableCheckbox: false,
selectable: true,
isLeaf: false,
children: [{ key: 'hz', title: '杭州' }],
},
];| 字段 | 说明 | 类型 | 默认值 |
|---|---|---|---|
key | 节点键值,全局唯一;不传按路径生成 | string | number | - |
title | 节点标题 | string | 取 key |
children | 子节点 | TreeSelectNodeData[] | - |
disabled | 是否禁用(不可选、不可勾选、不参与联动) | boolean | false |
checkable | 是否可勾选 | boolean | true |
disableCheckbox | 是否禁用复选框(仍可被父节点联动) | boolean | false |
selectable | 是否可选中 | boolean | true |
isLeaf | 是否叶子节点(配合 loadMore) | boolean | 无子节点即叶子 |
icon | 节点前缀图标 | () => VNode | - |
无障碍
- 触发器输入为
role="combobox",带aria-haspopup="tree"、aria-expanded、aria-controls和aria-activedescendant。 - 树容器为
role="tree",节点为role="treeitem",带aria-level、aria-posinset、aria-setsize、aria-selected、aria-expanded、aria-checked(半选为mixed)。 - 键盘完整支持
↑/↓/←/→/Home/End/Enter/空格/Escape/Tab。 - 标签关闭按钮与清空按钮均为真实按钮并提供无障碍名称;移除标签后焦点回到输入框。
已知限制
- 勾选模式下
treeCheckStrictly为真时半选概念不生效,值完全由业务控制,请保持值与data同步。 checkedStrategy在treeCheckStrictly下不生效(严格模式直接返回勾选集合)。loadMore依赖节点的isLeaf: false标记;数据里已有children的节点不会再触发加载。- 搜索为前端过滤;远程搜索请设置
filterTreeNode=false,用@search请求数据并替换data。 - 节点
key必须唯一;重复键会退化为路径键并在控制台告警。 - 与 Arco 的差异:
checkedStrategy默认parent(Arco 默认all),并在其基础上提供child;值可为labelInValue对象且附带raw原始节点,属于 x-next 的扩展。