高德地图作业平台低代码实战
背景
问题与分析
一个简单的联动示例
示例中代码分析
// 以上示例的伪代码实现
class Main extends PurComponents {
constructor() {
this.state = {
A: '',
B: '',
C: '',
}
}
// 响应逻辑
handleAChange = (e) => {
// 1.数据源筛选
const { target: { value } } = e;
// 2.双向绑定,组织数据结构
let stateOptions = {
A: value;
};
// 3.数据联动
if (value === '1') {
stateOptions = {
...stateOptions,
B: '',
C: '',
}
}
// 4.更新数据
this.setState(stateOptions);
};
// UI组件
render() {
<div className="pic-wrapper">
<PicPlayer ... />
</div>
<div className="task-oper">
<Text>标牌是否是施工场景</Text>
<ButtonGroup onChange={(e) => this.handleAChange(e)} />
...
<ButtonGroup disabled={A !== '2'} ... />
...
<ButtonGroup disabled={B !== '2'} ... />
</div>
... bla bla bla
}
}
由上方伪代码可以看到,一个车间的实现分为:UI + 逻辑
在可视化搭建中,UI部分和简单交互由组件内部实现,跨组件的逻辑部分则由事件回调实现,回调函数可以拆分为以下四部分主体功能和相关概念:
数据筛选:从函数参数中筛选需要的数据(涉及事件驱动的概念)
双向绑定:受控组件由外部数据驱动,状态变化后需要更新依赖的数据源(受控组件,双向绑定,不可变原则)
数据联动:其它数据的更新,组件间的联动通信;(业务逻辑)
数据更新:前端框架Api;
示例中基本概念如何简化的思考
针对事件驱动中回调函数中的数据筛选能力,我们开发了数据筛选器,在设计页面的组件设置器中用树形结构静态声明函数参数,用户只需选择对应的参数即可实现类似数据解构和赋值的功能
针对双向绑定,数据联动,immutable不可变原则,我们约定了简洁的数据存储语法,并针对此语法开发了特定的语法的解析插件,具体设计如下:
页面中一个组件状态变更触发联动其它组件状态变化的场景,我们认为这是一个组件的状态变更过程的"副作用"saveEffect;
每一条"副作用"包含三部分,分别是源数据,目标数据,转换逻辑,以上面联动为例:源数据A, 目标数据B, 转换逻辑是当A为1时,B清空, 对应数据结构是:
一个"副作用"对象由三部分构成
{
// 源数据
fromPath: 'e.target.value',
// 目标数据
toPath: 'state.B',
// 转换逻辑
formatFunc: 'function switchStoreValue(value, state) { return A == '1' ? '' : state.B };'
}
我们在低代码组件中提供了saveEffect设置器, 让用户自行选择"副作用"的源数据、确定目标值,并书写转换逻辑,这部分逻辑被注入到组件中,组件在回调函数中将输出值 和 saveEffect对象同时抛出,最终交由我们的语法解析插件解析并执行,实现组件联动;
针对数据更新,我们认为用户根本无须关注框架层的API,所以开发了自动绑定设置器@ali/lowcode-setter-a-event-setter 会将组件的回调函数绑定到saveState全局函数中,在saveState全局函数中自动执行数据更新操作, 以下是一条联动"副作用"示例
// saveEffect 副作用对象的简单示例
// 变更前全局数据
state = {
temp: {
imageIndex: 0,
},
workResult: {
imageList: [
{
attr1: 1,
attr2: 2,
},
...
]
}
}
// 用户确定的saveEffect对象
{
// 源数据
fromPath: 'value' // 1
// 目标数据
toPath: 'workResult.imageList[state.temp.imageIndex].activeKey';
// 默认值,用户无须书写
formatFunc: 'function switchStoreValue(value, state) { return value }';
}
// 无转换逻辑,则经过插件解析后会生成以下结构并merge到state中
{
temp: {
imageIndex: 0,
},
workResult: {
imageList: [
{
attr1: 1,
attr2: 2,
...
activeKey:1
},
...
]
}
}
使用效果
一个线上车间的例子
使用saveEffect设置器节省代码的前后对比,以引导线内容车间为例,我们需要为一个引导线方向赋值一个属性,UI操作如下图,选中车道,更新车道的类型
function handleButtonGroupClick(value) {
// 获取当前选中的箭头选项
const { arrowList, currentIndex } = this.state;
const currentArrow = arrowList[currentIndex];
// 数据更新
const newArrow = {
...currentArrow,
type: value
};
// 数据不可变;
const newArrowList = arrowList.slice(0, currentIndex).concat([newArrow]).concat(arrowList.slice(currentIndex + 1));
// lowcode API更新数据
this.setState({
arrowList: newArrowList
});
}
state.arrowList[state.currentIndex].type = value
procode组件如何接入saveEffect
一个普通的组件是由userInterface 和 api驱动组件内部逻辑的运行;
saveEffect可以通过lowcode的setter层以api(props)的方式动态注入,然后在onChange等对外的接口中抛出即可,完全不影响组件的内部逻辑;
组件抛出value和saveEffect后,系统会自动调用我们开发的解析插件进行数据解析和保存,从而实现组件的"副作用"间联动效果;
总结与展望
关注「高德技术」,了解更多