Skip to content

Form 表单 ​

新粗野主义风格的表单系统,基于 vee-validate 构建,提供可组合的子组件用于结构化表单布局。

预览 ​

Preview

安装 ​

pnpm dlx brutx-vue@latest add form

需要额外安装依赖:

bash
pnpm add vee-validate @vee-validate/zod zod

用法 ​

vue
<script setup>
import { toTypedSchema } from '@vee-validate/zod'
import { z } from 'zod'
import { Form, FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage } from 'brutx-ui-vue/form'
import { Input, Button } from 'brutx-ui-vue'

const schema = toTypedSchema(z.object({
    username: z.string().min(2).max(50),
    email: z.string().email(),
}))

function onSubmit(values) {
    console.log('Form submitted:', values)
}
</script>

<template>
    <Form :validation-schema="schema" @submit="onSubmit">
        <FormField name="username" v-slot="{ componentField }">
            <FormItem>
                <FormLabel>Username</FormLabel>
                <FormControl>
                    <Input v-bind="componentField" placeholder="Enter username" />
                </FormControl>
                <FormDescription>This is your public display name.</FormDescription>
                <FormMessage />
            </FormItem>
        </FormField>

        <FormField name="email" v-slot="{ componentField }">
            <FormItem>
                <FormLabel>Email</FormLabel>
                <FormControl>
                    <Input v-bind="componentField" type="email" placeholder="you@example.com" />
                </FormControl>
                <FormMessage />
            </FormItem>
        </FormField>

        <Button type="submit" variant="primary">Submit</Button>
    </Form>
</template>

FormWizard 多步骤表单 ​

基于 Stepper 组件构建的向导式表单,支持步骤验证、线性导航和自定义步骤内容。

vue
<script setup>
import { ref } from 'vue'
import { FormWizard } from 'brutx-ui-vue/form'
import type { FormStep } from 'brutx-ui-vue/form'
import { Input } from 'brutx-ui-vue'

const values = ref({})

const steps = [
    { id: 'personal', title: '个人信息' },
    { id: 'address', title: '地址' },
    { id: 'review', title: '确认' },
]

function onComplete(finalValues) {
    console.log('提交:', finalValues)
}
</script>

<template>
    <FormWizard
        v-model="values"
        :steps="steps"
        @complete="onComplete"
    >
        <template #step-personal>
            <Input v-model="values.name" placeholder="姓名" />
        </template>
        <template #step-address>
            <Input v-model="values.address" placeholder="地址" />
        </template>
        <template #step-review>
            <p>确认信息:{{ values }}</p>
        </template>
    </FormWizard>
</template>

FormConditional 条件字段 ​

使用判断函数控制字段组是否挂载。此例通过闭包读取调用方管理的响应式值,并为 Form 提供初始字段值:

vue
<script setup>
import { ref } from 'vue'
import { Form, FormConditional, FormField } from 'brutx-ui-vue/form'

const values = ref({ type: 'company' })
const showCompany = () => values.value.type === 'company'
const showPersonal = () => values.value.type === 'personal'
</script>

<template>
    <Form :initial-values="values">
        <FormField name="type" />

        <FormConditional :when="showCompany">
            <FormField name="companyName" />
            <FormField name="taxId" />
        </FormConditional>

        <FormConditional :when="showPersonal">
            <FormField name="idNumber" />
        </FormConditional>
    </Form>
</template>

绑定 FormControl 与输入控件 ​

FormControl 要求默认插槽只有一个根控件。作用域插槽提供生成的 ID、描述关联、无效状态和表单统一尺寸:

vue
<FormControl v-slot="{ id, ariaDescribedby, ariaInvalid, size }">
    <Input :id="id" :size="size" :aria-describedby="ariaDescribedby" :aria-invalid="ariaInvalid" />
</FormControl>

子组件 ​

组件说明
Form根表单组件,集成 vee-validate
FormField字段包装器,连接表单状态,提供字段上下文
FormItem标签、控件和消息的布局容器,生成唯一 ID,并按表单布局配置排列内容
FormLabel支持错误状态的标签,注入字段上下文
FormControl输入控件的包装器,按表单布局放入控件列,并通过 slot 提供无障碍属性和尺寸
FormDescription输入框下方的辅助文本
FormMessage验证错误消息,注入字段上下文
FormWizard多步骤向导式表单
FormConditional根据表单值动态显示/隐藏字段组

数据类型 ​

FormFieldContext ​

FormField 通过 provide/inject 向子组件提供 FormFieldContext:

