下图这种表单在后台管理项目中很常见,为了能一天写999个表单,我认真的研究了下如何根据配置项直接生成表单(严肃脸)。
配置项中需要有哪些字段?
每一个表单项必需的值有key
,label
,下面这些为非必需的:
required
字段(该字段是否必填)component
字段(默认的输入组件为Input
,如果有component
则用传入的component
作为输入组件)rules
字段(校验规则,默认只会校验是否必填)options
字段(与Antd中Form的getFieldDecorator
函数接受的options
定义一致)
举例:
// formItems.js
import React from 'react'
import { Input, DatePicker, Radio, Select, Checkbox, Switch } from 'antd'
const { TextArea } = Input
const RadioGroup = Radio.Group
const RadioButton = Radio.Button
const Option = Select.Option
const CheckboxGroup = Checkbox.Group
const RangePicker = DatePicker.RangePicker
const selectExample = (
<Select>
<Option value="option1">option1</Option>
<Option value="option2">option2</Option>
<Option value="option3">option3</Option>
</Select>
)
const radioGroupExample = (
<RadioGroup
options={[
{ label: 'radio1', value: 'radio1' },
{ label: 'radio2', value: 'radio2' },
{ label: 'radio3', value: 'radio3' },
]}
/>
)
const radioButtonGroupExample = (
<RadioGroup>
<RadioButton value="radio1">radio1</RadioButton>
<RadioButton value="radio2">radio2</RadioButton>
<RadioButton value="radio3">radio3</RadioButton>
</RadioGroup>
)
const checkboxGroupExample = (
<CheckboxGroup
options={[
{ label: 'checkbox1', value: 'checkbox1' },
{ label: 'checkbox2', value: 'checkbox2' },
{ label: 'checkbox3', value: 'checkbox3' },
]}
/>
)
export default [
{
label: 'Input',
key: 'Input',
required: true,
},
{
label: '密码输入框',
key: 'password',
component: <Input type="password" />,
rules: [
{
required: true,
pattern: /^[0-9a-zA-Z]{8,16}$/,
message: '密码长度为8-16位,只能包含数字和英文',
},
],
},
{
label: 'TextArea',
key: 'TextArea',
component: <TextArea />,
},
{
label: 'Select',
key: 'Select',
required: true,
component: selectExample,
},
{
label: 'RadioGroup',
key: 'RadioGroup',
required: true,
component: radioGroupExample,
options: { initialValue: 'radio1' },
},
{
label: 'RadioButtonGroup',
key: 'RadioButtonGroup',
required: true,
component: radioButtonGroupExample,
options: { initialValue: 'radio2' },
},
{
label: 'CheckboxGroup',
key: 'CheckboxGroup',
required: true,
component: checkboxGroupExample,
options: { initialValue: ['checkbox3'] },
},
{
label: 'DatePicker',
key: 'DatePicker',
required: true,
component: <DatePicker />,
},
{
label: 'RangePicker',
key: 'RangePicker',
required: true,
component: <RangePicker />,
},
{
label: 'Switch',
key: 'Switch',
component: <Switch />,
options: { valuePropName: 'checked', initialValue: false },
},
]
封装Form
// MyForm.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Form, Input } from 'antd'
const FormItem = Form.Item
// 默认的layout
export const defaultLabelColSpan = 6
const defaultFormItemLayout = {
labelCol: { span: defaultLabelColSpan },
wrapperCol: { span: 14 },
}
// 渲染单个表单项
const renderFormItem = ({ item, layout, getFieldDecorator }) => {
const { label, key, required, component, options = {}, rules } = item
return (
<FormItem key={key} label={label} {...layout}>
{getFieldDecorator(key, {
...options,
rules: rules || [{ required, message: `${label}为空` }],
})(component || <Input />)}
</FormItem>
)
}
class MyForm extends Component {
render() {
// items格式即为上文配置的表单项
const { items, layout, form: { getFieldDecorator } } = this.props
return (
<Form>
{items.map(item => renderFormItem({ item, layout, getFieldDecorator }))}
</Form>
)
}
}
MyForm.propTypes = {
items: PropTypes.array.isRequired,
layout: PropTypes.object,
form: PropTypes.object.isRequired,
}
MyForm.defaultProps = {
layout: defaultFormItemLayout,
}
export default Form.create()(MyForm)
根据配置项生成表单页面
在写好MyForm
这个组件后,我们根据安装格式定义好的formItems
即可快速生成表单页面。
// App.js
import React, { Component } from 'react'
import moment from 'moment'
import { Button } from 'antd'
import MyForm, { defaultLabelColSpan } from './MyForm'
// formItems即为表单的配置项
import formItems from './formItems'
// 模拟发请求(在做修改操作时,表单需要先填充已有数据,这里写了个假的获取详情接口)
const requestDetail = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve({
Input: 'Input',
password: 'password',
Select: 'option2',
RadioGroup: 'radio2',
RadioButtonGroup: 'radio2',
CheckboxGroup: ['checkbox2'],
DatePicker: '2018-05-15T13:36:27.132Z',
RangePicker: ['2018-04-15T13:36:27.132Z', '2018-05-15T13:36:27.132Z'],
Switch: true,
})
}, 1500)
})
}
class App extends Component {
constructor(props) {
super(props)
this.formRef = React.createRef()
}
getDetail = () => {
requestDetail().then(res => {
// 如果字段的值是日期,要先转成moment格式
res.DatePicker = moment(res.DatePicker)
res.RangePicker = res.RangePicker.map(d => moment(d))
this.formRef.current.setFieldsValue(res)
})
}
onClickSubmit = () => {
this.formRef.current.validateFieldsAndScroll((err, values) => {
console.log(values)
if (err) {
return
}
console.log('校验通过')
})
}
render() {
return (
<div>
<Button style={{ margin: 24 }} type="primary" onClick={this.getDetail}>
模拟请求数据然后设置表单值
</Button>
<MyForm ref={this.formRef} items={formItems} />
<Button
style={{ marginLeft: `${defaultLabelColSpan / 24 * 100}%` }}
type="primary"
onClick={this.onClickSubmit}
>
提交
</Button>
</div>
)
}
}
export default App
完。