前期工作已搞定,现在可以搭建桌面应用了。这个阶段可以结合前面定义好的数据格式构建流程图。
模板
还是使用熟悉的技术栈vite + react + electron,模板
流程图
官方文档
自定义 节点样式
因为配置化的操作类型较多,因此可以利用自定义节点样式区分(常规的就是一个长方形);
例如:开始节点
import React, { memo } from 'react';
import { Card } from 'antd'
import { Handle } from 'react-flow-renderer';
import StartSvg from '../assets/start.svg'
import './flow.node.css'
export default memo(({ isConnectable, selected, data }) => {
return (
<>
<div>
<Card
size="small"
title={<img src={StartSvg} className={`card-img ${selected && 'selected'}`} />}
style={{ maxWidth: 300, minWidth: 260 }}
>
<p className="wrap-txt">打开页面:{data.url}</p>
{/* <span>下一步</span> */}
</Card>
</div>
<Handle
type="source"
position="bottom"
style={{ backgroundColor: '#576B95' }}
isConnectable={isConnectable}
></Handle>
</>
);
});
使用:
const nodeTypes = useMemo(() => ({
start: StartNode,})
实现修改参数
监听流程图节点的点击事件,并弹窗。每个节点的参数都不太一样,因此需要定义不同类型的组件。流程图点击事件:
const onNodeClick = useCallback((event, node) => {
setNodeDrawer({
title: '详情',
open: true,
node,
})
}, [])
元素之外的节点操作
想让软件流程走通,出来chrom插件圈选元素的节点之外,我们还需要其他节点操作,例如:刷新页面,获取当前页面。这个时候就可以根据之前的定义数据格式预设一些节点,例如:
{
imgSrc: NewSvg,
disable: false,
txt: '获取最新页面',
nodeType: 'logic_new_page',
data: {
type: 'logic_new_page',
data: {
logicsetting: {
logicType: 'logic_new_page',
waitTime: 1,
}
}
}
},
通过拖拽的方式,把节点添加到流程图内:
const onDragStart = (event, data) => {
event.dataTransfer.setData('application/reactflow', JSON.stringify(data));
event.dataTransfer.effectAllowed = 'move';
};
限制
限制1: 一个六流程图不能有环,如果存在环可能导致死循环
解决: 两个节点连接时,判断是否有一个节点已经存在连接
const onConnect = useCallback((params) => {
const edges = reactFlowInstance.getEdges()
const nodes = reactFlowInstance.getNodes()
console.log('params===', params, edges)
.....
const { source, target, sourceHandle, targetHandle } = params
if (edges.find(item => item.source === source && item.sourceHandle === sourceHandle)
|| edges.find(item => item.target === target && item.targetHandle === targetHandle)) {
messageApi.open({
type: 'warning',
content: '只允许一个流程',
});
return;
}
...
})
限制2:部分节点只能连接特定的节点类型,例如: 循环条件分支只能连接条件判断、自定义类型的节点
const onConnect = (
...
if (sourceHandle === 'loopcondition') {
if (!['logic_func', 'opt_verify', 'opt_exists'].includes(fTarget.type)) {
messageApi.open({
type: 'error',
content: '不能连接该类型节点!!!',
});
return
}
} else if (sourceHandle === 'listbody') {
if (!['logic_listitem'].includes(fTarget.type)) {
messageApi.open({
type: 'error',
content: '不能连接该类型节点!!!',
});
return
}
} else if (fSource && fSource.type === 'logic_listitem' && sourceHandle === 'next'
&& fTarget && fTarget.type !== 'logic_listitem') {
messageApi.open({
type: 'error',
content: '不能连接该类型节点!!!',
});
return
}
...
最后
源码