Skip to content

UseElForm - 表单

组件介绍

UseElForm 是基于 Element Plus 的 el-form 组件进行二次封装的高性能表单组件,旨在简化表单开发流程、统一表单配置范式、提升开发效率。该组件通过配置化的方式定义表单结构与交互逻辑,覆盖输入框、数字输入框、单选框、下拉选择器、级联选择器、日期选择器、时间选择器、开关等主流表单控件,支持行式表单(rowForm)和内联表单(inlineForm)两种展示模式,适配各类业务场景的表单开发需求。

核心亮点

  1. 配置化驱动开发,降低编码成本
  2. 一站式控件覆盖,适配全场景需求
  3. 字典映射自动化,简化选项渲染逻辑
  4. 灵活的布局与样式控制
  5. 精细化的交互与事件封装
  6. 插槽与扩展性兼容

解决痛点

  1. 原生 el-form 模板冗余问题

痛点:原生开发表单时,需重复编写 el-form/el-form-item/el-input 等嵌套标签,每个字段的模板结构高度相似,代码冗余且维护成本高。

解决方案:通过配置化驱动渲染表单,将重复的模板逻辑封装到组件内部,开发者仅需关注字段配置,无需编写模板。

  1. 校验规则重复编写与管理混乱

痛点:不同表单的相同类型字段(如手机号、邮箱)需重复编写校验规则,校验逻辑分散在各个表单组件中,难以统一维护。

解决方案:内置通用校验规则,支持全局注册自定义校验,校验规则与字段配置绑定,统一管理且可复用。

  1. 表单联动逻辑复杂且耦合度高

痛点:字段联动(如选择省份后加载城市、勾选某选项后显示额外字段)需手动监听 watch 或 change 事件,逻辑与模板耦合,易出错且难以维护。

解决方案:配置化定义联动规则,组件内部封装联动监听逻辑,业务侧仅需声明联动关系,无需编写监听代码。

  1. 自定义控件集成成本高

痛点:原生 el-form 集成自定义组件(如富文本、自定义上传)时,需手动处理值绑定、校验触发、状态同步等逻辑,适配成本高。

解决方案:提供自定义控件扩展入口,统一值绑定和校验触发逻辑,自定义组件可快速集成到表单中,无需适配底层逻辑。

  1. 表单状态与数据格式管理繁琐

痛点:表单禁用、只读等状态需手动控制每个字段,日期、数字等字段的格式转换需手动处理,易出现格式不一致问题。

解决方案:内置全局状态控制和数据格式化 / 反格式化能力,统一管理表单状态和数据格式,减少手动处理逻辑。

  1. 多端适配与布局调整效率低

痛点:原生表单需手动编写栅格布局代码适配不同屏幕,调整列数、间距时需逐个修改 el-col 属性。

解决方案:内置栅格配置项,一键设置表单列数、间距,自动适配多端布局,无需手动调整每个字段的布局属性。

使用

输入框

本例演示输入框的常用场景,包括:基础输入必填校验、单位外置、单位内置、自定义单位样式、图标前置、图标后置、金额格式化、手机号码自定义校验、身份证号码自定义校验、textarea多行文本等。其它诸如根据身份证号码自动解析年龄、性别、出生日期等联动实用功能请移步万有前端·iAdmin

选择下拉框

本例演示选择下拉框的常用场景,包括:同步字典、异步字典、多选等。其它诸如根据选择下拉框选中的数据联动其它表单项等实用功能请移步万有前端·iAdmin

级联选择器

本例演示级联选择器的常用场景,包括:懒加载、非懒加载字典、是否严格遵守父子节点不互相关联等。

Types

js
import type { DictMap, DictProps } from '@/types/dict'

/**
 * inputConfig接口
 */
