demo体验地址:dbfu.github.io/lowcode-dem…
前言
前面我们实现了组件事件绑定动作,但是一个事件只能绑定一个动作,大大限制了开发复杂功能的能力。
这一篇来实现一个事件可以绑定多个动作,并且通过可视化的方式设置,让流程配置清晰明了。
思路来源于虚幻游戏引擎的蓝图功能,这一篇先简单实现一下,主要是给大家分享一下实现思路。
可视化库使用antv
的G6开源库,功能强大,使用起来也简单。
往期回顾
案例分析
上一篇文章后,留了一张图,下面给大家分析一下这张图的意义。
触发某个事件后,执行组件方法,然后动作执行成功后调一个接口,接口执行成功或失败后执行动作。
从上图可以得知:
每个组件的每个事件都可以绑定一个事件流,当触发组件事件的时候,执行这个事件流。
事件流设计页面,有4种类型节点,第一个是开始节点,第二个是动作节点、第三个是条件节点、第四个是事件节点,开始节点也算是事件节点。
开始节点:没有意义,表示入口。
动作节点:可以绑定动作,可以连接事件节点和条件节点
条件节点:可以配置多个条件分支,每个条件分支可以绑定一个动作节点,只能连接事件节点。
事件节点:每个动作节点都会有事件节点,只能连接动作节点。
效果展示
上图可以使用antv的G6库来实现,因为官方给的demo中有类似的案例,可以参考使用。
经过一番改造,终于实现了需求。
动作节点配置
条件节点配置
核心功能讲解
前言
这一块内容比较多,我挑几个核心功能和大家分享一下。
数据结构
节点数据结构
export interface Node {
/**
* 节点id
*/
id: string;
/**
* 节点描述
*/
label: string;
/**
* 节点类型
*/
type: string;
/**
* 节点绑定的下拉菜单
*/
menus: Menu[];
/**
* 节点子节点
*/
children?: Node[];
/**
* 节点额外配置
*/
config?: any;
/**
* 节点条件结果
*/
conditionResult?: boolean;
/**
* 节点事件key
*/
eventKey?: string;
}
下拉菜单
export interface Menu {
/**
* 菜单key
*/
key: string;
/**
* 菜单描述
*/
label: string;
/**
* 即将生成的节点类型
*/
nodeType?: string;
/**
* 将生成的节点名称
*/
nodeName?: string;
/**
* 如果节点为条件节点,每个菜单表示一个条件,conditionId对应定义的条件id
*/
conditionId?: string;
}
初始数据
export const data: Node = {
id: 'root',
label: '开始',
type: 'start',
menus: [
{
key: 'action',
label: '动作',
nodeType: 'action',
nodeName: '动作',
},
{
key: 'condition',
label: '条件',
nodeType: 'condition',
nodeName: '条件',
},
],
};
实现画布居中偏上
G6提供了垂直水平居中方法 graph.fitCenter()
,但是只支持垂直水平居中,根据上图我们只需要水平居中,所以在画布渲染后,需要用translate
方法向上移动一下位置。
自定义带添加图标节点
import { COLLAPSE_ICON, EXPAND_ICON } from '../icons';
export const actionNode: any = {
options: {
style: {
fill: '#F9F0FF',
stroke: '#B37FEB',
radius: 8,
lineWidth: 1,
},
stateStyles: {
hover: {},
selected: {},
},
labelCfg: {
style: {
fill: '#000000',
fontSize: 14,
fontWeight: 400,
fillOpacity: '0.7',
},
},
size: [120, 40],
},
afterDraw(cfg: any, group: any) {
const styles = this.getShapeStyle(cfg);
const h = styles.height;
const w = styles.width;
const keyShape: any = group.addShape('rect', {
attrs: {
x: 0,
y: 0,
...styles,
},
});
group.addShape('marker', {
attrs: {
x: w / 2 - 20,
y: 0,
r: 6,
stroke: '#ff4d4f',
cursor: 'pointer',
symbol: COLLAPSE_ICON,
},
name: 'remove-item',
});
if (cfg.menus?.length) {
group.addShape('marker', {
attrs: {
x: 0,
y: h / 2 + 7,
r: 6,
stroke: '#73d13d',
cursor: 'pointer',
symbol: EXPAND_ICON,
},
name: 'add-item',
});
}
if (cfg.label) {
group.addShape('text', {
// attrs: style
attrs: {
x: 0, // 居中
y: 0,
textAlign: 'center',
textBaseline: 'middle',
text: cfg.label,
fill: '#000000',
fontSize: 12,
fontWeight: 400,
fillOpacity: '0.7',
},
name: 'text-shape',
});
}
return keyShape;
},
update(cfg: any, node: any) {
const styles = this.getShapeStyle(cfg);
const h = styles.height;
const group = node.getContainer();
const child = group.find((item: any) => {
return item.get('name') === 'add-item';
});
const text = group.find((item: any) => {
return item.get('name') === 'text-shape';
});
if (text) {
text.attr({ text: cfg.label })
}
if (!child && cfg.menus?.length) {
group.addShape('marker', {
attrs: {
x: 0,
y: h / 2 + 7,
r: 6,
stroke: '#73d13d',
cursor: 'pointer',
symbol: EXPAND_ICON,
},
name: 'add-item',
});
}
},
};
根据是否有菜单动态添加加号图标
这里复杂一点的是算坐标,y为0的表示节点中心,所以需要下移半个节点高度(h/2),再加上图标的高度(+7),多加1是为了好看点。
连接线上加文本
如图所示,如果线的源节点是条件节点,获取条件名称,添加到线上。
实现下拉菜单
监听添加按钮点击事件,获取当前节点位置,根据当前节点的位置和菜单配置渲染下拉菜单。
// src/editor/layouts/flow-event/context-menu.tsx
import React from 'react';
import { Dropdown } from 'antd';
interface Props {
position: {
top?: number;
left?: number;
};
onSelect: (item: any) => void;
items: { label: string, key: string }[];
open: boolean;
}
const ContextMenu: React.FC<Props> = (props) => {
const { position, onSelect, items, open } = props;
return (
<div
style={{
position: 'absolute',
top: position?.top,
left: position?.left,
}}
>
<Dropdown
menu={{
items: items.map(item => ({ label: item.label, key: item.key })),
onClick: onSelect
}}
open={open}
>
<a onClick={(e) => e.preventDefault()}></a>
</Dropdown>
</div>
);
}
export default ContextMenu;
执行事件流
遍历节点,判断是否是动作节点,如果是动作节点并且条件结果不为false,则根据不同动作类型执行不同动作;如果是条件节点,执行条件脚本,把结果注入到子节点conditionResult
属性中。
显示提示动作实现
组件方法动作实现
设置变量动作实现
执行脚本动作实现
最后
这一篇我们实现了事件可视化配置,下一篇会封装一些常用组件,并且使用低代码做一个完整功能实战一下。
demo体验地址:dbfu.github.io/lowcode-dem…
demo仓库地址:github.com/dbfu/lowcod…