引言

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

主要思路

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

  • 可以将FormItem的信息和Col的信息以对象方式传入,这样我们可以更加专注于组件的包含的信息 减少CV代码可能导致的bug
  • Col可以进行布局的调整,可以调整表单单元位置和所占宽度
  • 表单的组件形式是不定的,可能是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托管
}

baseForm组件

 * @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>
)

统一方法

  • 根据组件类型 给出提示文字
  /**根据组件类型 给出提示文字
* @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}`
}
  • 生成验证规则
  // 生成验证规则
* @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]
  • 根据组件类型 给出统一样式修改
  /**根据组件类型 给出统一样式修改
* @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
}

注意:当出现表单显示一个详情文字或者一个按钮 此时需要用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封装常用的水平 垂直布局方式

这里结合了我们业务里的表单布局 大标题 和小标题对表单区域进行分组,我将布局单独搞了个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>

结语

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

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

基于antd封装一个高可用form组件 减少cv代码导致的bug的更多相关文章

  1. 基于iview 封装一个vue 表格分页组件

    iview 是一个支持中大型项目的后台管理系统ui组件库,相对于一个后台管理系统的表格来说分页十分常见的 iview是一个基于vue的ui组件库,其中的iview-admin是一个已经为我们搭好的后天 ...

  2. 如何基于 React 封装一个组件

    如何基于 React 封装一个组件 前言 很多小伙伴在第一次尝试封装组件时会和我一样碰到许多问题,比如人家的组件会有 color 属性,我们在使用组件时传入组件文档中说明的属性值如 primary , ...

  3. 基于Containerd安装部署高可用Kubernetes集群

    转载自:https://blog.weiyigeek.top/2021/7-30-623.html 简述 Kubernetes(后续简称k8s)是 Google(2014年6月) 开源的一个容器编排引 ...

  4. SpringCloud学习系列之一 ----- 搭建一个高可用的注册中心(Eureka)

    前言 本篇主要介绍的是SpringCloud相关知识.微服务架构以及搭建一个高可用的服务注册与发现的服务模块(Eureka). SpringCloud介绍 Spring Cloud是在Spring B ...

  5. 美团点评基于MGR的CMDB高可用架构搭建之路【转】

    王志朋 美团点评DBA 曾在京东金融担任DBA,目前就职于美团点评,主要负责金融业务线数据库及基础组件数据库的运维. MySQL Group Replication(以下简称MGR),于5.7.17版 ...

  6. 如何安装一个高可用K3s集群?

    作者介绍 Janakiram MSV是Janakiram & Associates的首席分析师,也是国际信息技术学院的兼职教师.他也是Google Qualified Developer.亚马 ...

  7. 阿里云有奖体验:用PolarDB-X搭建一个高可用系统

    体验简介 场景将提供一台配置了CentOS 8.5操作系统和安装部署PolarDB-X集群的ECS实例(云服务器).通过本教程的操作,带您体验如何使用PolarDB-X搭建一个高可用系统,通过直接ki ...

  8. 微服务配置内容《网上copy》=========》如何创建一个高可用的服务注册中心

    前言:首先要知道什么是一个高可用的服务注册中心,基于spring boot建成的服务注册中心是一个单节点的服务注册中心,这样一旦发生了故障,那么整个服务就会瘫痪,所以我们需要一个高可用的服务注册中心, ...

  9. 基于Consul的数据库高可用架构【转】

    几个月没有更新博客了,已经长草了,特意来除草.本次主要分享如何利用consul来实现redis以及mysql的高可用.以前的公司mysql是单机单实例,高可用MHA加vip就能搞定,新公司mysql是 ...

随机推荐

  1. setAccessible()方法

    在java代码中,我们经常使用private来控制类中成员变量的访问权限,在类的外边我们一般使用get方法获取私有成员变量的值,但是如果类中没有get方法,但我们又想获取该类私有成员变量的值,该怎么办 ...

  2. maven编码配置

    在环境变量里添加变量 MAVEN_OPTS -Xms256m -Xmx512m -Dfile.encoding=UTF-8

  3. ThinkPHP 6.0 管道模式与中间件的实现分析

    设计模式六大原则 开放封闭原则:一个软件实体如类.模块和函数应该对扩展开放,对修改关闭. 里氏替换原则:所有引用基类的地方必须能透明地使用其子类的对象. 依赖倒置原则:高层模块不应该依赖低层模块,二者 ...

  4. [LC]235题 二叉搜索树的最近公共祖先 (树)(递归)

    ①题目 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个结点 x,满足 x 是 p.q 的祖先 ...

  5. lufylegend.js教程(1)

    1.图片元素如何缩小? 在LSprite类中,有两个属性:{scaleX,scaleY},这两个属性属于按比例缩放精灵对象,可以放大,可以缩小,注意这两个属性是在图片中心点位置开始缩放. 代码: Bo ...

  6. ubuntu 18 怎样对Windows进行远程桌面控制

    ubuntu 18 怎样对Windows进行远程桌面控制: 1. 先安装一个redesktop 工具(sudo apt-get install redesktop) 2. 在通过 redesktop ...

  7. hdu 1878 欧拉回路(联通<并查集> + 偶数点)

    欧拉回路Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  8. 半自动安装 linux 系统

    基于图形软件生成应答文件,实现半自动化安装 linux 操作系统 1.yum install -y system-config-kickstart 安装生成自动化安装操作系统的应答文件 2.syste ...

  9. Nginx下HTTP强制重定向至HTTPS

    Nginx下HTTP强制重定向至HTTPS 对于nginx来说,配置http强制重定向至https有多种多样的写法.可以直接rewrite,也可以用301重定向.但是直接拷贝网上的配置往往会出现问题, ...

  10. Alibaba Nacos 学习(三):Spring Cloud Nacos Discovery - FeignClient,Nacos 服务注册与发现

    Alibaba Nacos 学习(一):Nacos介绍与安装 Alibaba Nacos 学习(二):Spring Cloud Nacos Config Alibaba Nacos 学习(三):Spr ...