Files
ruoyi-plus-vben5-h/docs/src/components/common-ui/vben-form.md
Caisin 5b84ac5b13 feat(form-ui): support schema valueFormat for getValues payload shaping (#7804)
* 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>
2026-04-13 11:22:04 +08:00

20 KiB
Raw Blame History

outline
outline
deep

Vben Form 表单

框架提供的表单组件,可适配 Element PlusAnt Design VueNaive UI 等框架。

如果文档内没有参数说明,可以尝试在在线示例内寻找

::: info 写在前面

如果你觉得现有组件的封装不够理想,或者不完全符合你的需求,大可以直接使用原生组件,亦或亲手封装一个适合的组件。框架提供的组件并非束缚,使用与否,完全取决于你的需求与自由。

:::

适配器

表单底层使用 vee-validate 进行表单验证,所以你可以使用 vee-validate 的所有功能。对于不同的 UI 框架,我们提供了适配器,以便更好的适配不同的 UI 框架。

适配器说明

每个应用都有不同的 UI 框架,所以在应用的 src/adapter/formsrc/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 属性进行联动。

注意 需要指定 dependenciestriggerFields 属性,设置由谁的改动来触发,以便表单组件能够正确的联动。

自定义组件

如果你的业务组件库没有提供某个组件,你可以自行封装一个组件,然后加到表单内部。

操作

一些常见的表单操作。

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 是否折叠,在showCollapseButtontrue时生效 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格式化后映射到startTimeendTime字段上。每一项的第三个参数是一个可选的格式掩码,

:::

::: 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的值将会被忽略。

:::