ts
interface FormFieldContext {
    name: ComputedRef<string>
    error: Ref<string | undefined>
    value: Ref<unknown>
    setValue: (value: unknown) => void
    setError: (message: string | undefined) => void
}

FormItemContext ​

FormItem 通过 provide/inject 向子组件提供 FormItemContext:

ts
interface FormItemContext {
    id: string
    formItemId: string
    formDescriptionId: string
    formMessageId: string
}

FormControl、FormLabel、FormMessage 均注入 FormFieldContext 和 FormItemContext,可直接访问字段值和错误状态。

FormStep ​

ts
interface FormStep {
    id: string
    title: string
    description?: string
    icon?: Component
    validator?: (values: Record<string, unknown>) => ValidationResult
    optional?: boolean
}

ValidationResult ​

ts
interface ValidationResult {
    valid: boolean
    errors: Record<string, string>
}

组合式函数 ​

useFormWizard ​

在 FormWizard 子组件中获取向导上下文:

ts
const {
    currentStep,    // Ref<number> - 当前步骤
    steps,          // ComputedRef<FormStep[]> - 步骤配置
    values,         // ComputedRef<Record<string, unknown>> - 表单数据
    updateValues,   // (values: Record<string, unknown>) => void - 更新表单数据
    nextStep,       // () => void - 下一步
    previousStep,   // () => void - 上一步
    goToStep,       // (step: number) => void - 跳转步骤
    complete,       // () => void - 完成表单
    getStepErrors,  // (step: number) => Record<string, string> | undefined - 获取指定步骤的错误
    isFirstStep,    // ComputedRef<boolean>
    isLastStep,     // ComputedRef<boolean>
    canGoNext,      // ComputedRef<boolean>
} = useFormWizard()

程序化控制 ​

通过 ref 访问 Form 组件实例后可调用以下方法:

vue
<script setup>
import { ref } from 'vue'
import { Form } from 'brutx-ui-vue/form'

const formRef = ref(null)

async function handleSubmit() {
    const isValid = await formRef.value?.validate()
    if (isValid) {
        // 提交表单
    }
}

function handleReset() {
    formRef.value?.resetFields()
}
</script>

<template>
    <Form ref="formRef" :scroll-to-error="true">
        <!-- 表单字段 -->
    </Form>
</template>

API 参考 ​

Form

Form — 属性
名称类型默认值说明
inline
boolean | undefined
false
让表单项在可换行的横向布局中排列。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
false

来源

Form.vue:9

packages/ui/src/components/form/Form.vue

labelPosition
"right" | "left" | "top" | undefined
"right"
left 与 right 将标签放在控件左侧并分别左对齐或右对齐;top 将标签放在控件上方。
详情

声明类型

"right" | "left" | "top" | undefined

字面量

  • "left"
  • "right"
  • "top"

默认值详情

解析值
"right"

来源

Form.vue:11

packages/ui/src/components/form/Form.vue

labelWidth
string | number | undefined
undefined
横向标签布局的标签列宽度;数值按像素处理,字符串按 CSS 宽度处理,top 布局不使用此值。
详情

声明类型

string | number | undefined

默认值详情

解析值
undefined

来源

Form.vue:13

packages/ui/src/components/form/Form.vue

scrollToError
boolean | undefined
false
调用实例 validate() 且验证失败时,平滑滚动到首个错误字段;原生提交处理不使用此选项。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
false

来源

Form.vue:15

packages/ui/src/components/form/Form.vue

size
"default" | "lg" | "sm" | undefined
"default"
为表单项间距、标签、说明和错误消息提供统一尺寸,并通过 FormControl 插槽向控件提供 size。
详情

声明类型

"default" | "lg" | "sm" | undefined

字面量

  • "default"
  • "lg"
  • "sm"

默认值详情

解析值
"default"

来源

Form.vue:17

packages/ui/src/components/form/Form.vue

class
string | undefined
undefined
合并到原生 form 根元素的自定义 CSS 类。
详情

声明类型

string | undefined

默认值详情

解析值
undefined

来源

Form.vue:18

packages/ui/src/components/form/Form.vue

initialValues
Record<string, unknown> | undefined
undefined
传给 vee-validate 的表单初始字段值;属性引用改变且表单尚未被用户修改时,使用新值重置表单。
详情

声明类型

Record<string, unknown> | undefined

默认值详情

解析值
undefined

来源

Form.vue:19

packages/ui/src/components/form/Form.vue

validationSchema
unknown
undefined
传给 vee-validate 的响应式验证模式,可使用其支持的规则或转换后的 Zod schema。
详情

声明类型

unknown

默认值详情

解析值
undefined

来源

Form.vue:20

