FieldArray 字段数组
用于管理可添加、删除和排序的重复字段。
用法
添加和删除重复字段
组件结构
加粗的是必需部件。
data-scope="field-array":root · item · item-label · item-content · item-action · add-trigger · item-delete-trigger · move-up-trigger · move-down-trigger
示例
数量限制
设置最少和最多行数
排序
上移或下移字段
多字段行
每行包含多个输入框
设计指引
何时使用
- 联系方式、规格参数、收件人等数量可变的字段。
何时不用
- 行数固定时直接使用普通字段。
- 每项只是短文本时使用标签输入。
特性
min与max限制行数。movable启用上移和下移操作。createItem设置新增行的初始值。- 每行可以包含一个或多个字段。
- 在 Form 中会同步迁移数组子字段的值、规则和错误。
组合
最佳实践
- 新增后将焦点移到新行的第一个输入框。
- 删除按钮应说明目标行。
- 到达数量限制时保持操作按钮可见并禁用。
反模式
- 删除后无法撤销。
- 只在提交时提示数量限制。
API 参考
产物
| 层 | 值 |
|---|---|
| 自定义元素 | <xh-field-array> |
| Vue 组件 | XhFieldArrayAddTrigger XhFieldArrayItem XhFieldArrayItemAction XhFieldArrayItemContent XhFieldArrayItemDeleteTrigger XhFieldArrayItemLabel XhFieldArrayMoveDownTrigger XhFieldArrayMoveUpTrigger XhFieldArrayRoot |
| 组合式函数 | useFieldArray |
| 状态机 | fieldArrayMachine |
| 皮肤 | @xihan-ui/styles/field-array.css |
Props
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
value | unknown[] | 受控数据数组;提供后由宿主决定,状态机不自行修改,只发 onValueChange。 | |
defaultValue | unknown[] | 非受控初始数据数组。 | |
min | number | 最少行数。到达该数值时删除把手不可按下。默认 0。 | |
max | number | 最多行数。到达该数值时新增把手不可按下。默认不限。 | |
createItem | () => unknown | 新增一行时创建一个空项。未提供时插入 null。 | |
movable | boolean | 是否显示换序把手。关闭(默认)时两个换序把手一律收起。 | |
disabled | boolean | 禁用:新增、删除、换序三路都不可按下。 | |
readOnly | boolean | 只读:行数不可修改(新增、删除、换序都不可按下),行内的控件仍由作者自行设置只读。 | |
invalid | boolean | 校验失败标注:写在根与每一行上。 | |
name | FormPath | 整份数组的表单字段名。嵌套在 Form 中时会自动接入其值、规则、错误与校验真源; 每一行经 item.name 获得显式数组 FormPath,不拼接字符串下标。 | |
translations | Partial<FieldArrayTranslations> | ||
onValueChange | (details: FieldArrayValueChangeDetails) => void |
事件
自定义元素将载荷放在 detail;Vue 使用同名 emit。
| 事件 | 载荷 | 说明 |
|---|---|---|
value-change | FieldArrayValueChangeDetails | 数据数组变化;detail 为 { value: unknown[] } |
插槽
仅列出带载荷的插槽。
| Vue 组件 | 插槽 | 载荷 | 说明 |
|---|---|---|---|
XhFieldArrayRoot | default | FieldArrayRootSlotProps |
React 适配器 props
只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。
| React 组件 | 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
XhFieldArrayItem | index | number | string | 是 | 下标由作者声明;兼收字符串,与另外两个适配器的属性口径对齐。 |
XhFieldArrayRoot | children | SlotChildren<FieldArrayRootSlotProps> |
状态
以下名称仅用于内部状态机。
状态:idle
事件:VALUE.SET · ITEM.ADD · ITEM.REMOVE · ITEM.MOVE · FORM.RESET · PRESS.START · PRESS.END
判据:canAdd · canRemove · canMove · canPress
connect API
getXxxProps() 返回对应部件的宿主属性。
| 成员 | 类型 | 说明 |
|---|---|---|
value | unknown[] | |
items | FieldArrayItem[] | 逐行的读侧投影,含渲染用的 key。 |
count | number | |
empty | boolean | |
disabled | boolean | |
readOnly | boolean | |
invalid | boolean | |
movable | boolean | |
atMin | boolean | 已到下限:再删除会少于 min。 |
atMax | boolean | 已到上限:再新增会多于 max。 |
canAdd | boolean | |
setValue | (next: unknown[]) => void | 整份替换,不受 min / max 约束。 |
add | () => void | |
remove | (index: number) => void | |
move | (from: number, to: number) => void | |
moveUp | (index: number) => void | |
moveDown | (index: number) => void | |
getRootProps | () => T['element'] | |
getItemProps | (item: FieldArrayItemProps) => T['element'] | |
getItemLabelProps | (item: FieldArrayItemProps) => T['element'] | 行前的行号或名目:纯标注,不与行内的控件建立 for 关联。 |
getItemContentProps | (item: FieldArrayItemProps) => T['element'] | |
getItemActionProps | (item: FieldArrayItemProps) => T['element'] | |
getAddTriggerProps | () => T['button'] | |
getItemDeleteTriggerProps | (item: FieldArrayItemProps) => T['button'] | |
getMoveUpTriggerProps | (item: FieldArrayItemProps) => T['button'] | |
getMoveDownTriggerProps | (item: FieldArrayItemProps) => T['button'] |
无障碍
键盘
规格出处:W3C APG
| 按键 | 生效条件 | 行为 |
|---|---|---|
Enter / Space | held on add-trigger / item-delete-trigger / move-up-trigger / move-down-trigger, not aria-disabled | 按住期间该把手投影 data-pressed,与指针 :active 同一副按压面;抬起或失焦撤下,删除 / 换序落地后把手随行离场或换位时一并撤下 |
ARIA
以下属性由 connect 生成。
| 部件 | 属性 | 值 |
|---|---|---|
add-trigger | aria-disabled | 'false' | 'true' |
item-delete-trigger | aria-disabled | 'false' | 'true' |
item-delete-trigger | aria-label | label.deleteItem(item.index + 1, count) |
样式参考
皮肤
@xihan-ui/styles/field-array.css 使用 [data-scope="field-array"][data-part="root"] 部件选择器,位于 xihan.components 层。覆盖样式使用 xihan.overrides。
数据属性
由 connect 生成;条件不成立时不输出无值属性。
| 部件 | 属性 | 值 |
|---|---|---|
root | data-at-max | ''(条件成立时才出现) |
root | data-at-min | ''(条件成立时才出现) |
root | data-disabled | ''(条件成立时才出现) |
root | data-empty | ''(条件成立时才出现) |
root | data-invalid | ''(条件成立时才出现) |
root | data-movable | ''(条件成立时才出现) |
root | data-readonly | ''(条件成立时才出现) |
item | data-at-max | ''(条件成立时才出现) |
item | data-at-min | ''(条件成立时才出现) |
item | data-disabled | ''(条件成立时才出现) |
item | data-first | ''(条件成立时才出现) |
item | data-index | String(item.index) |
item | data-invalid | ''(条件成立时才出现) |
item | data-last | ''(条件成立时才出现) |
item | data-readonly | ''(条件成立时才出现) |
item-label | data-at-max | ''(条件成立时才出现) |
item-label | data-at-min | ''(条件成立时才出现) |
item-label | data-disabled | ''(条件成立时才出现) |
item-label | data-index | String(item.index) |
item-label | data-invalid | ''(条件成立时才出现) |
item-label | data-readonly | ''(条件成立时才出现) |
item-content | data-at-max | ''(条件成立时才出现) |
item-content | data-at-min | ''(条件成立时才出现) |
item-content | data-disabled | ''(条件成立时才出现) |
item-content | data-index | String(item.index) |
item-content | data-invalid | ''(条件成立时才出现) |
item-content | data-readonly | ''(条件成立时才出现) |
item-action | data-at-max | ''(条件成立时才出现) |
item-action | data-at-min | ''(条件成立时才出现) |
item-action | data-disabled | ''(条件成立时才出现) |
item-action | data-index | String(item.index) |
item-action | data-invalid | ''(条件成立时才出现) |
item-action | data-readonly | ''(条件成立时才出现) |
add-trigger | data-disabled | ''(条件成立时才出现) |
add-trigger | data-pressed | ''(条件成立时才出现) |
add-trigger | data-xh-action-control | '' |
add-trigger | data-xh-action-display | 'always' |
add-trigger | data-xh-action-profile | 'text' |
add-trigger | data-xh-action-size | 'md' |
add-trigger | data-xh-action-variant | 'outline' |
item-delete-trigger | data-at-max | ''(条件成立时才出现) |
item-delete-trigger | data-at-min | ''(条件成立时才出现) |
item-delete-trigger | data-disabled | ''(条件成立时才出现) |
item-delete-trigger | data-index | String(item.index) |
item-delete-trigger | data-invalid | ''(条件成立时才出现) |
item-delete-trigger | data-pressed | ''(条件成立时才出现) |
item-delete-trigger | data-readonly | ''(条件成立时才出现) |
item-delete-trigger | data-xh-action-control | '' |
item-delete-trigger | data-xh-action-display | 'always' |
item-delete-trigger | data-xh-action-profile | 'icon' |
item-delete-trigger | data-xh-action-size | 'xs' |
item-delete-trigger | data-xh-action-variant | 'ghost' |
move-up-trigger | data-pressed | ''(条件成立时才出现) |
move-up-trigger | data-xh-action-control | '' |
move-up-trigger | data-xh-action-display | 'always' |
move-up-trigger | data-xh-action-profile | 'icon' |
move-up-trigger | data-xh-action-size | 'xs' |
move-up-trigger | data-xh-action-variant | 'ghost' |
move-down-trigger | data-pressed | ''(条件成立时才出现) |
move-down-trigger | data-xh-action-control | '' |
move-down-trigger | data-xh-action-display | 'always' |
move-down-trigger | data-xh-action-profile | 'icon' |
move-down-trigger | data-xh-action-size | 'xs' |
move-down-trigger | data-xh-action-variant | 'ghost' |
CSS 变量
本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。
| 变量 | 部件 | CSS 属性 | 状态 | 默认来源 | 说明 |
|---|---|---|---|---|---|
--xh-field-array-action-gap | add-triggeritem-action | gap | default | --xh-space-1 | field-array 的 add-trigger、item-action 部件 gap 覆盖槽。 |
--xh-field-array-add-bg | add-trigger | background-color | default | --xh-_action-variant-bg-rest | field-array 的 add-trigger 部件 background-color 覆盖槽。 |
--xh-field-array-add-bg-active | add-trigger | background-color | disabledis(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_action-variant-bg-pressed | field-array 的 add-trigger 部件 background-color 覆盖槽。 |
--xh-field-array-add-bg-hover | add-trigger | background-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-bg-hover | field-array 的 add-trigger 部件 background-color 覆盖槽。 |
--xh-field-array-add-border | add-trigger | border | default | --xh-_action-variant-border-rest | field-array 的 add-trigger 部件 border 覆盖槽。 |
--xh-field-array-add-border-disabled | add-trigger | border-color | disabled | --xh-_action-variant-border-disabled | field-array 的 add-trigger 部件 border-color 覆盖槽。 |
--xh-field-array-add-border-hover | add-trigger | border-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-border-hover | field-array 的 add-trigger 部件 border-color 覆盖槽。 |
--xh-field-array-add-fg | add-trigger | color | defaultdisabledhoveris(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-fg-brand | field-array 的 add-trigger 部件 color 覆盖槽。 |
--xh-field-array-add-font-size | add-trigger | font-size | default | --xh-text-label-size | field-array 的 add-trigger 部件 font-size 覆盖槽。 |
--xh-field-array-add-height | add-trigger | block-sizeinline-size | defaultxh-action-profile=icon | --xh-_action-profile-visual-size | field-array 的 add-trigger 部件 block-size、inline-size 覆盖槽。 |
--xh-field-array-add-px | add-trigger | padding-inline | default | --xh-_action-profile-padding-inline | field-array 的 add-trigger 部件 padding-inline 覆盖槽。 |
--xh-field-array-add-radius | add-trigger | border-radius | default | --xh-shape-control | field-array 的 add-trigger 部件 border-radius 覆盖槽。 |
--xh-field-array-content-gap | item-content | gap | default | --xh-space-2 | field-array 的 item-content 部件 gap 覆盖槽。 |
--xh-field-array-gap | root | gap | default | --xh-space-2 | field-array 的 root 部件 gap 覆盖槽。 |
--xh-field-array-icon-size | add-triggeritem-delete-triggermove-down-triggermove-up-trigger | --xh-icon-size | default | --xh-_action-profile-glyph-size | field-array 的 add-trigger、item-delete-trigger、move-down-trigger、move-up-trigger 部件 --xh-icon-size 覆盖槽。 |
--xh-field-array-item-delete-fg-hover | item-delete-trigger | color | disabledhoveris(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-fg-danger-hover | field-array 的 item-delete-trigger 部件 color 覆盖槽。 |
--xh-field-array-item-gap | item | gap | default | --xh-space-2 | field-array 的 item 部件 gap 覆盖槽。 |
--xh-field-array-item-label-fg | item-label | color | default | --xh-fg-muted | field-array 的 item-label 部件 color 覆盖槽。 |
--xh-field-array-item-label-font-size | item-label | font-size | default | --xh-text-secondary-size | field-array 的 item-label 部件 font-size 覆盖槽。 |
--xh-field-array-item-padding | item | padding | default | --xh-space-0 | field-array 的 item 部件 padding 覆盖槽。 |
--xh-field-array-item-radius | item | border-radius | default | --xh-shape-surface | field-array 的 item 部件 border-radius 覆盖槽。 |
--xh-field-array-trigger-bg | item-delete-triggermove-down-triggermove-up-trigger | background-color | default | --xh-_action-variant-bg-rest | field-array 的 item-delete-trigger、move-down-trigger、move-up-trigger 部件 background-color 覆盖槽。 |
--xh-field-array-trigger-bg-active | item-delete-triggermove-down-triggermove-up-trigger | background-color | disabledis(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_action-variant-bg-pressed | field-array 的 item-delete-trigger、move-down-trigger、move-up-trigger 部件 background-color 覆盖槽。 |
--xh-field-array-trigger-bg-hover | item-delete-triggermove-down-triggermove-up-trigger | background-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-bg-hover | field-array 的 item-delete-trigger、move-down-trigger、move-up-trigger 部件 background-color 覆盖槽。 |
--xh-field-array-trigger-fg | item-delete-triggermove-down-triggermove-up-trigger | color | default | --xh-fg-muted | field-array 的 item-delete-trigger、move-down-trigger、move-up-trigger 部件 color 覆盖槽。 |
--xh-field-array-trigger-fg-hover | item-delete-triggermove-down-triggermove-up-trigger | color | disabledhoveris(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-fg-default | field-array 的 item-delete-trigger、move-down-trigger、move-up-trigger 部件 color 覆盖槽。 |
--xh-field-array-trigger-font-size | item-delete-triggermove-down-triggermove-up-trigger | font-size | default | --xh-text-secondary-size | field-array 的 item-delete-trigger、move-down-trigger、move-up-trigger 部件 font-size 覆盖槽。 |
--xh-field-array-trigger-radius | item-delete-triggermove-down-triggermove-up-trigger | border-radius | default | --xh-shape-control | field-array 的 item-delete-trigger、move-down-trigger、move-up-trigger 部件 border-radius 覆盖槽。 |
--xh-field-array-trigger-size | item-delete-triggermove-down-triggermove-up-trigger | block-sizeinline-sizemin-inline-size | defaultxh-action-profile=icon | --xh-_action-profile-visual-size | field-array 的 item-delete-trigger、move-down-trigger、move-up-trigger 部件 block-size、inline-size、min-inline-size 覆盖槽。 |
动效
本组件皮肤不含过渡与关键帧,也没有脚本驱动的动效:状态一变,外观立即到位。
RTL
皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。
