SearchForm
简介
MSearchForm 是一个 Schema 驱动的搜索表单组件,内置网格布局、搜索/重置按钮和折叠行为。当搜索项较多时,超出可见行数的字段会自动折叠,用户可以点击展开/收起按钮查看全部搜索项。
复用 AutoForm 的底层基础设施(schema 内省、控件映射、字段渲染器),支持通过 Zod schema 定义搜索字段。
基础用法
最简单的搜索表单,3 列布局,超出 1 行的字段自动折叠:
<script setup lang="ts">
const { afz } = useAutoForm()
const schema = afz.object({
name: afz.string({ controlProps: { placeholder: '请输入姓名' } }).meta({ label: '姓名' }).optional(),
status: afz.enum(['启用', '禁用', '待审核']).meta({ label: '状态' }).optional(),
department: afz.string({ controlProps: { placeholder: '请输入部门' } }).meta({ label: '部门' }).optional(),
keyword: afz.string({ controlProps: { placeholder: '请输入关键词' } }).meta({ label: '关键词' }).optional(),
email: afz.email({ controlProps: { placeholder: '请输入邮箱' } }).meta({ label: '邮箱' }).optional()
})
const params = ref({})
const result = ref('')
function handleSearch(value: Record<string, unknown>) {
result.value = JSON.stringify(value, null, 2)
}
function handleReset() {
result.value = ''
}
</script>
<template>
<div class="space-y-4">
<MSearchForm
v-model="params"
:schema="schema"
@search="handleSearch"
@reset="handleReset"
/>
<pre v-if="result" class="text-sm bg-muted p-3 rounded-(--ui-radius)">{{ result }}</pre>
</div>
</template>
v-model 绑定
通过 v-model 双向绑定表单数据。传入的初始值会被组件记录为重置基准——点击「重置」按钮时恢复到初始值而非清空:
{
"name": "张三",
"status": "启用"
}<script setup lang="ts">
const { afz } = useAutoForm()
const schema = afz.object({
name: afz.string({ controlProps: { placeholder: '请输入姓名' } }).meta({ label: '姓名' }).optional(),
status: afz.enum(['启用', '禁用', '待审核']).meta({ label: '状态' }).optional(),
keyword: afz.string({ controlProps: { placeholder: '请输入关键词' } }).meta({ label: '关键词' }).optional()
})
const params = ref<Record<string, unknown>>({
name: '张三',
status: '启用'
})
</script>
<template>
<div class="space-y-4">
<MSearchForm v-model="params" :schema="schema" />
<pre class="text-sm bg-muted p-3 rounded-(--ui-radius)">{{ JSON.stringify(params, null, 2) }}</pre>
<UButton
size="sm"
color="neutral"
variant="outline"
@click="params = { name: '李四', status: '禁用', keyword: '测试' }"
>
外部设值
</UButton>
</div>
</template>
多列布局
通过 cols 属性控制网格列数:
4 列布局
2 列布局
<script setup lang="ts">
const { afz } = useAutoForm()
const schema = afz.object({
name: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '姓名' }).optional(),
status: afz.enum(['启用', '禁用']).meta({ label: '状态' }).optional(),
department: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '部门' }).optional(),
role: afz.enum(['管理员', '编辑', '查看者']).meta({ label: '角色' }).optional(),
keyword: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '关键词' }).optional()
})
const params = ref({})
</script>
<template>
<div class="space-y-6">
<div class="space-y-2">
<p class="text-sm text-muted">
4 列布局
</p>
<MSearchForm v-model="params" :schema="schema" :cols="4" />
</div>
<div class="space-y-2">
<p class="text-sm text-muted">
2 列布局
</p>
<MSearchForm v-model="params" :schema="schema" :cols="2" />
</div>
</div>
</template>
折叠行为
通过 visibleRows 控制可见行数,defaultExpanded 设置默认展开状态:
2 行可见(cols=3),默认展开
<script setup lang="ts">
const { afz } = useAutoForm()
const schema = afz.object({
name: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '姓名' }).optional(),
status: afz.enum(['启用', '禁用', '待审核']).meta({ label: '状态' }).optional(),
department: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '部门' }).optional(),
role: afz.enum(['管理员', '编辑', '查看者']).meta({ label: '角色' }).optional(),
email: afz.email({ controlProps: { placeholder: '请输入' } }).meta({ label: '邮箱' }).optional(),
phone: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '手机号' }).optional(),
keyword: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '关键词' }).optional()
})
const params = ref({})
</script>
<template>
<div class="space-y-6">
<div class="space-y-2">
<p class="text-sm text-muted">
2 行可见(cols=3),默认展开
</p>
<MSearchForm
v-model="params"
:schema="schema"
:cols="3"
:visible-rows="2"
default-expanded
/>
</div>
</div>
</template>
按钮显隐
通过 showSearchButton / showResetButton 控制按钮显隐,配合 actions slot 可完全自定义按钮区域:
隐藏重置按钮
隐藏全部按钮,通过 actions slot 自定义
<script setup lang="ts">
const { afz } = useAutoForm()
const schema = afz.object({
name: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '姓名' }).optional(),
status: afz.enum(['启用', '禁用']).meta({ label: '状态' }).optional(),
department: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '部门' }).optional(),
keyword: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '关键词' }).optional()
})
const params = ref({})
</script>
<template>
<div class="space-y-6">
<div class="space-y-2">
<p class="text-sm text-muted">
隐藏重置按钮
</p>
<MSearchForm
v-model="params"
:schema="schema"
:show-reset-button="false"
/>
</div>
<div class="space-y-2">
<p class="text-sm text-muted">
隐藏全部按钮,通过 actions slot 自定义
</p>
<MSearchForm
v-model="params"
:schema="schema"
:show-search-button="false"
:show-reset-button="false"
>
<template #actions="{ search, reset }">
<div class="flex items-end gap-2 justify-end">
<UButton
color="primary"
variant="solid"
icon="i-lucide-filter"
@click="search"
>
筛选
</UButton>
<UButton
color="neutral"
variant="ghost"
icon="i-lucide-x"
@click="reset"
>
清空
</UButton>
</div>
</template>
</MSearchForm>
</div>
</div>
</template>
自定义按钮
自定义按钮文本和样式:
<script setup lang="ts">
const { afz } = useAutoForm()
const schema = afz.object({
name: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '姓名' }).optional(),
status: afz.enum(['启用', '禁用']).meta({ label: '状态' }).optional(),
keyword: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '关键词' }).optional(),
department: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '部门' }).optional()
})
const params = ref({})
</script>
<template>
<MSearchForm
v-model="params"
:schema="schema"
search-text="查询"
reset-text="清空"
expand-text="更多"
collapse-text="收起"
:search-button-props="{ color: 'primary', variant: 'solid' }"
:reset-button-props="{ color: 'error', variant: 'outline' }"
/>
</template>
响应式列数
cols 支持响应式对象,根据屏幕宽度自动调整列数:
调整浏览器窗口宽度查看效果:sm=1, md=2, lg=3, xl=4
<script setup lang="ts">
const { afz } = useAutoForm()
const schema = afz.object({
name: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '姓名' }).optional(),
status: afz.enum(['启用', '禁用', '待审核']).meta({ label: '状态' }).optional(),
department: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '部门' }).optional(),
email: afz.email({ controlProps: { placeholder: '请输入' } }).meta({ label: '邮箱' }).optional(),
keyword: afz.string({ controlProps: { placeholder: '请输入' } }).meta({ label: '关键词' }).optional()
})
const params = ref({})
</script>
<template>
<div class="space-y-2">
<p class="text-sm text-muted">
调整浏览器窗口宽度查看效果:sm=1, md=2, lg=3, xl=4
</p>
<MSearchForm
v-model="params"
:schema="schema"
:cols="{ sm: 1, md: 2, lg: 3, xl: 4 }"
/>
</div>
</template>
API
Props
| Prop | Default | Type |
|---|---|---|
schema | SZod 对象 schema,定义搜索字段 | |
cols | 3 | number | { sm?: number; md?: number; lg?: number; xl?: number; }网格列数
|
visibleRows | 1 | number可见行数(折叠时显示的行数) |
controls | AutoFormControls自定义控件映射(复用 AutoForm 的控件系统) | |
globalMeta | ZodAutoFormFieldMeta全局字段元数据
| |
searchButtonProps | ButtonProps搜索按钮属性
| |
resetButtonProps | ButtonProps重置按钮属性
| |
searchText | '搜索' | string搜索按钮文本 |
resetText | '重置' | string重置按钮文本 |
icon | 'i-lucide-chevron-down' | string展开/收起按钮图标 |
expandText | '展开' | string展开按钮文本 |
collapseText | '收起' | string收起按钮文本 |
gap | 'gap-4' | string网格间距 |
id | string | number | |
state | N extends false ? Partial<InferInput<S>> : neverAn object representing the current state of the form. | |
validate | (state: Partial<InferInput<S>>) => FormError<string>[] | Promise<FormError<string>[]>Custom validation function to validate the form state. | |
validateOn | `['blur', 'change', 'input']` | FormInputEvents[]The list of input events that trigger the form validation. |
name | N extends true ? string : neverPath of the form's state within it's parent form.
Used for nesting forms. Only available if | |
validateOnInputDelay | `300` | numberDelay in milliseconds before validating the form on input events. |
transform | `true` | TIf true, applies schema transformations on submit. |
nested | `false` | NIf true, this form will attach to its parent Form and validate at the same time. |
onSubmit | (): void | Promise<void> | (event: FormSubmitEvent<FormData<S, T>>): void | Promise<void> | |
acceptcharset | string | |
action | string | |
autocomplete | string | |
enctype | string | |
method | string | |
novalidate | false | true | "true" | "false" | |
target | string | |
modelValue | Partial<InferInput<S>> | |
showSearchButton | true | boolean是否显示搜索按钮 |
showResetButton | true | boolean是否显示重置按钮 |
loading | false | boolean搜索按钮加载状态 |
defaultExpanded | false | boolean默认展开状态 |
disabled | booleanDisable all inputs inside the form. | |
loadingAuto | `true` | booleanWhen |
ui | { base?: any; }
|
Emits
| Event | Type |
|---|---|
search | [value: Partial<InferInput<S>>] |
reset | [] |
expand | [expanded: boolean] |
update:modelValue | [value: Partial<InferInput<S>>] |
Slots
| Slot | Type |
|---|---|
actions | { expanded: boolean; toggle: () => void; search: () => void; reset: () => void; loading: boolean; }替换默认按钮区域 |
extraActions | { expanded: boolean; }追加在默认按钮后面 |
field-default | { error?: string | boolean; } & AutoFormFieldContext<Partial<InferInput<S>>, string> |
field-error | { error?: string | boolean; } & AutoFormFieldContext<Partial<InferInput<S>>, string> |
field-description | { description?: string; } & AutoFormFieldContext<Partial<InferInput<S>>, string> |
field-label | { label?: string; } & AutoFormFieldContext<Partial<InferInput<S>>, string> |
field-help | { help?: string; } & AutoFormFieldContext<Partial<InferInput<S>>, string> |
field-hint | { hint?: string; } & AutoFormFieldContext<Partial<InferInput<S>>, string> |
Expose
您可以通过 useTemplateRef 访问该类型化组件实例。
| Name | Type |
|---|---|
formRef | Ref<InstanceType<typeof UForm>> UForm 组件引用 |
reset() | void 恢复到初始值( |
clear() | void 清空表单所有字段为空值,不触发事件 |
expanded | Ref<boolean> 当前展开/收起状态 |
toggle() | void 切换展开/收起 |