内容级插槽

View source
内容插槽提供更深层次的自定义能力,允许你完全接管对象和数组字段的渲染逻辑。

可用插槽

内容插槽(field-content, field-before, field-after仅适用于对象字段和数组字段,不适用于基础类型字段(如 string, number, boolean 等)。
基础类型字段请使用 field-default 插槽来自定义输入控件。
NameDescription
field-before:{path}在字段渲染之前插入自定义内容,不影响字段本身的渲染
field-after:{path}在字段渲染之后插入自定义内容,不影响字段本身的渲染
field-content:{path}完全替换字段的默认渲染逻辑

使用方式

field-content

完全替换对象或数组字段的默认渲染,适用于需要完全自定义字段内容的场景:

email 通知

接收邮件通知

sms 通知

接收短信通知

push 通知

接收推送通知

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

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

const schema = afz.object({
  email: afz.email().meta({ label: '邮箱' }).default('test@example.com'),
  notifications: afz.object({
    email: afz.boolean().meta({ label: 'Email 通知' }),
    sms: afz.boolean().meta({ label: 'SMS 通知' }),
    push: afz.boolean().meta({ label: 'Push 通知' })
  }).meta({ label: '通知设置' })
})

type Schema = z.output<typeof schema>

const form = ref<Partial<Schema>>({})

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

<template>
  <UCard>
    <MAutoForm
      :schema="schema"
      :state="form"
      :global-meta="{ collapsible: { defaultOpen: true } }"
      @submit="onSubmit"
    >
      <template #field-default:email="{ value, error, setValue }">
        <UInput
          :model-value="value"
          type="email"
          placeholder="your@email.com"
          icon="i-lucide-mail"
          class="w-full"
          :trailing-icon="value ? 'i-lucide-circle-check' : undefined"
          :color="error ? 'error' : 'primary'"
          @update:model-value="setValue"
        />
      </template>

      <template #field-content:notifications="{ value, setValue }">
        <UCard>
          <div class="space-y-3">
            <div
              v-for="key in ['email', 'sms', 'push']"
              :key="key"
              class="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors cursor-pointer"
              @click="setValue(key, !value?.[key as keyof typeof value])"
            >
              <div class="flex items-center gap-3">
                <UIcon
                  :name="key === 'email' ? 'i-lucide-mail' : key === 'sms' ? 'i-lucide-message-square' : 'i-lucide-bell'"
                  class="size-5"
                />
                <div>
                  <p class="font-medium text-sm capitalize">
                    {{ key }} 通知
                  </p>
                  <p class="text-xs text-gray-500">
                    {{ key === 'email' ? '接收邮件通知' : key === 'sms' ? '接收短信通知' : '接收推送通知' }}
                  </p>
                </div>
              </div>
              <UCheckbox disabled :model-value="value?.[key as keyof typeof value]" />
            </div>
          </div>
        </UCard>
      </template>
    </MAutoForm>
  </UCard>
</template>

field-before

在字段渲染之前插入内容,常用于添加分隔线、标题或说明信息:

如果字段通过 type 覆盖了默认控件(如数组字段使用 inputTags),field-before 插槽将不会生效。

0 项已添加技能

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

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

const schema = afz.object({
  profile: afz.object({
    firstName: afz.string().meta({ label: '' }),
    lastName: afz.string().meta({ label: '' }),
    age: afz.number().min(0).meta({ label: '年龄' })
  }).meta({ label: '个人资料' }),
  skills: afz.array(afz.string(), {
    type: 'inputTags',
    controlProps: { icon: 'i-lucide-briefcase' }
  })
    .default(['编程', '设计'])
    .meta({ label: '技能列表' })
})

type Schema = z.output<typeof schema>

const form = ref<Partial<Schema>>({})

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

<template>
  <UCard>
    <MAutoForm
      :global-meta="{ collapsible: { defaultOpen: true } }"
      :schema="schema"
      :state="form"
      @submit="onSubmit"
    >
      <template #field-before:profile>
        <USeparator icon="i-lucide-circle-user" label="个人资料" />
      </template>

      <template #field-description:skills="{ value }">
        {{ value?.length || 0 }} 项已添加技能
      </template>
    </MAutoForm>
  </UCard>
</template>

field-after

在字段渲染之后插入内容,常用于添加提示信息、统计数据或验证反馈:

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

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

const schema = afz.object({
  address: afz.object({
    street: afz.string().meta({ label: '街道' }),
    city: afz.string().meta({ label: '城市' }),
    zipCode: afz.string().meta({ label: '邮编' })
  }).meta({ label: '地址信息', collapsible: { defaultOpen: true } }),

  skills: afz.array(afz.string(), {
    type: 'inputTags',
    controlProps: { icon: 'i-lucide-briefcase' }
  })
    .default(['Vue', 'TypeScript', 'Zod', 'Nuxt', 'UI'])
    .meta({ label: '技能标签' })
})

type Schema = z.output<typeof schema>

const form = ref<Partial<Schema>>({})

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

<template>
  <UCard>
    <MAutoForm :schema="schema" :state="form" @submit="onSubmit">
      <template #field-after:address="{ value }">
        <UAlert
          v-if="value && value.street && value.city && value.zipCode"
          color="success"
          variant="subtle"
          icon="i-lucide-check-circle"
          class="mt-4"
        >
          <template #title>
            地址验证通过
          </template>
          <template #description>
            完整地址: {{ value.street }}, {{ value.city }}, {{ value.zipCode }}
          </template>
        </UAlert>
      </template>

      <template #field-hint:skills="{ value }">
        <UBadge
          v-if="value && value.length >= 5"
          color="success"
          variant="subtle"
          icon="i-lucide-award"
        >
          技能丰富
        </UBadge>
      </template>
    </MAutoForm>
  </UCard>
</template>

示例

数组字段自定义

使用 field-content 插槽完全自定义数组字段的渲染逻辑:

任务列表
管理您的任务列表 - 使用 setValue 简化数组操作
0 项
暂无任务
点击添加按钮创建第一个任务
<script lang="ts" setup>
import type { FormSubmitEvent } from '@nuxt/ui'
import type { z } from 'zod/v4'

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

const schema = afz.object({
  tasks: afz.array(
    afz.object({
      title: afz.string().min(1).meta({ label: '标题' }),
      priority: afz.enum(['low', 'medium', 'high']).default('medium').meta({ label: '优先级' }),
      completed: afz.boolean().meta({ label: '已完成' })
    })
  ).default([
    { title: '完成项目文档', priority: 'high', completed: false },
    { title: '代码审查', priority: 'medium', completed: false }
  ]).meta({ label: '任务列表' })
})

type Schema = z.output<typeof schema>

const form = ref<Partial<Schema>>({})

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

// 获取优先级颜色
function getPriorityColor(priority: string) {
  const priorityStr = String(priority)
  switch (priorityStr) {
    case 'high': return 'error'
    case 'medium': return 'warning'
    case 'low': return 'success'
    default: return 'neutral'
  }
}

// 获取优先级文本
function getPriorityText(priority: string) {
  const priorityStr = String(priority)
  switch (priorityStr) {
    case 'high': return ''
    case 'medium': return ''
    case 'low': return ''
    default: return priorityStr || '未设置'
  }
}
</script>

<template>
  <UCard>
    <MAutoForm
      :schema="schema"
      :state="form"
      :global-meta="{ collapsible: { defaultOpen: true } }"
      @submit="onSubmit"
    >
      <template #field-content:tasks="{ path, value, setValue, state }">
        <div class="space-y-4">
          <UAlert
            color="success"
            variant="subtle"
            icon="i-lucide-list-checks"
            title="任务列表"
            description="管理您的任务列表 - 使用 setValue 简化数组操作"
          >
            <template #actions>
              <UBadge :color="!!value?.length ? 'success' : 'neutral'" variant="subtle" size="lg">
                {{ value?.length || 0 }}              </UBadge>
            </template>
          </UAlert>

          <div v-if="value" class="space-y-3">
            <div
              v-for="(task, index) in value"
              :key="index"
              class="relative p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors group"
            >
              <div class="absolute top-3 right-3 opacity-0 group-hover:opacity-100 transition-opacity">
                <UButton
                  icon="i-lucide-trash-2"
                  color="error"
                  variant="ghost"
                  size="xs"
                  @click="setValue(value.filter((_, i) => i !== index))"
                />
              </div>

              <div class="grid grid-cols-1 md:grid-cols-3 gap-4 items-start">
                <UFormField :label="`任务 ${index + 1}`" :name="`${path}[${index}].title`" required>
                  <UInput
                    :model-value="task?.title"
                    placeholder="请输入任务标题"
                    icon="i-lucide-pencil-line"
                    @update:model-value="setValue(`[${index}].title`, $event)"
                  />
                </UFormField>

                <UFormField label="优先级" :name="`${path}[${index}].priority`">
                  <USelect
                    :model-value="task?.priority"
                    placeholder="选择优先级"
                    :items="[
                      { value: 'low', label: '低优先级' },
                      { value: 'medium', label: '中优先级' },
                      { value: 'high', label: '高优先级' }
                    ]"
                    @update:model-value="setValue(`[${index}].priority`, $event)"
                  >
                    <template #leading="{ modelValue }">
                      <UBadge
                        v-if="modelValue"
                        :color="getPriorityColor(modelValue)"
                        variant="subtle"
                        size="xs"
                      >
                        {{ getPriorityText(modelValue) }}
                      </UBadge>
                    </template>
                  </USelect>
                </UFormField>

                <UFormField label="完成状态" :name="`${path}[${index}].completed`">
                  <USwitch
                    :model-value="task?.completed"
                    unchecked-icon="i-lucide-x"
                    checked-icon="i-lucide-check"
                    :label="task?.completed ? '已完成' : '进行中'"
                    @update:model-value="setValue(`[${index}].completed`, $event)"
                  />
                </UFormField>
              </div>

              <div class="mt-3 flex items-center gap-2">
                <UBadge
                  v-if="task?.priority"
                  :color="getPriorityColor(task.priority)"
                  variant="subtle"
                  size="xs"
                >
                  {{ getPriorityText(task.priority) }}优先级
                </UBadge>
                <UBadge
                  v-if="task?.completed"
                  color="success"
                  variant="subtle"
                  size="xs"
                >
                  已完成
                </UBadge>
                <UBadge
                  v-else
                  color="warning"
                  variant="subtle"
                  size="xs"
                >
                  进行中
                </UBadge>
              </div>
            </div>
          </div>

          <UAlert
            v-if="!state.tasks || state.tasks.length === 0"
            color="neutral"
            variant="subtle"
            icon="i-lucide-inbox"
            title="暂无任务"
            description="点击添加按钮创建第一个任务"
          />
        </div>
      </template>
    </MAutoForm>
  </UCard>
