Appearance
QyqForm 动态表单
说明
QyqForm 是基于 Vue 3 与 Element Plus 的动态表单组件,通过 formOptions 配置字段、通过 v-model:modelValue 绑定数据,适合后台查询筛选、客户录入和合同编辑。组件负责字段展示与校验,查询请求、保存接口和提交状态由调用方维护。
快速接入
当前组件通过仓库源码接入,尚未发布独立 npm 包。文档工程使用 Vue 3.5、Element Plus 2、@element-plus/icons-vue 和 dayjs;复制到其他项目时,需要保留整个 src/components/qyq-form/ 目录与 src/components/CardTitle.vue,并注册 Element Plus、引入对应样式。下方示例假设 @ 指向 src。
vue
<script setup lang="ts">
import { ref } from 'vue'
import QyqForm from '@/components/qyq-form/index.vue'
import type { FormItemProp } from '@/components/qyq-form/types'
const filters = ref({ keyword: '', status: '' })
const submittedFilters = ref({ ...filters.value })
const formOptions: FormItemProp[] = [
{ prop: 'keyword', label: '关键词', type: 'input', clearable: true },
{
prop: 'status',
label: '状态',
type: 'select',
options: [
{ label: '待处理', value: 'pending' },
{ label: '已完成', value: 'done' },
],
},
]
function handleSearch() {
// 可在这里使用 filters.value 请求列表接口,并处理加载与错误状态。
submittedFilters.value = { ...filters.value }
}
</script>
<template>
<QyqForm
v-model:modelValue="filters"
:form-options="formOptions"
:columns="2"
@search="handleSearch"
@reset="handleSearch"
/>
<pre>{{ submittedFilters }}</pre>
</template>内置查询按钮先校验,通过后触发无参数的 search 事件,调用方从绑定的数据中读取条件。内置重置按钮重置后触发无参数的 reset 事件;上面的示例会在重置后同步查询条件。
适用场景与边界
| 场景 | 建议用法 |
|---|---|
| 列表查询 | 配置 formOptions,监听 search 与 reset,把条件传给 QyqTable 所在页面的数据请求 |
| 录入与编辑 | 使用 required 或 rules,在保存前调用 validate() |
| 特殊字段 | 使用 type: 'custom' 和以字段 prop 命名的插槽,自行绑定字段值 |
| 自定义操作区 | 使用 elgsFormBtn 插槽,并保持 showBtn 为 true |
该组件依赖 Element Plus 的表单行为。需要保留任意复杂布局或特殊业务流程时,可以用局部插槽或直接组合 Element Plus 表单项。
功能概览
查询筛选
适合列表页条件栏,支持列数、标签位置、查询重置与轻量搜索。
内置校验
支持 required、格式校验、自定义 rules 与暴露的验证方法。
日期金额
支持日期范围、快捷项、禁用日期、金额与金额区间格式化。
组合扩展
支持 selectInput、标题区 slot、按钮 slot、自定义字段 slot。
复杂录入
可组合 searchSelect、autocomplete、moneyrange 等能力构建产品级表单。
源码位置
src/components/qyq-form/index.vue
1. 基础配置
这一段偏查询筛选场景,用来展示一行几列、标签位置、标签宽度、按钮位置、禁用态等基础能力。
基础查询表单
右侧配置面板用于模拟不同后台页面中的筛选栏布局。
业务查询表单
基础查询示例点击展开
<script setup lang="ts">
import { ref } from 'vue'
import QyqForm from '@/components/qyq-form/index.vue'
const formData = ref({
keyword: '',
status: '',
createdAt: [],
})
const formOptions = [
{ prop: 'keyword', label: '关键词', type: 'input', clearable: true },
{
prop: 'status',
label: '状态',
type: 'select',
options: [
{ label: '全部', value: '' },
{ label: '待处理', value: 'pending' },
{ label: '已完成', value: 'done' },
],
},
{
prop: 'createdAt',
label: '创建时间',
type: 'daterange',
dateValidateData: { isBefore: true, isShowShortcuts: true },
},
]
</script>
<template>
<QyqForm
v-model:modelValue="formData"
:form-options="formOptions"
:columns="3"
:show-btn="true"
:form-label-info="{ labelPosition: 'top', labelWidth: '110px', showColon: false }"
title="业务查询表单"
/>
</template> 前半部分文档都偏查询筛选场景,重点体现
columns、formLabelInfo、showBtn、btnPosition 和轻量检索能力。 2. 内置校验
这一段偏录入编辑场景,重点展示 required、rules、文本格式校验以及组件暴露出来的校验方法。
客户录入校验 (?)
内置校验示例点击展开
<script setup lang="ts">
import { ref } from 'vue'
import type { FormRules } from 'element-plus'
import QyqForm from '@/components/qyq-form/index.vue'
const formData = ref({
customerName: '',
phone: '',
email: '',
})
const formOptions = [
{ prop: 'customerName', label: '客户名称', type: 'input', required: true },
{ prop: 'phone', label: '手机号', type: 'input' },
{ prop: 'email', label: '邮箱', type: 'input' },
]
const rules: FormRules = {
phone: [
{ required: true, message: '请输入手机号', trigger: ['blur', 'change'] },
{
trigger: 'blur',
validator: (_rule, value, callback) => {
/^1\d{10}$/.test(String(value || ''))
? callback()
: callback(new Error('请输入正确的手机号'))
},
},
],
}
</script>
<template>
<QyqForm
v-model:modelValue="formData"
:form-options="formOptions"
:rules="rules"
:columns="2"
title="客户录入校验"
/>
</template>3. 日期与金额逻辑
这一段专门展示组件的日期和金额封装能力,包括快捷时间、范围限制、禁用日期、金额自动格式化与金额区间;当前示例里,服务周期可选去年到明年,且起止最大跨度为 2 个月。
日期与金额能力 (?)
日期与金额示例点击展开
<script setup lang="ts">
import { ref } from 'vue'
import QyqForm from '@/components/qyq-form/index.vue'
const formData = ref({
signDate: '',
servicePeriod: [],
budgetAmount: '',
taxMin: '',
taxMax: '',
})
const formOptions = [
{
prop: 'servicePeriod',
label: '服务周期',
type: 'daterange',
required: true,
dateValidateData: {
isLimitRange: false,
isBefore: false,
validateType: 'months',
validateNumber: 2,
limitRangeArr: [
{ number: 1, type: 'year' },
{ number: 1, type: 'year' },
],
},
hint: {
text: '可选去年到明年,且起止最大跨度为 2 个月。',
},
},
]
</script>
<template>
<QyqForm
v-model:modelValue="formData"
:form-options="formOptions"
:columns="2"
title="日期与金额示例"
/>
</template>4. 组合输入与 Slot
这一段展示 selectInput、标题区插槽、字段提示插槽和按钮区扩展,同时也演示如何通过 item.style 让字段横跨多列布局。
组合输入与扩展示例
产品化录入
组合输入与 Slot 模板片段点击展开
<template>
<QyqForm
v-model:modelValue="formData"
:form-options="formOptions"
:columns="2"
title="组合输入与插槽"
>
<template #cardTitleInfo>
<span>支持在标题右侧补充业务状态</span>
</template>
<template #addressTip>
<span>此处可接地图选点或地址解析能力</span>
</template>
<template #previewAction>
<el-button type="primary" plain>预览</el-button>
</template>
</QyqForm>
</template> 组合输入更适合“区号 + 电话”“币种 + 金额”这类高频业务字段;如果后面还有需要,依然可以继续通过
item.style 做 Grid 跨列布局。 5. 综合复杂表单
这一段把查询和录入里常用的能力集中到一个正式业务案例里,采用“合同录入 / 编辑”场景,包含 searchSelect、autocomplete、日期、金额、组合输入、校验和自定义按钮。
合同录入表单 (?)
草稿中
综合复杂表单模板片段点击展开
<template>
<QyqForm
ref="formRef"
v-model:modelValue="formData"
:form-options="formOptions"
:rules="rules"
:columns="3"
:show-btn="true"
title="合同录入表单"
>
<template #elgsFormBtn>
<el-button>取消</el-button>
<el-button type="primary">保存草稿</el-button>
<el-button type="success">提交审批</el-button>
</template>
</QyqForm>
</template>6. 暴露方法
QyqForm 暴露了常用的表单实例方法,适合在父组件里配合按钮、步骤条、抽屉页脚等外部操作一起使用。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| validate(callback?) | FormInstance["validate"] | - | 触发整表校验;不传回调时使用 Promise,校验失败需要捕获拒绝。 |
| resetFields(props?) | (string | string[]) => void | - | 重置整表或指定字段;普通字段恢复为表单项挂载时的初始值,复合字段见下方限制。 |
| validateField(prop) | FormInstance["validateField"] | - | 只校验指定字段,返回 Promise;校验失败需要捕获拒绝。 |
| clearValidate(prop?) | (string | string[]) => void | - | 清理校验错误提示,适合回填数据、切换步骤或重置局部状态后调用。 |
暴露方法示例点击展开
<script setup lang="ts">
import { ref } from 'vue'
import QyqForm from '@/components/qyq-form/index.vue'
const formRef = ref<InstanceType<typeof QyqForm> | null>(null)
const formData = ref({ keyword: '', phone: '' })
const validationMessage = ref('')
const formOptions = [
{ prop: 'keyword', label: '关键词', type: 'input' },
{ prop: 'phone', label: '手机号', type: 'input', required: true },
]
async function handleValidate() {
if (!formRef.value) return
try {
await formRef.value.validate()
validationMessage.value = '校验通过'
} catch {
validationMessage.value = '请修正表单中的校验项'
}
}
function handleResetKeyword() {
formRef.value?.resetFields('keyword')
}
function handleClearPhoneError() {
formRef.value?.clearValidate('phone')
}
</script>
<template>
<QyqForm ref="formRef" v-model:modelValue="formData" :form-options="formOptions" />
<button type="button" @click="handleValidate">校验</button>
<button type="button" @click="handleResetKeyword">重置关键词</button>
<button type="button" @click="handleClearPhoneError">清理手机号校验提示</button>
<p role="status">{{ validationMessage }}</p>
</template>参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| modelValue | object | {} | 表单数据,使用 v-model:modelValue 绑定。 |
| formOptions | array | [] | 表单项配置。 |
| rules | object | {} | 表单校验规则。 |
| formLabelInfo | object | { labelPosition: 'top' } | 标签位置、宽度、星号位置和冒号展示配置。 |
| showBtn | boolean | true | 是否显示查询和重置按钮。 |
| btnPosition | string | 'right' | 默认按钮区域靠左或靠右。 |
| columns | number | 4 | 每行展示列数。 |
| validateOnRuleChange | boolean | false | 规则变更后是否立即触发表单校验。 |
| disabled | boolean | false | 是否禁用整张表单。 |
| title | string | '' | 表单标题。 |
| titleTip | string | '' | 标题右侧提示文案。 |
| titleFontWeight | string | number | 'bold' | 表单标题字重。 |
| titleSize | number | 16 | 表单标题字号。 |
| selectOptions | object | {} | 下拉选项映射,key 对应表单 prop。 |
常见问题
如何在保存前校验表单?
通过组件 ref 调用 validate()。不传回调时,校验通过返回成功结果,失败会拒绝 Promise;捕获校验失败后应停止保存。下面是保存按钮的处理片段,表单需已绑定 ref="formRef":
ts
async function handleSave() {
if (!formRef.value) return
try {
await formRef.value.validate()
} catch {
return
}
// 在此调用保存接口,并单独处理接口失败。
}required: true 用于声明必填,复杂规则通过 rules 传入。字段的 prop、数据对象的键和规则对象的键需要一致。
重置为什么没有把所有值清空?
普通字段沿用 Element Plus 的 resetFields() 行为,恢复为表单项挂载时记录的初始值。需要“清空为指定值”或“恢复最近一次接口回填”时,应由调用方设置数据,再调用 clearValidate() 清理提示。
复合字段另有配对值清理逻辑,当前对字段数组的处理与单字段不完全一致。重置金额区间或组合输入时,建议显式恢复两端的数据,不依赖 resetFields(['字段一', '字段二']) 自动同步配对值。直接调用 resetFields() 不会触发组件的 reset 事件,该事件由内置重置按钮触发。
为什么自定义按钮或字段没有显示?
按钮插槽 elgsFormBtn 位于 showBtn 控制的区域内,设置 showBtn: false 会连同插槽一起隐藏。自定义字段使用 type: 'custom',插槽名要与 formOptions 中的 prop 一致,字段内部的 v-model 由调用方绑定。
输入内容可以包含空格吗?
普通文本输入默认去除首尾空格,并对输入中的空格进行校验。需要保留首尾空格时设置 isNotTrim: true;需要允许文本输入中的空格时设置 allowSpecialChars: true。两个配置分别控制输入修剪与校验,可按业务需要组合使用。
配置字典补充
formOptions[].type:当前文档已覆盖input、textarea、select、radioGroup、date、daterange、money、moneyrange、selectInput、searchSelect、autocomplete和自定义 slot。formOptions[].required:适合快速声明必填;复杂格式校验建议配合rules使用。formOptions[].tip / hint:适合补充字段说明、帮助文本和风险提示,既支持纯文本也支持 slot。formOptions[].propOption:用于selectInput组合输入,适合区号 + 电话、币种 + 金额等复合场景。formOptions[].style:会透传到字段容器上,适合做gridColumn、gridRow等跨列跨行布局。formLabelInfo:统一控制标签布局,查询表单更常用top,录入表单可根据字段密度切到left。