Skip to content

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 个月。

日期与金额能力 (?)

签约日期
服务周期
至
示例中可选去年到明年,且起止最大跨度为 2 个月。
预算金额
CNY
税费区间
至
最晚开票日
示例中限制为今天之后可选。
日期与金额示例点击展开
<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>

参数 ​

参数类型默认值说明
modelValueobject{}表单数据,使用 v-model:modelValue 绑定。
formOptionsarray[]表单项配置。
rulesobject{}表单校验规则。
formLabelInfoobject{ labelPosition: 'top' }标签位置、宽度、星号位置和冒号展示配置。
showBtnbooleantrue是否显示查询和重置按钮。
btnPositionstring'right'默认按钮区域靠左或靠右。
columnsnumber4每行展示列数。
validateOnRuleChangebooleanfalse规则变更后是否立即触发表单校验。
disabledbooleanfalse是否禁用整张表单。
titlestring''表单标题。
titleTipstring''标题右侧提示文案。
titleFontWeightstring | number'bold'表单标题字重。
titleSizenumber16表单标题字号。
selectOptionsobject{}下拉选项映射,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。