引言

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

主要思路

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

  • 可以将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. CSPS模拟 50

    收获很多,良心出题人 T1 施工 研究半天,最后30分暴力走人 考后看了题解,稍神仙这题弃对了...... 要拿30+,必须发现要填的话一定是填一个坑使它底部变平,最终底部高度小于等于两边 为什么是坑 ...

  2. NOIP模拟测试13

    考得还算可以,T3还有提升空间(没看清题&&样例没过 拿了4分). 期望得分:80+40+0=120 实际得分:80+85+4=169 一脸黑线.....是数据比较水的原因,T2分都比 ...

  3. 手写jwt验证,实现java和node无缝切换

    前言 前端时间和我朋友写了一个简易用户管理后台,功能其实很简单,涉及到的技术栈有:vue+elementUI,java+spring MVC以及node+egg,数据库用的mysql,简单方便. 一开 ...

  4. Vue基础系列(二)——Vue中的methods属性

      写在前面的话: 文章是个人学习过程中的总结,为方便以后回头在学习. 文章中会参考官方文档和其他的一些文章,示例均为亲自编写和实践,若有写的不对的地方欢迎大家指出. 作者简介: 一个不知名的前端开发 ...

  5. java多线程回顾4:线程通信

    1.线程的协调运行 线程的协调运行有一个经典案例,即生产者和消费者问题. 假设有一个货架,生产者往货架上放货物,消费者从货架上取货物. 为了方便讲解,制定一个规则,生产者每放上一个货物,消费者就得取走 ...

  6. etcd-operator快速入门完全教程

    Operator是指一类基于Kubernetes自定义资源对象(CRD)和控制器(Controller)的云原生拓展服务,其中CRD定义了每个operator所创建和管理的自定义资源对象,Contro ...

  7. 类加载器 - ClassLoader详解

    获得ClassLoader的途径 获得当前类的ClassLoader clazz.getClassLoader() 获得当前线程上下文的ClassLoader Thread.currentThread ...

  8. Java关于Resource leak: 's' is never closed的问题

    Resource leak: 's' is never closed的问题 问题:在编写Java时出现了Resource leak: 's' is never closed的问题,也就是对象s下面的波 ...

  9. 图解 Spring:HTTP 请求的处理流程与机制【3】

    3. HTTP 请求在 Web 应用中的处理流程 在穿越了 Web 容器之后,HTTP 请求将被投送到 Web 应用,我们继续以 Tomcat 为例剖析后续流程.Web 容器与 Web 应用的衔接是通 ...

  10. VS Code 中文社区正式成立啦!VS Code Day 圆满落幕!

    背景简介 Visual Studio Code 是一款现代化轻量级代码编辑器,它免费.开源.跨平台.功能强大.本次 VS Code Day 是广大 VS Code 爱好者一起学习与交流的盛会,让我们对 ...