菜鸟前端【自定义表单】初体验

2,380 阅读8分钟

由于公司经常需要做一些表单页,每次都是重复开发,所以决定搞一下自定义表单,可以大大的减少工作量,说干就干,冲冲冲!

由于是公司内部项目,所以本文只记录实现的思路,代码是不可能放出来的,毕竟我还年轻,我不想进去...

通用组件配置

首先得是定义一些常用的组件,后续可根据业务需求新增

比如:【微信名(小程序或公众号网页版本)】、【定位】、【录音】、【视频】和【手写签名】等组件

[{
    "type": "text",
    "name": "单行文本"
}, {
    "type": "multiline",
    "name": "多行文本"
}, {
    "type": "number",
    "name": "数字"
}, {
    "type": "date",
    "name": "日期"
}, {
    "type": "time",
    "name": "时间"
}, {
    "type": "radio",
    "name": "单选项"
}, {
    "type": "checkbox",
    "name": "多选项"
}, {
    "type": "cascader",
    "name": "级联选择"
}, {
    "type": "address",
    "name": "地址"
}, {
    "type": "image",
    "name": "图片"
}, {
    "type": "formList",
    "name": "表单组"
}]

表单配置

然后就是各表单项的配置,后续可以根据业务拓展

{
    id: "xxx",    // 前端随机生成
    name: "表单项名称",     // 单选、多选、单行文本等
    type: "表单项类型",     // 与后端定义相同规则,如text,radio,checkbox,file,date,time,datetime等
    title: "表单项标题",
    options: [{     // 单选、多选独有配置
        title: "选项名称", 
        id: "选项id,前端随机生成"
    }],
    other: [],       // 其他配置,如是否必填、是否记录上次填写的内容、是否默认使用当前日期或时间等
    conditions: [], // 表单渲染条件,后面会介绍
    [其他字段]: xxx // 针对不同组件增加相应字段。如表单组增加children字段描述表单组内的表单项;级联选择增加placeholder描述各级标题,options变为[{lable: 'xxx', value: 'xxx'}]等。也可以用统一的字段名去描述,这个看个人喜好,能描述清楚就行
}

一些难点

页面交互

这块主要是多借鉴(chao)参考(xi)各个大厂的实现,如腾讯问卷、番茄问卷、草料二维码等,这里就不详细介绍了。

数据转换

antd Form组件收集到的源数据转换成上述【表单配置】格式,并在表单编辑和复制时转换回来便于回填(当然,也可以直接存一份源数据到数据库,就只需要转换一遍。优点是减少代码量,缺点是前后端传输成本增加,毕竟一个表单的源数据转换成json也是挺长的)

这一步也是些常见的数据处理手段

逻辑设置

表单必备的功能,根据单选或多选所选项来控制接下来的表单项是否展示。

  1. 配置

上几个截图

逻辑配置的数据格式,这个数据结构用来描述逻辑规则应该是没问题的

这个格式是哪个表单项配置了就会增加一条规则

[
    {
        item: 'xxx',   // 单选或多选选项id
        rules: [
            {
                option: 'xxx',   // 单选或多选选中项id
                displayItems: ['xxx', 'xxx', ...], // 选中指定选项时页面展示的表单项id
            }
        ]
    }
]
  1. 表单渲染

本来渲染也是想借鉴番茄表单用css控制表单项显示隐藏的,但是由于antd Form组件的校验规则(css隐藏的表单项也会校验),所以将上述规则处理成下面的格式,比较方便渲染

这个规则是哪个表单项需要被控制 conditions 就会多一条规则

{
    id: "xxx",    // 前端随机生成
    ...[上面介绍过],
    conditions: [
        {
            id: 'xxx',      // 控制该表单项展示的表单项id
            value: 'xxx',   // 控制该表单项展示的表单项选中的值
            isParent: Boolean,  // 该规则是本身的规则还是父表单项的规则
        }
    ], // 表单渲染条件
}

为什么需要 isParent 字段呢?因为逻辑规则可能会有嵌套,比如下面的情况:

选中性别为男 => 选中喜欢女生的类型 => 根据不同类型展示不同的输入框

渲染的时候遍历 conditions 中的规则,如果是本身的规则,则满足其中一项即可渲染,使用some如果是父表单项的规则,则需全部满足才能渲染,使用every

这一步格式转换说实话有点恶心,最后还是在老大的帮助下写出来的,但总感觉实现方式不是很优雅,等后面有空了再看看能不能优化下。

还要提一下,在删除表单项或者单选多选的选项时,记得将规则也同步删除,不然渲染会有问题。