packages/ui/src/components/form/Form.vue

Form — 事件
名称签名或类型说明
@submit
[values: Record<string, unknown>]
表单提交且 vee-validate 验证通过后发出字段值。
详情

声明类型

[values: Record<string, unknown>]

来源

Form.vue

packages/ui/src/components/form/Form.vue

Form — 插槽
名称签名或类型说明
#default
{}
表单字段、说明与提交操作,子组件可读取 Form 提供的验证和布局上下文。
详情

声明类型

{}

来源

Form.vue

packages/ui/src/components/form/Form.vue

Form — 暴露成员
名称签名或类型说明
validate
() => Promise<boolean>
验证全部字段并异步返回是否有效;失败且 scrollToError 开启时滚动到第一个错误字段。
详情

声明类型

() => Promise<boolean>

来源

Form.vue:140

packages/ui/src/components/form/Form.vue

validateField
(field: string) => Promise<boolean>
按字段名称验证单个字段,并异步返回该字段是否有效。
详情

声明类型

(field: string) => Promise<boolean>

来源

Form.vue:141

packages/ui/src/components/form/Form.vue

resetFields
() => void
调用 vee-validate 的 resetForm,将字段和表单状态恢复到初始状态。
详情

声明类型

() => void

来源

Form.vue:142

packages/ui/src/components/form/Form.vue

clearValidate
(fields?: string[] | undefined) => void
清除指定字段的验证错误;未传参数或传空数组时清除全部错误,保留当前字段值。
详情

声明类型

(fields?: string[] | undefined) => void

来源

Form.vue:143

packages/ui/src/components/form/Form.vue

scrollToField
(field: string) => void
通过表单控件的 name 查找字段并平滑滚动到其顶部;同名单选组取第一个控件,找不到时不执行滚动。
详情

声明类型

(field: string) => void

来源

Form.vue:144

packages/ui/src/components/form/Form.vue

FormConditional

FormConditional — 属性
名称类型默认值说明
when
必填
(values: Record<string, unknown>) => boolean
—
决定条件内容是否挂载的判断函数,需要在 Form 内使用;建议使用稳定的具名函数引用。
  • 当前实现从表单上下文读取 values.value,缺失时传入空对象;普通的扁平字段记录不会作为完整 values 传入。
详情

声明类型

(values: Record<string, unknown>) => boolean

默认值详情

未声明默认值
—

来源

FormConditional.vue:8

packages/ui/src/components/form/FormConditional.vue

class
string | undefined
undefined
条件为真时合并到包裹内容的 div 元素上的 CSS 类。
详情

声明类型

string | undefined

默认值详情

解析值
undefined

来源

FormConditional.vue:9

packages/ui/src/components/form/FormConditional.vue

FormConditional — 插槽
名称签名或类型说明
#default
{}
条件为真时挂载的字段或其他内容;条件为假时卸载整个内容区域。
详情

声明类型

{}

来源

FormConditional.vue

packages/ui/src/components/form/FormConditional.vue

FormControl

FormControl — 属性
名称类型默认值说明
class
string | undefined
—
与表单控件布局样式合并,并通过 as-child 应用到默认插槽唯一根控件的 CSS 类。
详情

声明类型

string | undefined

默认值详情

未声明默认值
—

来源

FormControl.vue:9

packages/ui/src/components/form/FormControl.vue

FormControl — 插槽
名称签名或类型说明
#default
{ ariaDescribedby: string | undefined; ariaInvalid: true | undefined; size: FormLayoutSize | undefined; class: string; id: string | undefined; }
放置唯一根控件。提供与 FormItem 关联的 id、class、ariaDescribedby、ariaInvalid 和 Form 的 size,可显式绑定到控件;有效错误文本存在时描述关系包含错误消息。
详情

声明类型

{ ariaDescribedby: string | undefined; ariaInvalid: true | undefined; size: FormLayoutSize | undefined; class: string; id: string | undefined; }

关联类型

FormLayoutSize
类型定义 — FormLayoutSize
export type FormLayoutSize = 'sm' | 'default' | 'lg'

来源

FormControl.vue

packages/ui/src/components/form/FormControl.vue

FormDescription

FormDescription — 属性
名称类型默认值说明
class
string | undefined
—
合并到字段辅助说明段落的 CSS 类;段落使用 FormItem 提供的描述 ID。
详情

声明类型

string | undefined

默认值详情

未声明默认值
—

来源

FormDescription.vue:12

packages/ui/src/components/form/FormDescription.vue

FormDescription — 插槽
名称签名或类型说明
#default
{}
字段的辅助说明;未提供此插槽时不渲染说明段落。
详情

