基于antd封装一个高可用form组件10bet,减少cv代码导

来源:http://www.chinese-glasses.com 作者:Web前端 人气:137 发布时间:2020-03-24
摘要:时间: 2019-11-09阅读: 46标签: antd引言 JSON表单描述 在开发中台过程中我们的原型中有很多表单,antd有表单组件,但是粒度比较细,就单纯组件而言,无可厚非,但是在开发过程中,可能

时间: 2019-11-09阅读: 46标签: antd引言

JSON表单描述

在开发中台过程中 我们的原型中有很多表单,antd有表单组件,但是粒度比较细,就单纯组件而言,无可厚非,但是在开发过程中,可能会造成代码不够聚合,有些表单公共逻辑无法提取,copy paste比较多,所以可以加以封装,搞一个兼容性和扩展性都契合项目本身的组件。

JSON表单是一个基于React的抽象组件,它可以把JSON数据格式描述的表单转换成项目中的表单,它可以用简短的几行代码,快速的生成Form表单。

主要思路

JSON表单的优点是:

我简单查阅了一番资料,参考了一下别人的封装方式,决定以Col和FormItem作为基本单元,进行表单的搭建,主要原因

可以快速构建出一个表单 表单的数据、逻辑、视图分离,方便抽离和进一步抽象 提供校验、自动缓存等额外功能,提升录入体验 可以跨项目的共用复杂的表单组件

可以将FormItem的信息和Col的信息以对象方式传入,这样我们可以更加专注于组件的包含的信息 减少CV代码可能导致的bugCol可以进行布局的调整,可以调整表单单元位置和所占宽度表单的组件形式是不定的,可能是input也有可能是select,所以可以进行外部传入,通用属性可以内部修改复用性高,可以用它来实现一个纯提交表单 和列表组件结合成可搜索表单 或者其他任何项目里需要自定义的一个表单实现细节抽象的FormItemInfo

原始表单的缺点

export interface FormItemInfo { name: string, //label id: string, // 属性 colLayOut?: object,// 列布局 formItemLayout?: object,// 表单项布局 Comp?: any, // 传入的组件 不传默认input ruleArr?: RuleObj[],// 验证规则 initialValue?: string, // 初始值 required?: boolean, // 是否必填 CompExtraProps?: object // 传入组件额外属性 isDetail?: boolean //是否需要被getFieldDecorator托管}

1:代码量庞大,开发效率低每次开发一个表单页的时候,都需要重复编写表单组件及其交互事件的代码,这块代码重复编写且与主线业务逻辑无关,除此之外,表单的校验、缓存等额外功能,也需要不少的代码量,这样就造成了一个表单页的代码量庞大。

baseForm组件

2:不便于抽离和进一步的抽象在一个表单页内,往往会将表单数据、表单组件、控制逻辑杂糅在一起,当发现某一个子功能在很多场景下都需要用到的时候,想把该子功能拆分出来发现并不容易,因为逻辑、数据、视图的杂糅,导致想把子功能不受影响的剔除出来需要很仔细的检查,这样就导致功能的抽离和抽象的不便

