AutoForm

Legacy

根据 Zod 模式自动生成表单。

什么是 AutoForm

AutoForm 是一个即插即用的表单构建器,适用于已有 Zod 模式的内部和低优先级表单。例如,如果您已为 API 准备了 Zod 模式,并希望创建一个简单的管理面板来编辑用户资料,只需将模式传递给 AutoForm 即可完成。

安装

运行以下命令

pnpm dlx shadcn-vue@latest update form
npx shadcn-vue@latest add auto-form

字段类型

目前,以下字段类型已支持开箱即用:

  • 布尔值(复选框、开关)
  • 日期(日期选择器)
  • 枚举(选择器、单选框组)
  • 数字(输入框)
  • 字符串(输入框、文本域)
  • 文件(文件)

您可以通过在 auto-form/constants.ts 中的 INPUT_COMPONENTS 对象中添加其他字段类型来扩展支持。

Zod 配置

验证

表单模式可以使用 Zod 的任何验证方法,包括 refine。

描述

您可以使用 describe 方法为每个字段设置标签。如果未设置标签,将使用字段名并取消驼峰命名。

ts
const formSchema = z.object({
  username: z.string().describe('您的用户名'),
  someValue: z.string(), // 将显示为 "Some Value"
})

您也可以通过 fieldConfig 配置标签。

可选字段

默认情况下,所有字段均为必填。您可以使用 optional 方法使字段变为可选。

ts
const formSchema = z.object({
  username: z.string().optional(),
})

默认值

您可以使用 default 方法为字段设置默认值。

ts
const formSchema = z.object({
  favouriteNumber: z.number().default(5),
})

如果要设置日期的默认值,请先使用 new Date(val) 将其转换为 Date 对象。

子对象

您可以嵌套对象以创建可折叠的部分。

ts
const formSchema = z.object({
  address: z.object({
    street: z.string(),
    city: z.string(),
    zip: z.string(),

    // 您可以按需任意深度嵌套对象
    nested: z.object({
      foo: z.string(),
      bar: z.string(),

      nested: z.object({
        foo: z.string(),
        bar: z.string(),
      }),
    }),
  }),
})

与普通对象一样,您可以使用 describe 方法为部分设置标签和描述:

ts
const formSchema = z.object({
  address: z
    .object({
      street: z.string(),
      city: z.string(),
      zip: z.string(),
    })
    .describe('您的地址'),
})

选择/枚举

AutoForm 支持 enumnativeEnum 来创建选择字段。

ts
const formSchema = z.object({
  color: z.enum(['red', 'green', 'blue']),
})

enum BreadTypes {
  // 对于原生枚举,您也可以定义带背书的枚举以设置自定义标签
  White = '白面包',
  Brown = '棕面包',
  Wholegrain = '全麦面包',
  Other,
}
// 请注意,Zod 将验证并返回枚举标签,而不是枚举值!
const formSchema = z.object({
  bread: z.nativeEnum(BreadTypes),
})

数组

AutoForm 支持对象数组。由于从字符串/数字等数组推断字段标签等元素较为困难,因此仅支持对象数组。

ts
const formSchema = z.object({
  guestListName: z.string(),
  invitedGuests: z
    .array(
      // 为每个项目定义字段
      z.object({
        name: z.string(),
        age: z.number(),
      })
    )
    // 可选择设置自定义标签 - 否则将从字段名推断
    .describe('受邀参加派对的客人'),
})

数组不支持作为表单模式的根元素。

您也可以使用 .default() 设置数组的默认值,但请确保数组元素与模式结构相同。

ts
const formSchema = z.object({
  guestListName: z.string(),
  invitedGuests: z
    .array(
      // 为每个项目定义字段
      z.object({
        name: z.string(),
        age: z.number(),
      })
    )
    .describe('受邀参加派对的客人')
    .default([
      { name: 'John', age: 24, },
      { name: 'Jane', age: 20, },
    ]),
})

字段配置

由于 Zod 不允许向模式添加其他属性,您可以使用 fieldConfig 属性为每个字段的 UI 添加额外配置。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        // 字段配置
      },
    }"
  />
</template>

标签

如果您想覆盖通过 Zod 描述 预定义的标签,可以使用 label 属性自定义标签。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        label: '自定义用户名',
      },
    }"
  />
</template>

描述

您可以使用 description 属性在字段下方添加描述。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        description: '输入一个唯一的用户名。这将显示给其他用户。',
      },
    }"
  />
</template>

输入属性

您可以使用 inputProps 属性向输入组件传递属性。可以使用 HTML 组件接受的任何属性。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        inputProps: {
          type: 'text',
          placeholder: '用户名',
        },
      },
    }"
  />
</template>

// 这将渲染为:
<input type="text" placeholder="用户名" />

可以通过在 inputProps 中使用 showLabel 属性来禁用输入的标签。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        inputProps: {
          type: 'text',
          placeholder: '用户名',
          showLabel: false,
        },
      },
    }"
  />
</template>

组件

默认情况下,AutoForm 将使用 Zod 类型来确定使用哪个输入组件。您可以通过 component 属性覆盖此行为。

vue
<template>
  <AutoForm
    :field-config="{
      acceptTerms: {
        // 布尔值默认使用复选框,改用开关
        component: 'switch',
      },
    }"
  />
</template>

支持的字段类型的完整列表已类型化。当前支持的类型包括:

  • checkbox(布尔值默认)
  • switch
  • date(日期默认)
  • select(枚举默认)
  • radio
  • textarea

或者,您可以将 Vue 组件传递给 component 属性以使用自定义组件。

CustomField.vue