声明类型

{}

来源

FormDescription.vue

packages/ui/src/components/form/FormDescription.vue

FormField

FormField — 属性
名称类型默认值说明
name
必填
string
—
向 vee-validate 注册的字段名称,在组件生命周期内应保持稳定;更换名称需要处理旧字段值和错误残留。
详情

声明类型

string

默认值详情

未声明默认值
—

来源

FormField.vue:8

packages/ui/src/components/form/FormField.vue

FormField — 插槽
名称签名或类型说明
#default
{ componentField: { name: string; modelValue: unknown; 'onUpdate:modelValue': (e: unknown, shouldValidate?: boolean | undefined) => void; onFocusout: (event: FocusEvent) => void; }; }
字段内容,通常放置 FormItem。提供 componentField,可通过 v-bind 绑定到控件,传递字段名称、当前值、值更新和失焦校验事件。
详情

声明类型

{ componentField: { name: string; modelValue: unknown; 'onUpdate:modelValue': (e: unknown, shouldValidate?: boolean | undefined) => void; onFocusout: (event: FocusEvent) => void; }; }

来源

FormField.vue

packages/ui/src/components/form/FormField.vue

FormItem

FormItem — 属性
名称类型默认值说明
class
string | undefined
—
合并到字段布局容器的 CSS 类,容器按 Form 的标签位置、宽度和尺寸布局。
详情

声明类型

string | undefined

默认值详情

未声明默认值
—

来源

FormItem.vue:12

packages/ui/src/components/form/FormItem.vue

FormItem — 插槽
名称签名或类型说明
#default
{}
字段标签、控件、说明及错误消息,共享此 FormItem 自动生成的关联 ID。
详情

声明类型

{}

来源

FormItem.vue

packages/ui/src/components/form/FormItem.vue

FormLabel

FormLabel — 属性
名称类型默认值说明
class
string | undefined
—
合并到字段标签的 CSS 类;存在非空错误消息时自动使用错误颜色。
详情

声明类型

string | undefined

默认值详情

未声明默认值
—

来源

FormLabel.vue:9

packages/ui/src/components/form/FormLabel.vue

FormLabel — 插槽
名称签名或类型说明
#default
{}
字段标签内容,通过 for 关联 FormItem 的控件 ID。
详情

声明类型

{}

来源

FormLabel.vue

packages/ui/src/components/form/FormLabel.vue

FormMessage

FormMessage — 属性
名称类型默认值说明
class
string | undefined
—
合并到验证错误段落的 CSS 类;仅当字段错误去除空白后非空时渲染,并使用 role="alert"。
详情

声明类型

string | undefined

默认值详情

未声明默认值
—

来源

FormMessage.vue:12

packages/ui/src/components/form/FormMessage.vue

FormWizard

FormWizard — 属性
名称类型默认值说明
steps
必填
FormStep[]
—
步骤配置列表,包含稳定 id、标题及可选 validator;id 决定 step-{id} 插槽名称。
详情

声明类型

FormStep[]

关联类型

FormStep
类型定义 — FormStep
export interface FormStep {
    id: string
    title: string
    description?: string
    icon?: Component
    /** 入参为全量 values(跨步骤共享的扁平字段集合),约定只返回当前步骤相关字段的错误 */
    validator?: (values: Record<string, unknown>) => ValidationResult
    /** 线性导航下可跳过(不要求完成即可前进),但不豁免 validator */
    optional?: boolean
}

默认值详情

未声明默认值
—

来源

form-wizard-types.ts:20

packages/ui/src/components/form/form-wizard-types.ts

modelValue
Record<string, unknown> | undefined
{ }

按实例调用工厂函数

跨步骤共享的扁平字段记录,通过 v-model 控制;useFormWizard().updateValues() 会浅合并字段补丁后发出更新。
详情

声明类型

Record<string, unknown> | undefined

默认值详情

解析值
{ }

来源

form-wizard-types.ts:21

packages/ui/src/components/form/form-wizard-types.ts

initialStep
number | undefined
0
初始步骤索引,从 0 开始,创建时约束到 steps 的有效范围。
详情

声明类型

number | undefined

默认值详情

解析值
0

来源

form-wizard-types.ts:22

packages/ui/src/components/form/form-wizard-types.ts

validateOnNext
boolean | undefined
true
前进时验证当前步骤;完成时重新验证全部带 validator 的步骤,包括 optional 步骤。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
true

来源

form-wizard-types.ts:23

packages/ui/src/components/form/form-wizard-types.ts

