前言
低代码平台不是什么新鲜事儿,很早就有了。比如国内早期的建站程序 Discuz,云平台上一键快速部署企业网站的服务,这些是不是低代码平台呢?我觉得这些都是。如果泛化低代码的概念,前端 UI 组件库算不算低代码呢?甚至一切可以通过 UI 操作批量生产交付物的平台,应该都算低代码。因为它们都提供了一些开箱即用的功能,降低了日常研发的复杂度和成本,提升了交付效率。
为什么近两年低代码这个概念突然火起来了?我想其中的原因离不开时代背景和产业变革。
社会加速进入数字时代… 在产业数字化的大背景下,传统企业在数字化转型的进程中,需要解决提升企业的经营效率等问题,于是企业级市场就出现了一批提供类似的服务的厂商。这类厂商提供的服务在创投圈、在资本市场就被包装成了具有低代码概念的独角兽,于是低代码这个概念就火了。
通常情况下低代码平台具备以下几个能力:
- 可视化页面搭建,通过简单的拖拽完成应用页面开发;
- 可视化模型设计,与业务相关的数据存储变得更容易理解;
- 可视化流程设计,业务流程或审批流程,通过简单的点线连接来进行配置;
- 可视化表单设计,业务流程或审批流程,通过简单的拖拽完成表单页面的配置;
- 可视化报表及数据分析,提供 BI 数据分析能力,通过拖拽选择来配置自定义分析报表。
这个世界上没有一款产品能解决所有的问题。同样都是低代码平台,不同厂商、团队推出的产品,由于定位不同,面向的场景不同,产品功能设计上自然会有差异。
自我介绍
在正式开始之前,先介绍下自己,本人从事前端研发已经超过10年,做过美工切过页面,写过 jQuery、Backbone、Angular、React、Vue等,经历了从刀耕火种到工业革命的整个前端大发展时期。先后参与并负责过 BI 数据分析平台、BPM 工作流平台等几个系统的前端研发,目前是低代码应用平台方向的前端负责人。
今天主要是基于本人在低代码领域的研发经验,分享下 BPM 表单设计器的一些研发实践。 😊
BPM 工作流平台介绍
研发过 BPM 的同学都知道,定位于审批工作流的一款平台级产品,其中工作量最大、复杂度最高的模块,主要是流程设计器和表单设计器。
因为表单设计器是大多数低代码平台里的常用部件,是比较有代表性的低代码产品形态。尤其在 BPM 这种定位于审批场景下平台,表单设计器的作用更加突出,所以今天的分享我主要是通过 BPM 的表单设计器展开。
先说明下,虽然本文介绍的 BPM 表单设计器是基于 Vue.js 开发,但今天的分享我想传递的更多的是一种低代码平台的设计思路,及对部分核心功能点的研发技术方案解析。这方面本身与编程语言和框架也无关,无论你是从事 Vue.js 还是 React.js 研发的前端同学,亦或是对低代码研发感兴趣的后端同学,希望通过这次分享都能有所收获和启发。
BPM 表单设计器案例前端技术解析
我们先来看一下 BPM 中表单设计器的界面图:
如果你接到这个任务,要开发这个表单设计器,当你看到这个 UI 界面时,你的研发思路是什么呢?🤔
好,在进入技术解析之前,先重点强调一个概念:前端状态管理。
前端状态管理
前端状态管理,本质上是一种前端本地存储技术,又叫 Store,这个概念前端同学都不陌生。放在开始讲这个概念,是因为设计好 Store,对复杂项目的程序设计而言,至关重要。这一点怎么强调都不为过。如何设计程序的数据结构、如何管理和使用这些数据?这个问题在研发初期就应该确定的。
Store,就好比运行在客户端的 Redis,数据是存储在内存中的。设计 Store 的过程,本质上和在服务端设计数据库表结构的过程是一样的。数据结构是 JSON 格式,表单组件的配置数据和表单布局的数据,最终都持久化的存储在 MongoDB 中。
前端不同技术栈都有各自的实现,如 Vue 方面的 Vuex、Pinia,React 方面的 Redux、MobX,还有近几年开始流行的函数式组件状态管理 Hooks,这些都实现了对前端数据的统一管理、以及如何对数据的操作 (CRUD)。相关技术无论怎么发展,改变的是编程范式,不变的是状态管理的本质。
Store 的作用:第一次从服务端获取到数据,之后再次访问这些数据就直接从 Store 中获取。
Store 的好处:首先是解决前端模块之间的数据共享,其次是通过减少 HTTP 请求,提升页面的渲染性能。
通过这种设计,在表单设计器这种交互密集、模块联动频繁的场景下,产品体验可以达到肉眼可见的提升。
在数据驱动界面的前端技术背景下,首先要解决的是对数据的设计和管理。越是项目体量大、复杂度高的前端项目,前端的状态管理的使用会越深入。
想象一下,如果低代码平台的前端采用 jQuery 开发,研发的难度有多大呢?可想而知!
表单组件
常规的组件有:文本组件、文本框组件、单选框组件、多选框组件、下拉框组件。
复杂的组件有:日期选择组件、上传文件组件、子表单组件等等。
不同的平台,表单组件生态有较大的差异。
表单组件的数据结构
简单列举几个:
文本组件:
文本框组件:
下拉框组件:
认真的你可能已经发现,几个表单组件中都包含一些公共字段。
表单组件的公共字段:
除以上这些公共字段,其他的都是不同表单组件专有的扩展字段,比如 options 是单选框、多选框和下拉框的选项列表字段,fileList 是上传文件的文件列表字段等。
表单组件依赖的 UI 组件库
BPM 的表单设计器的表单组件是基于第三方 UI 组件库二次封装实现的,PC端和移动端分别选择了不同的 UI 组件库。表单组件和业务逻辑是分离的设计,假如未来因为 UI 交互需要改版、升级,或增加新的组件,都可以很容易的对代码进行替换或是扩展,模块整体设计的是可插拔的。
之所以采用第三方 UI 组件库进行封装,纯粹是为了减少研发工作量,这样才能把精力放在项目主体功能的研发上。如何开发一套 UI 组件库属于另一个话题,这里不展开讲了。
表单组件的扩展
表单组件越丰富,表单设计器适用的审批场景就越多,这依赖于表单组件的生态建设。
扩展表单组件
很好理解,基于现有表单组件增加新的属性字段,基于此扩展对应的组件能力。
表单设计
表单设计和表单组件一样,是衡量表单核心能力的指标之一,表单设计决定着最终生成的表单的版式、风格,它在表单设计器中的重要性,不言而喻。
表单设计主要分表格布局、组件拖放、表单样式、表单交互四部分。
表格布局
可编辑表格组件 - EditableTable
BPM 的表单设计器的表单布局,是基于自研的一套可编辑表格组件实现的。它的设计和 Word 中的插入表格类似,一些在线文档也有类似的功能,实际开发中也确实借鉴了这类插入表格的交互效果。
Word 中的插入表格:
自研的可编辑表格组件:
可编辑表格组件支持以下功能:
- 根据输入的行数和列数创建表格
- 支持通过百分比或固定值设置表格宽度
- 支持在指定行的上方或下方插入新行
- 支持在指定列的左侧或右侧插入新列
- 支持删除行或列、支持框选删除
- 支持合并单元格和拆分合并的单元格
- 支持按行或列拖拽调整行高或列宽
可编辑表格组件还增强了单元格的扩展性,基于此单元格集成了可拖放组件 Draggable,从而使单元格中可以添加、移动、删除表单组件。
可编辑表格的设计思路
以下是一个三行两列的表格:
对应的数据结构是一个二维数组:
以及渲染表格的模版:
倘若渲染一个静态的表格,其实只需要 merged、colspan、rowspan 几个字段就够了。但显然,我们需要做的事情更多。
拖动框选的实现
我们需要给每个单元格设置一个坐标,借助单元格的 x、y 字段确定当前单元格在表格中的坐标,x、y 字段很重要,配合 selected 字段和鼠标事件(mousedown、mousemove、mouseup),即可实现拖动框选单元格。
这里有个技术点是框选范围内如果包含一部分被合并的单元格时,要注意对 x、y 坐标边界的处理。
抛个问题:你觉得上图中单元格框选范围的最大坐标是多少呢?🤔
对表格行、列的操作
主要是通过不同的钩子函数对表格数据进行计算,来控制单元格的 colspan、rowspan、merged、mergeCellId 等字段,来实现符合预期的表格渲染。
和拖动框选一样,针对表格行、列的操作,也要注意当遇到被合并的单元格时,对单元格数据计算的边界处理。
通过拖拽调整行高或列宽的实现比较简单,这里不再赘述了。
以上就是 BPM 表单设计器中关于表单布局的设计方案。
当然,其他低代码平台的表单设计器还有一种表单布局方案,比如通过原生 CSS 的 Flex 布局实现,然后通过设置表单组件的宽度比例,从而实现一行能放几个表单组件。
这种设计方案没有表格、单元格的概念,整体表单就是一个拖放区域。BPM 表单设计器是每个单元格都是一个拖放区域。
通过 CSS 实现的这种表单布局一般情况下也基本够用,而 BPM 的表单设计可以实现更复杂的布局效果。
组件拖放
BPM 的表单设计器采用的是第三方开源的 Vue.Draggable 来实现的组件拖放。如果你有兴趣也可以考虑下自己实现,基于 HTML5 拖放(Drag and Drop)那套 API 进行封装。
表单中所有组件的数据以 Map 形式存储在 Store 中,表单组件的数据和表单布局的数据是完全解耦的。
每一个单元格中都有一个 components 字段,存放着当前单元格中所有组件的 id、type,通过组件 id 和 type 和 Store 中具体的表单数据进行关联、并渲染出对应的组件。
表单组件在单元格中的移动对应的就是对 Store 中单元格数据中 components 字段的组件的增、删的操作:
- 向表单中增加一个组件,对应的表单组件列表的 Store 中就增加一条组件的数据;
- 从表单中删除一个组件,对应的表单组件列表的 Store 中就删除这条组件的数据。
而如果只是修改了表单组件在表单布局,如在单元格的位置变化(把组件从一个单元格中移动到另一个单元格中,或调整一个单元格中组件的顺序),则只是修改表单布局的数据即可,表单组件的数据并没有改动。
表单样式
从表格和单元格两个维度配置前景色、背景色、字号,及基于 Flex 的表单组件对齐方式。
原理:向 table、td 标签上增加行内样式,利用 CSS 优先级规则, td 标签上的样式会覆盖 table 标签的样式。
表单交互
通过 JSON 数据配置组件之间的交互联动。
支持的交互类型:
- 控制目标组件是否显示
- 控制目标组件是否禁用
- 修改目标元素的值(如:input 的 value)
- 修改目标元素的选项(如:select 的 options)
- 触发提示(如:toast)
配置规则一览:
总体思路:源组件触发事件,目标组件符合条件时响应事件。
条件匹配的原理:
这样设计的优点:支持按需定制、具备可移植性。
按需定制
举个例子,当初一个来自业务方的需求:群组必填校验。
假设表单中有 4 个文本框组件,业务方希望审批时至少要填写 2 个就可以通过。
我们表单组件的必填设置是只针对当前组件有效的,满足不了业务方的需求。所以我就在 JSON 配置中扩展了一条 JSON 规则。如图:
通过增加新的 type,并设计配套的业务字段,便能支持一些定制化场景。这是如何按需定制、扩展 JSON 配置的思路。
可移植性
用户在使用表单设计器的过程中一定要写 JSON 吗?🤔
其实不一定,功能做成什么样,也取决于业务场景和产品设计的策略,毕竟技术是为产品服务的,产品是为业务服务的。
总之基于 JSON 的配置未来也可以升级为通过 UI 配置,这也是从低代码升级成了零代码的一个演进过程。
组件设置
修改组件的配置,本质上就是对 Store 中组件数据的读、写操作。
不同类型的表单组件对应不同的配置项,比如:文本框设置提示文字、文本格式校验;下拉框设置默认值、通过调用API、或枚举的方式设置选项列表等。
数据源管理
数据源管理也是低代码平台的一个标配功能,对表单设计器来说,不支持数据源,意味着表单组件的初始数据都是静态的。
BPM 的表单设计器的表单组件支持枚举和 API 方式设置数据源,主要用途是获取组件的默认值,或动态获取组件选项列表等。
设计思路:
简单粗暴,用户在表单组件配置中选择数据源类型为 API 并设置 API 的 URL 后,当渲染真实表单时,由服务端请求 API,并对表单组件数据进行设置,前端是无感知的。
表单权限
主要是三层权限控制:
- 审批节点权限
- 表单行权限
- UI 层控制表格
- 数据层控制组件
- 表单组件权限
- 是否可见
- 是否禁用
- 是否必填
表单预览
绘制完表单原型后,再对不同的表单组件、表单布局中的某些行设置权限后,可以在表单预览中查看最终的表单效果。
通过切换审批节点,查看不同节点下的实际表单。
移动端表单和 PC 端表单实现方案是一致的,差异仅仅在表单布局和 UI 组件库上,主要是为了视觉层面的适配。效果如图:
表单预览中模拟的提交按钮,调用全局钩子函数,触发表单提交的事件,先对表单进行校验,校验成功后通过事件回调可以拿到需要提交的表单数据。
表单SDK
- 蓝色是 PC 端表单设计器的业务模块
- 绿色是 移动端表单设计器的业务模块
- 黄色是 两者的公共模块
基于这种架构设计,输出两个独立的表单 SDK 且独立部署,分别应用于 PC 端和移动端的审批场景。
性能优化点
通过模块拆分、代码分割,可以进一步实现组件的按需加载,减小打包文件的大小,提升性能。
以上就是 BPM 表单设计器前端技术解析的全部内容。
结尾
要说低代码类型的项目和日常的一些项目的前端研发的区别在哪,我觉得就是低代码项目通常都是体量大、模块多、联动多、交互复杂,依赖通过 UI 操作等,从全局看主要考验的是前端工程师的技术视野和工程设计能力。其实当我们把这样一个大型项目的核心功能进行拆解后再逐个解决时,聚焦某一个单一问题的解决上时,和传统项目的研发体验也没有区别。
数字时代,低代码正在成为“新基建”,产业数字化的时代背景下,有技术又懂业务的工程师,大有可为!
全文完。
本文正在参加阿里低代码引擎征文活动