本文共 6866 字,大约阅读时间需要 22 分钟。
react后台项目,大多都是表单处理,比如下列4种常见1*n布局 (如果手工编码,大量的Row,Col, Form.Item的嵌套,排列,如果加上联动处理,代码将十分臃肿,不易维护)
一行一列
一行两列
一行三列
一行四列
对于这列表单开发, 完全可以基于配置生成, 我们可以定义一个数组,数组的每一项都是一个表单项, 对于一行一列的排列, 我们可以自上而下一行一个组件进行render , 伪代码如下
return arr.map((item, idx) => itemRender(item, idx, 24))
对于一行n列 (n=2/3/4 , 参考antd grid布局, 一行最多不超过4个表单项 )
基于24 栅格系统,可以我们计算出每个组件占用的栅格数24/n , 基于此,我们可以动态创建Grid,自上而下一行一组进行render实现一行多列布局 ,伪代码如下const len = group.length; const span = 24 / len; return ({arr.map((item, subIndex) => itemRender(item, subIndex, span))}
);
上述 itemRender 用于render表单组件, antd4表单项通常这么写 ,一个Form.Item 包裹一个表单控件,参考如下
基于js我们可以抽离出如下配置项, 1. render什么组件,例如Input/Select/等,另外配置组件props 2. Form.Item 配置 , 我们可以设计如下通用js对象表示这些信息
{ type?: React.ComponentType | string; // 组件类型, 比如Input 等 name?: string; //Form.Item的name label?: string; // Form.Item的label elProps?: Record; // 组件的props配置 , 比如type为Input, elProps则会配置到Input itemProps?: Record ; // Form.Item的props配置,除了上面name,lable,rules三个常用的,其他的可以放在这里配置 rules?: Rule[]; // Form.Item的rules};
根据上面的js对象配置信息,我们可以实现itemRender动态创建组件和布局
const itemRender = (item: Item, key: number | string, span = 24) => { if (typeof item !== 'object' || !item) return null; const { type, name, rules, label, elProps = {}, itemProps = {}, render, ...props } = item; return (
为了更方便实现表单联动和支持render任意组件(不仅仅是表单), 我们可以扩展js加上render和getJSON 方法(当然叫getConfigJs更合适)
{ type?: React.ComponentType | string; // 组件类型, 比如Input 等 name?: string; //Form.Item的name label?: string; // Form.Item的label render?: () => React.ReactNode; //自定义 render getJSON?: () => Item | null; // 动态返回Item配置 elProps?: Record; // 组件的props配置 , 比如type为Input, elProps则会配置到Input itemProps?: Record ; // Form.Item的props配置,除了上面name,lable,rules三个常用的,其他的可以放在这里配置 rules?: Rule[]; // Form.Item的rules};
自此, 一个通用的antd form-render 就编写完了, 可以参考
用 / 安装:
$ npm install --save antd-form-render$ yarn add antd-form-render
import React, { useState } from 'react';import FormRender from 'antd-form-render';import { Form, Button, Space, Input, Radio, Select } from 'antd';export default function App() { const [data, setData] = useState({}); // 定义form const [form] = Form.useForm(); // 一维数组定义layout,从上往下一行放一个表单控件 const layout = [ { type: Input, label: '手机号', placeholder: '请输入', name: 'tel', // 对Input的配置 , elProps对type指定的组件配置 elProps: { maxLength: 11, }, // 对Form.Item的配置 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: Radio.Group, label: '性别', name: 'gender', elProps: { options: [ { label: '男', value: '男' }, { label: '女', value: '女' }, ], }, }, { // 根据条件动态返回object getJSON() { return data.gender === '男' ? { type: Input, label: '兴趣爱好(男)', placeholder: '请输入兴趣爱好', name: 'hobby', itemProps: { rules: [{ required: true, message: '请输入兴趣爱好' }], }, } : data.gender === '女' ? { type: Select, label: '兴趣爱好(女)', placeholder: '请选择兴趣爱好', name: 'hobby', itemProps: { itemProps: { rules: [{ required: true, message: '请选择兴趣爱好' }], }, }, elProps: { options: [ { label: '画画', value: '画画' }, { label: '唱歌', value: '唱歌' }, { label: '跳舞', value: '跳舞' }, ], }, } : null; }, }, { type: Input.TextArea, name: 'desc', label: '简介', elProps: { placeholder: '个人简介', rows: 4, }, itemProps: { rules: [ { required: true, }, ], }, }, { // 自定义render render() { return (
const layout = [ [ { type: Input, label: '11', placeholder: '请输入', name: '11', }, { type: Input, label: '12', placeholder: '请输入', name: '12', }, ], [ { type: Input, label: '21', placeholder: '请输入', name: '21', }, { type: Input, label: '22', placeholder: '请输入', name: '22', }, ],];
// 一维数组,设置了cols 为1/2/3/4 ,实现自动从左至右,从上到下的 1*cols 1行多列自动布局const layout3 = [];for (let i = 0; i < 11; i++) { layout3.push({ type: Input, label: `输入框${i + 1}`, placeholder: '请输入', name: `name${i}`, });};
配置说明
// 组件export default function FormRenderer({ layoutData, cols }: FormRenderProps): React.ReactNode;// 组件propsexport declare type FormRenderProps = { layoutData: Array- ; // 1/2维数组 cols: null | 1 | 2 | 3 | 4; // 自动布局1行显示几列 default 1};// 数组配置项export declare type Item = { type?: React.ComponentType | string; // 组件类型, 比如Input 等 name?: string; //Form.Item的name label?: string; // Form.Item的label render?: () => React.ReactNode; //自定义 render getJSON?: () => Item | null; // 动态返回Item配置 elProps?: Record
; // 组件的props配置 , 比如type为Input, elProps则会配置到Input itemProps?: Record ; // Form.Item的props配置,除了上面name,lable,rules三个常用的,其他的可以放在这里配置 rules?: Rule[]; // Form.Item的rules};
运行示例, yarn start / npm start 查看 demo , 效果如下
转载地址:http://mrjyz.baihongyu.com/