Vue前端表单解决方案VSchemaForm

2,590 阅读5分钟

在前端开发的工作当中,表单的开发占据了很大的一部分。表单页面和报表页面是中后台业务的主要展现形式,但随着业务越来越复杂,表单开发和维护成为了前端工程师一个无法逃避的痛点。

目前的表单开发主要存在的问题有以下几点

  1. 表单状态管理随业务复杂度增加变得越来越难以维护,需要引入状态管理库
  2. 传统表单开发的数据结构往往都是扁平结构,没法很好的处理嵌套复杂数据的情况, 如果要处理,工作量会非常大
  3. 当业务有动态输出表单的需求的时候,不得不自己开发一个基于某种数据协议的动态输出表单的组件
  4. 当业务需要在可视化界面配置产出表单时,需要自己开发一个基于json的动态输出表单组件

解决方案

基于以上问题的探索,以及受到UForm的启发,研发了VSchemaForm的Vue表单解决方案:

  1. 使用json描述表单内容
  2. 支持多平台(移动端和桌面端)
  3. 支持多个组件库(Element UI、Ant Design Vue、Antd Mobile Vue)
  4. 支持嵌套表单
  5. 支持任意数据的数组解构
  6. 支持复杂布局
  7. 支持副作用函数,统一处理表单内各项的数据联动
  8. 支持解构数据,减少自定义的数据转换
  9. 支持表单校验

这是一个完整的表单解决方案,并且不和任何第三方库绑定,项目文档地址项目主页,github地址为github.com/wuhao000/vu…

使用JSON描述表单

{
  fields: {
    card: {
      type: 'card',
      props: {
        title: '基本信息'
      },
      fields: {
        basic: {
          type: 'object',
          fields: {
            str: {
              type: 'string',
              title: '字段1'
            },
            num: {
              type: 'number',
              title: '字段2'
            },
            date: {
              type: 'date',
              title: '字段3'
            }
          }
        }
      }
    },
    detailCard: {
      type: 'card',
      props: {
        'title': '详细信息'
      },
      fields: {
        detail: {
          type: 'object',
          props: {
            labelCol: 8,
            wrapperCol: 12
          },
          fields: {
            gridLayout: {
              type: 'grid',
              layout: [6, 11],
              props: {
                gutter: 10,
                title: '字段3'
              },
              fields: {
                field3Num: {type: 'number'},
                field3Date: {'type': 'date'}
              }
            },
            gridLayout2: {
              type: 'grid',
              layout: [6, 16],
              props: {gutter: 10, title: '对象字段'},
              fields: {
                objNum: {type: 'number'},
                '[startDate,endDate]': {type: 'daterange'}
              }
            },
            textbox: {
              type: 'text-box',
              title: '文本串联',
              layout: '订%s元/票 退%s元/票 改%s元/票',
              fields: {
                text1: {
                  type: 'number',
                  default: 10,
                  required: true
                },
                text2: {
                  type: 'number',
                  default: 20,
                  required: true
                },
                text3: {
                  type: 'number',
                  default: 30,
                  required: true
                }
              }
            },
            field4: {
              type: 'string',
              title: '字段4'
            },
            section: {
              type: 'object',
              props: {
                title: '区块'
              },
              fields: {
                field5: {
                  type: 'string',
                  title: '字段5'
                },
                field6: {
                  type: 'string',
                  title: '字段6'
                }
              }
            }
          }
        }
      }
    }
  }
}

这是一个比较复杂的表单的布局示例,布局效果如下:

多平台支持

桌面端和移动端只要指定platform属性(mobile或desktop),这对于响应式的系统来说,只需要一个属性就能达到响应式布局的效果。

副作用处理

表单副作用,也就是由表单字段的内部事件所产生的联动,校验,异步逻辑,如何更好的管理和维护副作用逻辑,恰好就是rxjs的最大优势,所以,本方案采用了rxjs来管理副作用逻辑

表单的API中包含的effects即为表单的副作用函数,这个effects是一个功能极为强大的回调函数, 它接收了一个selector函数作为参数,我们可以用selector来选择表单内的任意一个或多个字段, 对其做状态修改,即便存在异步逻辑,也是可以很方便的在各种异步环境下对字段的状态做修改, 所以,我们的表单联动,是不限于时空的。

effects示例

const effects = ($: EffectsContext) => {
  $('s1').onFieldChange(value => {
     $('s2').value(value);
  });
  $('s1').onFieldChange(value => {
    if (value !== '3') {
      $('s3', 's4'). hide();
    } else {
      $('s3', 's4').show();
    }
  });
}

以上事例代码展示了,在一个表单中有s1,s2,s3,s4四个输入组件,注册了两个事件:

  1. 当s1的值发生变化时,将s1的值复制给s2
  2. 当s1的值发生变化时,如果值等于'3',则显示s3,s4,反之则隐藏

副作用函数还有很多强大功能,不限于:赋值、事件监听、更改属性、隐藏/显示等

字段解构

字段解构是一个非常强大的特性,它可以对组件生产的值做解构转换,使得快速贴合服务端数据结构要求,无需再做二次转换 字段解构主要是对 property 用 ES Deconstruction 语法做解构,需要注意的是,不支持...语法

通常,我们使用日期范围组件的时候组件生产的值是一个数组,但是往往服务端都是以 startDate,endDate 的方式做存储,如果每次前端都花大量精力去转换的话, 其实成本还是很高的。所以,我们可以借助字段解构,轻松解决该问题. 另外,还有如省市区选择等

{
    fields: {
      '[start, end]': {
            type: 'daterange',
            title: '时间范围'
       },
      '[province,city,town]': {
            type: 'cascader',
            title: '省市区',
            placeholder: '请选择',
            enum: this.options
       }
    }
}

以上就是一个使用了解构的表单的schema描述,其中只有两个输入组件,但实际的数据格式是:

{
  "start": "2019-11-18T00:15:10.495Z",
  "end": "2019-11-18T00:15:10.495Z",
  "province": "jiangsu",
  "city": "nj",
  "town": "gl"
}

布局支持

线性布局

组合布局

编辑与详情模式切换

可直接切换为详情模式

数据校验

支持数据

支持表单部分自定义

使用slot可以达到部分表单内容使用自定义内容

以上是VSchemaForm的主要功能,其他功能详见项目主页