export interface InputConfig {
  type?:
    | 'text'
    | 'textarea'
    | 'number'
    | 'password'
    | 'email'
    | 'search'
    | 'tel'
    | 'url'
  prefixIcon?: string
  suffixIcon?: string
  clearable?: boolean
  maxlength?: string
  formatter?: string
  parser?: string
  autosize?: boolean | { minRows?: number; maxRows?: number }
  rows?: number
  resize?: string
  showPassword?: boolean
  placeholder?: string
  disabled?: boolean
  width?: string // 默认el-input的width为100% 可设置width为''或具体宽度值,为''则使用默认的el-input宽度
  autoFocus?: boolean // 为true则对el-input使用v-focus聚焦指令
  prefixContent?: string | object // el-input头部插槽内容
  suffixContent?: string | object // el-input尾部插槽内容
  prependContent?: string | object // el-input前置插槽内容
  appendContent?: string | object // el-input后置插槽内容
  unit?: string | object // 在el-input右外部显示单位
  onInput?: (...args: any) => any // el-input的input事件
  onKeyupEnter?: (...args: any) => any // el-input的keyup.enter事件
}

/**
 * inputNumberConfig接口
 */
export interface InputNumberConfig {
  min?: number
  max?: number
  step?: number
  precision?: number
  placeholder?: string
  disabled?: boolean
  disabledScientific?: boolean
  unit?: string | object // 在el-input-number右外部显示单位
}

/**
 * radioConfig接口
 */
export interface RadioConfig {
  disabled?: boolean
  options?: Array<{ label: string; value: string | number }> // el-radio的选项集
  optionKey?: string // 当dictMap的key与formColumns的label不匹配时,通过optionKey指定dictMap的key值
  props?: { value: string; label?: string } // el-radio的props配置项
  reverse?: boolean // 是否反转单选框和标签的位置
  onChange?: (...args: any) => any // el-radio的change事件
}

/**
 * selectConfig接口
 */
export interface SelectConfig {
  disabled?: boolean
  multiple?: boolean
  clearable?: boolean
  placeholder?: string
  options?: Array<{ label: string; value: string | number }> // el-select的选项集
  optionKey?: string // 当dictMap的key与formColumns的label不匹配时,通过optionKey指定dictMap的key值
  props?: { value: string; label?: string } // el-select的props配置项
  reverse?: boolean // 是否反转单选框和标签的位置
  width?: string // 默认el-select的width为100% 可设置width为''或具体宽度值,为''则使用默认的el-select宽度
  onChange?: (...args: any) => any // el-select的change事件
}

/**
 * cascaderConfig接口
 */
export interface CascaderConfig {
  disabled?: boolean
  clearable?: boolean
  options?: Array<{ label: string; value: string | number }> //el-cascader的选项集
  optionKey?: string // 当dictMap的key与formColumns的label不匹配时,通过optionKey指定dictMap的key值
  props?: { value: string; label?: string } //el-cascader的props配置项
  width?: string //默认el-cascader的width为100% 可设置width为''或具体宽度值,为''则使用默认的el-cascader宽度
  onChange?: (...args: any) => any //el-select的change事件
}

/**
 * datePickerConfig接口
 */
export interface DatePickerConfig {
  type?:
    | 'year'
    | 'years'
    | 'month'
    | 'months'
    | 'date'
    | 'dates'
    | 'datetime'
    | 'week'
    | 'datetimerange'
    | 'daterange'
    | 'monthrange'
    | 'yearrange'
  clearable?: boolean
  disabledDate?: Function
  placeholder?: string
  startPlaceholder?: string
  endPlaceholder?: string
  rangeSeparator?: string
  width?: string // 默认el-date-picker的width为100% 可设置width为''或具体宽度值,为''则使用默认的el-date-picker宽度
  fieldProps?: string[] // 当type为daterange或datetimerange时,传递fieldProps属性可将fieldProps数组中的值与日期范围选择器选中值一一对应赋值给formModel
  onCalendarChange?: Function // el-date-picker的calendar-change事件
  onClear?: Function // el-date-picker的clear事件
  onChange?: Function // el-date-picker的change事件
}

/**
 * timePickerConfig接口
 */
export interface TimePickerConfig {
  startPlaceholder?: string
  endPlaceholder?: string
  rangeSeparator?: string
  isRange?: boolean
  editable?: boolean
  fieldProps?: string[] // 传递fieldProps属性可将fieldProps数组中的值与时间选择器选中值一一对应赋值给formModel
  onChange?: (...args: any) => any //el-date-picker的change事件
}

/**
 * timeSelectConfig接口
 */
export interface TimeSelectConfig {
  startProp: string // 时间选择范围开始时间在formModel中的属性名
  endProp: string // 时间选择范围结束时间在formModel中的属性名
}

