Skip to content

DataForm 动态表单

配置驱动的动态表单组件,通过 columns 配置数组自动渲染表单控件。支持动态显示/隐藏、校验、嵌套分组、自定义渲染等。

在线演示

包含 select / radio / checkbox、日期范围、文件上传、可编辑表格、显隐联动:

tsx
import { defineComponent, reactive, h } from 'vue'
import DataForm from '@/components/FormLayout'
import { ElMessage as message } from 'element-plus'
import type { FormProps } from '@/types/DataFormProps'

export default defineComponent({
  name: 'DemoDataFormFull',
  setup() {
    const model = reactive({
      name: '',
      dept: '',
      roles: [],
      gender: '',
      dateRange: ['', ''],
      enable: false,
      remark: '',
      extra: '',
      items: [
        { name: '商品A', qty: 2, price: 99.9 },
      ],
      terms: [
        { title: '付款条款', content: '合同签订后 15 个工作日内支付 30% 预付款' },
      ],
      file: '',
      level: '',
    })

    const columns: FormProps['columns'] = [
      // ---- 基础信息 ----
      { label: '姓名', field: 'name', type: 'el-input', span: 12, rules: [{ required: true, message: '请输入姓名' }] },

      // ---- data-picker: select ----
      {
        label: '部门(select)',
        field: 'dept',
        type: 'data-picker',
        span: 12,
        props: {
          type: 'select',
          query: () =>
            Promise.resolve([
              { label: '技术部', value: 'tech' },
              { label: '市场部', value: 'mkt' },
              { label: '财务部', value: 'finance' },
            ]),
        },
      },

      // ---- data-picker: radio ----
      {
        label: '性别(radio)',
        field: 'gender',
        type: 'data-picker',
        span: 12,
        props: {
          type: 'radio',
          query: () =>
            Promise.resolve([
              { label: '男', value: 'male' },
              { label: '女', value: 'female' },
            ]),
        },
      },

      // ---- data-picker: checkbox ----
      {
        label: '角色(checkbox)',
        field: 'roles',
        type: 'data-picker',
        span: 12,
        props: {
          type: 'checkbox',
          query: () =>
            Promise.resolve([
              { label: '管理员', value: 'admin' },
              { label: '编辑', value: 'editor' },
              { label: '访客', value: 'guest' },
            ]),
        },
      },

      // ---- date-picker: daterange ----
      {
        label: '日期范围',
        field: 'dateRange',
        type: 'date-picker',
        span: 24,
        props: { type: 'daterange' },
      },

      // ---- 显隐联动 ----
      {
        label: '启用扩展字段',
        field: 'enable',
        type: 'el-switch',
        span: 12,
      },
      {
        label: '扩展信息',
        field: 'extra',
        type: 'el-input',
        span: 12,
        props: { placeholder: '启用后显示此字段' },
        show: (data: any) => !!data.enable,
      },

      // ---- form-edit-table 数组 ----
      {
        label: '订单明细(form-edit-table)',
        field: 'items',
        type: 'form-edit-table',
        span: 24,
        props: {
          editMode: 'table',
          height: 160,
          min: 1,
          columns: [
            { label: '商品名', field: 'name', editType: 'el-input', width: 200 },
            { label: '数量', field: 'qty', editType: 'el-input-number', width: 120, props: { min: 1 } },
            { label: '单价', field: 'price', editType: 'el-input-number', width: 120, props: { min: 0, precision: 2 } },
          ],
        },
      },

      // ---- form-edit-list ----
      {
        label: '合同条款(form-edit-list)',
        field: 'terms',
        type: 'form-edit-list',
        span: 24,
        props: {
          min: 1,
          columns: [
            { label: '条款标题', field: 'title', type: 'el-input' },
            { label: '条款内容', field: 'content', type: 'el-input', props: { type: 'textarea' } },
          ],
        },
      },

      // ---- 日期 + 上传 ----
      { label: '日期', field: 'date', type: 'date-picker', span: 24, props: { type: 'date' } },
      {
        label: '附件',
        field: 'file',
        type: 'file-upload',
        span: 24,
        props: {
          type: 'drag',
          placeholder: '点击或将文件拖拽到这里上传',
          accept: '.jpg,.png,.pdf',
        },
      },

      { label: '备注', field: 'remark', type: 'el-input', span: 24, props: { type: 'textarea', rows: 3 } },
    ]

    return () => (
      <div class="demo-block">
        <DataForm
          model={model}
          columns={columns}
          showButton={true}
          labelWidth={130}
          onConfirm={() => message.success(JSON.stringify(model, null, 2))}
        />
        <div style="margin-top:12px;padding:12px;background:#f5f7fa;border-radius:6px;font-size:13px;color:#666;white-space:pre-wrap;font-family:monospace;">
          当前数据:{JSON.stringify(model, null, 2)}
        </div>
      </div>
    )
  },
})

