如何以一人之力设计动态表单

动态表单,听起来似乎非常庞大的概念,这不是10人团队做一年以上的活吗?一人之力不是开玩笑吗?
不开玩笑,如果限定微信公众号,事情就变得简单的多,当时只抽出了一个前端和一个后端,设计加开发一共花了3周左右时间,后来前端妹子走了,也再也没有其他前端开发补充到团队中,之后的半年中需求却还是一如既往,后来,后端也走了,系统继续运行着…

这事从公司来看,却极大的降低了开发成本,如果从个人来看,似乎是革了自己的命,但如果对技术有点追求,你也会增加一份特殊的成就感。同样的需求,如果不做动态表单,每次需求一来,copy一份,修改下,再加上测试发版,以互联网公司的效率少说也要3人天,如果是非互联网公司,估计1人月都还不止,这还是一次,每次都要重复,资源的浪费是可想而知的,而动态表单却是一劳永逸的解决了问题,配置下0.5人天最多,并且不需要发版直接上线!!!

干货开始。

需求:未开发之前称之为需求,既然开发完了,也是公开的公众号,直接贴个截图当做需求吧,
如何以一人之力设计动态表单
如何以一人之力设计动态表单
如何以一人之力设计动态表单
如何以一人之力设计动态表单
如何以一人之力设计动态表单

首先选择电动车品牌,每个电动车品牌后续的向导页面数量和字段是不一样的,这个就是做动态表单的最大出发点。

设计:
如何以一人之力设计动态表单
省去非关键信息后,模型设计非常简单,只要两个一对多关系即可,主要的解释可以查看类注释。

/**
 * 向导页
 * 一个向导页有多个控件和多个操作按钮组成
 * 相比于真正的动态表单,这里的向导页是弱化的,只要按照itemIndex指定的顺序码下来即可
 * 这里还有一些特殊的控件
 * 空白行:在需要展示空行时非常有用
 * 身份证扫描控件和车架号控件:因项目的特殊性,该类控件在本项目是非常有用的,所有的品牌都需要
 * 省市区控件:如很多手机端常用控件,其实就是对级联下拉框的扩展,只是数据固定为省市区
 *
 */
@Data
@Table(name = "t_wizard")
public class Wizard extends BaseEntity {

  @Id
  private String id;                         //id字段,UUID,有代码生成,不使用数据库生成
  private Boolean deleted = Boolean.FALSE;   //是否已删除,类属性,默认就是否
  private Long createdAt;                    //记录创建时间,long值,默认为当前时间
  private Long lastModifiedAt;               //最后修改时间,long值,默认为当前时间
  private String lastModifiedBy;             //最后修改人
  private int steps;                         //总步数
  private int currentStep;                   //当前步数
  private String title;                      //标题
  private String tips;                       //提示
  private String brandId;                    //品牌ID,表示该向导页所属的品牌
  private int itemIndex;                     //排序号,有点与当前步数重复

}


/**
 * 向导页中的控件,即文本输入框、下拉列表、开关等
 */
@Data
@Table(name = "t_control")
public class Control extends BaseEntity {

  @Id
  private String id;                             //id字段,UUID,有代码生成,不使用数据库生成
  private Boolean deleted = Boolean.FALSE;       //是否已删除,类属性,默认就是否
  private Long createdAt;                        //记录创建时间,long值,默认为当前时间
  private Long lastModifiedAt;                   //最后修改时间,long值,默认为当前时间
  private String lastModifiedBy;                 //最后修改人
  private ControlType dataType;                  //控件类型
  private String model;                          //用户在前端的输入值需要填入的字段,层次以.符号分割
  private String label;                          //标签信息,前端显示的字段名
  private String placeholder;                    //前端显示的占位符
  private String pattern;                        //正则表达式,即用户输入后所需要进行何种格式检验
  private String error;                          //pattern检验出错时的错误提示
  private Boolean required;                      //用户是否必须输入
  private String options;                        //下拉框的选项,取决于空间类型,并不是所有空间都需要
  private Boolean checked;                       //默认是否勾选,部分空间需要
  private String value;                          //默认值
  private String wizardId;                       //向导页ID,即所属向导页
  private int itemIndex;                         //在向导页显示的序号或者位置,相比于真正的动态,这里是弱化的
}


/**
 * 向导页中的操作按钮,如下一步、上一步、提交等
 */
@Data
@Table(name = "t_action")
public class Action extends BaseEntity {

  @Id
  private String id;                           //id字段,UUID,有代码生成,不使用数据库生成
  private Boolean deleted = Boolean.FALSE;     //是否已删除,类属性,默认就是否
  private Long createdAt;                      //记录创建时间,long值,默认为当前时间
  private Long lastModifiedAt;                 //最后修改时间,long值,默认为当前时间
  private String lastModifiedBy;               //最后修改人
  private String name;                         //按钮名称
  private String intro;                        //简短描述,介绍
  private ActionType action;                   //操作类型,如下一步还是上一步,提交还是获取校验码等
  private String wizardId;                     //向导页ID,即所属向导页
  private int itemIndex;                       //在向导页显示的序号或者位置,相比于真正的动态,这里是弱化的
}


/**
 * 控件类型,其实这个完美的情况下,这里枚举类型也可以作为数据存到数据库
 * 因为前后端分离的情况下,该类型对于后端来说就是一个字符串,不需要实现,实现在前端,
 * 增加一种类型的情况下,后端增加一条数据,不需要发版,前端可以自行发版,做到真正的前后端分离
 */
public enum ControlType {

  IdCardScanner("身份证扫描"),
  VinScanner("车架号扫描"),
  SubTitle("子标题"),
  Select("单选框"),
  Date("日期"),
  Text("文本"),
  MultiSelect("多选框"),
  BlankLine("空白行"),
  NationalDistrict("省市区选择"),
  CheckBox("勾选框"),
  HiddenText("隐藏框"),
  Agreement("协议"),
  Switch("开关");

  private String desc;

  ControlType(String desc){
    this.desc = desc;
  }

  public String toString(){
    return desc;
  }
}


/**
 * 操作按钮类型,
 * 该类型决定了向导页的后续走向,需要和前端约定好
 */
public enum ActionType {

  Next,
  Submit,
  Back,
  Verify,
  Captcha
}

具体的实现代码就不分享了,大家可以将下图标注转化为数据自行实现,毕竟内容太大,再说思想比代码重要的多,有机会我会和大家分享一个完整的并且通用的项目,记得关注。授之以鱼,不如授之以渔,不是吗?

如何以一人之力设计动态表单

后话:
任职的前三家公司都在不停地做从单体应用到微服务的转化,几乎每一个业务系统都会有自己的管理平台,技术实现还不一样,有angularjs的,有vue的,甚至还有jsp实现的,一个小公司算下来都几乎有10+,甚至100个管理系统,大家是否想过,累加起来需要多少的人力资源,温水煮青蛙,成本高的同时,对于开发永远是重复造*,没有任何帮助。差不多8年前开始接触动态表单,那时确实觉得非常复杂,这就是为什么开头说的10人团队一年的工作量,那是真正的动态表单,但事实上,我们可以在限定的范围内以1/100的成本高效实现,又不是专业的动态表单产品,为啥不可以呢?希望有机会能与大家再分享。