ProForm
ProForm 基于 el-form 封装,保留原生表单能力,并通过 fields 与 ProCol 快速构建配置式栅格表单。
基础用法
表单项可以通过 component、render 或 slotName 三种方式描述,并支持:
componentPropscomponentSlotscolPropshiddenisNewLine
{
"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 的布局属性 | RowProps | el-row 默认值 |
ProFormField
| 字段 | 说明 | 类型 |
|---|---|---|
component | 组件对象或字符串组件名 | Component | keyof ProFormComponentTypeMap |
componentProps | 传给子组件的 props | Record<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-form 的 validate 事件 | (prop, isValid, message) => void |
Exposes
ProForm 对外暴露了常用的 el-form 实例方法:
getInstancevalidatevalidateFieldresetFieldsscrollToFieldclearValidatefieldsgetField
类型扩展
ProForm 内置了一批常用组件的类型提示,例如:
ElInputElInputNumberElDatePickerElTimePickerElCheckboxElRadioGroupElSelectElSwitch
如果你有自己的业务组件,也可以通过 declaration merging 扩展:
ts
declare module '@coderhd/pro-element-plus' {
interface ProFormComponentTypeMap {
UserSelect: {
props: InstanceType<typeof UserSelect>['$props']
slots: InstanceType<typeof UserSelect>['$slots']
}
}
}