showIndicator
boolean | undefined
true
显示顶部步骤指示器,用户可通过指示器请求导航。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
true

来源

form-wizard-types.ts:24

packages/ui/src/components/form/form-wizard-types.ts

linear
boolean | undefined
true
向前导航前要求目标之前的必选步骤已完成;optional 步骤可跳过,向后导航不受此限制。
详情

声明类型

boolean | undefined

字面量

  • false
  • true

默认值详情

解析值
true

来源

form-wizard-types.ts:25

packages/ui/src/components/form/form-wizard-types.ts

class
string | undefined
undefined
合并到带 role="form" 的向导根容器的 CSS 类。
详情

声明类型

string | undefined

默认值详情

解析值
undefined

来源

form-wizard-types.ts:26

packages/ui/src/components/form/form-wizard-types.ts

FormWizard — 事件
名称签名或类型说明
@update:modelValue
[values: Record<string, unknown>]
通过向导上下文更新字段时,发出与现有数据浅合并后的完整字段记录。
详情

声明类型

[values: Record<string, unknown>]

来源

FormWizard.vue

packages/ui/src/components/form/FormWizard.vue

@complete
[values: Record<string, unknown>]
完成操作通过已启用的步骤验证后发出全部表单数据。
详情

声明类型

[values: Record<string, unknown>]

来源

FormWizard.vue

packages/ui/src/components/form/FormWizard.vue

@step-change
[step: number, previousStep: number]
导航切换或完成校验失败后回到错误步骤时,发出目标与先前的步骤索引,均从 0 开始。
详情

声明类型

[step: number, previousStep: number]

来源

FormWizard.vue

packages/ui/src/components/form/FormWizard.vue

@validation-error
[step: number, errors: Record<string, string>]
步骤 validator 返回无效结果时发出步骤索引及其字段错误记录。
详情

声明类型

[step: number, errors: Record<string, string>]

来源

FormWizard.vue

packages/ui/src/components/form/FormWizard.vue

@navigation-blocked
[targetStep: number, blockedStep: number]
线性导航遇到未完成的必选前置步骤时,发出目标索引和阻塞步骤索引。
详情

声明类型

[targetStep: number, blockedStep: number]

来源

FormWizard.vue

packages/ui/src/components/form/FormWizard.vue

FormWizard — 插槽
名称签名或类型说明
#step-{id}
{ step: FormStep; index: number }
按步骤 id 命名的动态插槽,接收步骤配置与从 0 开始的索引。全部步骤内容保持挂载,通过 v-show 切换当前面板。
详情

声明类型

{ step: FormStep; index: number }

来源

FormWizard.vue:217

packages/ui/src/components/form/FormWizard.vue

可访问性 ​

表单结构 ​

  • FormItem 自动生成唯一 ID,确保 FormLabel、FormControl、FormDescription、FormMessage 之间的关联关系正确。
  • FormLabel 通过 for 属性关联到对应的输入控件。
  • FormControl 通过作用域 slot 提供 id、aria-describedby、aria-invalid 属性,需绑定到内部输入控件以保证无障碍性。

验证错误 ​

  • 验证失败时,FormLabel 自动添加 text-brutal-destructive 样式。
  • FormMessage 使用 role="alert" 确保屏幕阅读器能及时播报错误信息。
  • 输入控件的 aria-invalid 属性会自动设置为 true。

FormWizard 导航 ​

  • 步骤指示器使用 role="tablist" 和 role="tab" 语义。
  • 支持键盘导航(Tab、Enter、Space)。
  • 当 linear 模式启用时,被阻止的步骤会通过 aria-disabled 标识。

常见问题 ​

Q: 提交时没有触发表单验证是怎么回事?

A: 请确认已正确安装并配置了 vee-validate 和 @vee-validate/zod 依赖,并且将 validationSchema 传递给了 Form 组件。同时,每个需要验证的字段必须用 FormField 包裹,并确保 FormField 的 name 属性与 schema 中的字段名一致。

Q: FormWizard 中如何跳过某些步骤的验证?

A: 在 steps 配置中设置 optional: true 可将步骤标记为可选,可选步骤在前进时不会触发验证。如果需要更精细的控制,可以在步骤的 validator 函数中自定义验证逻辑,根据条件返回 { valid: true, errors: {} } 来跳过验证。

Q: FormConditional 的条件函数为什么不生效?

A: FormConditional 的 when 函数接收的参数是当前表单的完整值对象。请确保字段名拼写正确,且表单值已通过 v-model 或 initialValues 正确初始化。如果条件依赖的字段尚未渲染或值为 undefined,条件判断可能会返回意外结果。

蛮力铸就。