TreeSelect 树形选择
树形数据下拉选择器,支持展开/折叠、多选级联、indeterminate、搜索、清空、禁用节点。
基础用法
CfTreeSelect 面向部门、权限、资源目录、地区等层级数据。每个分支节点旁边有展开/折叠按钮(chevron),搜索时会自动展开命中节点的所有祖先。
背景 视口
已选:edge-workers
<script setup lang="ts">
import { ref } from 'vue';
import { CfTreeSelect, type TreeSelectNode } from '@chufix-design/vue';
const value = ref('edge-workers');
const options: TreeSelectNode[] = [
{
value: 'platform',
label: '平台能力',
children: [
{ value: 'edge-workers', label: 'Workers' },
{ value: 'storage-d1', label: 'D1 数据库' },
{ value: 'storage-r2', label: 'R2 存储' },
],
},
{
value: 'product',
label: '产品体验',
children: [
{ value: 'docs', label: '文档站' },
{ value: 'dashboard', label: '控制台' },
{ value: 'billing', label: '计费中心', disabled: true },
],
},
];
</script>
<template>
<div class="demo-stack">
<CfTreeSelect v-model="value" :options="options" searchable clearable />
<p class="demo-hint">已选:<code>{{ value ?? 'undefined' }}</code></p>
</div>
</template> <script setup>
import { ref } from 'vue';
import { CfTreeSelect } from '@chufix-design/vue';
const value = ref('edge-workers');
const options= [
{
value: 'platform',
label: '平台能力',
children: [
{ value: 'edge-workers', label: 'Workers' },
{ value: 'storage-d1', label: 'D1 数据库' },
{ value: 'storage-r2', label: 'R2 存储' },
],
},
{
value: 'product',
label: '产品体验',
children: [
{ value: 'docs', label: '文档站' },
{ value: 'dashboard', label: '控制台' },
{ value: 'billing', label: '计费中心', disabled: true },
],
},
];
</script>
<template>
<div class="demo-stack">
<CfTreeSelect v-model="value" :options="options" searchable clearable />
<p class="demo-hint">已选:<code>{{ value ?? 'undefined' }}</code></p>
</div>
</template> import { useState } from 'react';
import { CfTreeSelect } from '@chufix-design/react';
export default function Demo() {
const [value, setValue] = useState('edge-workers');
const options: TreeSelectNode[] = [
{
value: 'platform',
label: '平台能力',
children: [
{ value: 'edge-workers', label: 'Workers' },
{ value: 'storage-d1', label: 'D1 数据库' },
{ value: 'storage-r2', label: 'R2 存储' },
],
},
{
value: 'product',
label: '产品体验',
children: [
{ value: 'docs', label: '文档站' },
{ value: 'dashboard', label: '控制台' },
{ value: 'billing', label: '计费中心', disabled: true },
],
},
];
return (
<>
<div className="demo-stack">
<CfTreeSelect value={value} onChange={setValue} options={options} searchable clearable />
<p className="demo-hint">已选:<code>{value ?? 'undefined'}</code></p>
</div>
</>
);
} import { useState } from 'react';
import { CfTreeSelect } from '@chufix-design/react';
export default function Demo() {
const [value, setValue] = useState('edge-workers');
const options= [
{
value: 'platform',
label: '平台能力',
children: [
{ value: 'edge-workers', label: 'Workers' },
{ value: 'storage-d1', label: 'D1 数据库' },
{ value: 'storage-r2', label: 'R2 存储' },
],
},
{
value: 'product',
label: '产品体验',
children: [
{ value: 'docs', label: '文档站' },
{ value: 'dashboard', label: '控制台' },
{ value: 'billing', label: '计费中心', disabled: true },
],
},
];
return (
<>
<div className="demo-stack">
<CfTreeSelect value={value} onChange={setValue} options={options} searchable clearable />
<p className="demo-hint">已选:<code>{value ?? 'undefined'}</code></p>
</div>
</>
);
} 多选与级联
multiple 开启多选;默认开启 cascade:点击父节点会级联选中所有可用子节点;当部分子节点被选中时,父节点显示 indeterminate 状态。disabled 子节点不会被级联影响。
背景 视口
已选 2 项 — 点父节点会级联选中所有可用子节点。
<script setup lang="ts">
import { ref } from 'vue';
import { CfTreeSelect, type TreeSelectNode } from '@chufix-design/vue';
const value = ref<string[]>(['edge-workers', 'storage-r2']);
const options: TreeSelectNode[] = [
{
value: 'platform',
label: '平台能力',
children: [
{ value: 'edge-workers', label: 'Workers' },
{ value: 'storage-d1', label: 'D1 数据库' },
{ value: 'storage-r2', label: 'R2 存储' },
{ value: 'storage-kv', label: 'KV 存储' },
],
},
{
value: 'product',
label: '产品体验',
children: [
{ value: 'docs', label: '文档站' },
{ value: 'dashboard', label: '控制台' },
{ value: 'billing', label: '计费中心', disabled: true },
],
},
];
</script>
<template>
<div class="demo-stack">
<CfTreeSelect
v-model="value"
:options="options"
multiple
searchable
clearable
:default-expanded-keys="['platform']"
/>
<p class="demo-hint">
已选 <code>{{ value.length }}</code> 项 — 点父节点会级联选中所有可用子节点。
</p>
</div>
</template> <script setup>
import { ref } from 'vue';
import { CfTreeSelect } from '@chufix-design/vue';
const value = ref<string[]>(['edge-workers', 'storage-r2']);
const options= [
{
value: 'platform',
label: '平台能力',
children: [
{ value: 'edge-workers', label: 'Workers' },
{ value: 'storage-d1', label: 'D1 数据库' },
{ value: 'storage-r2', label: 'R2 存储' },
{ value: 'storage-kv', label: 'KV 存储' },
],
},
{
value: 'product',
label: '产品体验',
children: [
{ value: 'docs', label: '文档站' },
{ value: 'dashboard', label: '控制台' },
{ value: 'billing', label: '计费中心', disabled: true },
],
},
];
</script>
<template>
<div class="demo-stack">
<CfTreeSelect
v-model="value"
:options="options"
multiple
searchable
clearable
:default-expanded-keys="['platform']"
/>
<p class="demo-hint">
已选 <code>{{ value.length }}</code> 项 — 点父节点会级联选中所有可用子节点。
</p>
</div>
</template> import { useState } from 'react';
import { CfTreeSelect } from '@chufix-design/react';
export default function Demo() {
const [value, setValue] = useState<string[]>(['edge-workers', 'storage-r2']);
const options: TreeSelectNode[] = [
{
value: 'platform',
label: '平台能力',
children: [
{ value: 'edge-workers', label: 'Workers' },
{ value: 'storage-d1', label: 'D1 数据库' },
{ value: 'storage-r2', label: 'R2 存储' },
{ value: 'storage-kv', label: 'KV 存储' },
],
},
{
value: 'product',
label: '产品体验',
children: [
{ value: 'docs', label: '文档站' },
{ value: 'dashboard', label: '控制台' },
{ value: 'billing', label: '计费中心', disabled: true },
],
},
];
return (
<>
<div className="demo-stack">
<CfTreeSelect value={value} onChange={setValue} options={options} multiple searchable clearable defaultExpandedKeys={['platform']} />
<p className="demo-hint">
已选 <code>{value.length}</code> 项 — 点父节点会级联选中所有可用子节点。
</p>
</div>
</>
);
} import { useState } from 'react';
import { CfTreeSelect } from '@chufix-design/react';
export default function Demo() {
const [value, setValue] = useState<string[]>(['edge-workers', 'storage-r2']);
const options= [
{
value: 'platform',
label: '平台能力',
children: [
{ value: 'edge-workers', label: 'Workers' },
{ value: 'storage-d1', label: 'D1 数据库' },
{ value: 'storage-r2', label: 'R2 存储' },
{ value: 'storage-kv', label: 'KV 存储' },
],
},
{
value: 'product',
label: '产品体验',
children: [
{ value: 'docs', label: '文档站' },
{ value: 'dashboard', label: '控制台' },
{ value: 'billing', label: '计费中心', disabled: true },
],
},
];
return (
<>
<div className="demo-stack">
<CfTreeSelect value={value} onChange={setValue} options={options} multiple searchable clearable defaultExpandedKeys={['platform']} />
<p className="demo-hint">
已选 <code>{value.length}</code> 项 — 点父节点会级联选中所有可用子节点。
</p>
</div>
</>
);
} 默认展开节点
defaultExpandedKeys 接受节点 value 数组,或字符串 'all' 一次性展开整棵树。未列出的分支节点会保持折叠。
背景 视口
<script setup lang="ts">
import { ref } from 'vue';
import { CfTreeSelect, type TreeSelectNode } from '@chufix-design/vue';
const a = ref<string | undefined>(undefined);
const b = ref<string | undefined>(undefined);
const options: TreeSelectNode[] = [
{
value: 'cn',
label: '中国',
children: [
{
value: 'cn-east',
label: '华东',
children: [
{ value: 'sh', label: '上海' },
{ value: 'hz', label: '杭州' },
],
},
{
value: 'cn-south',
label: '华南',
children: [
{ value: 'gz', label: '广州' },
{ value: 'sz', label: '深圳' },
],
},
],
},
{
value: 'us',
label: '美国',
children: [
{ value: 'us-west', label: '西海岸' },
{ value: 'us-east', label: '东海岸' },
],
},
];
</script>
<template>
<div class="demo-stack">
<CfTreeSelect
v-model="a"
:options="options"
placeholder="默认全部展开"
default-expanded-keys="all"
searchable
/>
<CfTreeSelect
v-model="b"
:options="options"
placeholder="只展开根 + 华东"
:default-expanded-keys="['cn', 'cn-east']"
/>
</div>
</template> <script setup>
import { ref } from 'vue';
import { CfTreeSelect } from '@chufix-design/vue';
const a = ref<string | undefined>(undefined);
const b = ref<string | undefined>(undefined);
const options= [
{
value: 'cn',
label: '中国',
children: [
{
value: 'cn-east',
label: '华东',
children: [
{ value: 'sh', label: '上海' },
{ value: 'hz', label: '杭州' },
],
},
{
value: 'cn-south',
label: '华南',
children: [
{ value: 'gz', label: '广州' },
{ value: 'sz', label: '深圳' },
],
},
],
},
{
value: 'us',
label: '美国',
children: [
{ value: 'us-west', label: '西海岸' },
{ value: 'us-east', label: '东海岸' },
],
},
];
</script>
<template>
<div class="demo-stack">
<CfTreeSelect
v-model="a"
:options="options"
placeholder="默认全部展开"
default-expanded-keys="all"
searchable
/>
<CfTreeSelect
v-model="b"
:options="options"
placeholder="只展开根 + 华东"
:default-expanded-keys="['cn', 'cn-east']"
/>
</div>
</template> import { useState } from 'react';
import { CfTreeSelect } from '@chufix-design/react';
export default function Demo() {
const [a, setA] = useState<string | undefined>(undefined);
const [b, setB] = useState<string | undefined>(undefined);
const options: TreeSelectNode[] = [
{
value: 'cn',
label: '中国',
children: [
{
value: 'cn-east',
label: '华东',
children: [
{ value: 'sh', label: '上海' },
{ value: 'hz', label: '杭州' },
],
},
{
value: 'cn-south',
label: '华南',
children: [
{ value: 'gz', label: '广州' },
{ value: 'sz', label: '深圳' },
],
},
],
},
{
value: 'us',
label: '美国',
children: [
{ value: 'us-west', label: '西海岸' },
{ value: 'us-east', label: '东海岸' },
],
},
];
return (
<>
<div className="demo-stack">
<CfTreeSelect value={a} onChange={setA} options={options} placeholder="默认全部展开" default-expanded-keys="all" searchable />
<CfTreeSelect value={b} onChange={setB} options={options} placeholder="只展开根 + 华东" defaultExpandedKeys={['cn', 'cn-east']} />
</div>
</>
);
} import { useState } from 'react';
import { CfTreeSelect } from '@chufix-design/react';
export default function Demo() {
const [a, setA] = useState<string | undefined>(undefined);
const [b, setB] = useState<string | undefined>(undefined);
const options= [
{
value: 'cn',
label: '中国',
children: [
{
value: 'cn-east',
label: '华东',
children: [
{ value: 'sh', label: '上海' },
{ value: 'hz', label: '杭州' },
],
},
{
value: 'cn-south',
label: '华南',
children: [
{ value: 'gz', label: '广州' },
{ value: 'sz', label: '深圳' },
],
},
],
},
{
value: 'us',
label: '美国',
children: [
{ value: 'us-west', label: '西海岸' },
{ value: 'us-east', label: '东海岸' },
],
},
];
return (
<>
<div className="demo-stack">
<CfTreeSelect value={a} onChange={setA} options={options} placeholder="默认全部展开" default-expanded-keys="all" searchable />
<CfTreeSelect value={b} onChange={setB} options={options} placeholder="只展开根 + 华东" defaultExpandedKeys={['cn', 'cn-east']} />
</div>
</>
);
} 关闭引导线
层级关系默认通过竖向引导线展示。在节点文本较长或想要更紧凑外观时,showLines={false} 只保留缩进。
背景 视口
<script setup lang="ts">
import { ref } from 'vue';
import { CfTreeSelect, type TreeSelectNode } from '@chufix-design/vue';
const value = ref<string | undefined>(undefined);
const options: TreeSelectNode[] = [
{
value: 'design',
label: '设计系统',
children: [
{ value: 'tokens', label: 'Tokens' },
{ value: 'components', label: 'Components' },
],
},
{
value: 'engineering',
label: '工程',
children: [
{ value: 'frontend', label: '前端' },
{ value: 'backend', label: '后端' },
],
},
];
</script>
<template>
<CfTreeSelect
v-model="value"
:options="options"
placeholder="紧凑:关闭引导线"
:show-lines="false"
default-expanded-keys="all"
/>
</template> <script setup>
import { ref } from 'vue';
import { CfTreeSelect } from '@chufix-design/vue';
const value = ref<string | undefined>(undefined);
const options= [
{
value: 'design',
label: '设计系统',
children: [
{ value: 'tokens', label: 'Tokens' },
{ value: 'components', label: 'Components' },
],
},
{
value: 'engineering',
label: '工程',
children: [
{ value: 'frontend', label: '前端' },
{ value: 'backend', label: '后端' },
],
},
];
</script>
<template>
<CfTreeSelect
v-model="value"
:options="options"
placeholder="紧凑:关闭引导线"
:show-lines="false"
default-expanded-keys="all"
/>
</template> import { useState } from 'react';
import { CfTreeSelect } from '@chufix-design/react';
export default function Demo() {
const [value, setValue] = useState<string | undefined>(undefined);
const options: TreeSelectNode[] = [
{
value: 'design',
label: '设计系统',
children: [
{ value: 'tokens', label: 'Tokens' },
{ value: 'components', label: 'Components' },
],
},
{
value: 'engineering',
label: '工程',
children: [
{ value: 'frontend', label: '前端' },
{ value: 'backend', label: '后端' },
],
},
];
return (
<>
<CfTreeSelect value={value} onChange={setValue} options={options} placeholder="紧凑:关闭引导线" showLines={false} default-expanded-keys="all" />
</>
);
} import { useState } from 'react';
import { CfTreeSelect } from '@chufix-design/react';
export default function Demo() {
const [value, setValue] = useState<string | undefined>(undefined);
const options= [
{
value: 'design',
label: '设计系统',
children: [
{ value: 'tokens', label: 'Tokens' },
{ value: 'components', label: 'Components' },
],
},
{
value: 'engineering',
label: '工程',
children: [
{ value: 'frontend', label: '前端' },
{ value: 'backend', label: '后端' },
],
},
];
return (
<>
<CfTreeSelect value={value} onChange={setValue} options={options} placeholder="紧凑:关闭引导线" showLines={false} default-expanded-keys="all" />
</>
);
} API
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
options | TreeSelectNode[] | — | 树节点列表 |
value / modelValue | string | string[] | — | 当前值;多选时为数组 |
placeholder | string | 请选择 | 空值占位 |
multiple | boolean | false | 多选 |
cascade | boolean | true | 多选时父节点点击是否级联选中后代 |
searchable | boolean | false | 显示搜索输入;匹配会沿路径展开祖先 |
clearable | boolean | false | 显示清空按钮 |
disabled | boolean | false | 禁用 |
size | 'sm' | 'md' | 'lg' | 'md' | 尺寸 |
defaultExpandedKeys | string[] | 'all' | — | 初始展开节点;'all' 全展开,未传则只显示根节点 |
showLines | boolean | true | 显示层级引导线 |
Events
| Vue 事件 | React 回调 | payload | 说明 |
|---|---|---|---|
update:modelValue | onChange | (value, node) | 选中变化;node 为最近点击的叶子节点(多选场景) |
change | onChange | (value, node) | 同上 |
TreeSelectNode
| 字段 | 类型 | 说明 |
|---|---|---|
value | string | 唯一值 |
label | string | 展示文本 |
disabled | boolean | 禁用节点(级联也不会勾选) |
children | TreeSelectNode[] | 子节点 |
反馈与讨论
TreeSelect 树形选择 的讨论