 * @param name 表单项label * @param id 属性字段名 * @param colLayOut 列布局 * @param formItemLayout 表单项布局 * @param Comp 传入的组件 不传默认input * @param ruleArr 验证规则 长度只需传入{max:xxx}验证信息是统一的 * @param initialValue 初始值 编辑时推荐使用antd的mapPropsToFields 不要手动设置回显值 * @param required 是否必填 默认否 因为要统一写验证提示 * @param CompExtraProps 组件额外属性 const mapToFormItem = (val: FormItemInfo) = { ...... // 是否传入组件 const hasCompType = Comp  Comp.type // 根据组件类型 给出提示文字 const msg = getMsgByCompType(hasCompType  hasCompType.name, name) // 判断是不是select组件 是的话 调整宽度样式 const mixStyle = fixStyleByCompType(hasCompType  hasCompType.name) // 生成验证规则 const rules = getFormRules(ruleArr || [], required || false, name) return ( Col {...(colLayOut || defaultColSpan)} key={id} {!isDetail ? FormItem label={name} key={id} {...(formItemLayout || {})} {/*用cloneElement方法给传入的组件加新属性*/} { getFieldDecorator(id, { initialValue: initialValue || '', rules, })(Comp ? React.cloneElement(Comp, { placeholder: msg, ...mixStyle, ...CompExtraProps }) : Input type="text" placeholder={msg}/) } /FormItem : FormItem label={name} key={id} {...(formItemLayout || {})} {Comp}/FormItem} /Col )}

3:维护成本高这个和第二个问题是同样的原因,这也是我的亲身经历,当我在项目中想优化一个小功能的时候,发现不仅把之前的逻辑改没了,还引出了不少的bug,这导致在一个逻辑很复杂的表单里,维护变成了一件高危工作。

统一方法根据组件类型 给出提示文字

4:需要额外处理校验和缓存等功能

 /**根据组件类型 给出提示文字 * @param type 组件类型 根据传入组件的函数名判断 在map里维护 * @param name label名称 * @returns 提示文字 * */ const getMsgByCompType = (name: string, type?: string): string = { if (!type) { return `请输入${name}` } const map: { [props: string]: string } = { Select: '请选择', Input: '请输入', default: '请输入', } return `${map[type] || map.default}${name}` }

一个栗子

生成验证规则

const config = { formKey: 'example-form', data: { name: '', descr: '', typeName: '' }, config: [ { type: 'input', dataKey: 'name', label: 'param', placeholder: '请输入param', validate: ['required', /^[a-zA-Z_{}0-9]+$/g], style: { display: 'inline-block', width: 270, }, }, { type: 'select', dataKey: 'typeName', options: ['string', 'integer', 'float'], style: { display: 'inline-block', width: 100, margin: '0 15px' }, validate: [{type: 'required', message: 'param类型不能为空'}] }, { type: 'textarea', dataKey: 'descr', placeholder: '请输入param含义', label: 'param含义', validate: ['required'], style: { width: 385, } }, ]} this.FormWrap = ref} config={config}>
 // 生成验证规则 * @param ruleArr 验证规则 长度只需传入{max:xxx}验证信息是统一的 * @param name 表单项label * @param required 是否必填 默认否 因为要统一写验证提示 const getFormRules = (val: RuleObj[], required: boolean, name: string) = { // 根据max生成最长输入提示 const ruleArr: object[] = [] val.forEach(item = { if ('max' in item) { ruleArr.push({ ...item, message: `输入信息不能超过${item.max}字` }) } else { ruleArr.push(item) } }) // 根据name生成报错提示 return [{ required: required, message: `${name}不能为空` }, ...ruleArr]

上面是用JSON描述的三个常用的表单组件组合成的表单,其效果图如下:

根据组件类型 给出统一样式修改

JSON表单的格式

 /**根据组件类型 给出统一样式修改 * @param type 组件类型 根据传入组件的函数名判断 在map里维护 * @returns 样式对象 * */ const fixStyleByCompType = (type?: string): object = { if (!type) { return {} } const map: { [props: string]: object } = { Select: { style: { width: '100%' } }, default: {}, } return map[type] || map.default }
{ formKey: 'paramAddForm', data: {}, config: []}

注意:当出现表单显示一个详情文字或者一个按钮 此时需要用isDetail干掉getFieldDecorator托管

属性

实现提交表单SubmitForm

是否必传

