引言

在开发中台过程中 我们的原型中有很多表单,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. csps模拟测试70

    又炸了,T1没开$long long$,炸掉$50pts$,昨天因为SB错误挂掉$100pts$. 我kuku了,以后细心点吧.

  2. 使用Typescript重构axios(二十七)——添加请求状态码合法性校验

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  3. P3084 [USACO13OPEN]照片(差分约束)

    (已经有了简化版题面) 又秒了一次dp233 本来按照感觉瞎写了一发... 但还是老老实实列式子吧.... 对差分约束有了更深的理解 #include<cstdio> #include&l ...

  4. 百度艾尼(ERNIE)常见问题汇总及解答

    一.ERNIE安装配置类问题 Q1:最适合ERNIE2.0的PaddlePaddle版本是?A1:PaddlePaddle版本建议升级到1.5.0及以上版本. Q2:ERNIE可以在哪些系统上使用?A ...

  5. 1000m交叉网线最简单做法

    1-3,2-6,3-1,4-7,5-8,6-2,7-4,8-5 1,2,3,4,5,6,7,8即为网线内部8跟线编号,两头颜色和数字要对应.

  6. 关于vue ui组件

    一.vue ui  组件 1: vue 当前很火前端框架vue 针对PC用户 pc 端与移动端区别 (1)屏幕宽度:992px > (2)操作方式:鼠标 事件   手指:触碰操作 -饿了么:基于 ...

  7. 【algo&ds】【吐血整理】4.树和二叉树、完全二叉树、满二叉树、二叉查找树、平衡二叉树、堆、哈夫曼树、B树、字典树、红黑树、跳表、散列表

    本博客内容耗时4天整理,如果需要转载,请注明出处,谢谢. 1.树 1.1树的定义 在计算机科学中,树(英语:tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结 ...

  8. Ubuntu18.04.3 LTS初体验

    安装系统 想来虚拟机安装太慢,正好有一台旧电脑,干脆整个乌班图系统. 启动盘工具:UltraISO,老牌工具了. 上官网下载ISO镜像文件: https://cn.ubuntu.com/downloa ...

  9. [LC] 112题 路径总和(在二叉树里判断是否有哪条路径之和等于某个值)

    ①题目 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和. 说明: 叶子节点是指没有子节点的节点. 示例: 给定如下二叉树,以及目标和 sum ...

  10. nyoj 199-无线网络覆盖 (ceil())

    199-无线网络覆盖 内存限制:64MB 时间限制:3000ms 特判: No 通过数:4 提交数:13 难度:3 题目描述: 我们的乐乐同学对于网络可算得上是情有独钟,他有一个计划,那就是用无线网覆 ...