前期工作已搞定,现在可以搭建桌面应用了。这个阶段可以结合前面定义好的数据格式构建流程图。
模板
还是使用熟悉的技术栈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><Cardsize="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><Handletype="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 } = paramsif (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}
...
最后
源码