表单配置 (FormProps)

属性名类型默认值说明
modelObject-必填。表单绑定的数据对象
columnsFormColumn[]-必填。字段配置数组
textModebooleanfalse文本阅读模式,不显示输入框
labelWidthstring | number'120px'标签宽度
labelPosition'left' | 'top' | 'right''right'标签对齐位置
inlinebooleanfalse行内表单模式
disabledbooleanfalse全局禁用
showButtonbooleanfalse是否显示底部提交/重置按钮
buttonsButtonProps[]-自定义按钮组
onConfirmFunction-确认回调
onCancelFunction-取消回调
onSubmitFunction-提交回调(同 onConfirm)

字段配置 (FormColumn)

基础属性

属性名类型说明
fieldstring字段名,对应 model 中的 key。支持路径写法 user.name
labelstring (动态)标签名
typestring控件类型。data-pickerel-inputel-select
initialValueany默认值,仅在初始化时生效
valueKeystring绑定值的属性名,默认为 modelValue
spannumber (动态)栅格占位,24 栅格制,默认 24(占满一行)
showboolean (动态)是否显示
destroyOnHideboolean隐藏时是否销毁 DOM 并清空值

交互与校验

属性名类型说明
propsObject (动态)透传属性给具体 UI 控件
rulesRule[] (动态)校验规则
requiredboolean (动态)是否必填
eventsObject (动态)事件监听,无需 on 前缀
slotsRecord<string, ()=>VNode> (动态)插槽传递
suffixVNode (动态)控件后置内容
prefixVNode (动态)控件前置内容
extraVNode (动态)额外提示信息

动态计算 (ComputedField)

大多数属性支持静态值或动态回调:

ts
// 静态值
{ show: false }

// 动态回调 — 根据表单其他字段的值决定
{ show: (formData) => formData.type === 'A' }

// props 也支持动态
{ props: (formData) => ({ placeholder: `当前值: ${formData.name}` }) }

校验规则 (Rule)

ts
{
  required?: boolean    // 是否必填
  message?: string      // 错误提示
  trigger?: string      // 触发方式: 'blur' | 'change'
  pattern?: RegExp      // 正则
  type?: string         // 数据类型: 'email', 'number'
  validator?: Function  // 自定义校验函数
}

预置组件类型

type说明
(空)默认为 input-plus 文本输入
el-input / el-input-numberElement Plus 输入框
el-selectElement Plus 下拉选择
el-switch开关
el-date-picker日期选择器
el-radio-group单选组
el-checkbox-group多选组
data-picker增强选择器
data-text纯文本显示
file-upload文件上传
input-plus增强输入框
form-edit-table可编辑表格
form-edit-list可编辑列表

实例方法

通过 ref 调用:

方法说明
validate()校验表单,返回 Promise
resetFields()重置表单到初始值
clearValidate()移除校验结果
getFieldsValue()获取当前表单值
scrollToField(prop)滚动到指定字段

useFormLayout — 弹窗表单

useFormLayout 是一个函数式弹窗/抽屉钩子,可以在 JS 逻辑中直接唤起包含 DataForm 或自定义内容的弹窗。

ts
import { useFormLayout } from 'yoyoo-ui'

const { open } = useFormLayout()

// 打开一个表单弹窗
open({
  title: '编辑用户',
  type: 'modal', // 或 'drawer'
  size: 'md',
  form: {
    model: { name: '', age: 18 },
    columns: [
      { field: 'name', label: '用户名', required: true },
      { field: 'age', label: '年龄', type: 'el-input-number' },
    ],
  },
  onConfirm: async ({ form }) => {
    console.log('表单数据:', form)
    await saveUser(form)
  },
})

LayoutOptions

属性名类型默认值说明
titlestring'标题'弹窗标题
type'modal' | 'drawer''modal'展现形式
size'sm' | 'md' | 'l' | 'lg' | 'xlg''md'尺寸(对应 400/800/1000/1200/1400px)
formFormProps-DataForm 配置
contentVNode | Component-自定义内容组件
showButtonbooleantrue是否显示底部按钮
buttonsButtonProps[]-自定义按钮组
onConfirm(data) => Promise | void-确认回调
onCancel() => void-取消回调
propsObject{}透传给 ElDialog/ElDrawer 的 props