/**
 * switchConfig接口
 */
export interface SwitchConfig {
  disabled?: boolean
  activeValue?: string | number | boolean
  inactiveValue?: string | number | boolean
  onChange?: (...args: any) => any // el-switch的change事件
}

/**
 * formColumn接口
 */
export interface FormColumn {
  label: string //el-form-item的label
  prop: string //el-form-item的prop
  required?: boolean | object //是否为必填项 为object类型则为自定义校验
  errorMessage?: string //必填项错误消息 如不指定则默认为“请输入xxx”或“请选择xxx”
  span?: number //el-col的span
  hideLabel?: boolean //为true则不显示label
  labelWidth?: string //el-form-item的labelWidth
  show?: boolean //为true则不显示当前项el-form-item
  formItemClass?: string //el-form-item的class
  slot?: string | boolean //slot为string类型则使用slot值作为具名插槽值 slot为boolean类型则使用prop值作为具名插槽值
  inputConfig?: InputConfig //表单项为【输入框】的配置项
  inputNumberConfig?: InputNumberConfig //表单项为【数字输入框】的配置项
  selectConfig?: SelectConfig //表单项为【选择下拉框】的配置项
  cascaderConfig?: CascaderConfig //表单项为【级联选择器】的配置项
  datePickerConfig?: DatePickerConfig //表单项为【日期选择器】的配置项
  timePickerConfig?: TimePickerConfig //表单项为【时间选择器】的配置项
  timeSelectConfig?: TimeSelectConfig //表单项为【时间选择范围】的配置项
  radioConfig?: RadioConfig //表单项为【单选框】的配置项
  switchConfig?: SwitchConfig //表单项为【开关】的配置项
}

/**
 * props接口
 */
export interface Props {
  /**
   * 表单列 必传
   */
  formColumns: FormColumn[]
  /**
   * 表单数据 必传
   */
  formModel: Record<string, unknown>
  /**
   * 表单模式 默认为rowForm
   */
  formMode?: 'rowForm' | 'inlineForm'
  /**
   * 字典映射 一般数据结构为{ tableColumns[].label: Array<{ label: string, value: any }> },亦可自定义key,然后通过optionKey与其匹配
   */
  dictMap?: DictMap
  /**
   * 字典属性 默认为{ value: 'value', label: 'label' },可通过全局配置进行设置
   */
  dictProps?: DictProps
  /**
   * el-form-item宽度值
   */
  formItemWidth?: string
  /**
   * label后缀
   */
  labelSuffix?: string
  /**
   * label宽度
   */
  labelWidth?: string
  /**
   * label定位
   */
  labelPosition?: string
  /**
   * el-row的gutter值
   */
  rowGutter?: number
  /**
   * el-col的span值
   */
  colSpan?: 24 | 12 | 8 | 6 | 4 | 3 | 2 | 1
  /**
   * 是否禁用
   */
  disabled?: boolean
  /**
   * 查询按钮loading 仅formMode为inlineForm有效
   */
  queryLoading?: boolean
  /**
   * 是否显示底部border 仅formMode为inlineForm有效
   */
  showBorberBottom?: boolean
}

/**
 * emits接口
 */
export interface Emits {
  /**
   * 查询事件 仅formMode为inlineForm时支持
   */
  (e: 'onQuery', val: Record<string, unknown> | any): void
}

Props && Emits && Expose

js
const props = withDefaults(defineProps<Props>(), {
  formMode: 'rowForm',
  // 一般数据结构为{ formColumns[].label: Array<{ label: string, value: any }> },亦可自定义key,然后通过optionKey与其匹配
  dictMap: () => ({}) as DictMap,
  // 默认为{ value: 'value', label: 'label' } 为适配全局配置此处不再设置默认值
  dictProps: () => ({}) as DictProps,
  formItemWidth: '180px',
  labelWidth: 'auto',
  labelPosition: 'right',
  rowGutter: 20,
  colSpan: 12,
  showBorberBottom: true
} satisfies DefaultsType<Props>)

// 仅formMode为inlineForm时支持
const emits = defineEmits<Emits>()

defineExpose({
  validate,
  resetFields,
  clearValidate,
  showInlineForm,
  handleInlineFormDisplay
})

基于 MIT 许可发布