</template>

嵌套对象自定义

使用 field-content 插槽自定义嵌套对象字段的渲染逻辑:

个人资料
完善您的个人信息
可选
联系方式
提供您的联系信息,方便其他人与您交流

contact 数据 :

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

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

const schema = afz.object({
  profile: afz.object({
    name: afz.string().min(2).meta({ label: '姓名' }),
    email: afz.email().meta({ label: '邮箱' }),
    bio: afz.string().optional().meta({ label: '简介' })
  }).meta({ label: '个人资料' }),
  contact: afz.object({
    $layout: afz.layout({
      class: 'grid grid-cols-2 gap-4',
      fields: {
        phone: afz.string().optional().meta({ label: '电话' }),
        website: afz.url().optional().meta({ label: '网站' })
      }
    })
  }).meta({ label: '联系方式' })
})

type Schema = z.output<typeof schema>

const form = ref<Partial<Schema>>({})

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

<template>
  <UCard>
    <MAutoForm
      :schema="schema"
      :state="form"
      :global-meta="{ collapsible: { defaultOpen: true } }"
      @submit="onSubmit"
    >
      <template #field-content:profile="{ path, value, setValue }">
        <UAlert
          color="primary"
          variant="subtle"
          icon="i-lucide-user"
          title="个人资料"
          description="完善您的个人信息"
        />

        <div class="grid grid-cols-2 gap-4">
          <UFormField label="姓名" :name="`${path}.name`" required>
            <UInput
              :model-value="value?.name"
              placeholder="请输入您的姓名"
              icon="i-lucide-user"
              class="w-full"
              @update:model-value="setValue('name', $event)"
            />
          </UFormField>
          <UFormField label="电子邮箱" :name="`${path}.email`" required>
            <UInput
              :model-value="value?.email"
              placeholder="请输入您的电子邮箱"
              icon="i-lucide-mail"
              type="email"
              class="w-full"
              @update:model-value="setValue('email', $event)"
            />
          </UFormField>
        </div>
        <UFormField label="简介" :name="`${path}.bio`" hint="可选">
          <UTextarea
            :model-value="value?.bio"
            placeholder="请输入您的个人简介"
            :rows="3"
            resize
            class="w-full"
            @update:model-value="setValue('bio', $event)"
          />
        </UFormField>
      </template>

      <template #field-before:contact="{ value, path }">
        <UAlert
          color="neutral"
          variant="subtle"
          icon="i-lucide-book-user"
          title="联系方式"
          description="提供您的联系信息,方便其他人与您交流"
        />

        <p class="text-gray-600 dark:text-gray-400 text-xs">
          {{ path }} 数据 :{{ value }}
        </p>
      </template>
    </MAutoForm>
  </UCard>
</template>

插槽参数

内容插槽接收以下参数:

NameTypeDescription
stateFormState表单数据
pathstring字段路径
valueany当前字段值
setValueSetValueFn设置字段值的函数,支持相对路径
errorsunknown[]字段错误列表
loadingboolean表单加载状态
在内容插槽中,setValue 支持相对路径更新子字段,详见 setValue 函数详解
Copyright © 2025 - 2025 YiXuan - MIT License