 * @param form -`antd`的form * @param title - 主标题 * @param subTitle - 分组标题 * @param FormItemInfos - 二维数组 顺序和数量和分组标题对应 存放表单项信息 * @param isLoading - `dva-loading` 执行effects过程中的loading * @param onSubmit - 传入的submit方法 * @param buttonArea - 可选 不传默认数提交和取消 传入覆盖 * @param children - 在表单下面 按钮区域上面的传入内容 * @param formLayOutType - 布局方式 详情见`getLayOutByType`方法 * @returns ReactNode...... Form onSubmit={onSubmit} SubmitLayOut subTitle={subTitle} title={title} renderFormArea={renderFormArea} {children} {buttonArea?buttonArea: Row type="flex" gutter={24} justify="center" Col Button type="primary" htmlType="submit" loading={isLoading}提交/Button /Col Col Button type="default" onClick={() = router.go(-1)}取消/Button /Col /Row} /SubmitLayOut /Form

说明

对BaseForm的调用在renderFormArea方法里

类型

// 水平和垂直布局export enum FormLayOutType { normal = 'normal', vertical = 'vertical'}const renderFormArea = (idx: number) = { // 在这边加上normal表单的layout const FormItemInfo = FormItemInfos[idx] // 根据传入参数 返回布局类型 const _FormItemInfo = getLayOutByType(formLayOutType||FormLayOutType.normal, FormItemInfo) return BaseForm formItems={_FormItemInfo} form={form}/ }

默认值

FormItemInfos这里是二维数组,二维分别承载分组信息和表单项信息 而且在这里用getLayOutByType封装常用的水平 垂直布局方式

formKey 否 用来自动缓存,localStorage的key,不传表示不自动缓存 string - className 否 用来添加一些自定义样式 string - data 是 表单的提交数据,有自动缓存和校验功能 object - assisData 否 用于表单控制逻辑的额外数据 object - config 是 组件配置,表单组件的配置 Array - realTimeSubmit 否 表单是否实时提交,一般用于筛选表单 boolean false

这里结合了我们业务里的表单布局 大标题 和小标题对表单区域进行分组,我将布局单独搞了个SubmitLayOut组件 将渲染逻辑和样式组件在其中实现 这样也方便更换成其他LayOut

表单组件的配置

// SubmitLayOut * @param title - 主标题 * @param subTitle - 分组标题 * @param renderFormArea - 根据分组的顺序 渲染表单区域 * @param children - 传入内容...... Card title={title} bordered={false} className={styles["special-card"]} List itemLayout="vertical" className="special-list" {subTitle.map((item,idx) = { return (  Meta title={item}/ List.Item {renderFormArea(idx)} /List.Item / ) })} /List {children} /Card
{ type: 'input', dataKey: 'name', label: 'param', validate: ['required'], style: {}}

结语

属性

后续还实现了serachForm等业务相关性高的组件,所以个人觉得封装的思路主要是

是否必传

底层组件越纯粹越好中层可以实现一些适用性比较高的具体业务组件和通用方法高层就具体要页面的细节的方方面面了

说明

原文:-form-component-rebuild.html

类型

默认值

type 是 表单组件的类型,其值可以为: input、select、textarea、form_array、container和一些自定义表单组件 string - dataKey 否 指定表单组件值的key,可以为param.name.firstName形式 string - label 否 表单组件的label string - placeholder 否 表单组件的placeholder string - validate 否 表单组件的校验规则 Array - style 否 表单组件的布局样式 string - options 否 当表单组件为select时,需要传入的options Array - render 否 当type为container时,为自定义组件,render为渲染方法 Function - preventSubmit 否 当realTimeSubmit为true时,控制当前表单组件是否实时提交 boolean false children 否 当type为form_array时,children表示子组件配置列表 Array - modifyDataFn 否 当type为自定义组件时,且需要覆盖render方法中的提交数据方法,可以使用modifyDataFn来重新自定义提交数据 Function -

关键字段解释

1. type

type是用来唯一表示表单组件类型的字段,其中JSON表单提供了三种默认的表单组件:input、select、textarea,还有两种复杂类型的表单组件:form_array、container。

form_array表单组件表示其数据结构为Array,含有增加项删除项的复合表单组件,该表单组件的配置里多一个children的字段,里面是每一项里面的表单组件配置的集合,其表单组件的效果如下图所示:

container是用来自定义表单的接口,具体用法参考下面具体的介绍。

2. validate

validate是校验表单组件数据正确性的字段,其值为数组,里面的数组元素可以为String、object、RegExp、Function。

JSON表单采用的是async-validator异步处理校验,在JSON表单内部会将validate传入的校验关键字解析为async-validator的rules。所以validate数组元素如果为object的话,其内容就是async-validator的rules。

  1. 数组元素为string,其值可以为:

string,值必须为string number,值必须为数字 required,值不能为空 boolean,值必须为布尔值 integer,值必须为整数形 float,值必须为浮点型 email,值必须为邮箱类型

  1. 数组元素为object,其值为rules:{type: 'enum', enum: ['1', '2'], message: '值不在确定范围内'}

  2. 数组元素为RegExp, validate: [/^[a-zA-Z_{}0-9]+$/g]

  3. 数组元素为Function, validate: [ (rules, value, callback) => {}]

3. style

用来确定表单组件在表单内的布局样式,比如想让表单组件行内显示,且宽度为200,其style值如下:

{ display: 'inline-block', width: 200}

container表单组件

本文由10bet发布于Web前端,转载请注明出处:基于antd封装一个高可用form组件10bet,减少cv代码导

关键词:

最火资讯