Skip to content

ProForm 表单

基于 el-form 进行封装的 JSON 配置化动态表单,属性、方法完全兼容,用于快速构建表单

基础用法

活动名称:
活动区域:
请选择活动区域
活动时间:
-
即时配送:
活动性质:
特殊资源:
活动形式:
0 / 300
{ "delivery": true }

<template>
  <div>
    <pro-form
      ref="formRef"
      label-width="100px"
      label-suffix=":"
      :model="form"
      :gutter="20"
      :fields="fields"
      :rules="rules"
    />

    <div style="white-space: pre-wrap">
      {{ JSON.stringify(form, null, 2) }}
    </div>
  </div>
</template>

<script setup lang="tsx">
import { ref, computed, reactive } from 'vue'
import type { ProFormFields } from '@coderhd/pro-element-plus'
import type { FormInstance } from 'element-plus'

type Form = {
  name?: string
  region?: string
  date1?: Date
  date2?: Date
  delivery?: boolean
  type?: string[]
  resource?: string
  desc?: string
}

const form = reactive<Form>({
  delivery: true,
})

const rules = reactive({
  name: [
    { required: true, message: '请输入活动名称', trigger: 'blur' },
    { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' },
  ],
  region: [{ required: true, message: '请选择活动区域', trigger: 'change' }],
  date1: [
    { type: 'date', required: true, message: '请选择日期', trigger: 'change' },
  ],
  date2: [
    { type: 'date', required: true, message: '请选择时间', trigger: 'change' },
  ],
  type: [
    {
      type: 'array',
      required: true,
      message: '请至少选择一个活动性质',
      trigger: 'change',
    },
  ],
  resource: [{ required: true, message: '请选择活动资源', trigger: 'change' }],
  desc: [{ required: true, message: '请填写活动形式', trigger: 'blur' }],
})

const formRef = ref<FormInstance>()

const fields = computed<ProFormFields>(() => [
  {
    label: '活动名称',
    prop: 'name',
    required: true,
    component: 'ElInput',
    componentProps: {
      placeholder: '请输入活动名称',
      clearable: true,
    },
    colProps: {
      span: 24,
    },
  },
  {
    label: '活动区域',
    prop: 'region',
    required: true,
    component: 'ElSelect',
    componentProps: {
      placeholder: '请选择活动区域',
      clearable: true,
      options: [
        { label: '区域一', value: 'shanghai' },
        { label: '区域二', value: 'beijing' },
      ],
    },
    colProps: {
      span: 24,
    },
  },
  {
    label: '活动时间',
    required: true,
    render: () => (
      <el-row style="flex: 1;">
        <pro-col span={11}>
          <el-form-item prop="date1">
            <el-date-picker
              type="date"
              v-model={form.date1}
              placeholder="选择日期"
              value-format="x"
              style="width: 100%;"
            />
          </el-form-item>
        </pro-col>

        <pro-col span={2} style="text-align: center;">
          -
        </pro-col>

        <pro-col span={11}>
          <el-form-item prop="date2">
            <el-time-picker
              v-model={form.date2}
              placeholder="选择时间"
              value-format="x"
              style="width: 100%;"
            />
          </el-form-item>
        </pro-col>
      </el-row>
    ),
    colProps: {
      span: 24,
    },
  },
  {
    label: '即时配送',
    prop: 'delivery',
    required: true,
    component: 'ElSwitch',
    componentProps: {
      activeText: '是',
      inactiveText: '否',
    },
    colProps: {
      span: 24,
    },
  },
  {
    label: '活动性质',
    prop: 'type',
    required: true,
    component: 'ElCheckboxGroup',
    componentProps: {
      options: [
        { label: '美食/餐厅线上活动', value: '1' },
        { label: '地推活动', value: '2' },
        { label: '线下主题活动', value: '3' },
        { label: '单纯品牌曝光', value: '4' },
      ],
    },
    colProps: {
      span: 24,
    },
  },
  {
    label: '特殊资源',
    prop: 'resource',
    required: true,
    component: 'ElRadioGroup',
    componentProps: {
      options: [
        { label: '线上品牌商赞助', value: '1' },
        { label: '线下场地免费', value: '2' },
      ],
    },
    colProps: {
      span: 24,
    },
  },
  {
    label: '活动形式',
    prop: 'desc',
    component: 'ElInput',
    componentProps: {
      type: 'textarea',
      autosize: {
        minRows: 4,
        maxRows: 6,
      },
      maxlength: 300,
      showWordLimit: true,
      placeholder: '请输入活动形式',
      clearable: true,
    },
    colProps: {
      span: 24,
    },
  },
  {
    render: () => (
      <div>
        <el-button type="primary" onClick={onSubmit}>
          立即创建
        </el-button>

        <el-button onClick={onReset}>重置</el-button>
      </div>
    ),
    colProps: {
      useGrid: false,
    },
  },
])

const onSubmit = () => {
  formRef.value?.validate((valid) => {
    if (valid) {
      // eslint-disable-next-line no-console
      console.log('submit!', form)
    } else {
      // eslint-disable-next-line no-console
      console.log('error submit!')
    }
  })
}

const onReset = () => {
  formRef.value?.resetFields()
}
</script>

<style scoped></style>

Attributes

ProForm属性完全继承ElForm,更多属性参考ElementPlus Form Attributes

属性名类型默认值说明
fieldsProFormFields[]表单项
gutternumber0参考ElementPlus Row Attributes
justifyenumstart参考ElementPlus Row Attributes
alignenum——参考ElementPlus Row Attributes
tagstringdiv参考ElementPlus Row Attributes

Slots

插槽名说明
default默认插槽

Exposes

更多实例方法参考ElementPlus Form Methods

类型定义
ts
import type { ProColProps } from '../../col/src/types'
import type {
  FormProps,
  FormItemProps,
  TimePickerDefaultProps,
  RowProps,
  InputInstance,
  InputNumberInstance,
  InputTagInstance,
  DatePickerInstance,
  TimeSelectInstance,
  CheckboxInstance,
  CheckboxGroupInstance,
  RadioInstance,
  RadioGroupInstance,
  SelectInstance,
  SwitchInstance,
  FormItemProp,
} from 'element-plus'
import type { VNodeChild } from 'vue'

export interface ComponentNamesMap {
  ElInput: 'ElInput'
  ElInputNumber: 'ElInputNumber'
  ElInputTag: 'ElInputTag'
  ElDatePicker: 'ElDatePicker'
  ElTimePicker: 'ElTimePicker'
  ElTimeSelect: 'ElTimeSelect'
  ElCheckbox: 'ElCheckbox'
  ElCheckboxGroup: 'ElCheckboxGroup'
  ElRadio: 'ElRadio'
  ElRadioGroup: 'ElRadioGroup'
  ElSelect: 'ElSelect'
  ElSwitch: 'ElSwitch'
}

export interface ComponentPropsMap {
  ElInput: InputInstance['$props']
  ElInputNumber: InputNumberInstance['$props']
  ElInputTag: InputTagInstance['$props']
  ElDatePicker: DatePickerInstance['$props']
  ElTimePicker: TimePickerDefaultProps
  ElTimeSelect: TimeSelectInstance['$props']
  ElCheckbox: CheckboxInstance['$props']
  ElCheckboxGroup: CheckboxGroupInstance['$props']
  ElRadio: RadioInstance['$props']
  ElRadioGroup: RadioGroupInstance['$props']
  ElSelect: SelectInstance['$props']
  ElSwitch: SwitchInstance['$props']
}

export interface ComponentSlotsMap {
  ElInput: InputInstance['$slots']
  ElInputNumber: InputNumberInstance['$slots']
  ElInputTag: InputTagInstance['$slots']
  ElDatePicker: DatePickerInstance['$slots']
  ElTimePicker: DatePickerInstance['$slots']
  ElTimeSelect: TimeSelectInstance['$slots']
  ElCheckbox: CheckboxInstance['$slots']
  ElCheckboxGroup: CheckboxGroupInstance['$slots']
  ElRadio: RadioInstance['$slots']
  ElRadioGroup: RadioGroupInstance['$slots']
  ElSelect: SelectInstance['$slots']
  ElSwitch: SwitchInstance['$slots']
}

/**
 * 内置如下组件`Props` `Slots`类型:
 * - `ElInput`
 * - `ElInputNumber`
 * - `ElInputTag`
 * - `ElDatePicker`
 * - `ElTimePicker`
 * - `ElTimeSelect`
 * - `ElCheckbox`
 * - `ElCheckboxGroup`
 * - `ElRadio`
 * - `ElRadioGroup`
 * - `ElSelect`
 * - `ElSwitch`
 */
export type ProFormField<
  T extends keyof ComponentNamesMap = keyof ComponentNamesMap,
> = Partial<FormItemProps> & {
  /**
   * 组件名称
   */
  component?: T
  /**
   * 组件属性
   */
  componentProps?: ComponentPropsMap[T]
  /**
   * 组件插槽
   */
  componentSlots?: ComponentSlotsMap[T]
  /**
   * 栅格布局
   * @default 24
   */
  colProps?: ProColProps
  /**
   * 是否独占一行
   * @default false
   */
  isNewLine?: boolean
  /**
   * 是否隐藏表单项
   * @default false
   */
  hidden?: (model?: Record<string, any>) => boolean | boolean
  /**
   * 自定义渲染
   */
  render?: () => VNodeChild | VNodeChild
}

export type ProFormFields = {
  [K in keyof ComponentNamesMap]: ProFormField<K>
}[keyof ComponentNamesMap][]

export type ProFormProps = Partial<FormProps> &
  Partial<RowProps> & {
    /**
     * 表单项
     */
    fields?: ProFormFields
  }

export type ProFormEmits = {
  (e: 'validate', prop: FormItemProp, isValid: boolean, message: string): void
}
类型扩展
ts
// types/pro-form.d.ts
import type {
  ComponentNamesMap,
  ComponentPropsMap,
  ComponentSlotsMap,
} from '@coderhd/pro-element-plus'

import type { ButtonInstance } from 'element-plus'

declare module '@coderhd/pro-element-plus' {
  // 扩展 ElButton 的类型
  export interface ComponentNamesMap {
    ElButton: 'ElButton'
  }
  export interface ComponentPropsMap {
    ElButton: ButtonInstance['$props']
  }
  export interface ComponentSlotsMap {
    ElButton: ButtonInstance['$slots']
  }
}