* feat(@vben-core/form-ui): support schema valueFormat on getValues Some form fields emit UI-friendly structures such as time-range arrays, while consumers and backend APIs often need a different payload shape. This adds schema-level `valueFormat` hooks so `getValues()` can normalize field output at read time without forcing callers to post-process every submission path. Constraint: Must preserve existing range-time mapping and nested field behavior Constraint: Must not mutate live vee-validate form state while formatting output Rejected: Global formatter config | too coarse for per-field payload shaping Rejected: Post-submit-only transform | misses reset/query/change handlers Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep `getValues()` output derivation side-effect free Directive: Clone raw form values before formatting derived payloads Tested: vitest form-api test for valueFormat and existing getValues paths Tested: oxlint on changed form-ui source and test files Not-tested: Full repo typecheck baseline has unrelated .vue module resolution errors * fix(@vben-core/form-ui): restore mount compatibility and share field path parsing Follow-up review found two real regressions and one missing assertion in the new value formatting flow. `FormApi.mount()` had become breaking by requiring `componentRefMap`, and delete path resolution duplicated field-name parsing instead of sharing the reader grammar. This patch restores backward compatibility, centralizes field-name path parsing, and extends the test to prove formatting does not mutate live form values. Constraint: Must preserve current valueFormat behavior and nested field support Constraint: Must not reintroduce mutation of live vee-validate values Rejected: Keep duplicated delete parsing | risks grammar drift from read path Rejected: Only loosen mount tests | would leave consumer-facing API breakage Confidence: high Scope-risk: narrow Reversibility: clean Directive: Reuse shared field-name parsing for read/delete semantics in form-ui Tested: vitest form-api test suite Tested: oxlint on changed form-ui files Not-tested: Full repo typecheck baseline has unrelated .vue module resolution errors EOF && git push hekx feature-form-value-format * fix(@vben-core/form-ui): clear stale component refs on unmount A follow-up review found that `unmount()` left the private component ref map populated. Because `mount()` now accepts an optional `componentRefMap`, a later mount without a new map could silently reuse stale refs from a prior form instance. This change clears the ref map on unmount and adds a regression test covering remount behavior without a new ref map. Constraint: Must preserve backward-compatible optional `mount()` ref map behavior Constraint: Focus and field-ref lookups must not observe stale refs after unmount Rejected: Clear refs only during next mount | stale state would still leak between lifecycle calls Rejected: Remove mount fallback entirely | would undo the compatibility fix Confidence: high Scope-risk: narrow Reversibility: clean Directive: When mount falls back to internal refs, unmount must always reset that cache Tested: vitest form-api test suite Tested: oxlint on changed form-api source and test files Not-tested: Full repo typecheck baseline has unrelated .vue module resolution errors * refactor(@vben-core/form-ui): trim redundant valueFormat plumbing Review feedback identified a few small cleanups in the value formatting path. This removes an unnecessary shallow clone in `getValues()`, reuses the already-parsed `rawKey` from `resolveFieldNamePath()` instead of re-resolving it in multiple helpers, and clarifies the `FormValueFormat` contract for undefined-as-delete decomposition behavior. Constraint: Must not change runtime valueFormat behavior or payload shape Constraint: Documentation and helper cleanup should stay behavior-preserving Rejected: Leave duplicate raw-key resolution in place | adds needless parsing churn Rejected: Expand the formatter API further | outside the scope of this cleanup Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep read/format helper plumbing lean and avoid duplicate field-name parsing Tested: vitest form-api test suite Tested: oxlint on changed form-ui source and test files Not-tested: Full repo typecheck baseline has unrelated .vue module resolution errors * feat(@vben-core/form-ui): document valueFormat with live examples The new `valueFormat` feature needed a concrete usage path in both the playground and the docs so users can understand how raw component values differ from the final payload returned by `getValues()`. This adds a dedicated form example, wires it into the playground menu, and documents the API with an interactive docs demo. The preview panels now stay in sync when values are set, reset, or submitted. Constraint: Must demonstrate both return-value and setValue decomposition flows Constraint: Example previews must react to setValues, reset, and manual edits Rejected: Only document via markdown snippet | insufficient for verifying live payload behavior Rejected: Reuse an existing basic form page | would bury feature-specific behavior Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep playground and docs demos behaviorally aligned when extending valueFormat examples Tested: eslint on playground/docs valueFormat demo files and route module Tested: oxlint on playground route module Not-tested: Full docs/playground app runtime was not launched in this session * chore(@vben-core/form-ui): normalize valueFormat demo formatting The previous feature/docs commit left a few formatter-only adjustments unstaged after hooks rewrote line wrapping in the new demo and docs pages. This commit captures those final non-behavioral formatting updates so the branch matches the current working tree. Constraint: Must not change runtime behavior or docs meaning Rejected: Leave post-hook diffs unstaged | branch would not reflect local state Confidence: high Scope-risk: narrow Reversibility: clean Directive: After hook-driven rewrites, verify the working tree is clean before final push Tested: Git diff inspection of remaining changes Not-tested: No additional runtime verification needed; formatting-only follow-up EOF && git push hekx feature-form-value-format * fix(@vben-core/form-ui): remove docs demo dayjs dependency The docs valueFormat demo imported `dayjs` directly even though the docs package does not declare it as a dependency. That caused `@vben/docs:build` to fail in CI during VitePress bundling. This change removes the direct import, keeps the preview formatter generic for day-like values, and drops the docs-only preset button that required constructing dayjs instances. Constraint: Docs build must succeed without adding new package dependencies Constraint: Playground example should remain unchanged and fully interactive Rejected: Add dayjs to docs dependencies | unnecessary for a small display demo Rejected: Externalize dayjs in VitePress build | hides a package boundary issue Confidence: high Scope-risk: narrow Reversibility: clean Directive: Docs demos should avoid imports only available through transitive deps Tested: pnpm exec eslint docs/src/demos/vben-form/value-format/index.vue Tested: pnpm --dir docs run build Not-tested: No browser-side manual verification of the docs demo in this session --------- Co-authored-by: caisin <caisin@caisins-Mac-mini.local>
20 KiB
outline
| outline |
|---|
| deep |
Vben Form 表单
框架提供的表单组件,可适配 Element Plus、Ant Design Vue、Naive UI 等框架。
如果文档内没有参数说明,可以尝试在在线示例内寻找
::: info 写在前面
如果你觉得现有组件的封装不够理想,或者不完全符合你的需求,大可以直接使用原生组件,亦或亲手封装一个适合的组件。框架提供的组件并非束缚,使用与否,完全取决于你的需求与自由。
:::
适配器
表单底层使用 vee-validate 进行表单验证,所以你可以使用 vee-validate 的所有功能。对于不同的 UI 框架,我们提供了适配器,以便更好的适配不同的 UI 框架。
适配器说明
每个应用都有不同的 UI 框架,所以在应用的 src/adapter/form 和 src/adapter/component 内部,你可以根据自己的需求,进行组件适配。下面是 Ant Design Vue 的适配器示例代码,可根据注释查看说明:
::: details ant design vue 表单适配器
import type {
VbenFormSchema as FormSchema,
VbenFormProps,
} from '@vben/common-ui';
import type { ComponentType } from './component';
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { initComponentAdapter } from './component';
initComponentAdapter();
setupVbenForm<ComponentType>({
config: {
// ant design vue组件库默认都是 v-model:value
baseModelPropName: 'value',
// 一些组件库空值为 null,重置表单时需要和实际组件行为保持一致
emptyStateValue: null,
// 一些组件是 v-model:checked 或者 v-model:fileList
modelPropNameMap: {
Checkbox: 'checked',
Radio: 'checked',
Switch: 'checked',
Upload: 'fileList',
},
},
defineRules: {
// 输入项目必填国际化适配
required: (value, _params, ctx) => {
if (value === undefined || value === null || value.length === 0) {
return $t('ui.formRules.required', [ctx.label]);
}
return true;
},
// 选择项目必填国际化适配
selectRequired: (value, _params, ctx) => {
if (value === undefined || value === null) {
return $t('ui.formRules.selectRequired', [ctx.label]);
}
return true;
},
},
});
const useVbenForm = useForm<ComponentType>;
export { useVbenForm, z };
export type VbenFormSchema = FormSchema<ComponentType>;
export type { VbenFormProps };
:::
::: details ant design vue 组件适配器
/**
* 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
*/
import type { BaseFormComponentType } from '@vben/common-ui';
import type { Component, SetupContext } from 'vue';
import { h } from 'vue';
import { globalShareState } from '@vben/common-ui';
import { $t } from '@vben/locales';
import {
AutoComplete,
Button,
Checkbox,
CheckboxGroup,
DatePicker,
Divider,
Input,
InputNumber,
InputPassword,
Mentions,
notification,
Radio,
RadioGroup,
RangePicker,
Rate,
Select,
Space,
Switch,
Textarea,
TimePicker,
TreeSelect,
Upload,
} from 'ant-design-vue';
const withDefaultPlaceholder = <T extends Component>(
component: T,
type: 'input' | 'select',
) => {
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
return h(component, { ...props, ...attrs, placeholder }, slots);
};
};
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType =
| 'AutoComplete'
| 'Checkbox'
| 'CheckboxGroup'
| 'DatePicker'
| 'DefaultButton'
| 'Divider'
| 'Input'
| 'InputNumber'
| 'InputPassword'
| 'Mentions'
| 'PrimaryButton'
| 'Radio'
| 'RadioGroup'
| 'RangePicker'
| 'Rate'
| 'Select'
| 'Space'
| 'Switch'
| 'Textarea'
| 'TimePicker'
| 'TreeSelect'
| 'Upload'
| BaseFormComponentType;
async function initComponentAdapter() {
const components: Partial<Record<ComponentType, Component>> = {
// 如果你的组件体积比较大,可以使用异步加载
// Button: () =>
// import('xxx').then((res) => res.Button),
AutoComplete,
Checkbox,
CheckboxGroup,
DatePicker,
// 自定义默认按钮
DefaultButton: (props, { attrs, slots }) => {
return h(Button, { ...props, attrs, type: 'default' }, slots);
},
Divider,
Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮
PrimaryButton: (props, { attrs, slots }) => {
return h(Button, { ...props, attrs, type: 'primary' }, slots);
},
Radio,
RadioGroup,
RangePicker,
Rate,
Select: withDefaultPlaceholder(Select, 'select'),
Space,
Switch,
Textarea: withDefaultPlaceholder(Textarea, 'input'),
TimePicker,
TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
Upload,
};
// 将组件注册到全局共享状态中
globalShareState.setComponents(components);
// 定义全局共享状态中的消息提示
globalShareState.defineMessage({
// 复制成功消息提示
copyPreferencesSuccess: (title, content) => {
notification.success({
description: content,
message: title,
placement: 'bottomRight',
});
},
});
}
export { initComponentAdapter };
:::
基础用法
::: tip README
下方示例代码中的,存在一些国际化、主题色未适配问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。
:::
使用 useVbenForm 创建最基础的表单。
查询表单
查询表单是一种特殊的表单,用于查询数据。查询表单不会触发表单验证,只会触发查询事件。
值格式化
当组件的展示值与后端真正需要的 payload 不一致时,可以在 schema 上使用 valueFormat。它会在 getValues()、提交、以及依赖这些输出的方法中生效。
return xxx:回写当前字段setValue('startTime', xxx):写入其他字段return undefined:保持当前字段已被移除,适合把一个字段拆成多个字段
表单校验
表单校验是一个非常重要的功能,可以通过 rules 属性进行校验。
表单联动
表单联动是一个非常常见的功能,可以通过 dependencies 属性进行联动。
注意 需要指定 dependencies 的 triggerFields 属性,设置由谁的改动来触发,以便表单组件能够正确的联动。
自定义组件
如果你的业务组件库没有提供某个组件,你可以自行封装一个组件,然后加到表单内部。
操作
一些常见的表单操作。
API
useVbenForm 返回一个数组,第一个元素是表单组件,第二个元素是表单的方法。
<script setup lang="ts">
import { useVbenForm } from '#/adapter/form';
// Form 为弹窗组件
// formApi 为弹窗的方法
const [Form, formApi] = useVbenForm({
// 属性
// 事件
});
</script>
<template>
<Form />
</template>
FormApi
useVbenForm 返回的第二个参数,是一个对象,包含了一些表单的方法。
| 方法名 | 描述 | 类型 | 版本号 |
|---|---|---|---|
| submitForm | 提交表单 | (e:Event)=>Promise<Record<string,any>> |
- |
| validateAndSubmitForm | 提交并校验表单 | (e:Event)=>Promise<Record<string,any>> |
- |
| resetForm | 重置表单 | ()=>Promise<void> |
- |
| setValues | 设置表单值, 默认会过滤不在schema中定义的field, 可通过filterFields形参关闭过滤 | (fields: Record<string, any>, filterFields?: boolean, shouldValidate?: boolean) => Promise<void> |
- |
| getValues | 获取表单值 | (fields:Record<string, any>,shouldValidate: boolean = false)=>Promise<void> |
- |
| validate | 表单校验 | ()=>Promise<void> |
- |
| validateField | 校验指定字段 | (fieldName: string)=>Promise<ValidationResult<unknown>> |
- |
| isFieldValid | 检查某个字段是否已通过校验 | (fieldName: string)=>Promise<boolean> |
- |
| resetValidate | 重置表单校验 | ()=>Promise<void> |
- |
| updateSchema | 更新formSchema | (schema:FormSchema[])=>void |
- |
| setFieldValue | 设置字段值 | (field: string, value: any, shouldValidate?: boolean)=>Promise<void> |
- |
| setState | 设置组件状态(props) | (stateOrFn:| ((prev: VbenFormProps) => Partial<VbenFormProps>)| Partial<VbenFormProps>)=>Promise<void> |
- |
| getState | 获取组件状态(props) | ()=>Promise<VbenFormProps> |
- |
| form | 表单对象实例,可以操作表单,见 useForm | - | - |
| getFieldComponentRef | 获取指定字段的组件实例 | <T=unknown>(fieldName: string)=>T |
>5.5.3 |
| getFocusedField | 获取当前已获得焦点的字段 | ()=>string|undefined |
>5.5.3 |
Props
所有属性都可以传入 useVbenForm 的第一个参数中。
| 属性名 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| layout | 表单项布局 | 'horizontal' | 'vertical'| 'inline' |
horizontal |
| showCollapseButton | 是否显示折叠按钮 | boolean |
false |
| wrapperClass | 表单的布局,基于tailwindcss | any |
- |
| actionWrapperClass | 表单操作区域class | any |
- |
| actionLayout | 表单操作按钮位置 | 'newLine' | 'rowEnd' | 'inline' |
rowEnd |
| actionPosition | 表单操作按钮对齐方式 | 'left' | 'center' | 'right' |
right |
| handleReset | 表单重置回调 | (values: Record<string, any>,) => Promise<void> | void |
- |
| handleSubmit | 表单提交回调 | (values: Record<string, any>,) => Promise<void> | void |
- |
| handleValuesChange | 表单值变化回调 | (values: Record<string, any>, fieldsChanged: string[]) => void |
- |
| handleCollapsedChange | 表单收起展开状态变化回调 | (collapsed: boolean) => void |
- |
| actionButtonsReverse | 调换操作按钮位置 | boolean |
false |
| resetButtonOptions | 重置按钮组件参数 | ActionButtonOptions |
- |
| submitButtonOptions | 提交按钮组件参数 | ActionButtonOptions |
- |
| showDefaultActions | 是否显示默认操作按钮 | boolean |
true |
| collapsed | 是否折叠,在showCollapseButton为true时生效 |
boolean |
false |
| collapseTriggerResize | 折叠时,触发resize事件 |
boolean |
false |
| collapsedRows | 折叠时保持的行数 | number |
1 |
| fieldMappingTime | 用于将表单内的数组值映射成 2 个字段 | [string, [string, string],Nullable<string>|[string,string]|((any,string)=>any)?][] |
- |
| commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | FormCommonConfig |
- |
| schema | 表单项的每一项配置 | FormSchema[] |
- |
| submitOnEnter | 按下回车健时提交表单 | boolean |
false |
| submitOnChange | 字段值改变时提交表单(内部防抖,这个属性一般用于表格的搜索表单) | boolean |
false |
| compact | 是否紧凑模式(忽略为校验信息所预留的空间) | boolean |
false |
| scrollToFirstError | 表单验证失败时是否自动滚动到第一个错误字段 | boolean |
false |
::: tip handleValuesChange
handleValuesChange 回调函数的第一个参数values装载了表单改变后的当前值对象,第二个参数fieldsChanged是一个数组,包含了所有被改变的字段名。注意:第二个参数仅在v5.5.4(不含)以上版本可用,并且传递的是已在schema中定义的字段名。如果你使用了字段映射并且需要检查是哪些字段发生了变化的话,请注意该参数并不会包含映射后的字段名。
:::
::: tip fieldMappingTime
此属性用于将表单内的数组值映射成 2 个字段,它应当传入一个数组,数组的每一项是一个映射规则,规则的第一个成员是一个字符串,表示需要映射的字段名,第二个成员是一个数组,表示映射后的字段名,第三个成员是一个可选的格式掩码,用于格式化日期时间字段;也可以提供一个格式化函数(参数分别为当前值和当前字段名,返回格式化后的值)。如果明确地将格式掩码设为null,则原值映射而不进行格式化(适用于非日期时间字段)。例如:[['timeRange', ['startTime', 'endTime'], 'YYYY-MM-DD']],timeRange应当是一个至少具有2个成员的数组类型的值。Form会将timeRange的值前两个值分别按照格式掩码YYYY-MM-DD格式化后映射到startTime和endTime字段上。每一项的第三个参数是一个可选的格式掩码,
:::
::: tip valueFormat
valueFormat 适合处理“组件值”和“提交值”不一致的场景。例如:
RangePicker返回[dayjs, dayjs],但后端需要{ startTime, endTime }DatePicker返回dayjs,但后端只需要时间戳
valueFormat 会在 getValues() 过程中执行:
- 返回
undefined:当前字段保持删除状态 - 返回其他值:回写当前字段
- 调用
setValue(key, nextValue):写入一个或多个新字段
{
component: 'RangePicker',
fieldName: 'reportRange',
valueFormat(value, setValue) {
setValue('startTime', value?.[0]?.valueOf());
setValue('endTime', value?.[1]?.valueOf());
},
}
:::
TS 类型说明
::: details ActionButtonOptions
export interface ActionButtonOptions {
/** 样式 */
class?: ClassType;
/** 是否禁用 */
disabled?: boolean;
/** 是否加载中 */
loading?: boolean;
/** 按钮大小 */
size?: ButtonVariantSize;
/** 按钮类型 */
variant?: ButtonVariants;
/** 是否显示 */
show?: boolean;
/** 按钮文本 */
content?: string;
/** 任意属性 */
[key: string]: any;
}
:::
::: details FormCommonConfig
export interface FormCommonConfig {
/**
* 所有表单项的props
*/
componentProps?: ComponentProps;
/**
* 所有表单项的控件样式
*/
controlClass?: string;
/**
* 在表单项的Label后显示一个冒号
*/
colon?: boolean;
/**
* 所有表单项的禁用状态
* @default false
*/
disabled?: boolean;
/**
* 所有表单项的控件样式
* @default {}
*/
formFieldProps?: Partial<typeof Field>;
/**
* 所有表单项的栅格布局
* @default ""
*/
formItemClass?: (() => string) | string;
/**
* 隐藏所有表单项label
* @default false
*/
hideLabel?: boolean;
/**
* 是否隐藏必填标记
* @default false
*/
hideRequiredMark?: boolean;
/**
* 所有表单项的label样式
* @default ""
*/
labelClass?: string;
/**
* 所有表单项的label宽度
*/
labelWidth?: number;
/**
* 所有表单项的model属性名。使用自定义组件时可通过此配置指定组件的model属性名。已经在modelPropNameMap中注册的组件不受此配置影响
* @default "modelValue"
*/
modelPropName?: string;
/**
* 所有表单项的wrapper样式
*/
wrapperClass?: string;
}
:::
::: details FormSchema
export interface FormSchema<
T extends BaseFormComponentType = BaseFormComponentType,
> extends FormCommonConfig {
/** 组件 */
component: Component | T;
/** 组件参数 */
componentProps?: ComponentProps;
/** 默认值 */
defaultValue?: any;
/** 依赖 */
dependencies?: FormItemDependencies;
/** 描述 */
description?: string;
/** 字段名,也作为自定义插槽的名称 */
fieldName: string;
/** 帮助信息 */
help?: CustomRenderType;
/** 是否隐藏表单项 */
hide?: boolean;
/** 表单的标签(如果是一个string,会用于默认必选规则的消息提示) */
label?: CustomRenderType;
/** 自定义组件内部渲染 */
renderComponentContent?: RenderComponentContentType;
/** 字段规则 */
rules?: FormSchemaRuleType;
/** 后缀 */
suffix?: CustomRenderType;
/** 获取 getValues() 输出时格式化当前字段 */
valueFormat?: FormValueFormat;
}
:::
::: details FormValueFormat
type FormValueFormat = (
value: any,
setValue: (fieldName: string, value: any) => void,
values: Record<string, any>,
) => any;
- 返回
undefined:保持当前字段已被移除 - 返回其他值:将当前字段恢复/写回为该值
setValue(fieldName, value):用于把一个字段拆分写入其他字段
:::
表单联动
表单联动需要通过 schema 内的 dependencies 属性进行联动,允许您添加字段之间的依赖项,以根据其他字段的值控制字段。
dependencies: {
// 触发字段。只有这些字段值变动时,联动才会触发
triggerFields: ['name'],
// 动态判断当前字段是否需要显示,不显示则直接销毁
if(values,formApi){},
// 动态判断当前字段是否需要显示,不显示用css隐藏
show(values,formApi){},
// 动态判断当前字段是否需要禁用
disabled(values,formApi){},
// 字段变更时,都会触发该函数
trigger(values,formApi){},
// 动态rules
rules(values,formApi){},
// 动态必填
required(values,formApi){},
// 动态组件参数
componentProps(values,formApi){},
}
表单校验
表单校验需要通过 schema 内的 rules 属性进行配置。
rules的值可以是字符串(预定义的校验规则名称),也可以是一个zod的schema。
预定义的校验规则
// 表示字段必填,默认会根据适配器的required进行国际化
{
rules: 'required';
}
// 表示字段必填,默认会根据适配器的required进行国际化,用于下拉选择之类
{
rules: 'selectRequired';
}
zod
rules也支持 zod 的 schema,可以进行更复杂的校验,zod 的使用请查看 zod文档。
import { z } from '#/adapter/form';
// 基础类型
{
rules: z.string().min(1, { message: '请输入字符串' });
}
// 可选(可以是undefined),并且携带默认值。注意zod的optional不包括空字符串''
{
rules: z.string().default('默认值').optional();
}
// 可以是空字符串、undefined或者一个邮箱地址(两种不同的用法)
{
rules: z.union([z.string().email().optional(), z.literal('')]);
}
{
rules: z.string().email().or(z.literal('')).optional();
}
// 复杂校验
{
z.string()
.min(1, { message: '请输入' })
.refine((value) => value === '123', {
message: '值必须为123',
});
}
Slots
可以使用以下插槽在表单中插入自定义的内容
| 插槽名 | 描述 |
|---|---|
| reset-before | 重置按钮之前的位置 |
| submit-before | 提交按钮之前的位置 |
| expand-before | 展开按钮之前的位置 |
| expand-after | 展开按钮之后的位置 |
::: tip 字段插槽
除了以上内置插槽之外,schema属性中每个字段的fieldName都可以作为插槽名称,这些字段插槽的优先级高于component定义的组件。也就是说,当提供了与fieldName同名的插槽时,这些插槽的内容将会作为这些字段的组件,此时component的值将会被忽略。
:::