基于二次封装的的 Form 使用示例

1,013 阅读7分钟

Form 表单组件

基于 element-plus 二次封装的表单组件,支持配置化表单渲染、表单校验、动态表单项、远程/本地数据源、插槽自定义等能力。

具体的相关配置项,在下方都有说明,请先阅读完下方文档

组件导入

import { Form, useForm } from '@/components/Form'

快速开始

1.基本使用

<template>
  <Form @register="formRegister" />
  <el-button @click="onSubmit">提交</el-button>
</template>

<script setup lang="ts">
import { useForm, Form } from '@/components/Form'
const formColumns = [
  { 
    prop: 'name', 
    label: '姓名', 
    component: 'Input', 
    required: true 
   },
  { 
    prop: 'type', 
    label: '类型', 
    component: 'Select', 
    required: true, 
    componentProps: { 
        option: [
            { label: 'A', value: 'A' }, 
            { label: 'B', value: 'B' }
            ] 
        } 
   },
  { 
    prop: 'date', 
    label: '日期', 
    component: 'DatePicker' 
  },
]
const { register: formRegister, methods: formMethods } = useForm({
  formColumns,
  formCols: 2,
})
async function onSubmit() {
  await formMethods.validate()

  // 只能获取到表单 formColumns配置项 中 含有prop 的 值
  const data = await formMethods.getValue()
  console.log('表单数据', data)
}
</script>

表单子组件

如何向表单子组件 componentProps 传递 属性 和 事件

该子组件将完全支持 element-plus 的 属性 与 事件

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    prop: 'name', 
    label: '姓名', 
    component: 'Input', 
    // 传递到 componentProps 的属性和事件 将会完全的交给 el-input 组件,所以属性配置请阅读 el-input 的文档说明
    componentProps:{
            // 属性统一采用驼峰形式
            maxlength: 200,
            
            // 事件需要用 on 开头
            onChange:(value){
            ...
            }
        }
   }
]
Input 输入框

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    prop: 'name', 
    label: '姓名', 
    component: 'Input', 
   }
]
InputPassword 密码框

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    label: '密码',
    prop: 'password',
    component: 'InputPassword',
   }
]
InputTextArea 多行文本

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    prop: 'description',
    label: '说明',
    component: 'InputTextArea',
   }
]
InputNumber 数字输入

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    prop: 'sort',
    label: '排序',
    component: 'InputNumber',
   }
]
DatePicker 日期选择

基本使用

//.ts
import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    prop: 'dateRange',
    label: '创建时间段',
    component: 'DatePicker',
    componentProps: {
      type: 'daterange',
      unlinkPanels: true,
      valueFormat: 'YYYY-MM-DD'
    }
   }
]

// .vue
import { tableColumns } from './index';
import { useTable, Table } from '@/components/Table'
const { register: tableRegister, methods: tableMethods } = useTable({
  api: getSupplierRoadBlacklistLogList,
  tableColumns: tableColumns,
  searchColumns: searchColumns,
  isAutoHeight: true,
  showPagination: true,
  
  // 时间组件的 prop dateRange,需在这个地方格式化为 想要的字段
  onBeforeLoad: (params) => {
    let { dateRange = [], ...rest } = params
    return { endDate: dateRange?.[1], startDate: dateRange?.[0], ...rest }
  },
})
TimePicker 时间选择

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    prop: 'createTime',
    label: '创建时间段',
    component: 'TimePicker',
   }
]
RadioGroup 单选组

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    prop: 'winningPriceVisible',
    label: '竞价价格',
    component: 'RadioGroup',
    componentProps: {
        option: [
            {
              label: '显示',
              value: 1,
            },
            {
              label: '隐藏',
              value: 0,
            },
          ],
    },
   }
]
Select 下拉选择

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    prop: 'winningPriceVisible',
    label: '竞价价格',
    component: 'Select',
    componentProps: {
        option: [
            {
              label: '显示',
              value: 1,
            },
            {
              label: '隐藏',
              value: 0,
            },
          ],
    },
   }
]
字典使用
import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    prop: 'winningPriceVisible',
    label: '竞价价格',
    component: 'Select',
    componentProps: {
        // 传入 字典名称
        dict: 'ORDER_STOP_STATUS'
    },
   }
]
ApiSelect 远程下拉

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    prop: 'privinceId',
    label: '考评省份',
    component: 'ApiSelect',
    componentProps: {
      api: getProvinceList,
      
      //value 的字段,该字段值将注入 该prop的值 
      valueField: 'privinceId',
      
      // 显示的字段
      labelField: 'privinceName',
      params: {
        pageSize: 10,
        pageNum: 1
      }
    }
   }
]
ApiSelectTree 远程树选择

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
    prop: 'categoryId',
    label: '流程分类',
    component: 'ApiSelectTree',
    componentProps: {
    //`filterable` 属性即可启用搜索功能
      filterable: true,
      
    // 传入 在 apis 文件中定义的 api 函数
      api: listActTypes,
      props: {
        label: 'name',
        children: 'children',
        value: 'id'
      }
    }
  }
]
ApiSearchSelect 远程搜索下拉

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
     prop: 'deptName',
    label: '需求机构',
    component: 'ApiSearchSelect',
    componentProps: {
    // 传入在 apis 文件中定义的 函数
      api: listDeptPages,
      labelField: 'name',
      valueField: 'name',
      
      // 根据个字段搜索
      searchField: 'keyword',
      pageSize: 50,
      
      // 为true时,空值也可以搜索
      // 默认为 false,即只有输入内容时,才允许搜索
      defaultSearch: true
    }
]
Text 纯广本

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
     prop: 'deptName',
    label: '需求机构',
    component: 'Text',
]
读取字典内容

