这两种方案都有维护成本比较大的弊端,那么有没有更好点的解决方案呢。本文就带你了解一下动态化表单搭建。
容器组件——是用来存放基础组件的组件。例如表单组件,子表单组件,表格表单组件。这些组件都是内部可以存放其他子组件的组件。属于容器组件。系统内置了 3 个容器组件,对于中台系统而言,容器组件不会太多样化。所以容器组件没有做自定义组件的功能。当然后期如果需要的话也可以扩展出容器组件的自定义组件功能
基础组件——就是普通的表单项。系统内置了 13 个基础组件。这些组件都是基于 Antd 的 React 组件做的二次包装
具体实现方案采用的是 React-DnD。
[{
label: '是否可见',
code: 'visible',
widget: 'switchBtn',
initialValue: true
},
{
label: '是否可编辑', // 标签文案
code: 'code', // 字段编码
widget: 'switchBtn', // 组件类型
initialValue: true, // 初始值 默认可编辑
hide: 'exp: visible === false', // 是否隐藏
required: true // 是否必填
}]
细心的同学会发现 hide 字段写了个表达式。这里通过 exp:
开头作为一个表达式的标识。表达式的可以使用的变量是属性表单内的值。
容器属性 共有的属性有标题、编码、是否可见、以及容器结构是否对数据透明。
前面三个好理解。容器结构是否对数据透明是什么呢?前面说过,我们的容器组件是可多层嵌套的,那问题来了,数据咋办,表单嵌套会导致数据也跟着嵌套。所以这里参考了阿里的 Formily 开源表单方案。使用一个 skip ,来使其对数据透明。即:{
"title": "表单",
"type": "form",
"fields": [{
"name": "name",
"label": "姓名"
}, {
"title": "子表单",
"skip": true, // 表单结构对数据透明
"name": "item",
"type": "form",
"fields": [
{
"name": "object",
"label": "物品"
}, {
"name": "brand",
"label": "品牌"
}
]
}]
}
skip 为 false 时返回的数据为:
{
"name": "简名",
"item": {
"object": "电脑",
"brand": "Mac"
}
}
skip 为 true 时返回的数据为:
{
"name": "简名",
"object": "电脑",
"brand": "Mac"
}
组件属性 分为基本属性和组件属性,基本属性是所有属性共有的。标题,编码,是否可见,是否必填等属性都是基本属性。组件属性则是组件私有的属性。
比如 Select 组件会需要一个数据来源,以及该组件是否多选之类的。基本属性直接写死。组件私有属性则通过远程数据库维护。自定义组件的注册就需要涉及到这部分的数据管理。自定义组件的注册表单如下: 图片 其中组件可配置属性就是组件私有的属性的定义,注册时定义,配置该组件时赋值,渲染端渲染时应用。可配置属性还需要支持表达式的填写。比如某个组件需要远程数据,url 提供了,但是参数需要取当前时间,这个时候就需要组件属性支持表达式的解析或者少量代码读写运行了。这些属性除了组件自定义属性以外,还有组件默认值,组件自定义校验,组件 onChange 事件。以自定义校验为例: 图片交互规则 表单交互规则在表单级别绑定,而不是在字段级别。进行就近配置的目的,是为了方便管理,进入一个表单配置,该表单的交互在右侧一目了然。
接口绑定 则是表单渲染过程中有可能涉及到一些远程数据的读取,比如默认值等。这部分数据的配置需要用到远程数据。表单上绑定了接口之后,表单初始化之前先发请求获取绑定接口的数据,相应的表单组件里就可以使用到该数据进行初始化。
import { FormPageWrap, MainForm } from './index';
@FormPageWrap({
prefix: '/api/budget', // 业务方接口前缀
getFormParams: (props) => { // 获取表单结构参数
return {};
},
getDataParams: (props) => { // 获取表单回填数据参数
return {};
}
})
export default class FormPage extends Component {
render() {
return (
// 表单各种额外显示内容 test: TestComp,
}}extraParams={
{}}
/>
// 表单各种额外显示内容div>
);
}
}内部实现则是根据 Schema 渲染相应的组件。
待完善
目前系统部分功能还有待完善。具体有几点:
自定义组件的异步加载。当一个表单需要新增加一个自定义组件时,项目需要重新构建发版。如果自定义组件可以单独发布,就可以做到及时添加一个自定义组件,不需要项目重新构建发布了。当然如果自定义组件太多,异步加载还是会有些性能问题。而这就需要做到同页面下多组件代码合并了
一些配置的沉淀复用。比如某些经常配置的表单块。可以沉淀为常用组件。直接选择使用,可进一步简化配置流程
- 同页面下的一些相同区块,如果每个页面都单独维护,会极大的增加维护成本、抽取并联动,可以极大的减少维护表单的成本
展望
对于动态化表单的能力远不止目前看到的动态表单搭建:
对于管理端流转出来的 Schema 数据可以进行二次加工,从而实现对于用户的权限,业务配置等能力的扩展
管理端配置出来的 Schema 不止可以用在动态表单渲染中,还可以作为数据模型去描述一个静态数据的结构。从而提供各业务系统配置数据的构建能力
- 前端渲染组件也不一定要和管理端的 Schema 完全耦合在一起。单独拿来使用也是完全没问题的,这样对于某些简化版动态表单的能力也能做到支持