也不知道我有没有讲清楚,好气哦...

表单组组件

需求就是类似这种效果

可以添加很多组,然后超过两组时可以删除

这个我看那些现有的都好像没有这个组件,它们用的都是【矩阵】组件,没得借鉴了,自己干吧

分三步走:打开编辑器 => 编写代码 => 保存代码并关闭编辑器

皮一下很开心,emmmm,说正经的...

  1. 首先要把通用组件(除表单组之外的组件)配置抽离,方便表单组配置时复用代码。然后将表单组的配置全部丢到 children 中,渲染的时候根据 type 区分,typeformList 的渲染它的 children,否则直接渲染。
  2. 思考需不需要无限嵌套,表单组中能不能再包含表单组,按道理是可以的,但是业务不需要所以就没做,只做了一个层级的。

这个组件考虑比较多的其实是表单组里的逻辑设置,关于配置方面我是实现了,但是由于 antd Form组件的 Form.List 目前有点问题,就是会自动收集页面未渲染的表单项的值,但是可以跳过校验,所以暂时先把这一块砍掉了。

关于 Form.Listissue

级联选择组件

刚开始想的比较简单,以为这个应该不难,直接像下面这样配置就行了

但是由于要做数据统计,如果编辑的话生成的 id 肯定会变,然后又开始了借鉴之路。看了下腾讯问卷的实现方式,哇,大厂就是不一样,各种情况都考虑到了,但是要我这个菜鸡实现的话还是有点难度的(看着这么复杂的交互有点懒不想动),所以新增我还是使用原来的方式,编辑的话我分别提供了单项编辑和新增,后面如果过需要删除的话也可以加上去。实现效果如下:

这样就不会影响历史数据了,虽然好像不太优雅,但好歹实现了。

这个组件耗时比较久的应该是将 textarea 的值转换成级联选择所需的数据格式了,本来用递归应该是比较好的实现方式,但是我没写出来。。。后面用的一堆 if 加循环实现的

所以大佬们如果有空的话可以实现下,然后评论区讨论下

const str = '省份/城市/区县/学校\n广东省/深圳市/宝安区/学校1\n广东省/深圳市/宝安区/学校2\n广东省/深圳市/南山区/学校1\n广东省/深圳市/南山区/学校2\n广东省/深圳市/龙华区/学校1\n广东省/深圳市田区/学校1\n广东省/深圳市田区/学校2\n广东省/深圳市/龙岗区/学校1\n广东省/深圳市/龙岗区/学校2\n广东省/广州市/番禺区/学校1\n江西省/南昌市/青山湖区/学校1\n江西省/南昌市/西湖区/学校1\n江西省/赣州市/南康区/学校1'

const result = [
  {
    label: '江西省',
    value: '江西省',
    children: [
      {
        label: '南昌市',
        value: '南昌市',
        children: [
          {
            label: '青山湖区',
            value: '青山湖区',
            children: [
              {
                label: '学校1',
                value: '学校1'
              }
            ]
          },
          {
            label: '西湖区',
            value: '西湖区',
            children: [
              {
                label: '学校1',
                value: '学校1'
              }
            ]
          }
        ]
      },
      {
        label: '赣州市',
        value: '赣州市',
        children: [
          {
            label: '南康区',
            value: '南康区',
            children: [
              {
                label: '学校1',
                value: '学校1'
              }
            ]
          }
        ]
      }
    ]
  }
]

strresultstr 的层级不固定,最多 4 层,最少 1 层,示例是 4

表单数据查看及统计

数据统计是后端的活儿,我只负责展示,,,但是后端大哥返回给我的是 id 和值,然后一个 mapping 对象。我要根据 id 去查对应的标题,然后再组装成我要的格式。虽说难度不大,但是耐不住层级多(表单组中包含的单选多选等)啊,现在 ?. 的兼容性又不是很好,所以只能写一堆的三目运算符,真是太快(e)乐(xin)了

然后就是对于单选和多选做的可视化统计了,这个用的 antd-pro 的【图表】组件,不得不说,站在巨人的肩膀上还是舒服呀。

echarts 也很香,都是大佬给我们小码农的福利

最后

这个项目总体还是让我比较兴奋的(除了中间代码写不出来的时候),因为还算是比较有挑战性(不像之前不同形式的增删改查),后面肯定还需要继续完善,架子搭好了后面各种奇奇怪怪的配置应该都可以实现。

这也算是跳出舒适圈的一种形式吧,继续前行吧

对了,还得感谢下各个大厂产品给的思路,这应该不算抄袭吧,不算吧,不算吧......我真的只是借鉴而已~