京东低代码平台:水滴表单联动可视化配置的实现与思考

3,438 阅读7分钟
  • 水滴:京东内部CMS低代码平台
  • drip-form:水滴表单垂直域解决方案
  • Drip-form generator:是基于drip-form实现的表单设计器组件。可通过拖拽快速创建表单

源码:Github

image.png

TL;DR

  • drip-form在0.9.0的alpha版支持了可视化配置联动的功能(仍在测试中)
  • drip-form通过协议到代码的转换,尽可能降低常见联动配置的开发成本
  • 探讨:JSON diff动态生成常见联动和校验
  • drip form的后续更新:v0.9.0是v0最后一个版本,后续v0以维护为主并更新v0的文档。v1规划中。
  • 联动或表单有更好的想法,欢迎讨论

前言

重要: 这篇文章不是表单联动实现的最佳方案,只是个人对联动的一些想法,欢迎探讨

表单联动是表单可视化配置的难点之一,如何以最少的代码实现尽可能多的联动功能感觉是一个悖论。

  • 联动功能多,代码量会增加
    我们可以对联动到代码的这层去做转化配置逻辑,减少代码的开发工作量。
  • 联动协议转换联动代码 ,降低了代码编写工作,却带来了新的可视化平台配置上手成本和适配成本

表单联动的实现方式

表单联动实现大体分为三种方式:

  • 纯代码开发
  • 联动协议配置化
  • 部分联动配置化、复杂联动代码开发

开发一个可视化配置联动的方案需要考虑三个成本:

  • 不使用可视化配置联动的代码编写成本、
  • 使用可视化配置表单联动带来的协议理解和配置成本
  • 使用低代码平台的理解成本

本质上并没有一个最佳的方案,本文只是针对固定表单联动场景,提高搭建效率的探索和纯配置实现尽可能多的联动功能的思考

联动常见场景

在设计配置协议前,我们需要了解常见的表单联动场景

  • 表单A控制表单B的数据
  • 表单A控制表单B的样式
    • 表单A控制表单B的展示隐藏
  • 表单A控制表单B的校验
    还有更复杂的联动可能还涉及表单的数据处理和转换逻辑,在涉及到复杂逻辑的时候,纯配置去搭建的联动的成本,可能没有配置+代码或纯代码实现来的快。所以这里不过多讨论

方案设计

实现分析

对上述几种场景进行分析,发现大部分的联动代码逻辑都可以通过编写if语句实现。

if(条件A){执行B}

条件A

针对不同的数据类型,梳理常见的判断表达式以及对应类型的比较操作符

  • 表单A 是string类型,且为某个值时
  • 表单A 是number类型,大于某个值时
  • 表单A 是boolean类型,为真时
  • 表单A 是对象类型,包括某个属性时
  • 表单A 是数组类型,包括某个值时
    从上面例子判断表达式的组成:表单A的值、比较操作符、被比较值

以下是常见的数据类型对应的比较操作符

// nubmer 类型支持的操作符
type NumberOperator = '>=' | '<=' | '<' | '>' | '===' | '!=='
// string 类型支持的操作符
type StringOperator = '===' | '!==' | 'includes' | '-includes'
// boolean 类型支持的操作符
type BooleanOperator = 'true' | 'false'
// object 类型支持的操作符
type ObjectOperator = 'in' | '-in'
// array 类型支持的操作符
type ArrayOperator = 'includes' | '-includes'

我们需要获取表单A的值,这个值对应的可能是表单数据(data)、表单的ui数据(uiSchema)、校验数据(dataSchema)。
现在有一个如下的get函数去获取表单的数据

//获取表单A的数据
get(A).data
//获取表单A的ui数据
get(A).uiSchema
//获取表单A的校验数据
get(A).dataSchema

最终,条件A可以用下面代码实现

//如果表单A是string类型,并且为1时,执行B
if(get(A).data === '1'){执行B}

执行B

条件A满足的时候,我们需要执行B。
在表单联动中,执行B的语句通常是需要设置表单B的数据、ui数据、校验数据

现有一个set函数可以设置表单B的数据

