Skip to content

ProForm

ProForm 基于 el-form 封装,保留原生表单能力,并通过 fieldsProCol 快速构建配置式栅格表单。

基础用法

表单项可以通过 componentrenderslotName 三种方式描述,并支持:

  • componentProps
  • componentSlots
  • colProps
  • hidden
  • isNewLine

姓名
手机号
部门
请选择部门
状态
启用时间
城市
请选择城市
jsx
这是通过 render 字段渲染的内容
{
  "name": "",
  "mobile": "",
  "department": "",
  "status": "enabled",
  "enableTime": "",
  "city": ""
}

<template>
  <div class="pro-form-doc-demo">
    <ProForm
      ref="formRef"
      label-width="92px"
      :model="formModel"
      :rules="rules"
      :fields="fields"
      :gutter="16"
    >
      <template #departmentSelect>
        <el-select
          v-model="formModel.department"
          placeholder="请选择部门"
          clearable
        >
          <el-option
            v-for="item in departmentOptions"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          />
        </el-select>
      </template>

      <ProCol :use-grid="false" full>
        <el-button type="primary" @click="handleSubmit">提交</el-button>
        <el-button @click="handleReset">重置</el-button>
      </ProCol>
    </ProForm>

    <pre class="json">{{ JSON.stringify(formModel, null, 2) }}</pre>
  </div>
</template>

<script setup lang="tsx">
import { computed, h, reactive, ref } from 'vue'
import {
  ElDatePicker,
  ElInput,
  ElMessage,
  ElOption,
  ElRadio,
  ElRadioGroup,
  ElSelect,
  type FormRules,
} from 'element-plus'
import {
  provideProFormComponents,
  type ProFormField,
  type ProFormInstance,
} from '@coderhd/pro-element-plus'

type FormModel = {
  name: string
  mobile: string
  department: string
  status: 'enabled' | 'disabled'
  enableTime: string
  city: string
}

provideProFormComponents({
  ElInput,
  ElRadioGroup,
  ElDatePicker,
  ElSelect,
})

const formRef = ref<ProFormInstance | null>(null)
const formModel = reactive<FormModel>({
  name: '',
  mobile: '',
  department: '',
  status: 'enabled',
  enableTime: '',
  city: '',
})

const departmentOptions = [
  { label: '研发部', value: 'rd' },
  { label: '产品部', value: 'pm' },
  { label: '运营部', value: 'op' },
]

const cityOptions = [
  { label: '杭州', value: 'hangzhou' },
  { label: '上海', value: 'shanghai' },
  { label: '深圳', value: 'shenzhen' },
]

const rules: FormRules<FormModel> = {
  name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
  mobile: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
  city: [{ required: true, message: '请选择城市', trigger: 'change' }],
}

const fields = computed((): ProFormField<FormModel>[] => [
  {
    prop: 'name',
    label: '姓名',
    component: 'ElInput',
    componentProps: {
      placeholder: '请输入姓名',
      clearable: true,
    },
    colProps: {
      span: 12,
    },
  },
  {
    prop: 'mobile',
    label: '手机号',
    component: 'ElInput',
    componentProps: {
      placeholder: '请输入手机号',
      clearable: true,
    },
    colProps: {
      span: 12,
    },
  },
  {
    prop: 'department',
    label: '部门',
    slotName: 'departmentSelect',
    colProps: {
      span: 12,
    },
  },
  {
    prop: 'status',
    label: '状态',
    component: 'ElRadioGroup',
    componentSlots: {
      default: () => [
        h(ElRadio, { value: 'enabled' }, () => '启用'),
        h(ElRadio, { value: 'disabled' }, () => '停用'),
      ],
    },
    colProps: {
      span: 12,
    },
  },
  {
    prop: 'enableTime',
    label: '启用时间',
    component: 'ElDatePicker',
    componentProps: {
      type: 'date',
      valueFormat: 'YYYY-MM-DD',
      placeholder: '请选择启用时间',
      clearable: true,
      style: {
        width: '100%',
      },
    },
    hidden: (model) => {
      if (model.status !== 'enabled') {
        formModel.enableTime = ''
      }
      return model.status !== 'enabled'
    },
    colProps: {
      span: 12,
    },
  },
  {
    prop: 'city',
    label: '城市',
    component: 'ElSelect',
    componentProps: {
      placeholder: '请选择城市',
      clearable: true,
    },
    componentSlots: {
      default: () =>
        cityOptions.map((item) =>
          h(ElOption, {
            key: item.value,
            label: item.label,
            value: item.value,
          }),
        ),
    },
    colProps: {
      span: 12,
    },
  },
  {
    label: 'jsx',
    render: () => <div>这是通过 render 字段渲染的内容</div>,
  },
])

const handleSubmit = async () => {
  const valid = await formRef.value?.validate().catch(() => false)
  if (!valid) return
  ElMessage.success('提交成功')
}

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

<style lang="scss" scoped>
.json {
  margin-top: 20px;
  padding: 16px;
  border-radius: 12px;
  background: var(--el-fill-color-light);
  color: var(--el-text-color-regular);
  overflow: auto;
}
</style>

组件解析

component 支持两种写法:

  • 直接传组件对象
  • 传字符串组件名

当你使用字符串组件名时,ProForm 会优先从注入表中查找组件,再回退到全局注册组件。

组件树内部可使用:

ts
import { ElInput, ElSelect } from 'element-plus'
import { provideProFormComponents } from '@coderhd/pro-element-plus'

provideProFormComponents({
  ElInput,
  ElSelect,
})

如果要在 main.ts 里统一注入,则直接使用:

ts
import { createApp } from 'vue'
import { ElInput, ElSelect } from 'element-plus'
import App from './App.vue'
import { proFormComponentsKey } from '@coderhd/pro-element-plus'

const app = createApp(App)

app.provide(proFormComponentsKey, {
  ElInput,
  ElSelect,
})

API

Props

属性名说明类型默认值
fields表单项配置数组ProFormField[][]
...el-form props完整透传 el-form 原生属性--
gutter / justify / align / tag透传给内部 el-row 的布局属性RowPropsel-row 默认值

ProFormField

字段说明类型
component组件对象或字符串组件名Component | keyof ProFormComponentTypeMap
componentProps传给子组件的 propsRecord<string, any>
componentSlots子组件原生插槽Record<string, (...args: any[]) => VNodeChild>
render自定义渲染函数(context) => VNodeChild
slotName普通具名插槽名string
colProps透传给 ProCol 的布局配置ProColProps
hidden控制字段显隐boolean | ((model, field) => boolean)
isNewLine是否强制从新一行开始boolean
...el-form-item props原生 el-form-item 属性-

Events

事件名说明回调参数
validate透传 el-formvalidate 事件(prop, isValid, message) => void

Exposes

ProForm 对外暴露了常用的 el-form 实例方法:

  • getInstance
  • validate
  • validateField
  • resetFields
  • scrollToField
  • clearValidate
  • fields
  • getField

类型扩展

ProForm 内置了一批常用组件的类型提示,例如:

  • ElInput
  • ElInputNumber
  • ElDatePicker
  • ElTimePicker
  • ElCheckbox
  • ElRadioGroup
  • ElSelect
  • ElSwitch

如果你有自己的业务组件,也可以通过 declaration merging 扩展:

ts
declare module '@coderhd/pro-element-plus' {
  interface ProFormComponentTypeMap {
    UserSelect: {
      props: InstanceType<typeof UserSelect>['$props']
      slots: InstanceType<typeof UserSelect>['$slots']
    }
  }
}