x-next 1.14.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/AI_USAGE/catalog.md +4 -2
  2. package/AI_USAGE/components/alert-api.md +1 -1
  3. package/AI_USAGE/components/alert.md +1 -1
  4. package/AI_USAGE/components/bar-chart-api.md +1 -1
  5. package/AI_USAGE/components/bar-chart.md +1 -1
  6. package/AI_USAGE/components/boxicons.md +1 -1
  7. package/AI_USAGE/components/button-api.md +1 -1
  8. package/AI_USAGE/components/button.md +1 -1
  9. package/AI_USAGE/components/charts.md +1 -1
  10. package/AI_USAGE/components/checkbox-api.md +1 -1
  11. package/AI_USAGE/components/checkbox.md +1 -1
  12. package/AI_USAGE/components/color-picker-api.md +1 -1
  13. package/AI_USAGE/components/color-picker.md +1 -1
  14. package/AI_USAGE/components/config-provider-api.md +1 -1
  15. package/AI_USAGE/components/config-provider.md +1 -1
  16. package/AI_USAGE/components/date-picker-api.md +1 -1
  17. package/AI_USAGE/components/date-picker.md +1 -1
  18. package/AI_USAGE/components/dialog-api.md +1 -1
  19. package/AI_USAGE/components/dialog.md +1 -1
  20. package/AI_USAGE/components/draggable-api.md +1 -1
  21. package/AI_USAGE/components/draggable.md +1 -1
  22. package/AI_USAGE/components/drawer-api.md +1 -1
  23. package/AI_USAGE/components/drawer.md +30 -1
  24. package/AI_USAGE/components/dropdown-api.md +1 -1
  25. package/AI_USAGE/components/dropdown.md +1 -1
  26. package/AI_USAGE/components/empty-api.md +1 -1
  27. package/AI_USAGE/components/empty.md +1 -1
  28. package/AI_USAGE/components/finicon.md +1 -1
  29. package/AI_USAGE/components/form-api.md +1 -1
  30. package/AI_USAGE/components/form.md +1 -1
  31. package/AI_USAGE/components/grid-api.md +1 -1
  32. package/AI_USAGE/components/grid.md +1 -1
  33. package/AI_USAGE/components/heatmap-api.md +1 -1
  34. package/AI_USAGE/components/heatmap.md +1 -1
  35. package/AI_USAGE/components/icon-api.md +1 -1
  36. package/AI_USAGE/components/icon.md +1 -1
  37. package/AI_USAGE/components/image-api.md +1 -1
  38. package/AI_USAGE/components/image.md +1 -1
  39. package/AI_USAGE/components/input-api.md +1 -1
  40. package/AI_USAGE/components/input-number-api.md +1 -1
  41. package/AI_USAGE/components/input-number.md +1 -1
  42. package/AI_USAGE/components/input-tag-api.md +1 -1
  43. package/AI_USAGE/components/input-tag.md +1 -1
  44. package/AI_USAGE/components/input.md +1 -1
  45. package/AI_USAGE/components/layout-api.md +1 -1
  46. package/AI_USAGE/components/layout.md +1 -1
  47. package/AI_USAGE/components/line-chart-api.md +3 -3
  48. package/AI_USAGE/components/line-chart.md +28 -1
  49. package/AI_USAGE/components/link-api.md +1 -1
  50. package/AI_USAGE/components/link.md +1 -1
  51. package/AI_USAGE/components/menu-api.md +1 -1
  52. package/AI_USAGE/components/menu.md +1 -1
  53. package/AI_USAGE/components/message-api.md +1 -1
  54. package/AI_USAGE/components/message-box-api.md +1 -1
  55. package/AI_USAGE/components/message-box.md +1 -1
  56. package/AI_USAGE/components/message.md +1 -1
  57. package/AI_USAGE/components/network-api.md +1 -1
  58. package/AI_USAGE/components/network.md +1 -1
  59. package/AI_USAGE/components/notification-api.md +1 -1
  60. package/AI_USAGE/components/notification.md +1 -1
  61. package/AI_USAGE/components/pagination-api.md +1 -1
  62. package/AI_USAGE/components/pagination.md +1 -1
  63. package/AI_USAGE/components/pie-chart-api.md +1 -1
  64. package/AI_USAGE/components/pie-chart.md +1 -1
  65. package/AI_USAGE/components/popconfirm-api.md +1 -1
  66. package/AI_USAGE/components/popconfirm.md +1 -1
  67. package/AI_USAGE/components/popover-api.md +1 -1
  68. package/AI_USAGE/components/popover.md +1 -1
  69. package/AI_USAGE/components/popup-api.md +1 -1
  70. package/AI_USAGE/components/popup.md +1 -1
  71. package/AI_USAGE/components/progress-api.md +1 -1
  72. package/AI_USAGE/components/progress.md +1 -1
  73. package/AI_USAGE/components/radio-api.md +1 -1
  74. package/AI_USAGE/components/radio.md +1 -1
  75. package/AI_USAGE/components/rate-api.md +1 -1
  76. package/AI_USAGE/components/rate.md +1 -1
  77. package/AI_USAGE/components/resize-box-api.md +1 -1
  78. package/AI_USAGE/components/resize-box.md +1 -1
  79. package/AI_USAGE/components/scrollbar-api.md +1 -1
  80. package/AI_USAGE/components/scrollbar.md +1 -1
  81. package/AI_USAGE/components/select-api.md +1 -1
  82. package/AI_USAGE/components/select.md +1 -1
  83. package/AI_USAGE/components/skeleton-api.md +1 -1
  84. package/AI_USAGE/components/skeleton.md +1 -1
  85. package/AI_USAGE/components/slider-api.md +1 -1
  86. package/AI_USAGE/components/slider.md +1 -1
  87. package/AI_USAGE/components/space-api.md +1 -1
  88. package/AI_USAGE/components/space.md +1 -1
  89. package/AI_USAGE/components/spin-api.md +1 -1
  90. package/AI_USAGE/components/spin.md +1 -1
  91. package/AI_USAGE/components/steps-api.md +1 -1
  92. package/AI_USAGE/components/steps.md +1 -1
  93. package/AI_USAGE/components/switch-api.md +1 -1
  94. package/AI_USAGE/components/switch.md +1 -1
  95. package/AI_USAGE/components/table-api.md +1 -1
  96. package/AI_USAGE/components/table.md +44 -1
  97. package/AI_USAGE/components/tabs-api.md +1 -1
  98. package/AI_USAGE/components/tabs.md +1 -1
  99. package/AI_USAGE/components/tag-api.md +1 -1
  100. package/AI_USAGE/components/tag.md +1 -1
  101. package/AI_USAGE/components/textarea-api.md +1 -1
  102. package/AI_USAGE/components/textarea.md +1 -1
  103. package/AI_USAGE/components/time-picker-api.md +1 -1
  104. package/AI_USAGE/components/time-picker.md +1 -1
  105. package/AI_USAGE/components/time-range-selector-api.md +1 -1
  106. package/AI_USAGE/components/time-range-selector.md +1 -1
  107. package/AI_USAGE/components/timeline-api.md +1 -1
  108. package/AI_USAGE/components/timeline.md +1 -1
  109. package/AI_USAGE/components/tooltip-api.md +1 -1
  110. package/AI_USAGE/components/tooltip.md +1 -1
  111. package/AI_USAGE/components/tree-select-api.md +498 -0
  112. package/AI_USAGE/components/tree-select.md +744 -0
  113. package/AI_USAGE/components/trend-chart-api.md +1 -1
  114. package/AI_USAGE/components/trend-chart.md +1 -1
  115. package/AI_USAGE/components/trigger-api.md +1 -1
  116. package/AI_USAGE/components/trigger.md +1 -1
  117. package/AI_USAGE/components/upload-api.md +1 -1
  118. package/AI_USAGE/components/upload.md +1 -1
  119. package/AI_USAGE/components/watermark-api.md +1 -1
  120. package/AI_USAGE/components/watermark.md +1 -1
  121. package/AI_USAGE/core.md +1 -1
  122. package/AI_USAGE/index.md +3 -2
  123. package/AI_USAGE/recipes/form-on-demand.md +1 -1
  124. package/AI_USAGE.md +1326 -5
  125. package/CHANGELOG.md +32 -0
  126. package/dist/_props/css.d.ts +2 -1
  127. package/dist/components/_components/input-label/input-label.d.ts +3 -0
  128. package/dist/components/bar-chart/BarChart.d.ts +1 -1
  129. package/dist/components/bar-chart/index.d.ts +3 -3
  130. package/dist/components/index.d.ts +1 -0
  131. package/dist/components/line-chart/LineChart.d.ts +1 -1
  132. package/dist/components/line-chart/index.d.ts +3 -3
  133. package/dist/components/line-chart/interface.d.ts +2 -2
  134. package/dist/components/tree-select/TreeNode.vue.d.ts +478 -0
  135. package/dist/components/tree-select/TreeSelect.d.ts +1549 -0
  136. package/dist/components/tree-select/TreeSelectDropdown.vue.d.ts +569 -0
  137. package/dist/components/tree-select/hooks/use-tree-select.d.ts +92 -0
  138. package/dist/components/tree-select/index.d.ts +3321 -0
  139. package/dist/components/tree-select/interface.d.ts +203 -0
  140. package/dist/components/tree-select/utils.d.ts +64 -0
  141. package/dist/es/_styles/palette-aliases.js +3 -0
  142. package/dist/es/components/_components/feedback-icon/index.vue2.js +10 -10
  143. package/dist/es/components/_components/input-label/input-label.js +69 -65
  144. package/dist/es/components/_components/select-view/select-view.js +1 -0
  145. package/dist/es/components/button/Button.js +9 -9
  146. package/dist/es/components/drawer/src/Drawer.vue.js +26 -26
  147. package/dist/es/components/form-switch/Switch.vue2.js +18 -18
  148. package/dist/es/components/image/Image.vue2.js +13 -13
  149. package/dist/es/components/image/ImagePreview.vue2.js +33 -33
  150. package/dist/es/components/index.js +55 -52
  151. package/dist/es/components/link/Link.vue2.js +12 -12
  152. package/dist/es/components/message/message.vue2.js +13 -13
  153. package/dist/es/components/spin/Spin.js +28 -28
  154. package/dist/es/components/tag/Tag.vue2.js +22 -22
  155. package/dist/es/components/timeline/src/Timeline.js +7 -7
  156. package/dist/es/components/tree-select/TreeNode.vue.js +91 -0
  157. package/dist/es/components/tree-select/TreeNode.vue2.js +162 -0
  158. package/dist/es/components/tree-select/TreeSelect.js +865 -0
  159. package/dist/es/components/tree-select/TreeSelectDropdown.vue.js +57 -0
  160. package/dist/es/components/tree-select/TreeSelectDropdown.vue2.js +79 -0
  161. package/dist/es/components/tree-select/hooks/use-tree-select.js +376 -0
  162. package/dist/es/components/tree-select/index.js +13 -0
  163. package/dist/es/components/tree-select/utils.js +181 -0
  164. package/dist/es/icons/animation/animation-loading.js +2 -2
  165. package/dist/es/index.js +71 -68
  166. package/dist/es/style.css +1 -1
  167. package/dist/index.cjs +3 -3
  168. package/dist/index.umd.js +3 -3
  169. package/dist/style/base.css +1 -1
  170. package/dist/style/dependencies.json +10 -0
  171. package/dist/style/drawer.css +1 -1
  172. package/dist/style/table.css +1 -1
  173. package/dist/style/tree-select.css +1 -0
  174. package/dist/style/tree-select.js +8 -0
  175. package/dist/style.css +1 -1
  176. package/dist/types.d.ts +2 -0
  177. package/package.json +2 -1
  178. package/volar.d.ts +2 -0
