引言

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

主要思路

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

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

实现细节

抽象的FormItemInfo

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

baseForm组件

  1. * @param name 表单项label
  2. * @param id 属性字段名
  3. * @param colLayOut 列布局
  4. * @param formItemLayout 表单项布局
  5. * @param Comp 传入的组件 不传默认input
  6. * @param ruleArr 验证规则 长度只需传入{max:xxx}验证信息是统一的
  7. * @param initialValue 初始值 编辑时推荐使用antdmapPropsToFields 不要手动设置回显值
  8. * @param required 是否必填 默认否 因为要统一写验证提示
  9. * @param CompExtraProps 组件额外属性
  10. const mapToFormItem = (val: FormItemInfo) => {
  11. ......
  12. // 是否传入组件
  13. const hasCompType = Comp && Comp.type
  14. // 根据组件类型 给出提示文字
  15. const msg = getMsgByCompType(hasCompType && hasCompType.name, name)
  16. // 判断是不是select组件 是的话 调整宽度样式
  17. const mixStyle = fixStyleByCompType(hasCompType && hasCompType.name)
  18. // 生成验证规则
  19. const rules = getFormRules(ruleArr || [], required || false, name)
  20. return (
  21. <Col {...(colLayOut || defaultColSpan)} key={id}>
  22. {!isDetail ?
  23. <FormItem label={name} key={id} {...(formItemLayout || {})}>
  24. {/*用cloneElement方法给传入的组件加新属性*/}
  25. {
  26. getFieldDecorator(id, {
  27. initialValue: initialValue || '',
  28. rules,
  29. })(Comp ? React.cloneElement(Comp, { placeholder: msg, ...mixStyle, ...CompExtraProps }) : <Input type="text" placeholder={msg}/>)
  30. }
  31. </FormItem> : <FormItem label={name} key={id} {...(formItemLayout || {})}> {Comp}</FormItem>}
  32. </Col>
  33. )

统一方法

  • 根据组件类型 给出提示文字
  1. /**根据组件类型 给出提示文字
  2. * @param type 组件类型 根据传入组件的函数名判断 在map里维护
  3. * @param name label名称
  4. * @returns 提示文字
  5. * */
  6. const getMsgByCompType = (name: string, type?: string): string => {
  7. if (!type) {
  8. return `请输入${name}`
  9. }
  10. const map: { [props: string]: string } = {
  11. Select: '请选择',
  12. Input: '请输入',
  13. default: '请输入',
  14. }
  15. return `${map[type] || map.default}${name}`
  16. }
  • 生成验证规则
  1. // 生成验证规则
  2. * @param ruleArr 验证规则 长度只需传入{max:xxx}验证信息是统一的
  3. * @param name 表单项label
  4. * @param required 是否必填 默认否 因为要统一写验证提示
  5. const getFormRules = (val: RuleObj[], required: boolean, name: string) => {
  6. // 根据max生成最长输入提示
  7. const ruleArr: object[] = []
  8. val.forEach(item => {
  9. if ('max' in item) {
  10. ruleArr.push({ ...item, message: `输入信息不能超过${item.max}字` })
  11. } else {
  12. ruleArr.push(item)
  13. }
  14. })
  15. // 根据name生成报错提示
  16. return [{ required: required, message: `${name}不能为空` }, ...ruleArr]
  • 根据组件类型 给出统一样式修改
  1. /**根据组件类型 给出统一样式修改
  2. * @param type 组件类型 根据传入组件的函数名判断 在map里维护
  3. * @returns 样式对象
  4. * */
  5. const fixStyleByCompType = (type?: string): object => {
  6. if (!type) {
  7. return {}
  8. }
  9. const map: { [props: string]: object } = {
  10. Select: { style: { width: '100%' } },
  11. default: {},
  12. }
  13. return map[type] || map.default
  14. }

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

实现提交表单SubmitForm

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

BaseForm的调用在renderFormArea方法里

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

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

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

  1. // SubmitLayOut
  2. * @param title - 主标题
  3. * @param subTitle - 分组标题
  4. * @param renderFormArea - 根据分组的顺序 渲染表单区域
  5. * @param children - 传入内容
  6. ......
  7. <Card title={title} bordered={false} className={styles["special-card"]}>
  8. <List itemLayout="vertical" className="special-list">
  9. {subTitle.map((item,idx) => {
  10. return (
  11. <>
  12. <Meta title={item}/>
  13. <List.Item>
  14. {renderFormArea(idx)}
  15. </List.Item>
  16. </>
  17. )
  18. })}
  19. </List>
  20. {children}
  21. </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. 小白学 Python(19):基础异常处理

    人生苦短,我选Python 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型(下) 小白学 Python(4):变 ...

  2. day1 晚上 P4145 上帝造题的七分钟2 / 花神游历各国 线段树

    #include<iostream> #include<cstdio> #include<cmath> using namespace std; ; struct ...

  3. python——多线程

    多线程特点: • 线程的并发是利用cpu上下文的切换(是并发,不是并行) • 多线程执行的顺序是无序的 • 多线程共享全局变量 • 线程是继承在进程里的,没有进程就没有线程 • GIL全局解释器锁 • ...

  4. Havok Physics 2012(1)

    目录 Chapter 1. Introduction 1. What is a Physics Engine? Chapter 1. Introduction ​ 欢迎来到Havok Physics ...

  5. Apache安装问题:APR not found

    资料来源:Apache遇到的问题:APR not found 安装apache时出现arp错误问题 按照以上文章试验之后整理如下: #./configure --prefix……检查编辑环境时出现: ...

  6. 【持续更新】【pat】pat刷题技巧记录

    修改code completion快捷键位CTRL+ENTER,帮助提示函数名称 修改命令行提示符的属性,开启快速编辑模式,方便调试 添加c++11语言标准支持 开启代码调试功能 对输入的字符串进行切 ...

  7. [JQuery] JQuery学习笔记

    1.2019年10月20日14:43:48 学习HOW2J. 2.JQuery是一个javascript的框架,是对javascript的一种封装, 通过JQuery可以非常方便的操作html的元素 ...

  8. oracle 数据库,能不能将查询的结果创建成新表。

    这个是可以的.sql:create table tablename1 as select t2. * from tablename2 t2 where t2.filename =‘张三’. 解释:就是 ...

  9. 分析facebook的AsyncDisplayKit框架,async-display使用async-transaction

    上一篇<分析facebook的AsyncDisplayKit框架中的Transaction的工作原理>介绍了fb的asdk的异步事务ASAsyncTransaction,本篇介绍其在asd ...

  10. mysql数据库E-R图

    学会绘制E-R图 绘制E-R图首先要了解什么是实体,什么是属性,什么是联系. 1.首先实体是指现实世界中具有区分其他事物的特征或属性与其他实体有联系的实体,针对于数据库中的表而言实体是指表中一行一行特 ...