antd-form-render
使用js配置,开发antd表单
安装
$ npm install --save antd-form-render
$ yarn add antd-form-render
功能
- 通过js配置开发 antd v4表单
- 等宽度布局,实现1行1列,1行2列,1行3列,1行4列 (基于Row与Col排列)
- 等间距布局 (基于Space排列)
1. 一行一列
import React from 'react';
import styled from 'styled-components';
import { Form, Button, Space, Input, message } from 'antd';
import FormRender from 'antd-form-render';
const StyledOneRow = styled.div`
width: 400px;
`;
const OneCol = () => {
const [form] = Form.useForm();
const oneRowLayout = [
{
type: Input,
label: '手机号',
placeholder: '请输入',
name: 'tel',
elProps: {
maxLength: 11,
},
itemProps: {
rules: [
{ required: true, message: '请输入' },
{ pattern: /^1\d{10}$/, message: '手机号必须为11位数字' },
],
},
},
{
type: Input.Password,
label: '密码',
placeholder: '请输入',
name: 'pwd',
itemProps: {
rules: [{ required: true, message: '请输入' }],
},
},
{
type: Input.Password,
label: '确认密码',
placeholder: '请输入',
name: 'confirmPwd',
itemProps: {
rules: [
{ required: true, message: '请输入' },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('pwd') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('两次密码不一致'));
},
}),
],
},
},
{
type: Input.TextArea,
name: 'desc',
label: '简介',
elProps: {
placeholder: '个人简介',
rows: 4,
},
},
{
// 自定义render
render() {
return (
<Form.Item wrapperCol={{ offset: 6 }}>
<Space>
<Button htmlType="submit" type="primary">
确定
</Button>
<Button htmlType="reset">重置</Button>
</Space>
</Form.Item>
);
},
},
];
return (
<StyledOneRow>
<Form
form={form}
labelCol={{ span: 6 }}
onFinish={(v) => {
message.success(JSON.stringify(v));
}}
>
<FormRender layoutData={oneRowLayout}></FormRender>
</Form>
</StyledOneRow>
);
};
export default OneCol;
2. 一行多列布局
import React, { useState } from 'react';
import styled from 'styled-components';
import { Input, Radio, Form } from 'antd';
import FormRender from 'antd-form-render';
const StyledOneRow = styled.div`
width: 800px;
`;
const MultipleCols = () => {
const layout = [];
const [cols, setCols] = useState(4);
for (let i = 0; i < 11; i++) {
layout.push({
type: Input,
label: `输入框${i + 1}`,
placeholder: '请输入',
name: `name${i}`,
});
}
return (
<StyledOneRow>
<Form layout="vertical">
<div style={{ margin: '16px 0' }}>
<Radio.Group
onChange={(e) => setCols(Number(e.target.value))}
optionType="button"
value={cols}
>
<Radio value={1}>1行1列</Radio>
<Radio value={2}>1行2列</Radio>
<Radio value={3}>1行3列</Radio>
<Radio value={4}>1行4列</Radio>
</Radio.Group>
</div>
<FormRender layoutData={layout} cols={cols}></FormRender>
</Form>
</StyledOneRow>
);
};
export default MultipleCols;
3. 等间距排列 (常用于列表页面的搜索等)
import React, { useState } from 'react';
import styled from 'styled-components';
import { Input, Radio, Form } from 'antd';
import { FormSpaceRender } from 'antd-form-render';
const StyledOneRow = styled.div`
width: 1000px;
`;
const SpaceLayout = () => {
const layout = [];
const [space, setSpace] = useState(8);
for (let i = 0; i < 3; i++) {
layout.push({
type: Input,
label: `输入框${i + 1}`,
placeholder: '请输入',
name: `name${i}`,
});
}
return (
<StyledOneRow>
<Form layout="horizontal">
<div style={{ margin: '16px 0' }}>
<Radio.Group
onChange={(e) => setSpace(Number(e.target.value))}
optionType="button"
value={space}
>
<Radio value={8}>8px</Radio>
<Radio value={16}>16px</Radio>
<Radio value={24}>24px</Radio>
<Radio value={32}>32px</Radio>
</Radio.Group>
</div>
<FormSpaceRender layoutData={layout} size={space}></FormSpaceRender>
</Form>
</StyledOneRow>
);
};
export default SpaceLayout;
4.表单联动
- 定义form onValuesChange 同步状态到外部state, 触发重新渲染实现表单联动(全量渲染)
- 利用Form.Item dependencies/shouldUpdate和自定义render实现表单联动 (非全量渲染)
import React, { useState } from 'react';
import styled from 'styled-components';
import { Form, Button, Radio, message } from 'antd';
import FormRender from 'antd-form-render';
const StyledOneRow = styled.div`
width: 600px;
`;
const StyledP = styled.p`
padding: 10px;
`;
const OneColWithDynamicControl = () => {
const [form] = Form.useForm();
const [form1] = Form.useForm();
// 用于同步表单状态
const [data, setData] = useState({});
const layout = [
{
type: Radio.Group,
label: '性别',
name: 'gender',
elProps: {
options: [
{ label: '男', value: '男生' },
{ label: '女', value: '女生' },
],
},
},
{
type: 'div',
label: '你是',
elProps: {
children: data.gender || '未选择'
},
},
{
type: Button,
elProps: {
htmlType: 'submit',
type: 'primary',
children: '确定',
},
itemProps: {
wrapperCol: { offset: 6 },
},
},
];
// 基于antd dependency 实现表单联动
const layout1 = [
{
type: Radio.Group,
label: '性别',
name: 'gender',
elProps: {
options: [
{ label: '男', value: '男生' },
{ label: '女', value: '女生' },
],
},
},
{
render() {
return (
<Form.Item label="你是" dependencies={['gender']}>
{() => {
const gender = form1.getFieldValue('gender');
return gender || '未选择';
}}
</Form.Item>
);
},
},
{
type: Button,
elProps: {
htmlType: 'submit',
type: 'primary',
children: '确定',
},
itemProps: {
wrapperCol: { offset: 6 },
},
},
];
return (
<StyledOneRow>
<StyledP>1.定义onValuesChange 同步状态到state , 触发重新渲染实现表单联动</StyledP>
<Form
form={form}
onValuesChange={(v) => {
setData((p) => ({ ...p, ...v }));
}}
labelCol={{ span: 6 }}
onFinish={(v) => {
message.success(JSON.stringify(v));
}}
>
<FormRender layoutData={layout}></FormRender>
</Form>
<StyledP>2.利用Form.Item dependencies/shouldUpdate 和自定义render 实现表单联动</StyledP>
<Form
form={form1}
labelCol={{ span: 6 }}
onFinish={(v) => {
message.success(JSON.stringify(v));
}}
>
<FormRender layoutData={layout1}></FormRender>
</Form>
</StyledOneRow>
);
};
export default OneColWithDynamicControl;
组件类型定义
export declare type FormRenderProps = {
/**
* 一维数组:从上往下一行放一个表单项 ,如果设置了cols=2/3/4 ,则一行放置cols(2/3/4)个表单项
* 二维数组:子数组配置的表单项目会被渲染为一行
*/
layoutData: Item[] | Item[][];
cols?: 1 | 2 | 3 | 4;
};
// 等间距布局组件
import type { SpaceProps } from 'antd';
export declare type SpaceLayoutProps = SpaceProps & {
layoutData: Item[];
};
export declare type Item = {
/** 组件类型,比如Input,Button,"input" */
type?: React.ComponentType | string;
/** 传给Form.Item的name,作为form data的key */
name?: string;
/** label名称 */
label?: string;
/** 自定义render */
render?: () => React.ReactNode;
/** 动态返回Item,优先级高于render */
getJSON?: () => Item | null;
/** 组件的props,比如Button,Input的props,会透传给type定义的组件 */
elProps?: Record<string, unknown>;
/** Form.Item的props,会透传给Form.Item */
itemProps?: Record<string, unknown>;
/** Form.Item的rules,在itemProps里面定义也可,放这里主要为了兼容 */
rules?: Rule[];
};