@@ -0,0 +1,744 @@
1
+ <!-- Generated by pnpm generate:ai-usage. Edit script/ai-usage/content.md and script/ai-usage/. -->
2
+ # TreeSelect AI 使用
3
+
4
+ 适用版本:x-next@1.16.0;来源指纹:`089f62cfcc5dbb14a7c66c139cdccc00de64d2ebdf90162e3dca9438c15ba4de`。
5
+
6
+ 先读 [通用接入](../core.md)。公开导出:`TreeSelect, TreeSelectDropdown`;主要模型:v-model、v-model:input-value、v-model:popup-visible、v-model:expanded-keys。完整 Props、Events、Slots、实例方法与服务签名见 [本地 API](./tree-select-api.md),包含本组件族全部子组件和别名。
7
+
8
+ 按需样式:`x-next/style/tree-select`(包含 base.css、empty.css、form-checkbox.css、form-select.css、scrollbar-v2.css、spin.css、trigger.css、tree-select.css)。代码块未单独引样式时在应用入口加载全量样式,或按实际所有组件加载 JS 入口;不要仅因复制一个组件页就漏掉其中的 Button 等组合依赖。
9
+
10
+ ## 已核验行为与示例
11
+
12
+ [组件文档与 live demo](https://x-next.yjmf.com/components/tree-select)。用于在层级数据中选择节点,`data` 是 `{ key, title, children }` 树。单选为标量、`multiple` 为数组、`treeCheckable` 勾选模式始终返回数组;`allowSearch` 默认多选 / 勾选开启、单选关闭,`checkedStrategy` 默认 `parent`。
13
+
14
+ 勾选模式默认父子联动:勾选叶子会向上影响父级半选 / 全选,禁用与 `disableCheckbox` 节点不参与联动。`checkedStrategy` 决定回填 key:`parent` 只回填最高层已选节点,`child` 只回填叶子,`all` 返回全部;`treeCheckStrictly` 关闭联动且策略不生效。`change(value, info)` 的 `info` 含 `node`、`checked` / `selected`;`expand(keys, info)` 报告展开变化,`node-click` 报告节点点击。`v-model:expanded-keys` 控制展开,`default-expand-all` 只初始化。
15
+
16
+ 多选 / 勾选模式下点击节点会清空搜索词,需要保留时用 `allowSearch: { retainInputValue: true }`。`labelInValue` 时值为 `{ value, label, raw }`;`data` 中不存在的选中值默认保留并按 key 回显,`fallbackOption` 传函数可自定义、传 `false` 丢弃。异步子节点用 `isLeaf: false` + `loadMore`,展开时先加载再展开。搜索默认按标题过滤并保留命中祖先,`filterTreeNode` 可自定义;远程搜索设置 `filterTreeNode=false` 并用 `@search` 替换 `data`。大数据量传 `virtualListProps` 开启虚拟滚动。实例方法 `focus()` / `blur()` / `expandAll()` / `checkAll()`。
17
+
18
+ ```vue
19
+ <script setup lang="ts">
20
+ import { ref } from 'vue';
21
+ import { TreeSelect } from 'x-next';
22
+ import 'x-next/dist/style.css';
23
+ const region = ref<string | number | undefined>('hz');
24
+ const feedback = ref('已选:hz');
25
+ const data = [
26
+ {
27
+ key: 'zj',
28
+ title: '浙江省',
29
+ children: [
30
+ { key: 'hz', title: '杭州' },
31
+ { key: 'nb', title: '宁波' },
32
+ { key: 'wz', title: '温州(暂停服务)', disabled: true },
33
+ ],
34
+ },
35
+ ];
36
+ function changed(value: unknown) {
37
+ feedback.value = value ? `已选:${String(value)}` : '已清空';
38
+ }
39
+ </script>
40
+ <template>
41
+ <TreeSelect v-model="region" :data="data" default-expand-all allow-clear placeholder="请选择城市" @change="changed" />
42
+ <p role="status">{{ feedback }}</p>
43
+ </template>
44
+ ```
45
+
46
+ ## 场景用法
47
+
48
+ 以下示例从本地 `src/docs/components/tree-select.md` 显式导入到独立 AI 源,补齐局部注册、状态和样式后进行严格模板检查。上方已核验行为优先;原始 prop 默认值、包装透传和示例业务适配分别查 [完整 API](./tree-select-api.md) 与场景说明。类型通过不代表所有散文和交互已验证。
49
+
50
+ ### 用途与选型
51
+
52
+ 用于在层级数据中选择单个、多个或勾选一批节点,适合地区、组织架构、分类目录、指标维度等字段。
53
+
54
+
55
+ ### 何时使用
56
+
57
+ 候选项存在父子层级,并希望像 Select 一样在下拉里完成选择时使用 TreeSelect;地区、部门、目录、指标树都是典型场景。平铺枚举用 Select,独立的多列级联用 Cascader;需要直接浏览树结构时使用独立的 Tree 组件。
58
+
59
+
60
+ ### 基础用法
61
+
62
+ 单选点击叶子节点后关闭面板并回显标题;点击分支节点只展开 / 收起。`allowClear` 提供清空按钮;禁用节点不可点击,也不会在清空时被误删。
63
+
64
+ ```vue
65
+ <script setup lang="ts">
66
+ import 'x-next/style.css';
67
+ import { ref } from 'vue';
68
+ import { TreeSelect } from 'x-next';
69
+ import type { TreeSelectNodeData, TreeSelectValue } from 'x-next';
70
+
71
+ const value = ref<string | number | undefined>('hz');
72
+ const feedback = ref('已选:hz');
73
+ const data: TreeSelectNodeData[] = [
74
+ {
75
+ key: 'zj',
76
+ title: '浙江省',
77
+ children: [
78
+ { key: 'hz', title: '杭州' },
79
+ { key: 'nb', title: '宁波' },
80
+ { key: 'wz', title: '温州(暂停服务)', disabled: true },
81
+ ],
82
+ },
83
+ ];
84
+ function changed(value: TreeSelectValue | TreeSelectValue[] | undefined) {
85
+ feedback.value = value ? `已选:${String(value)}` : '已清空';
86
+ }
87
+ </script>
88
+ <template>
89
+ <TreeSelect
90
+ v-model="value"
91
+ :data="data"
92
+ default-expand-all
93
+ allow-clear
94
+ placeholder="请选择城市"
95
+ @change="changed"
96
+ />
97
+ <p>{{ feedback }}</p>
98
+ </template>
99
+ ```
100
+
101
+ ### 多选
102
+
103
+ `multiple` 开启多选,值始终是数组,面板保持展开直到点击外部或按 Escape。`maxTagCount` 只折叠展示,不限制数量;`tagNowrap` 让标签不换行。多选 / 勾选模式下点击节点会清空搜索词(与 Select 一致),需要保留搜索词时用 `allowSearch: { retainInputValue: true }`。
104
+
105
+ ```vue
106
+ <script setup lang="ts">
107
+ import 'x-next/style.css';
108
+ import { ref } from 'vue';
109
+ import { TreeSelect } from 'x-next';
110
+ import type { TreeSelectNodeData, TreeSelectValue } from 'x-next';
111
+
112
+ const value = ref<(string | number)[]>(['hz', 'nb']);
113
+ const data: TreeSelectNodeData[] = [
114
+ {
115
+ key: 'zj',
116
+ title: '浙江省',
117
+ children: [
118
+ { key: 'hz', title: '杭州' },
119
+ { key: 'nb', title: '宁波' },
120
+ { key: 'wz', title: '温州(暂停服务)', disabled: true },
121
+ ],
122
+ },
123
+ ];
124
+ function removed(value: TreeSelectValue | undefined) {
125
+ console.log('移除:', value);
126
+ }
127
+ </script>
128
+ <template>
129
+ <TreeSelect
130
+ v-model="value"
131
+ :data="data"
132
+ multiple
133
+ default-expand-all
134
+ allow-clear
135
+ :max-tag-count="2"
136
+ tag-nowrap
137
+ placeholder="可多选城市"
138
+ @remove="removed"
139
+ />
140
+ </template>
141
+ ```
142
+
143
+ ### 勾选模式与父子联动
144
+
145
+ `treeCheckable` 开启复选框。勾选叶子后父节点自动变为半选;半选节点不出现在值里。默认 `checkedStrategy="parent"`,勾选整个分支时只回填父节点 key。禁用和 `disableCheckbox` 节点不参与联动,但仍可被父节点联动勾选。
146
+
147
+ ```vue
148
+ <script setup lang="ts">
149
+ import 'x-next/style.css';
150
+ import { ref } from 'vue';
151
+ import { TreeSelect } from 'x-next';
152
+ import type { TreeSelectNodeData, TreeSelectValue } from 'x-next';
153
+
154
+ const value = ref<(string | number)[]>(['hz']);
155
+ const data: TreeSelectNodeData[] = [
156
+ {
157
+ key: 'zj',
158
+ title: '浙江省',
159
+ children: [
160
+ { key: 'hz', title: '杭州' },
161
+ { key: 'nb', title: '宁波' },
162
+ ],
163
+ },
164
+ ];
165
+ function changed(value: TreeSelectValue | TreeSelectValue[] | undefined, info: Record<string, unknown>) {
166
+ console.log('当前值:', value, '节点:', info.node);
167
+ }
168
+ </script>
169
+ <template>
170
+ <TreeSelect
171
+ v-model="value"
172
+ :data="data"
173
+ tree-checkable
174
+ default-expand-all
175
+ placeholder="勾选城市"
176
+ @change="changed"
177
+ />
178
+ </template>
179
+ ```
180
+
181
+ ### 勾选策略
182
+
183
+ `checkedStrategy` 控制回填值的粒度:`parent`(默认)保留最高层已选节点,`child` 只保留叶子,`all` 返回全部勾选节点。切换策略只影响后续回填值,规范业务数据时用 `parent` 更省心。
184
+
185
+ ```vue
186
+ <script setup lang="ts">
187
+ import 'x-next/style.css';
188
+ import { ref } from 'vue';
189
+ import { TreeSelect, RadioGroup, Radio } from 'x-next';
190
+ import type { TreeSelectNodeData } from 'x-next';
191
+
192
+ const value = ref<(string | number)[]>(['zj']);
193
+ const strategy = ref<'parent' | 'child' | 'all'>('parent');
194
+ const data: TreeSelectNodeData[] = [
195
+ {
196
+ key: 'zj',
197
+ title: '浙江省',
198
+ children: [
199
+ { key: 'hz', title: '杭州' },
200
+ { key: 'nb', title: '宁波' },
201
+ ],
202
+ },
203
+ ];
204
+ </script>
205
+ <template>
206
+ <RadioGroup v-model="strategy">
207
+ <Radio value="parent">parent</Radio>
208
+ <Radio value="child">child</Radio>
209
+ <Radio value="all">all</Radio>
210
+ </RadioGroup>
211
+ <TreeSelect
212
+ v-model="value"
213
+ :data="data"
214
+ tree-checkable
215
+ default-expand-all
216
+ :checked-strategy="strategy"
217
+ />
218
+ <p>当前值:{{ value }}</p>
219
+ </template>
220
+ ```
221
+
222
+ ### 严格勾选模式
223
+
224
+ `treeCheckStrictly` 取消父子联动,勾选状态完全由业务控制;值为 `['zj']` 时子节点不会被自动勾选。适合「父级只作为分组标题、勾选互相独立」的场景。
225
+
226
+ ```vue
227
+ <script setup lang="ts">
228
+ import 'x-next/style.css';
229
+ import { ref } from 'vue';
230
+ import { TreeSelect } from 'x-next';
231
+ import type { TreeSelectNodeData } from 'x-next';
232
+
233
+ const value = ref<(string | number)[]>(['zj']);
234
+ const data: TreeSelectNodeData[] = [
235
+ {
236
+ key: 'zj',
237
+ title: '浙江省',
238
+ children: [
239
+ { key: 'hz', title: '杭州' },
240
+ { key: 'nb', title: '宁波' },
241
+ ],
242
+ },
243
+ ];
244
+ </script>
245
+ <template>
246
+ <TreeSelect v-model="value" :data="data" tree-checkable tree-check-strictly default-expand-all />
247
+ </template>
248
+ ```
249
+
250
+ ### 搜索与自定义过滤
251
+
252
+ `allowSearch` 开启搜索框。默认按标题忽略大小写匹配,命中节点的祖先会保留展示,命中的文字自动高亮。`filterTreeNode` 可换成按编码等业务字段匹配;`search` 事件在 `searchDelay`(默认 500ms)后触发,适合远程查询。
253
+
254
+ ```vue
255
+ <script setup lang="ts">
256
+ import 'x-next/style.css';
257
+ import { ref } from 'vue';
258
+ import { TreeSelect } from 'x-next';
259
+ import type { TreeSelectNodeData } from 'x-next';
260
+
261
+ const value = ref<string | number | undefined>('');
262
+ const input = ref('');
263
+ const data: TreeSelectNodeData[] = [
264
+ {
265
+ key: 'zj',
266
+ title: '浙江省',
267
+ code: '330000',
268
+ children: [{ key: 'hz', title: '杭州', code: '330100' }],
269
+ },
270
+ ];
271
+ const filterByCodeOrTitle = (input: string, node: TreeSelectNodeData) =>
272
+ String(node.code ?? '').includes(input) || String(node.title ?? '').includes(input);
273
+ </script>
274
+ <template>
275
+ <TreeSelect
276
+ v-model="value"
277
+ v-model:input-value="input"
278
+ :data="data"
279
+ allow-search
280
+ :filter-tree-node="filterByCodeOrTitle"
281
+ placeholder="输入城市名或行政区划代码"
282
+ @search="query => console.log('搜索:', query)"
283
+ />
284
+ </template>
285
+ ```
286
+
287
+ ### 异步加载子节点
288
+
289
+ 给节点的 `isLeaf: false`,并传入 `loadMore`,展开时先请求子级、成功后再展开;加载中的节点显示转圈图标。适合目录、组织架构等大数据量场景,配合 `search` 事件还能实现远程搜索。
290
+
291
+ ```vue
292
+ <script setup lang="ts">
293
+ import 'x-next/style.css';
294
+ import { ref } from 'vue';
295
+ import { TreeSelect } from 'x-next';
296
+ import type { TreeSelectNodeData } from 'x-next';
297
+
298
+ const value = ref<string | number | undefined>('');
299
+ const data = ref<TreeSelectNodeData[]>([
300
+ { key: 'region-east', title: '华东大区', isLeaf: false },
301
+ ]);
302
+ const loadRegion = (node: TreeSelectNodeData): Promise<void> =>
303
+ new Promise((resolve) => {
304
+ setTimeout(() => {
305
+ node.children = [
306
+ { key: `${node.key}-a`, title: `${node.title} · 一组` },
307
+ { key: `${node.key}-b`, title: `${node.title} · 二组` },
308
+ ];
309
+ resolve();
310
+ }, 600);
311
+ });
312
+ </script>
313
+ <template>
314
+ <TreeSelect v-model="value" :data="data" :load-more="loadRegion" placeholder="展开大区加载子级" />
315
+ </template>
316
+ ```
317
+
318
+ ### 展开控制
319
+
320
+ `default-expand-all` 首次渲染展开全部节点;`v-model:expanded-keys` 受控展开状态;`default-expanded-keys` 只指定部分节点;`auto-expand-parent`(默认开启)会在指定节点参与展开时自动补全祖先。
321
+
322
+ ```vue
323
+ <script setup lang="ts">
324
+ import 'x-next/style.css';
325
+ import { ref } from 'vue';
326
+ import { TreeSelect } from 'x-next';
327
+ import type { TreeSelectNodeData } from 'x-next';
328
+
329
+ const value = ref<string | number | undefined>('');
330
+ const expandedKeys = ref<(string | number)[]>(['zj']);
331
+ const data: TreeSelectNodeData[] = [
332
+ {
333
+ key: 'zj',
334
+ title: '浙江省',
335
+ children: [{ key: 'hz', title: '杭州' }],
336
+ },
337
+ ];
338
+ </script>
339
+ <template>
340
+ <TreeSelect
341
+ v-model="value"
342
+ v-model:expanded-keys="expandedKeys"
343
+ :data="data"
344
+ @expand="keys => console.log('展开:', keys)"
345
+ />
346
+ </template>
347
+ ```
348
+
349
+ ### 自定义字段名
350
+
351
+ `fieldNames` 把 `key`、`title`、`children`、`disabled` 等字段映射到业务数据上,无需在进入组件前转换结构。`checkable`、`disableCheckbox`、`selectable`、`isLeaf`、`icon` 同样可映射。
352
+
353
+ ```vue
354
+ <script setup lang="ts">
355
+ import 'x-next/style.css';
356
+ import { ref } from 'vue';
357
+ import { TreeSelect } from 'x-next';
358
+
359
+ const value = ref<string | number | undefined>('dept-design');
360
+ const data = [
361
+ {
362
+ id: 'dept',
363
+ name: '产品技术中心',
364
+ sub: [
365
+ { id: 'dept-design', name: '设计部' },
366
+ { id: 'dept-fe', name: '前端组' },
367
+ ],
368
+ },
369
+ ];
370
+ </script>
371
+ <template>
372
+ <TreeSelect
373
+ v-model="value"
374
+ :data="data"
375
+ :field-names="{ key: 'id', title: 'name', children: 'sub' }"
376
+ default-expand-all
377
+ placeholder="选择部门"
378
+ />
379
+ </template>
380
+ ```
381
+
382
+ ### 值与标题
383
+
384
+ `labelInValue` 让值变成 `{ value, label, raw }` 对象,提交表单或保存草稿时无需再反查标题;数据中已不存在的选中值会保留并在标签里回显,`fallbackOption` 关闭后则直接丢弃。
385
+
386
+ ```vue
387
+ <script setup lang="ts">
388
+ import 'x-next/style.css';
389
+ import { ref } from 'vue';
390
+ import { TreeSelect } from 'x-next';
391
+ import type { TreeSelectNodeData, TreeSelectValue } from 'x-next';
392
+
393
+ const value = ref<{ value: string | number; label: string } | undefined>({ value: 'hz', label: '杭州' });
394
+ const data: TreeSelectNodeData[] = [
395
+ {
396
+ key: 'zj',
397
+ title: '浙江省',
398
+ children: [{ key: 'hz', title: '杭州' }],
399
+ },
400
+ ];
401
+ function changed(value: TreeSelectValue | TreeSelectValue[] | undefined) {
402
+ console.log('当前值:', value);
403
+ }
404
+ </script>
405
+ <template>
406
+ <TreeSelect v-model="value" :data="data" label-in-value default-expand-all allow-clear @change="changed" />
407
+ </template>
408
+ ```
409
+
410
+ ### 自定义节点
411
+
412
+ `title` 插槽拿到节点状态(`checked` / `halfChecked` / `selected` / `expanded` / `level`),`node-icon` 与 `switcher-icon` 分别替换节点前缀图标和展开箭头;数据里的 `icon` 字段也可以直接给节点配置图标函数。
413
+
414
+ ```vue
415
+ <script setup lang="ts">
416
+ import 'x-next/style.css';
417
+ import { ref } from 'vue';
418
+ import { TreeSelect } from 'x-next';
419
+ import type { TreeSelectNodeData } from 'x-next';
420
+
421
+ const value = ref<string | number | undefined>('hz');
422
+ const data: TreeSelectNodeData[] = [
423
+ {
424
+ key: 'cloud',
425
+ title: '云平台',
426
+ children: [
427
+ { key: 'hz', title: '杭州集群', status: 'healthy' },
428
+ { key: 'nb', title: '宁波集群', status: 'warning' },
429
+ ],
430
+ },
431
+ ];
432
+ </script>
433
+ <template>
434
+ <TreeSelect v-model="value" :data="data" default-expand-all>
435
+ <template #title="{ node, checked }">
436
+ <span>
437
+ {{ node.title }}
438
+ <i v-if="node.status">{{ node.status === 'healthy' ? '●' : '▲' }}</i>
439
+ </span>
440
+ </template>
441
+ </TreeSelect>
442
+ </template>
443
+ ```
444
+
445
+ ### 连接线与整行节点
446
+
447
+ `showLine` 显示层级连接线,最后一个子节点的线止于自身行中;`blockNode` 让节点占据整行、悬停与选中背景铺满,适合表格筛选器里的窄宽度选择框。两者可以组合。
448
+
449
+ ```vue
450
+ <script setup lang="ts">
451
+ import 'x-next/style.css';
452
+ import { ref } from 'vue';
453
+ import { TreeSelect, Switch } from 'x-next';
454
+ import type { TreeSelectNodeData } from 'x-next';
455
+
456
+ const value = ref<string | number | undefined>('sz-tc');
457
+ const block = ref(true);
458
+ const data: TreeSelectNodeData[] = [
459
+ {
460
+ key: 'zj',
461
+ title: '浙江省',
462
+ children: [
463
+ { key: 'hz', title: '杭州' },
464
+ { key: 'nb', title: '宁波' },
465
+ ],
466
+ },
467
+ ];
468
+ </script>
469
+ <template>
470
+ <Switch v-model="block" checked-text="整行" unchecked-text="缩进" />
471
+ <TreeSelect
472
+ v-model="value"
473
+ :data="data"
474
+ show-line
475
+ :block-node="block"
476
+ default-expand-all
477
+ allow-clear
478
+ placeholder="带连接线的树"
479
+ />
480
+ </template>
481
+ ```
482
+
483
+ ### 禁用、错误、加载与尺寸
484
+
485
+ `disabled` 关闭交互,`error` 标记校验失败,`loading` 展示加载图标并在面板内显示加载态,`size` 支持 `mini` / `small` / `medium` / `large`。这些状态与 Select 保持一致,可直接放进 FormItem。
486
+
487
+ ```vue
488
+ <script setup lang="ts">
489
+ import 'x-next/style.css';
490
+ import { ref } from 'vue';
491
+ import { TreeSelect, Button } from 'x-next';
492
+
493
+ const value = ref<string | number | undefined>('a');
494
+ const loading = ref(false);
495
+ const data = [
496
+ { key: 'a', title: '可选节点 A' },
497
+ { key: 'b', title: '可选节点 B' },
498
+ ];
499
+ </script>
500
+ <template>
501
+ <TreeSelect :data="data" size="mini" placeholder="mini" />
502
+ <TreeSelect :data="data" size="small" placeholder="small" />
503
+ <TreeSelect :data="data" size="medium" placeholder="medium" />
504
+ <TreeSelect :data="data" size="large" placeholder="large" />
505
+ <TreeSelect :data="data" disabled placeholder="禁用" />
506
+ <TreeSelect v-model="value" :data="data" error placeholder="错误" />
507
+ <TreeSelect :data="data" :loading="loading" placeholder="加载" />
508
+ <Button size="small" @click="loading = !loading">{{ loading ? '结束加载' : '模拟加载' }}</Button>
509
+ </template>
510
+ ```
511
+
512
+ ### 缺失值回显
513
+
514
+ 当值对应的节点不在当前 `data`(数据权限、分页、远程未命中)时,默认保留该值并以原始 key 回显;`fallbackOption` 传函数可自定义回显内容,传 `false` 则丢弃不存在节点。下拉里也会把这类值显示为额外节点,方便用户主动清除。
515
+
516
+ ```vue
517
+ <script setup lang="ts">
518
+ import 'x-next/style.css';
519
+ import { ref } from 'vue';
520
+ import { TreeSelect } from 'x-next';
521
+
522
+ const value = ref<string | number | undefined>('archived-node');
523
+ const data = [
524
+ { key: 'a', title: '可选节点 A' },
525
+ { key: 'b', title: '可选节点 B' },
526
+ ];
527
+ const fallback = (value: string | number) => ({
528
+ key: value,
529
+ title: `归档节点(${String(value)})`,
530
+ });
531
+ </script>
532
+ <template>
533
+ <TreeSelect v-model="value" :data="data" :fallback-option="fallback" allow-clear />
534
+ </template>
535
+ ```
536
+
537
+ ### 虚拟列表
538
+
539
+ 数据量大时传入 `virtualListProps` 开启虚拟滚动,`height` 控制可视区高度;关闭 `scrollbar` 可退回原生滚动条。搜索命中的节点同样按扁平结果虚拟化,不会因为层级展开而失效。
540
+
541
+ ```vue
542
+ <script setup lang="ts">
543
+ import 'x-next/style.css';
544
+ import { ref } from 'vue';
545
+ import { TreeSelect } from 'x-next';
546
+ import type { TreeSelectNodeData } from 'x-next';
547
+
548
+ const value = ref<string | number | undefined>('');
549
+ const data: TreeSelectNodeData[] = Array.from({ length: 240 }, (_, index) => ({
550
+ key: `node-${index + 1}`,
551
+ title: `数据节点 ${index + 1}`,
552
+ isLeaf: true,
553
+ }));
554
+ </script>
555
+ <template>
556
+ <TreeSelect
557
+ v-model="value"
558
+ :data="data"
559
+ :virtual-list-props="{ height: 220 }"
560
+ allow-search
561
+ allow-clear
562
+ placeholder="240 个扁平节点"
563
+ />
564
+ </template>
565
+ ```
566
+
567
+ ### 下拉定制与批量操作
568
+
569
+ `header` / `footer` 插槽承载批量操作,`show-header-on-empty` / `show-footer-on-empty` 决定空态时是否保留;`empty` 替换空态。配合实例方法 `checkAll()` 与 `expandAll()` 可以做出「全选 / 展开全部」的工具条。
570
+
571
+ ```vue
572
+ <script setup lang="ts">
573
+ import 'x-next/style.css';
574
+ import { ref } from 'vue';
575
+ import { TreeSelect, Button } from 'x-next';
576
+ import type { TreeSelectInstance, TreeSelectNodeData } from 'x-next';
577
+
578
+ const value = ref<(string | number)[]>([]);
579
+ const select = ref<TreeSelectInstance>();
580
+ const data: TreeSelectNodeData[] = [
581
+ {
582
+ key: 'east',
583
+ title: '华东区',
584
+ children: [
585
+ { key: 'east-hz', title: '杭州' },
586
+ { key: 'east-nb', title: '宁波' },
587
+ ],
588
+ },
589
+ ];
590
+ </script>
591
+ <template>
592
+ <TreeSelect ref="select" v-model="value" :data="data" tree-checkable default-expand-all>
593
+ <template #header>
594
+ <Button size="mini" @click="select?.checkAll(true)">全选</Button>
595
+ <Button size="mini" @click="select?.checkAll(false)">清空</Button>
596
+ <Button size="mini" @click="select?.expandAll(true)">展开全部</Button>
597
+ </template>
598
+ <template #footer>已选 {{ value.length }} 项</template>
599
+ </TreeSelect>
600
+ </template>
601
+ ```
602
+
603
+ ### 自定义触发器与实例方法
604
+
605
+ 默认触发器承载输入与标签;如需完全自定义,使用 `trigger` 插槽并绑定 `onKeydown`,同时补齐 `role="combobox"` 与 ARIA。实例方法提供 `focus()` / `blur()` / `expandAll()` / `checkAll()`。
606
+
607
+ ```vue
608
+ <script setup lang="ts">
609
+ import 'x-next/style.css';
610
+ import { ref } from 'vue';
611
+ import { TreeSelect } from 'x-next';
612
+ import type { TreeSelectNodeData } from 'x-next';
613
+
614
+ const value = ref<string | number | undefined>('');
615
+ const popup = ref(false);
616
+ const data: TreeSelectNodeData[] = [
617
+ {
618
+ key: 'east',
619
+ title: '华东区',
620
+ children: [{ key: 'east-hz', title: '杭州' }],
621
+ },
622
+ ];
623
+ </script>
624
+ <template>
625
+ <TreeSelect v-model="value" v-model:popup-visible="popup" :data="data" default-expand-all>
626
+ <template #trigger="{ popupVisible, disabled, treeId, activeDescendant, onKeydown }">
627
+ <button
628
+ type="button" role="combobox" :disabled="disabled" aria-label="自定义地区选择"
629
+ aria-haspopup="tree" :aria-expanded="popupVisible" :aria-controls="treeId"
630
+ :aria-activedescendant="activeDescendant" @keydown="onKeydown"
631
+ >{{ value || '选择地区' }}</button>
632
+ </template>
633
+ </TreeSelect>
634
+ </template>
635
+ ```
636
+
637
+ ### 在 Form 中校验
638
+
639
+ 放在 FormItem 中即可继承尺寸、禁用与校验状态;值变化会触发 `validateTrigger`。必填校验使用 FormItem 的规则,不要依赖原生表单控件。
640
+
641
+ ```vue
642
+ <script setup lang="ts">
643
+ import 'x-next/style.css';
644
+ import { reactive, ref } from 'vue';
645
+ import { Form, FormItem, TreeSelect, Button, Space } from 'x-next';
646
+ import type { FieldRule, FormInstance, TreeSelectNodeData } from 'x-next';
647
+
648
+ const state = reactive<{ region: string | number | undefined }>({ region: undefined });
649
+ const rules: Record<string, FieldRule[]> = { region: [{ required: true, message: '请选择归属地区' }] };
650
+ const form = ref<FormInstance>();
651
+ const result = ref('');
652
+ const data: TreeSelectNodeData[] = [
653
+ {
654
+ key: 'zj',
655
+ title: '浙江省',
656
+ children: [{ key: 'hz', title: '杭州' }],
657
+ },
658
+ ];
659
+ const validate = async () => {
660
+ const errors = await form.value!.validate();
661
+ result.value = errors ? '校验失败' : '校验通过';
662
+ };
663
+ </script>
664
+ <template>
665
+ <Form ref="form" :model="state" :rules="rules">
666
+ <FormItem field="region" label="归属地区" required>
667
+ <TreeSelect v-model="state.region" :data="data" default-expand-all allow-clear />
668
+ </FormItem>
669
+ <Button type="primary" @click="validate">校验</Button>
670
+ <p>{{ result }}</p>
671
+ </Form>
672
+ </template>
673
+ ```
674
+
675
+ ### 键盘与无障碍
676
+
677
+ 面板打开后:`↑` / `↓` 在可见节点间移动,`→` 展开当前节点或进入第一个子节点,`←` 收起或回到父节点,`Enter` 选中节点,勾选模式下 `空格` 切换勾选,`Escape` 关闭面板,`Tab` 离开并关闭。Home / End 在搜索框为空时跳到首 / 末节点。触发器是 `role="combobox"` 并关联 `aria-controls` / `aria-activedescendant`,树容器是 `role="tree"`,节点带 `aria-level`、`aria-selected`、`aria-checked`(半选为 `mixed`)。
678
+
679
+ ```vue
680
+ <script setup lang="ts">
681
+ import 'x-next/style.css';
682
+ import { ref } from 'vue';
683
+ import { TreeSelect } from 'x-next';
684
+ import type { TreeSelectNodeData } from 'x-next';
685
+
686
+ const value = ref<string | number | undefined>('');
687
+ const popup = ref(false);
688
+ const data: TreeSelectNodeData[] = [
689
+ {
690
+ key: 'zj',
691
+ title: '浙江省',
692
+ children: [{ key: 'hz', title: '杭州' }],
693
+ },
694
+ ];
695
+ import type { HTMLAttributes } from 'vue';
696
+ const nativeAttrs1 = { "aria-label": "键盘选择地区" } satisfies HTMLAttributes;
697
+ </script>
698
+ <template>
699
+ <TreeSelect v-bind="nativeAttrs1"
700
+ v-model="value"
701
+ v-model:popup-visible="popup"
702
+ :data="data"
703
+ default-expand-all
704
+
705
+ placeholder="方向键选择,Enter 确认"
706
+ />
707
+ </template>
708
+ ```
709
+
710
+ ### 节点数据
711
+
712
+ | 字段 | 说明 | 类型 | 默认值 |
713
+ | --- | --- | --- | --- |
714
+ | `key` | 节点键值,全局唯一;不传按路径生成 | `string \| number` | `-` |
715
+ | `title` | 节点标题 | `string` | 取 `key` |
716
+ | `children` | 子节点 | `TreeSelectNodeData[]` | `-` |
717
+ | `disabled` | 是否禁用(不可选、不可勾选、不参与联动) | `boolean` | `false` |
718
+ | `checkable` | 是否可勾选 | `boolean` | `true` |
719
+ | `disableCheckbox` | 是否禁用复选框(仍可被父节点联动) | `boolean` | `false` |
720
+ | `selectable` | 是否可选中 | `boolean` | `true` |
721
+ | `isLeaf` | 是否叶子节点(配合 `loadMore`) | `boolean` | 无子节点即叶子 |
722
+ | `icon` | 节点前缀图标 | `() => VNode` | `-` |
723
+
724
+
725
+ ### 无障碍
726
+
727
+ - 触发器输入为 `role="combobox"`,带 `aria-haspopup="tree"`、`aria-expanded`、`aria-controls` 和 `aria-activedescendant`。
728
+ - 树容器为 `role="tree"`,节点为 `role="treeitem"`,带 `aria-level`、`aria-posinset`、`aria-setsize`、`aria-selected`、`aria-expanded`、`aria-checked`(半选为 `mixed`)。
729
+ - 键盘完整支持 `↑` / `↓` / `←` / `→` / `Home` / `End` / `Enter` / `空格` / `Escape` / `Tab`。
730
+ - 标签关闭按钮与清空按钮均为真实按钮并提供无障碍名称;移除标签后焦点回到输入框。
731
+
732
+
733
+ ### 已知限制
734
+
735
+ - 勾选模式下 `treeCheckStrictly` 为真时半选概念不生效,值完全由业务控制,请保持值与 `data` 同步。
736
+ - `checkedStrategy` 在 `treeCheckStrictly` 下不生效(严格模式直接返回勾选集合)。
737
+ - `loadMore` 依赖节点的 `isLeaf: false` 标记;数据里已有 `children` 的节点不会再触发加载。
738
+ - 搜索为前端过滤;远程搜索请设置 `filterTreeNode=false`,用 `@search` 请求数据并替换 `data`。
739
+ - 节点 `key` 必须唯一;重复键会退化为路径键并在控制台告警。
740
+ - 与 Arco 的差异:`checkedStrategy` 默认 `parent`(Arco 默认 `all`),并在其基础上提供 `child`;值可为 `labelInValue` 对象且附带 `raw` 原始节点,属于 x-next 的扩展。
741
+
742
+
743
+
744
+ 返回 [AI 索引](../index.md)。先查本页与 [本地 API](./tree-select-api.md);需要更深类型时查 [公开组件导航](../catalog.md),不默认上网查找本包用法。