import React, { useEffect, useState, useRef } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Button, Space, Card, Row, Col, message } from 'antd'; import { ArrowLeftOutlined, SaveOutlined, PlayCircleOutlined } from '@ant-design/icons'; import { Graph, Cell } from '@antv/x6'; import { DagreLayout } from '@antv/layout'; import { getDefinitionDetail, saveDefinition } from '../service'; import NodePanel from './components/NodePanel'; import NodeConfigDrawer from './components/NodeConfigModal'; import { NodeDefinition } from './types'; import { validateWorkflow } from './utils/validator'; import { GRID_CONFIG, CONNECTING_CONFIG, HIGHLIGHTING_CONFIG, DEFAULT_STYLES, generatePortGroups, generatePortItems, } from './constants'; const WorkflowDesign: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const [title, setTitle] = useState('工作流设计'); const graphContainerRef = useRef(null); const [graph, setGraph] = useState(null); const [selectedNode, setSelectedNode] = useState(null); const [selectedNodeDefinition, setSelectedNodeDefinition] = useState(null); const [configModalVisible, setConfigModalVisible] = useState(false); const [definitionData, setDefinitionData] = useState(null); useEffect(() => { if (graphContainerRef.current) { const graph = new Graph({ container: graphContainerRef.current, grid: GRID_CONFIG, connecting: CONNECTING_CONFIG, highlighting: HIGHLIGHTING_CONFIG, snapline: true, history: true, clipboard: true, selecting: true, keyboard: true, background: { color: '#f5f5f5', }, }); // 显示/隐藏连接桩 graph.on('node:mouseenter', ({ node }) => { const ports = document.querySelectorAll(`[data-cell-id="${node.id}"] .x6-port-body`); ports.forEach((port) => { port.setAttribute('style', 'visibility: visible'); }); }); graph.on('node:mouseleave', ({ node }) => { const ports = document.querySelectorAll(`[data-cell-id="${node.id}"] .x6-port-body`); ports.forEach((port) => { port.setAttribute('style', 'visibility: hidden'); }); }); // 节点双击事件 graph.on('node:dblclick', ({ node }) => { const nodeDefinition = node.getProp('nodeDefinition'); setSelectedNode(node); setSelectedNodeDefinition(nodeDefinition); setConfigModalVisible(true); }); setGraph(graph); // 在 graph 初始化完成后加载数据 if (id) { loadDefinitionDetail(graph, id); } return () => { graph.dispose(); }; } }, [graphContainerRef, id]); const loadDefinitionDetail = async (graphInstance: Graph, definitionId: number) => { try { const response = await getDefinitionDetail(definitionId); console.log('加载到的工作流数据:', response); setTitle(`工作流设计 - ${response.name}`); setDefinitionData(response); // 清空画布 graphInstance.clearCells(); const nodeMap = new Map(); // 动态注册节点类型 response.graph.nodes.forEach((nodeData: any) => { // 根据节点类型动态注册 const nodeConfig = { inherit: 'rect', // 使用基础的矩形节点作为默认继承 width: nodeData.graph.size.width, height: nodeData.graph.size.height, attrs: { body: nodeData.graph.style, }, ports: nodeData.graph.ports }; // 根据形状选择不同的基础节点类型 if (nodeData.graph.shape === 'circle') { nodeConfig.inherit = 'circle'; } else if (nodeData.graph.shape === 'polygon') { nodeConfig.inherit = 'polygon'; } // 注册节点类型 Graph.registerNode(nodeData.code, nodeConfig, true); // 创建节点 const node = graphInstance.addNode({ id: nodeData.id, shape: nodeData.code, position: nodeData.graph.position || { x: 100, y: 100 }, attrs: { body: nodeData.graph.style, label: { text: nodeData.name, fill: '#000000', fontSize: 12, } }, nodeDefinition: nodeData, }); nodeMap.set(nodeData.id, node); }); // 创建边 response.graph.edges.forEach((edge: any) => { const sourceNode = nodeMap.get(edge.from); const targetNode = nodeMap.get(edge.to); if (sourceNode && targetNode) { graphInstance.addEdge({ source: { cell: sourceNode.id }, target: { cell: targetNode.id }, attrs: { line: DEFAULT_STYLES.edge }, labels: [ { attrs: { label: { text: edge.name || '', }, }, }, ], }); } }); // 自动布局 const layout = new DagreLayout({ type: 'dagre', rankdir: 'LR', align: 'UL', ranksep: 80, nodesep: 50, }); const cells = graphInstance.getCells(); layout.layout(cells); graphInstance.resetCells(cells); graphInstance.centerContent(); } catch (error) { console.error('加载工作流定义失败:', error); message.error('加载工作流定义失败'); } }; const handleNodeDragStart = (node: NodeDefinition, e: React.DragEvent) => { e.dataTransfer.setData('node', JSON.stringify(node)); }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); if (graph) { const nodeData = e.dataTransfer.getData('node'); if (nodeData) { const node = JSON.parse(nodeData); const { clientX, clientY } = e; const point = graph.clientToLocal({ x: clientX, y: clientY }); const nodeConfig = { shape: node.graphConfig.uiSchema.shape, width: node.graphConfig.uiSchema.size.width, height: node.graphConfig.uiSchema.size.height, attrs: { body: { fill: node.graphConfig.uiSchema.style.fill, stroke: node.graphConfig.uiSchema.style.stroke, strokeWidth: node.graphConfig.uiSchema.style.strokeWidth, }, label: { text: node.name, fill: '#000000', fontSize: 12, }, }, ports: { groups: generatePortGroups(), items: generatePortItems(), }, }; // 创建节点时设置完整的属性 graph.addNode({ ...nodeConfig, x: point.x, y: point.y, // 保存完整的节点信息 type: node.type, code: node.code, graph: { shape: node.graphConfig.uiSchema.shape, size: node.graphConfig.uiSchema.size, style: node.graphConfig.uiSchema.style, ports: node.graphConfig.uiSchema.ports }, // 保存节点定义,用于后续编辑 nodeDefinition: node, }); } } }; const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); }; const handleNodeConfigUpdate = (values: any) => { if (!selectedNode) return; // 更新节点配置 selectedNode.setProp('config', values); // 更新节点显示名称 if (values.name) { selectedNode.attr('label/text', values.name); } setConfigModalVisible(false); message.success('节点配置已更新'); }; const handleSaveWorkflow = async () => { if (!graph || !definitionData) return; try { // 校验流程图 const validationResult = validateWorkflow(graph); if (!validationResult.valid) { message.error(validationResult.message); return; } // 获取所有节点和边的数据 const nodes = graph.getNodes().map(node => { const nodeDefinition = node.getProp('nodeDefinition'); const nodeType = node.getProp('type'); return { id: node.id, code: nodeType, type: nodeType, name: node.attr('label/text'), graph: { shape: nodeDefinition?.graphConfig.uiSchema.shape, size: { width: node.size().width, height: node.size().height }, style: nodeDefinition?.graphConfig.uiSchema.style, ports: nodeDefinition?.graphConfig.uiSchema.ports }, config: node.getProp('config') || {} }; }); const edges = graph.getEdges().map(edge => ({ id: edge.id, from: edge.getSourceCellId(), to: edge.getTargetCellId(), name: edge.getLabels()?.[0]?.attrs?.label?.text || '', config: { type: 'sequence' } })); // 构建保存数据 const saveData = { ...definitionData, graph: { nodes, edges } }; // 调用保存接口 await saveDefinition(saveData); message.success('流程保存成功'); } catch (error) { console.error('保存流程失败:', error); message.error('保存流程失败'); } }; return (
} >
setConfigModalVisible(false)} />
); }; export default WorkflowDesign;