Object 对象

View source
afz.object() 的调用方式、嵌套结构与额外字段处理策略。
两种调用方式的区别
// 直接调用
afz.object(shape, meta?)

// 柯里化调用(类型参数 `<T>` 仅用于 IDE 提示,不影响运行时验证。)
afz.object<T>()(shape, meta?)
const schema = afz.object({
  name: afz.string(),
  age: afz.number()
})

通用对象

通用对象类型通常与 selectMenu 结合使用,用于选择复杂数据结构:

<script lang="ts" setup>
import type { FormSubmitEvent } from '@nuxt/ui'
import type { z } from 'zod'

const { afz } = useAutoForm()
const toast = useToast()

const { data: users, execute, pending } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
  key: 'typicode-users-basic',
  immediate: false,
  transform: (data: { id: number, name: string }[]) => {
    return data?.map(user => ({
      label: user.name,
      value: String(user.id),
      avatar: { src: `https://i.pravatar.cc/120?img=${user.id}`, loading: 'lazy' as const }
    }))
  }
})

const schema = afz.object({
  name: afz.string().min(1, '请输入姓名').default('张三'),
  age: afz.number().min(1).max(150).default(30),
  email: afz.email('请输入有效的邮箱地址').default('zhangsan@example.com'),
  user: afz.object({
    label: afz.string(),
    value: afz.string(),
    avatar: afz.object({ src: afz.url() })
  }, {
    type: 'selectMenu',
    controlProps: ({ value }: { value: { avatar: { src: string } } }) => ({
      'placeholder': '请选择用户',
      'items': users.value || [],
      'avatar': value?.avatar,
      'loading': pending.value,
      'onUpdate:open': () => {
        if (!users.value && !pending.value) {
          execute()
        }
      }
    })
  })
})

async function onSubmit(event: FormSubmitEvent<z.output<typeof schema>>) {
  toast.add({
    title: '提交成功',
    color: 'success',
    description: JSON.stringify(event.data, null, 2)
  })
}
</script>

<template>
  <MAutoForm :schema="schema" @submit="onSubmit" />
</template>

嵌套对象

嵌套对象结构,支持折叠展开:

<script lang="ts" setup>
import type { FormSubmitEvent } from '@nuxt/ui'
import type { z } from 'zod'

const { afz } = useAutoForm()
const toast = useToast()

const schema = afz.object({
  user: afz.object({
    name: afz.string(),
    email: afz.email()
  }).meta({ label: '用户信息', collapsible: { defaultOpen: true } }),
  address: afz.object({
    street: afz.string(),
    city: afz.string(),
    zipCode: afz.string()
  }).optional().meta({ label: '地址信息' })
})

async function onSubmit(event: FormSubmitEvent<z.output<typeof schema>>) {
  toast.add({
    title: 'Success',
    color: 'success',
    description: JSON.stringify(event.data, null, 2)
  })
}
</script>

<template>
  <MAutoForm :schema="schema" @submit="onSubmit" />
</template>

类型化对象

使用 TypeScript 接口约束对象结构,在定义 schema 时获得字段名的 IDE 提示:
选择对象类型:
<script lang="ts" setup>
const { afz } = useAutoForm()
const toast = useToast()

interface UserInfo {
  name: string
  age: number
  email: string
}

const userSchema = {
  name: afz.string().min(1, '请输入姓名').default('张三'),
  age: afz.number().min(1).max(150).default(18),
  email: afz.email('请输入有效的邮箱地址').default('example@example.com')
}

const testUser = {
  name: '张三',
  age: 28,
  email: 'test@example.com',
  extraField: '我是额外字段'
}

const normalSchema = afz.object<UserInfo>()(userSchema)
const looseSchema = afz.looseObject<UserInfo>()(userSchema)
const strictSchema = afz.strictObject<UserInfo>()(userSchema)

const currentType = ref<'normal' | 'loose' | 'strict'>('normal')
const items = [
  { label: 'Normal', value: 'normal' },
  { label: 'Loose', value: 'loose' },
  { label: 'Strict', value: 'strict' }
]

const schema = computed(() => {
  switch (currentType.value) {
    case 'loose': return looseSchema
    case 'strict': return strictSchema
    default: return normalSchema
  }
})

async function click() {
  const parsed = schema.value.safeParse(testUser)
  if (!parsed.success) {
    toast.add({
      title: '验证失败',
      color: 'error',
      description: JSON.stringify(parsed.error.issues, null, 2)
    })
  } else {
    toast.add({
      title: '验证成功',
      color: 'success',
      description: JSON.stringify(parsed.data, null, 2)
    })
  }
}
</script>

<template>
  <UCard class="space-y-4">
    <template #header>
      <div class="flex items-center gap-2">
        <span class="text-sm font-medium">选择对象类型:</span>
        <USelect v-model="currentType" :items="items" />
      </div>
    </template>

    <MAutoForm :schema="schema" :submit="false">
      <template #footer>
        <UButton color="primary" @click="click">
          验证类型
        </UButton>
      </template>
    </MAutoForm>
  </UCard>
</template>
三种对象工厂对应不同的额外字段处理策略:
const schema = afz.object({ name: afz.string() })

schema.parse({ name: '张三', extra: 'removed' })
// 结果:{ name: '张三' }
Copyright © 2025 - 2026 YiXuan - MIT License