基本使用

import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
     prop: 'procurementMethodId',
    label: '采购方式: ',
    component: 'Text',
    componentProps: {
            dict: 'PURCHASE_DEPT_ID'
        }
]

插槽使用

基本使用

//.ts
import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
     prop: 'procurementMethodId',
    label: '采购方式: ',
    component: 'Text',
    
    // 在 Form 组件中,可以不用带 form- 前缀
    // 但在 Table 组件中,若是使用了 searchFormColumns,则需要带 form- 前缀
    slot:'procurementMethodId',
    //slot:'form-procurementMethodId'
  
]

//.vue
  <Form @register="formRegister">
        <template #procurementMethodId="{ model,prop }">
          <!-- 通过 model[prop] 获取或修改 该字段的值
                model 为表单对象, prop 为对应的 prop 字段 -->
            <div>{{model[prop]}}</div>   
        </template>
  </Form>

规则校验

必填校验

基本使用

//.ts
import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
     prop: 'procurementMethodId',
    label: '采购方式: ',
    component: 'Text',
    // 必填校验
    required:true
]
自定义校验

注:该 rules 校验规则与 el-form 的rules 一致,请查看 el-form 相关文档

//.ts
import type { FormColumn } from '@/components/Form'
const formColumns:FormColumn = [
  { 
     prop: 'procurementMethodId',
    label: '采购方式: ',
    component: 'Text',
    // 对象形式
    rules:{
        required:true,
        message:'采购方式为空'
    },
    // 数组形式(支持多个校验规则)
    rules:[
        {
            required:true,
            message:'采购方式为空',
            trigger: 'blur'
        },
        {
        ...
        }
    ]
]

表单布局

基本使用

//.vue
import { useForm, Form } from '@/components/Form'

const { register: formRegister, methods: formMethods } = useForm({

     // grid 布局属性, 代表一行展示 几个组件,3: 一行3列。 5:一行5列
  // 目前支持 1-7列。若想支持更多,请在  src/styles/safelist/grid.scss。文件内配置
    "formCols": 3,
    
    //每一列之前的间距,默认为 20,目前支持 1-100。 若想支持更多,请在  src/styles/safelist/grid.scss。文件内配置
    gapX: 0,
    labelPosition: 'right',
    labelWidth: '130px',
    formColumns,
})

表单禁用(禁用整个表单)

//.vue
import { useForm, Form } from '@/components/Form'

const { register: formRegister, methods: formMethods } = useForm({
    "formCols": 3,
    
    // 开启禁用
    disabled:true,
    gapX: 0,
    labelPosition: 'right',
    labelWidth: '130px',
    formColumns,
})

1. 定义表单项配置

// formColumns.ts
import type { FormColumn } from '@/components/Form'

export const formColumns: FormColumn[] = [
  {
    prop: 'supplierName',
    label: '供应商名称',

    // 组件类型,查看types.ts 里的 ComponentType 类型。
    component: 'Input', 
    required: true,     // 表单校验
    componentProps: {
      placeholder: '请输入供应商名称',
    },
  },
  {
    prop: 'supplierType',
    label: '供应商类型',
    component: 'Select',
    required: true,
    componentProps: {
      option: [
        { label: '企业', value: 'company' },
        { label: '个人', value: 'person' },
      ],
      placeholder: '请选择类型',
    },
  },
  {
    prop: 'registerDate',
    label: '注册日期',
    component: 'DatePicker',
    componentProps: {
      type: 'date',
      placeholder: '请选择注册日期',
    },
  },
]

2. 在页面中使用

<template>
  <Form @register="formRegister" />
  <el-button @click="onSubmit">提交</el-button>
</template>

<script setup lang="ts">
import { useForm, Form } from '@/components/Form'
import { formColumns } from './formColumns'

const { register: formRegister, methods: formMethods } = useForm({
  formColumns,
  formCols: 2, // 每行2列
  gapX: 40,    // 列间距
  labelPosition: 'right',
  labelWidth: '120px',
})

async function onSubmit() {
  await formMethods.validate()

  // 只能获取到表单 formColumns配置项 中 含有prop 的 值
  const data = await formMethods.getValue()
  console.log('表单数据', data)
}
</script>

API 说明

useForm(options)

  • options:表单配置对象,类型为 FormProps
  • 返回值:{ register, methods }
    • register:注册方法,传递给 <Form @register="..." />
    • methods:表单操作方法,详见下表
methods 支持的方法
方法名说明参数
setProps设置表单属性props: FormProps
validate校验整个表单
validateField校验部分字段props: string[]
resetFields重置表单props?: string[]
scrollToField滚动到指定字段prop: string
clearValidate清除校验props?: string[]
getValue获取表单数据
setValue设置表单数据props: any
setPropValue设置单个字段prop: string, value: any
updateFormColumn更新表单项props: PartialPartial[]

FormProps 配置项

属性名说明类型
formColumns表单项配置FormColumn[]
formCols每行列数string | number
gapX列间距number
inline行内表单boolean
size尺寸'large'|'default'|'small'
disabled禁用boolean
labelWidth标签宽度string | number
labelPosition标签位置'left'|'right'|'top'
requireAsteriskPosition必填星号位置'left'|'right'
hideRequiredAsterisk隐藏必填星号boolean
inlineMessage行内提示boolean
showMessage显示校验信息boolean
labelSuffix标签后缀string

FormColumn 表单项配置

属性名说明类型
prop字段名string
label标签string
component组件类型见下方支持组件
componentProps组件属性object/函数
required是否必填boolean
defaultValue默认值any
ifShow是否显示boolean/函数
rules校验规则array/object/函数
span跨度(最多24)string/number
tooltip标签提示string
divider分割线boolean
suffix标签后缀string
slot自定义插槽string
支持的 component 类型
  • Input(输入框)
  • InputPassword(密码框)
  • InputTextArea(多行文本)
  • InputNumber(数字输入)
  • DatePicker(日期选择)
  • TimePicker(时间选择)
  • RadioGroup(单选组)
  • Select(下拉选择)
  • ApiSelect(远程下拉)
  • ApiSelectTree(远程树选择)
  • Switch(开关)
  • CheckboxGroup(多选组)
  • IconPicker(图标选择)
  • ImageUpload(图片上传)
  • Tinymce(富文本)
  • ApiSearchSelect(远程搜索下拉)
  • Text(只读文本)
  • InputOnlyNumber(仅数字输入)

进阶用法

  • 支持通过 componentProps 传递自定义属性、事件、远程/本地数据源等
  • 支持通过 slot 实现自定义表单项内容
  • 支持动态控制表单项显示/隐藏、禁用、校验等

完整示例

<template>
  <Form @register="formRegister" />
  <el-button @click="onSubmit">提交</el-button>
</template>

<script setup lang="ts">
import { useForm, Form } from '@/components/Form'
const formColumns = [
  { 
    prop: 'name', 
    label: '姓名', 
    component: 'Input', 
    required: true 
   },
  { 
    prop: 'type', 
    label: '类型', 
    component: 'Select', 
    required: true, 
    componentProps: { 
        option: [
            { label: 'A', value: 'A' }, 
            { label: 'B', value: 'B' }
            ] 
        } 
   },
  { 
    prop: 'date', 
    label: '日期', 
    component: 'DatePicker' 
  },
]
const { register: formRegister, methods: formMethods } = useForm({
  formColumns,
  formCols: 2,
})
async function onSubmit() {
  await formMethods.validate()

  // 只能获取到表单 formColumns配置项 中 含有prop 的 值
  const data = await formMethods.getValue()
  console.log('表单数据', data)
}
</script>

常见问题

  • 表单项支持 element-plus 原生属性和事件
  • 支持自定义插槽、动态表单、远程数据等
  • 更多用法请参考实际业务代码和 element-plus 官方文档