从零实现低代码平台(一) -- 表单配置

1,167 阅读5分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

功能简述

我实现的第一个功能点是简单表单的配置,也就是在页面上配置表单的相关信息,然后生成DSL。

配置表单

GIF.gif

如上图所示,在页面的左边,是组件列表。目前只提供了一个组件(table暂时没做完),那就是表单。点击表单按钮之后,则中间的预览部分,会把表单的雏形渲染出来。然后点击表单,则开始编辑表单的相关属性,比如提交的链接、具体的每个字段的控件形式。

可以看到右边的配置区中,有一个Components字段,我们通过这个属性来为表单新增字段。在gif中我新增了一个input,一个number,一个下拉框以及一个textarea。

配置字段

2.gif

点击input组件,开始配置该字段,输入label为姓名

点击number组件,输入label为年龄

点击select组件,配置label为性别,配置两个option,分别是男、女

点击textarea,配置label为自我介绍

那么一份简单的表单就配置好了

模拟发送请求

3.gif

紧接着,打开f12,填写表单后点击按钮,可以看到请求按我的设想发送出去了,具体内容如下

curl "https://www.tosubmit.cn/" ^
  -H "sec-ch-ua: ^\^" Not;A Brand^\^";v=^\^"99^\^", ^\^"Google Chrome^\^";v=^\^"97^\^", ^\^"Chromium^\^";v=^\^"97^\^"" ^
  -H "Accept: application/json, text/plain, */*" ^
  -H "Referer: http://localhost:3000/" ^
  -H "Content-Type: application/json" ^
  -H "sec-ch-ua-mobile: ?0" ^
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36" ^
  -H "sec-ch-ua-platform: ^\^"Windows^\^"" ^
  --data-raw "^{^\^"0bb8a290-1a89-40ad-939f-8c49227eae5d^\^":^\^"^张^三^\^",^\^"43cc0478-f058-422c-b0fd-a87a569db2a5^\^":^\^"f73df88d-f55a-4050-83b0-430be72cd50c^\^",^\^"873289a0-ebc5-449e-9b5a-73a1a6edcacb^\^":18,^\^"9e0c4dea-95c2-4bc1-8938-e46c72ef134f^\^":^\^"^我^是^张^三^\^"^}" ^
  --compressed

接下来看看表单配置这个功能是如何实现的吧

生成DSL

以下就是刚才这份表单的DSL,我采用的是json格式,因为js的变量就是json格式,可以直接使用。

关于DSL的说明

DSL中有几个关键的地方

  • uuid 用于唯一标识一个组件
  • type 用于标识一个组件的类型,比如form、button、input等
  • 整个DSL采用的是树整个数据结构来表示页面,因为html中也是用树这个数据结构
[  {    "uuid": "206596c3-5b1d-4c32-9d1a-ac6d3266c5ec",    "title": "",    "submitUrl": "https://www.toSubmit.cn",    "components": [      {        "uuid": "0bb8a290-1a89-40ad-939f-8c49227eae5d",        "type": 2,        "title": "",        "label": "姓名"      },      {        "uuid": "43cc0478-f058-422c-b0fd-a87a569db2a5",        "type": 1,        "title": "",        "single": true,        "options": [          {            "label": "男",            "value": "f73df88d-f55a-4050-83b0-430be72cd50c"          },          {            "label": "女",            "value": "cc75ae1f-5a7c-4a7f-8d57-4e6fccc57d76"          }        ],
        "label": "性别"
      },
      {
        "uuid": "873289a0-ebc5-449e-9b5a-73a1a6edcacb",
        "type": 5,
        "title": "",
        "label": "年龄",
        "max": 120,
        "min": 0
      },
      {
        "uuid": "9e0c4dea-95c2-4bc1-8938-e46c72ef134f",
        "type": 3,
        "title": "",
        "label": "自我介绍"
      }
    ],
    "type": 7,
    "button": {
      "type": 6,
      "text": "Submit"
    }
  }
]

字段配置

每当一个组件被点击时

  • 会根据这个组件的类型,去获取该组件对应的配置表单

d31cc970ef52591628896d9ae513ca9.png

  • 以表单的形式,把配置展示在右边,供用户配置

1643012990(1).png

  • 根据这个组件的uuid去寻找已有的配置,作为表单的初始值展示出来。

image.png

  • 在配置修改的时候,把修改实时同步回DSL里

image.png

短短二三百行代码即可

预览(DSL解析)

从上面可以看到,配置的修改是实时同步到预览功能中的,也就是说每一次配置的变化,都要同步到DSL中,并且修改预览中的效果

解析的原理很简单

  • 项目初始化的时候,为每种组件的type都关联对应的组件

image.png

  • 采用深度优先的算法去遍历树的每个节点,然后根据节点的type属性去获取对应的组件

image.png

  • 把该组件的配置传入组件中即可 image.png

其他

vite

本来计划使用webpack做打包的,可是出于新鲜感使用了vite,确实vite的配置好少而且启动快,非常丝滑,对vite感兴趣的朋友可以去看看我的这篇文章 前端技术储备--vite

Context

目前使用的是react提供的context功能存储页面数据。考虑到后期需要实现的组件非常多,会有多处需要数据分发,因此采用context减少数据分发。目前Context的设计如下

export interface GlobalContextStateProps {
  components: ComponentProps[];
  editingComponentUuid: string;
  registedComponentsMap: Map<ComponentTypes, any>;
}

components字段是DSL数组,类型ComponentProps是所有组件的DSL类型的集合。

registedComponentsMap字段则是组件类型和组件的映射,渲染的时候,会根据DSL中的type字段,来这个map中取对应的组件,从而进行渲染。这里采用了注册模式来实现,目的是后面可以让用户自定义组件,或者通过微前端的方式自定义组件。

总结

综上所述就是用低代码配置表单的过程,如果需要支持更多类型的组件,那么仍然需要人为的开发。

目前获取到了表单的DSL,也能成功的把请求发送出去,但可以看到发送的请求中,内容可读性很差,几乎不可能让外部系统使用

接下来会用低代码平台实现列表页,通过共用DSL的方法来展现本次表单提交的内容

再后面,会用低代码平台实现

  • 自定义组件
  • 拖拽配置功能
  • 表达式解析

感兴趣的朋友可以关注一下