//设置表单B的数据
set(B,'data',value)
//设置表单的ui数据
set(B,'uiSchema',value)
//设置表单的校验数据
set(B,'dataSchema',value)

最终,整个语句用下面代码实现

//如果表单A是string类型,并且为1时,设置表单B的值为2
if(get(A).data === '1'){set(B,'data',2)}

协议定制

联动的纯配置化,是需要一个协议去描述上面的if逻辑。并通过一定的转换逻辑(比如:new Function)将配置转换为代码。

{
    //联动协议版本
    version:string
    //联动协议触发时机
    trigger:{
            //全局监听
            event:'globalChange'
    }
    //监听后执行的动作
    actions:[
            {
                //if语句
                type:'controlFlow'
                //if条件 (条件A)
                condintion:[
                        {
                            /**
                            * 表单A的值
                            * 格式:"fieldKey getType property" "fieldKey getType"
                            * fieldkey: 需要获取的表单的fieldkey	
                            * getType: 对应get的属性 data|uiSchema|dataSchema
                            * property: 可选 获取属性(用于处理对象)
                            */
                            fieldKey1:string
                            operator:string
                            //被比较值
                            value2:unknown
                            //可能存在多个条件 条件之间的关系 或/且
                            logicOperator:'||'
                        }
                ]
                //if条件触发的动作(执行B
                effect:[
                        {
                                //设置表单数据
                                type:'set',
                                /**
                                * 格式:"fieldKey setType property" "fieldKey setType"
                                * fieldkey: 需要获取的表单的fieldkey
                                * setType: data|uiSchema|dataSchema
                                * property: 可选 获取属性
                                */
                                fieldKey:string,
                                value:unknown
                            }
                ]
            }
    ]
}

协议定制的时候,需要考虑后续协议的扩展性。

  • 可能会添加新的逻辑判断语句支持(比如else语句、switch case语句等)
    action中添加type判断当前语句
  • 可能存在嵌套语句(比如if的嵌套,子逻辑等)
    语句中的effect添加type判断语句类型
  • 联动的触发时机(比如组件加载,组件变动,依赖变动等)
    添加trigger.event判断

交互设计

参考outlook的邮件规则,最终设计如下
image.png

  • 条件
    判断的条件可能存在多个,所以使用或或者且按钮添加更多的条件。
  • 结果
    条件成立,可能执行多个动作。可以使用新增动作添加更多的触发场景
  • if语句或其他语句可能存在多个
    使用新增条件添加更多的联动逻辑

目前方案和思考

联动二期规划

  • 目前联动一期 alpha版并没有实现获取ui配置和设置ui数据和校验数据
    二期需要支持配置ui配置和校验配置
    比如:选中表单A,设置表单B的任何样式,设置表单B的任何校验信息
  • 支持自增模式下嵌套内容的配置

思考

  • 是否有必要支持复杂联动的配置?
    复杂联动的配置本质上是对js的语法配置化,对于平台新手用户来说,可能并不了解代码的底层实现,配置成本巨高。而对于开发来说,可能复杂的配置还没有手写几行联动代码更快。

    因此联动配置化需要固定的表单业务场景,复杂联动的配置化并不适合。在开发联动配置化前需要梳理当前业务域、支持域。

  • 是否需要一键设置反转? 比如:表单A选中某个值时,表单B隐藏。
    开启自动反转,则表单A未选中某个值时,表单B都是展示的

  • 是否需要开启动作触发时机和次数(生命周期概念)
    比如:表单A选中某个值时,表单B值为1
    表单B的值后续是否可以更改?可以更改,则这个联动是一次性的。不可以更改则是永久的

  • 如何降低联动配置成本
    现在的交互设计有理解成本。对于不了解平台的新手,搭建仍然一脸懵。

    是否可以通过两次表单的JSON对比,自动生成联动逻辑

    1. 先拖拽没有触发联动时的表单样式
    2. 配置触发联动之后的表单样式
    3. 两次配置的JSON自动比对,自动生成联动语句
    4. 生成的联动信息提示用户确认

最后,欢迎star drip-form