天天看点

企业级自定义表单引擎解决方案(五)——表单模型管理

作者:spritekuang
  • .net core研发的低代码自定义表单引擎,采用强大的规则引擎将所有的业务串联起来的,和其他低代码平台是有本质的区别的,目标是完全解放繁琐的CRUD工作,其他很多称为低代码的不管界面介绍得多炫酷,本质上还是解决复制粘贴的问题。
  • 常规的业务,在需求以及数据库设计完成之后,可能就仅仅在界面上几分钟的配置就能够完成所有的开发、测试、部署工作,最后用户直接使用,完全解放繁琐的CRUD工作。
  • 表单模板能够快速创建常规的业务模块,系统尽量将常规的业务功能做成模板,方便快速地创建业务模块功能,选择一个模板之后,会将模板对应的表单、子表单、子视图、控件等所有自定义表单相关的定义全部自动创建出来。

之前介绍的自定义表单中的视图定义为单一功能的封装,比如列表视图(定义普通查询区域,高级查询区域,列表操作按钮区域,行操作按钮区域,分页控件区域,列显示区域等)或者表单视图(封装表单行列定义,表单验证等)等,都是具体某个特定功能的实现。而这里介绍的表单模型,则把它定义为一个容器,容器里面会进一步定义行列,容器里面可以包含容器或者表单,每一个页面会定义唯一一个最外层的表单容器,我们可以把它看作根容器,这样就整体形成了一棵树,根节点就是最外层的表单定义,树的节点可以是子表单、视图、表单行、表单列、视图行、视图列、视图控件等,整体就可以构造出一树庞大的树。

  自定义表单最终会转换为一棵树,树的话就会有树的特性,树上的每一个节点,都可以构造一个唯一的Code和PId,自定义表单中的树节点还会扩展出它属于哪个视图或者哪个表单的属性,那么这里就是引申出子表单子视图,父表单父视图的概念。

有了树模型的定义,那么后面绝大多数内容都是围绕树模型来实现的,前端在渲染界面的时候,根据树节点一层一层地渲染界面,渲染界面的同时,将每个节点的Code和PId,节点属于哪个表单或者视图都会赋值到每个树节点控件中,有了树模型的定义,那么规则引擎就有了理论支撑,界面中的任何一个事件,都可以定义规则来实现自定义的逻辑(比如点击列表视图的行编辑按钮,弹出编辑人员子表单,则大致的规则引擎执行逻辑为:找到列表视图特定行编辑按钮所在的列表视图,在列表视图中找到编辑人员子表单,把当前行的Id字段取出来作为参数,用模态对话框弹出子表单,用Id字段执行后端方法获取单条人员数据,将人员数据绑定到人员表单中)。

  表单模型没有具体的功能,它的作用是一个容器,它充当视图与视图之间交互的桥梁的作用,当然是通过规则引擎来串联起来的,另外表单也是页面的入口与缓存的存储数据的入口。

表单的数据库设计:

企业级自定义表单引擎解决方案(五)——表单模型管理

表单模型数据库设计

设计说明:

表单模型拆分为表单主表、表单项、表单行、表单列,关系都为1:n。常见的表单项只有一个,但像Tab布局或者有先后步骤的Step布局则会有多个。

表单主表关键字段说明:

  • Version(版本):每一次修改表单的任何信息(包括关联的数据),都会重新生成一个版本号,浏览器存储表单信息,每打开一个页面,会将本地表单版本和视图版本传递到服务端比较版本号,如果版本号发生变化,重新请求表单数据(一般系统交互后,视图及表单定义信息很少会发生变化)。
  • FormType(表单类型):分为常规表单、Tab表单、Div表单等,前端根据此类型找到实现定义好的控件渲染。
  • PropertySettings(表单属性):存储前端的一些样式,前端渲染时,读取属性并应用到控件中,一般需要结合具体使用的前端框架设置。
  • RelationInfos(关联信息):表单可能会关联其他表单或视图,比如弹窗,行存储的视图等,这个字段数据库不存储,通过动态计算出来放入缓存中。
  • Rules(规则):定义表单的规则,将规则信息冗余序列化存储到此字段,规则有改动时,会同时更新此字段(表单会冗余存储比较多的内容,这里的规则为一类,主要是为了以最快的速度读取表单相关数据,只需要表单Id访问一张表即可获取所有的数据)。
  • WrapInfos(表单包装器):前端在渲染视图时,如果有包装器,会用包装器包装视图之后再渲染,常见为弹出框的功能封装。
  • FormItems(表单项内容):将表单项、表单行、表单列全部读取出来序列化冗余存储到此字段,同样是为了读取效率。
  • IsTemplate(是否为模版):将一些典型的业务定义为模版,同样存储在表单中。

