Select
Select
多选时,inline 下拉只显示选项;已选内容、单项移除和清空由输入框承担。确认式编辑面板保留“已选/清空”,选择为草稿,确认后提交。
当用户需要从一组同类数据中选择一个或多个时,可以使用下拉选择器,点击后选择对应项。
阅读方向
选择框继承来源 dir;独立使用时默认按阅读起点对齐,在 FormItem 内继续继承其 content-align。显式 left/right 保持物理对齐;标签保持业务数组顺序,空搜索时 Backspace 删除最后一个可关闭值。下拉与选择面板携带来源方向,自定义 popup-container 不覆盖该方向。
基础示例
基础用法
点击展开选项,选中后回填并收起;清除后可重新选择。本例只演示单选。
外观与状态
外观配置对比
本组配置对照
| 配置 | 作用 | 相关示例 |
|---|---|---|
options | 原始值、对象、分组均支持;三种展示使用同一数据源 | 外观配置对比、表单控件尺寸对照 |
clearable | 允许清除,保留禁用的已选项 | 外观配置对比、表单控件尺寸对照 |
size | 完整支持 xs、sm、md、lg、xl,未设置时继承分组与表单 | 表单控件尺寸对照 |
variant | filled、outline、standard;未设置时继承 InputGroup | 表单控件尺寸对照 |
placeholder | 占位文本 | 表单控件尺寸对照 |
multiple | 多选;默认启用搜索 | 表单控件尺寸对照 |
选择一个对比维度后,同屏展示它的全部可选值;其余配置和内容保持一致。可切换其他配置检查组合效果;输入类预览共享一个值。
配置说明与使用限制
变体
设置variant="outline"开启变体模式,常用于沉浸式使用。
选择框大小
支持 xs、sm、md、lg、xl 五档尺寸。未设置时依次继承 InputGroup、FormItem、ConfigProvider,最终默认 md。
外观组合
适用于选项数据明确的单选输入。调整变体、尺寸,用相同数据观察变化。内容保持一致,便于比较不同设置的实际效果。
表单控件尺寸对照
切换五档 size 和三种字段变体,比较 Input、Button、Select、日期时间及其他单行表单控件的高度。Button 在这里固定使用 outline。DatePicker/TimePicker 是面板,此处使用带输入框的 Date/Time 字段。示例同时展示有值、可清空、标签删除与长标签多选;Select/Cascader 设置 tag-nowrap 时保持同档高度,长标签文字省略、删除图标保留;标签行可横向滚动,清空按钮和箭头固定在外侧。被省略的标签正文支持悬停、键盘聚焦或点按查看完整文字,点外部或按 Esc 关闭,不改变选中值。可清空后恢复选中值重复比较。多行文本和允许换行的多选内容按内容撑高。
选择模式与选项
多选选择器
本组配置对照
| 配置 | 作用 | 相关示例 |
|---|---|---|
placeholder | 占位文本 | 多选选择器、同名选项、分组 |
multiple | 多选;默认启用搜索 | 多选选择器、同名选项、分组 |
clearable | 允许清除,保留禁用的已选项 | 多选选择器、同名选项、子组件选项组合 |
filterable | 开启搜索,可设置 retainInputValue | 同名选项、子组件选项组合 |
options | 原始值、对象、分组均支持;三种展示使用同一数据源 | 同名选项、分组间距 |
scrollbar | Menu / List 滚动条配置 | 多选选择器 |
通过设置 multiple ,可以让选择器支持多选。此外通过 max-tag-count 可以设置最多显示的标签个数。
同名选项
标签展示允许同名,但每个选项的值必须唯一。删除第二个“设备”只移除 ID 2,不会误删 ID 1;搜索文字与已选值分别管理。
分组
使用 optgroup 组件添加分组选项。
分组间距
管理已选项
确认式多选编辑面板顶部提供已选数量与清空。Cedar 在列表上方显示可删除标签;Maple 点击数量查看已选明细,支持逐项移除并返回可选项。popup/popover 与 dialog 共用该内容。移除和清空先修改草稿,关闭取消会恢复原值,确认后才提交;禁用项保留,异步确认期间禁用操作。面板的清空入口独立于字段清空图标。
子组件选项组合
允许创建
通过设置 creatable ,让选择器可以创建选项中不存在的条目。
回退选项
使用 fallback-option 自定义选项中不存在的值,默认会在输入框中展示不存在的选项值。可能用于选项还没有获取完,或者远程搜索时选项改变了。
搜索与过滤
允许搜索
本组配置对照
| 配置 | 作用 | 相关示例 |
|---|---|---|
placeholder | 占位文本 | 允许搜索、搜索匹配规则、下拉菜单滚动 |
options | 原始值、对象、分组均支持;三种展示使用同一数据源 | 允许搜索、搜索匹配规则、虚拟列表 |
filterable | 开启搜索,可设置 retainInputValue | 允许搜索、搜索匹配规则 |
loading | 加载状态;禁止选择和确认 | 允许搜索 |
multiple | 多选;默认启用搜索 | 允许搜索 |
case-sensitive | 内置本地过滤是否区分大小写,自定义过滤函数优先 | 搜索匹配规则 |
通过设置 filterable ,可以让选择器支持对选项的搜索,配合 filter-option 可以自定义搜索。
搜索匹配规则
case-sensitive 默认 false,只控制内置本地匹配是否区分大小写;自定义过滤函数优先。远程结果可设置 :filter-option="false",不再执行本地筛选。
同时匹配 value 和 label 可直接配置 :search-fields="['value', 'label']";下面的示例不再需要自定义过滤函数。
下拉菜单滚动
可以通过dropdown-scroll监听下拉菜单的滚动事件,或者通过dropdown-reach-bottom监听下拉菜单滚动到底部的事件
虚拟列表
虚拟列表的使用方法
选项分组与 Menu 使用一致的间距逻辑:标题行高 20px,上下各 4px 留白(总高 28px);后续分组前额外留 8px,首组不添加组间距。标题与选项文字左对齐;AutoComplete 的标题缩进跟随其选项水平 padding 配置。
内容与下拉定制
已选 Tag 样式
本组配置对照
| 配置 | 作用 | 相关示例 |
|---|---|---|
placeholder | 占位文本 | 已选 Tag 样式、下拉菜单的页头、下拉菜单的页脚 |
multiple | 多选;默认启用搜索 | 已选 Tag 样式、下拉菜单的页头、复杂插槽组合 |
options | 原始值、对象、分组均支持;三种展示使用同一数据源 | 已选 Tag 样式、复杂插槽组合 |
filterable | 开启搜索,可设置 retainInputValue | 下拉菜单的页脚、复杂插槽组合 |
clearable | 允许清除,保留禁用的已选项 | 复杂插槽组合 |
多选时,已选项在选择框中以 Tag 展示。通过选项上的 tagProps(或 <c-option> 的 tag-props)可以把属性透传给 Tag,用来改颜色、状态、变体等。Tag 没有单独的最大宽度属性,需要截断长文本时传入 nowrap: true,并用 style.maxWidth 限制宽度。
下拉菜单的页头
自定义下拉菜单的页头
下拉菜单的页脚
自定义下拉菜单的页脚
自定义选择框展示内容
通过#label插槽可以自定义选择框展示内容。
复杂插槽组合
值模型与联动
候选高亮与表单数据流
本组配置对照
| 配置 | 作用 | 相关示例 |
|---|---|---|
options | 原始值、对象、分组均支持;三种展示使用同一数据源 | 候选高亮与表单数据流、联动选择框、空值识别与清空输出 |
clearable | 允许清除,保留禁用的已选项 | 空值识别与清空输出、交互状态对比 |
edit-mode | 字段展示;未指定时继承 FormItem/Form | 候选高亮与表单数据流 |
default-active-first-option | 打开选项列表时,无有效选中项则默认激活首个可用候选;仅高亮,不更新 modelValue 或触发 change。已有有效选中项优先激活该项 | 候选高亮与表单数据流 |
placeholder | 占位文本 | 候选高亮与表单数据流 |
empty-value | 清空输出覆盖;多选返回数组,保留不可清除项 | 空值识别与清空输出 |
初始 form.city 为 undefined。打开面板只激活候选行,不触发 update:modelValue 或 change,表单仍为空。inline 点击选项或按 Enter 后提交;click 在面板内选择只是草稿,确认后才提交,取消不改变表单。两个示例分别显示已提交值、更新次数和真实事件日志;日志按最新在前排列。
联动选择框
展示联动选择框的实现方法
自定义字段名
可以通过field-names属性定义options中数据的格式
空值识别与清空输出
本例把空字符串保留为合法的“全部”选项,清空按钮返回 null。先观察“全部”已选,再点击清空;两种状态在模型输出中分别是空字符串和 null。搜索框删空不会删除已选值。下方另列多选清空返回 []、单选用工厂明确返回 undefined 的情况,分别显示模型值。
交互状态对比
在同一实例上切换禁用、只读和错误状态后再操作控件,比较可编辑性和交互反馈。恢复开关位于目标控件外;错误外观本身不会执行校验。
异步数据与搜索
加载中
本组配置对照
| 配置 | 作用 | 相关示例 |
|---|---|---|
loading | 加载状态;禁止选择和确认 | 加载中、远程搜索、异步设备与 ID 回显 |
options | 原始值、对象、分组均支持;三种展示使用同一数据源 | 远程搜索、异步设备与 ID 回显、共用异步数据源(进阶可选) |
placeholder | 占位文本 | 加载中、远程搜索、异步设备与 ID 回显 |
filterable | 开启搜索,可设置 retainInputValue | 异步设备与 ID 回显、共用异步数据源(进阶可选)、函数属性:初始化与搜索 |
multiple | 多选;默认启用搜索 | 远程搜索、异步设备与 ID 回显 |
filter-option | false 关闭本地过滤;函数接收搜索词和原始选项 | 远程搜索 |
切换加载状态,对比默认指示器与底部加载线。加载期间保留已选值,暂停选择与确认;关闭加载后恢复。
远程搜索
使用 search 事件进行远程搜索,并改变选项。
异步设备与 ID 回显
主体、控制器、选项算法与样式在两端独立维护;展示方式继承 Form/Item。完整对比示例集中在 Form 文档。
未设置 disabled / error 时继承表单状态;显式 false 可以覆盖继承值。FieldDecorator 和 SelectionField 不重复注入表单上下文,focus / blur 由 Select 向表单分发一次。
共用异步数据源(进阶可选)
常规使用给 options 直接传入加载函数,组件内部负责异步状态。只有需要多个组件共享同一份结果、或组合不同控件时,才在业务层选用公开 useAsyncData;这不是组件的必需接入步骤。options/data 同时支持数组和加载函数,组件不解析业务响应。
函数属性:初始化与搜索
配置与生命周期
透传配置边界与优先级
本组配置对照
| 配置 | 作用 | 相关示例 |
|---|---|---|
options | 原始值、对象、分组均支持;三种展示使用同一数据源 | 透传配置边界与优先级、交互调度生命周期、复制与编辑 |
edit-mode | 字段展示;未指定时继承 FormItem/Form | 透传配置边界与优先级、复制与编辑 |
trigger-props | 可选的下拉浮层 Trigger 配置;placement 默认 bottom-start,空间不足时自动调整;字段拥有展开状态,不提供顶层 placement | 透传配置边界与优先级 |
open-trigger-target | 打开面板的点击区域;field 为整个字段,icon 仅右侧选择按钮。不影响键盘展开及受控 open。 | 复制与编辑 |
filterable | 开启搜索,可设置 retainInputValue | 复制与编辑 |
建议使用 SelectTriggerOptions 声明 trigger-props。open/defaultOpen、disabled 和展开更新归 Select 管理。popup-container 顶层明确传入时优先(包括 false),否则回退 triggerProps.popupContainer。其他属性保持现有顺序:Trigger 默认配置 → 顶层 auto-fit-popup-width / unmount-on-close → trigger-props;因此旧嵌套配置仍可覆盖后两项。此兼容阶段不改为“顶层永远优先”。内部展开监听继续执行,通知使用 Select 事件。
本次仅新增可主动选用的专用类型,不收窄已发布的宽属性类型,不增加运行时过滤、不改变覆盖顺序;旧业务写法继续可编译。若直接使用旧宽属性,内部托管字段仍可能通过类型检查,但不代表能控制内部状态。
import type { SelectTriggerOptions } from "@cedar-design/ui";
交互调度生命周期
复制与编辑
open-trigger-target="icon" 时,输入区可以编辑或复制,只有右侧选择按钮响应点击打开。关闭手输或搜索后,已有文本仍可复制。
共享字段加载
loading 控制共享字段加载状态;loading-mode 为 default(指示器)或 line(底部不定进度线),默认 default。渲染统一由 FieldDecorator 负责;Select 原有 loading 语义仍会暂停选择和确认,允许搜索。
API
空值识别与阅读空文案
empty-values 用于空值识别,empty-value 保留原清空输出;empty-text 仅在 read-pretty 空内容下展示,默认 '-',字段 > FormItem > Form,自定义 read 插槽优先。显式配置 empty-values 后,内置 required 接入该策略,自定义校验仍收到原值。详见统一说明。
| 参数 | 类型 | 缺省与范围 |
|---|---|---|
| empty-values | readonly unknown[] | 字段 > FormItem > Form > 组件族默认;完整覆盖,不合并 |
| empty-text | string | 字段 > FormItem > Form > '-';显式 '' 有效 |
以上是字段展示层 API;纯面板不因此新增阅读模式。Form 与 FormItem 提供继承配置。empty-value 的现有类型与默认输出仍按该组件原 API 执行。
单选和多选的已选行右侧均显示主色对勾;仅候选高亮时不显示对勾。click/dblclick 编辑面板内的对勾表示当前草稿选择,确认后才提交表单;禁用已选项保持禁用外观。
清空值与 1.x 兼容性
SelectValue 包含 undefined。默认单选清空发出 '';当前候选中有 value: '' 时发出 undefined,用于区分清空与选中空字符串选项。此已发布行为保持不变,change 和 update:modelValue 均使用 SelectValue 类型。
empty-value 指定清空输出;省略时保留原有默认值。单选可使用 () => undefined 明确返回 undefined;多选应返回符合模型形状的数组。存在不可清除的禁用选中项时,优先保留这些项,不执行清空覆盖。受控模式仍需父级接受更新。
根入口与组件入口均导出 SelectValue、SelectEmptyValue。
Select 属性
分别使用 v-model、v-model:open 和 v-model:input-value 控制选中值、展开状态和搜索词。单选清空为 '',多选为 [];Select 存在真实空字符串选项时,清空值为 undefined。
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
open-trigger-target | 打开面板的点击区域;field 为整个字段,icon 仅右侧选择按钮。不影响键盘展开及受控 open。 | 'field' | 'icon' | 'field' |
model-value / default-value | 受控值 / 非受控初始值 | Value | 单选 '',多选 [] |
open / default-open | 受控展开 / 默认展开 | boolean | undefined / false |
input-value / default-input-value | 搜索文本,独立于选中值 | string | undefined / '' |
multiple | 多选;默认启用搜索 | boolean | false |
size | 完整支持 xs、sm、md、lg、xl,未设置时继承分组与表单 | ComponentSize | md |
variant | filled、outline、standard;未设置时继承 InputGroup | FormControlVariant | filled |
disabled / readonly / error | 未设置时继承表单;显式 false 可覆盖 | boolean | undefined |
placeholder | 占位文本 | string | — |
hint / line | 浮动提示 / standard 变体下划线 | boolean | true |
text-color / focus-color / blur-color | 字段颜色 | string | — |
loading | 加载状态;禁止选择和确认 | boolean | false |
loading-mode | 加载布局:默认指示器或底部加载线 | default / line | default |
show-tags | 多选值以标签展示;false 改用分隔文本 | boolean | true |
separator | show-tags=false 时的多选分隔符 | string | , |
text-align | 文字对齐 left / center / right;未设置时继承 FormItem 的 content-align,独立使用默认阅读起点 start | 'left' | 'center' | 'right' | - |
max-tag-count | 标签数量上限,0 不限制 | number | 0 |
tag-nowrap | 标签单行显示;长文字省略、删除图标保留,标签行可横向滚动;悬停、聚焦或点按省略正文查看全文 | boolean | false |
popup-container | 选择器、元素、返回元素的函数或 false(原地渲染) | PopupContainer | — |
trigger-props | 可选的下拉浮层 Trigger 配置;placement 默认 bottom-start,空间不足时自动调整;字段拥有展开状态,不提供顶层 placement | TriggerProps | — |
virtual-list-props | Menu / List 的虚拟列表参数 | VirtualListProps | — |
search-delay | search 事件防抖时长(毫秒) | number | 500 |
format-label | 格式化已选标签;VNode 使用 label 插槽 | function | — |
clearable | 允许清除,保留禁用的已选项 | boolean | true |
empty-value | 清空输出覆盖;多选返回数组,保留不可清除项 | SelectEmptyValue | undefined |
options | 原始值、对象、分组均支持;三种展示使用同一数据源 | SelectOption[] / (query, { signal }) => Array / Promise | undefined |
field-names | value、label、disabled、tagProps、render 字段映射;对象值须有唯一标识字段 | SelectFieldNames | — |
filterable | 开启搜索,可设置 retainInputValue | boolean 或 { retainInputValue?: boolean } | 单选 false / 多选 true |
| case-sensitive | 内置本地过滤是否区分大小写,自定义过滤函数优先 | boolean | false |
filter-option | false 关闭本地过滤;函数接收搜索词和原始选项 | boolean 或 (input, option) => boolean | 自动:数组开启,加载函数关闭 |
creatable | 允许创建不存在的选项 | boolean | false |
fallback-option | 异步选项未到达时反显值,或返回完整回退选项 | boolean 或 (value) => SelectOptionData | true |
show-extra-options | 菜单中展示回退/创建选项 | boolean | true |
limit | 多选数量上限,0 不限制 | number | 0 |
auto-fit-popup-width | Menu 与字段等宽 | boolean | true |
default-active-first-option | 打开选项列表时,无有效选中项则默认激活首个可用候选;仅高亮,不更新 modelValue 或触发 change。已有有效选中项优先激活该项 | boolean | true |
unmount-on-close | 关闭 Menu 后销毁内容 | boolean | false |
scrollbar | Menu / List 滚动条配置 | boolean 或 ScrollbarProps | true |
show-header-on-empty / show-footer-on-empty | 空状态仍展示 header / footer | boolean | false |
edit-mode | 字段展示;未指定时继承 FormItem/Form | direct / click / dblclick | direct |
before-commit | 确认校验后可等待的保存拦截钩子 | (value, context) => void / false / Promise | — |
事件与方法
update:modelValue、change(value):提交时触发,编辑草稿不触发。update:open、open-change(open):请求改变展开状态。update:inputValue、input-value-change(text)、search(text):搜索状态。focus(event)、blur(event)、clear(event):原生字段事件;只有字段所有者通知 Form。- 方法:
focus()、blur()。
Select 还提供 remove(value)、exceed-limit(value, event)、dropdown-scroll(event)、dropdown-reach-bottom(event)。
插槽与选项数据
使用 label({ data }) 自定义已选标签,selected 替换整个已选区域。还支持 prefix、suffix、clear-icon({ clear })、arrow-icon、loading-icon、search-icon、empty、option({ data })。
选项 label 是用于搜索的纯文本;自定义选项内容使用 render 或 option 插槽,自定义反显使用 format-label 或 label 插槽。以 value 而非 label 识别选项。选项的 disabled 禁止选择和删除,tagProps 可定制已选标签。
Select 还支持 header、footer、trigger 和默认插槽中的 Option / Optgroup。Option 属性为 value、label、disabled、tagProps、index;默认插槽提供 selected。分组数据为 { isGroup: true, label, options }。所有编辑展示共用相同选项模型;read 插槽可自定义阅读内容。
单选和多选选项统一使用 popup-option-padding-horizontal。组件没有隐式搜索高亮 DOM;自定义选项插槽负责其高亮片段样式。已删除无效的 multi-popup-option-padding-horizontal 与 popup-option-highlight-* 变量。
read-pretty 为独立布尔属性,与 readonly、disabled 同级;字段 > FormItem > Form 继承,显式 false 覆盖父级。默认 false。纯阅读不显示编辑器。
Option Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| value | 选项值,未传时取 label 或默认插槽文本 | string | number | boolean | Record<string, unknown> | undefined |
| label | 搜索与已选标签使用的文本 | string | undefined |
| disabled | 禁止选择与移除 | boolean | false |
| tag-props | 已选标签属性 | TagProps | undefined |
| index | 手动指定选项顺序 | number | undefined |
Option Slots
| 插槽 | 说明 | 参数 |
|---|---|---|
| default | 自定义选项内容 | { selected: boolean } |
| icon | 选项图标 | - |
| suffix | 选项后缀 | - |
Optgroup Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| label | 分组标题 | string | undefined |
Optgroup Slots
| 插槽 | 说明 | 参数 |
|---|---|---|
| label | 自定义分组标题 | - |
| default | 分组下的 Option 子组件 | - |
Select Slots
| 插槽 | 说明 | 参数 |
|---|---|---|
| default | Option / Optgroup 子组件 | - |
| option | 自定义选项内容 | { data } |
| label | 自定义已选标签 | { data } |
| selected | 替换整个已选区域 | - |
| header | 菜单页头 | - |
| footer | 菜单页脚 | - |
| trigger | 自定义选择器触发器 | - |
| prefix | 字段前缀 | - |
| suffix | 字段后缀 | - |
| clear-icon | 自定义清空操作 | { clear } |
| arrow-icon | 下拉箭头 | - |
| search-icon | 搜索图标 | - |
| loading-icon | 加载图标 | - |
| empty | 无选项内容 | - |
| read | 阅读展示 | { value, text } |
Select Events
| 事件 | 说明 | 参数 |
|---|---|---|
| update:model-value | 更新绑定值 | value |
| change | 提交选择 | value |
| update:open | 请求展开状态 | open |
| open-change | 展开状态改变 | open |
| update:input-value | 更新搜索文本 | text |
| input-value-change | 搜索文本改变 | text |
| search | 搜索通知 | text |
| focus | 字段获得焦点 | event |
| blur | 字段失去焦点 | event |
| clear | 清空选择 | event |
| remove | 移除已选项 | value |
| exceed-limit | 超过多选上限 | value, event |
| dropdown-scroll | 菜单滚动 | event |
| dropdown-reach-bottom | 菜单滚到底部 | event |
Select Methods
| 方法 | 说明 | 参数 |
|---|---|---|
| focus | 聚焦实际字段 | - |
| blur | 移出实际字段焦点 | - |
选中对勾样式变量
| 变量 | 默认值 |
|---|---|
--c-select-popup-option-check-color | var(--c-sys-color-action-primary) |
--c-select-popup-option-check-size | var(--c-sys-font-size-md) |
--c-select-popup-option-check-gap | var(--c-sys-space-4) |
公开类型入口
根入口和组件入口均导出 SelectEmits、SelectSlots、SelectExpose,与 Props、Instance 配套。Emits 为从运行时事件声明推导的参数元组映射;Expose 包含 focus/blur。
内置搜索字段配置
searchFields / search-fields 是只读 ('label' | 'value')[],默认 ['label'];多字段为 OR 匹配,大小写默认不敏感。配置 ['value', 'label'] 可同时搜索编码和名称,fieldNames 将语义字段映射到业务字段;空数组对非空查询不匹配任何项。value 采用字符串比较,不做对象深层搜索,对象值或拼音等复杂规则仍用自定义回调。自定义回调优先并自行决定大小写;远程结果使用原有关闭本地过滤协议。Cascader 保留路径语义,会检查路径各节点的配置字段。
函数属性加载候选
直接传 options(query, { signal }),无需调用 useAsyncData。options 继续接收数组,显式数组(包括 [])优先。组件内部管理 loading、旧结果隔离与请求取消;load-error 通知失败,实例 refresh() 重试。业务函数可只接收 query,忽略第二参数。
filterable 表示允许输入搜索词。filterOption 只控制本地候选匹配,从不发请求。使用 options 加载函数时默认不再二次过滤远程结果;仅传数组时默认启用本地匹配。业务通常无需设置 filter-option。
<c-select v-model="value" :options="loadOptions" filterable />
empty-value 可显式设为 null(或 () => null),清空时原样提交 null;省略时保留组件原默认值。界面把 null 显示为空,不会把普通输入的空字符串自动转成 null。支持弹出编辑的字段中,清空先修改草稿,确认后提交,取消则保留原值。
守卫统一使用 (payload, done, context):关闭 payload 为 { action },切换和提交为 { value, previousValue },Popup 为 { reason }。context 提供 signal 和可选原始 event。返回 boolean/Promise,或调用 done()/done(false);同步 void 等待 done,Promise<void> 放行。首次完成生效。等待期间自动管理 loading/disabled,取消后旧结果失效,不暴露内部按钮 refs。