vue
<script setup lang="ts">
import type { FieldProps } from './interface'
import { computed } from 'vue'
import { AutoFormLabel } from '@/ui/auto-form'
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/ui/form'
import { Input } from '@/ui/input'
import AutoFormLabel from './AutoFormLabel.vue'

const props = defineProps<FieldProps>()
</script>

<template>
  <FormField v-slot="slotProps" :name="fieldName">
    <FormItem v-bind="$attrs">
      <AutoFormLabel v-if="!config?.hideLabel" :required="required">
        {{ config?.label }}
      </AutoFormLabel>
      <FormControl>
        <CustomInput v-bind="slotProps" />
      </FormControl>
      <FormDescription v-if="config?.description">
        {{ config.description }}
      </FormDescription>
      <FormMessage />
    </FormItem>
  </FormField>
</template>

fieldConfig 中传递上述组件。

vue
<template>
  <AutoForm
    :field-config="{
      username: {
        component: CustomField,
      },
    }"
  />
</template>

命名插槽

您可以使用 Vue 命名插槽来自定义渲染的 AutoFormField

vue
<template>
  <AutoForm
    :field-config="{
      customParent: {
        label: '包装器',
      },
    }"
  >
    <template #customParent="slotProps">
      <div class="flex items-end space-x-2">
        <AutoFormField v-bind="slotProps" class="w-full" />
        <Button type="button">
          检查
        </Button>
      </div>
    </template>
  </AutoForm>
</template>

访问表单数据

有两种方式可以访问表单数据:

@submit

首选方式是使用 submit 事件。当表单提交且数据有效时,将调用此事件。

vue
<template>
  <AutoForm
    @submit="(data) => {
      // 对数据进行操作
    }"
  />
</template>

受控表单

通过将 form 作为属性传递,您可以控制和使用 Form 提供的方法。

vue
<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import * as z from 'zod'
import { AutoForm } from '@/components/ui/auto-form'

const schema = z.object({
  username: z.string(),
})
const form = useForm({
  validationSchema: toTypedSchema(schema),
})

form.setFieldValue('username', 'bar')
</script>

<template>
  <AutoForm :form="form" :schema="schema" />
</template>

提交表单

您可以使用任何 button 组件创建提交按钮。最重要的是添加属性 type="submit"

vue
<template>
  <AutoForm>
    <CustomButton type="submit">
      立即发送
    </CustomButton>
  </AutoForm>

  // 或
  <AutoForm>
    <button type="submit">
      立即发送
    </button>
  </AutoForm>
</template>

添加其他元素

传递给 AutoForm 组件的所有子元素将渲染在表单下方。

vue
<template>
  <AutoForm>
    <Button>立即发送</Button>
    <p class="text-gray-500 text-sm">
      提交此表单即表示您同意我们的
      <a href="#" class="text-primary underline">
        条款和条件
      </a>。
    </p>
  </AutoForm>
</template>

依赖关系

AutoForm 允许您在字段之间添加依赖关系,以根据其他字段的值控制字段。为此,可以向 AutoForm 组件传递一个 dependencies 数组。

vue
<template>
  <AutoForm
    :dependencies="[
      {
        // 'age' 字段在年龄为 18 岁或以上时隐藏 'parentsAllowed' 字段
        sourceField: 'age',
        type: DependencyType.HIDES,
        targetField: 'parentsAllowed',
        when: age => age >= 18,
      },
      {
        // 'vegetarian' 复选框从 'mealOptions' 中隐藏 'Beef Wellington' 选项
        // 如果尚未选中
        sourceField: 'vegetarian',
        type: DependencyType.SETS_OPTIONS,
        targetField: 'mealOptions',
        when: (vegetarian, mealOption) =>
          vegetarian && mealOption !== 'Beef Wellington',
        options: ['Pasta', 'Salad'],
      },
    ]"
  />
</template>

支持以下依赖类型:

  • DependencyType.HIDES:当 when 函数返回 true 时隐藏目标字段
  • DependencyType.DISABLES:当 when 函数返回 true 时禁用目标字段
  • DependencyType.REQUIRES:当 when 函数返回 true 时将目标字段设置为必填
  • DependencyType.SETS_OPTIONS:当 when 函数返回 true 时将目标字段的选项设置为 options 数组

when 函数使用源字段的值和目标字段的值调用,并应返回一个布尔值以指示是否应用依赖关系。

请注意,当返回 false 时,依赖关系不会导致相反的操作 - 例如,如果您在 Zod 模式中将字段标记为必填(即未显式设置 optional),在 REQUIRES 依赖中返回 false 不会将其标记为可选。您应改用 Zod 的 optional 方法将其默认标记为可选,并使用 REQUIRES 依赖在满足依赖时将其标记为必填。

请注意,依赖关系对表单的验证没有任何影响。您应使用 Zod 的 refine 方法来根据其他字段的值验证表单。

您可以为同一字段和依赖类型创建多个依赖关系 - 例如,根据多个其他字段隐藏一个字段。当满足任何依赖关系时,这将隐藏该字段。

示例

基础

Your favourite number between 1 and 10.

I agree to the .

We need your birthday to send you a gift.

无标签输入

此示例展示如何使用无标签的 AutoForm 输入。

子对象

根据 Zod 模式自动生成表单。

受控

此示例展示如何以受控方式使用 AutoForm。

确认密码

使用精炼模式验证两个字段是否匹配。

API 示例

表单选择选项从 API 获取。

Loading...

数组支持

您可以在模式中使用数组以创建动态表单。

依赖关系

在字段之间创建依赖关系。

Setting this below 18 will require parents consent.

Setting this to true will remove non-vegetarian food options.

Edit this page on GitHub