表单项、表单行:

  • 对应物理结构的划分,字段比较好理解。

表单列:

  表单列可以存储单个控件、子表单、子视图等

  • ColType(列类型):可以是控件、视图或者表单
  • PropertySettings(列属性):存储前端的一些样式,前端渲染时,读取属性并应用到控件中,一般需要结合具体使用的前端框架设置。
  • ComponentName和ControlSettings(控件名称和控件设置):如果列类型是控件,则为控件名称与控件属性,前端找到对应的控件渲染。
  • ObjId(对象Id):表单或者视图Id,前端渲染时,根据此字段找到具体的表单或者视图。
  • WrapConfigs(包装定义):显示到表单中的子表单或者子视图的渲染封装(表单和视图可以用到任何需要的地方,相当于在用的地方再次进行样式封装,比如用Box样式再次封装子表单)。

缓存设计简单介绍:

  自定义表单是典型的修改非常少,访问非常平凡的,系统的每一个功能都需要读取自定义表单的定义信息。为了使自定义表单不影响性能,这里采用了双重缓存设计,浏览器每访问一个页面,都会将表单和视图的定义信息存储到浏览器本地数据库中(IndexDb),应用程序后端将表单和视图的定义信息全部放到应用程序内存中,且将表单或视图的相关信息以字段冗余的方式存储到特定字段中,任何信息的改变都会重新生成新的版本号并清空内存中的缓存,前端请求页面只,会带上浏览器本地存储的表单和关联子表单子视图版本号与服务器版本号对比,版本号不同时,刷新浏览器缓存数据,再渲染页面。分布式部署中就存储缓存一致的问题,后面单独写文章来整体讲解缓存这块的实现。

表单模版:

  自定义表单本来就是要解决繁琐的低效编码问题,但是要把一个表单配置出来,还是会花费比较多的时间,且需要对这套表单引擎比较熟悉,配置同样比较繁琐且低效,那么我们同样可以采用自定义表单的思路,将常见的业务封装为模版,(比如对单一表单进行的常规列表和表单操作,也就是最常见的CRUD操作。或者一对多表单,列表展示主表数据,点开一条件主表数据,对话框显示主表数据及子表列表,对子表列表进行操作等),只需要动态渲染不同的地方,那么就能够实现只需要设置几个简单的参数,就能够自动地生成自定义表单出来,这里的不同地方无非就是Object对象(Object就定义了不同的字段,在渲染字段的地方全部替换为新的Object的字段),标题内容等少数不同的地方。  

模版的实现思路大致为:根据模版Id找到表单模型相关的所有表单和视图,将关联的所用数据表数据读取到内存中,包括规则、控件、视图行、表单项、表单列等,再对Id进行Map映射(新建一个字典对象,读取所有Guid字段的地方,新建映射,Key存储老的Guid,Value存储新建的Guid),将所有数据Guid字段替换为将建的Guid值,将Object对象相关的数据全部删除,用将的Object字段重新生成数据,不同的字段类型设置默认的样式,再将所有内容存储到数据库。

随着表单引擎的使用,可以定义更多的表单模版,那么表单引擎的功能将越来越丰富也越来越容易使用。

部分核心部分代码可下载源码查看

感觉还是没有把这块内容描述得特别清楚,很多设计思想用文字还是有点难表现出来!

低代码自定义表单引擎绝对不是简单几个字就能够概括的,是一个非常庞大技术知识体系。

低代码自定义表单引擎、流程引擎,整整花了两年的时间,后续会陆续在头条上写一些文章,直接看代码基本很难整体理解这块的设计,文章前面部分主要是一些设计思想,后面会偏具体的一些技术以及场景应用。

wike文档地址:

https://gitee.com/kuangqifu/sprite/wikis/pages

后端开源地址:

https://gitee.com/kuangqifu/sprite

前端开源地址:

https://gitee.com/kuangqifu/spritefronts

继续阅读