diff --git a/frontend/src/pages/Workflow/Definition/Design/index.tsx b/frontend/src/pages/Workflow/Definition/Design/index.tsx index 726da214..4948fb8f 100644 --- a/frontend/src/pages/Workflow/Definition/Design/index.tsx +++ b/frontend/src/pages/Workflow/Definition/Design/index.tsx @@ -3,6 +3,7 @@ import { useParams, useNavigate } from 'react-router-dom'; import { Button, Space, Card, Row, Col, message, Modal } from 'antd'; import { ArrowLeftOutlined, SaveOutlined, PlayCircleOutlined } from '@ant-design/icons'; import { Graph, Cell } from '@antv/x6'; +import '@antv/x6-plugin-snapline'; import { getDefinitionDetail, saveDefinition } from '../service'; import { getNodeDefinitionList } from './service'; import NodePanel from './components/NodePanel'; @@ -119,9 +120,9 @@ const WorkflowDesign: React.FC = () => { }; }, [graphContainerRef, id, nodeDefinitions, isNodeDefinitionsLoaded]); - const loadDefinitionDetail = async (graphInstance: Graph, definitionId: number) => { + const loadDefinitionDetail = async (graphInstance: Graph, definitionId: string) => { try { - const response = await getDefinitionDetail(definitionId); + const response = await getDefinitionDetail(Number(definitionId)); setTitle(`工作流设计 - ${response.name}`); setDefinitionData(response); @@ -131,8 +132,6 @@ const WorkflowDesign: React.FC = () => { // 创建节点 response.graph?.nodes?.forEach((workflowDefinitionNode: any) => { - console.log('Creating node with data:', workflowDefinitionNode); // 添加日志 - const node = addNodeToGraph(false, graphInstance, workflowDefinitionNode, nodeDefinitions); // 保存节点配置 node.setProp('config', workflowDefinitionNode.config); @@ -155,8 +154,8 @@ const WorkflowDesign: React.FC = () => { } }); - // 应用自动布局 - applyAutoLayout(graphInstance); + // 传入节点数据,只在没有位置信息时应用自动布局 + applyAutoLayout(graphInstance, response.graph?.nodes || []); } catch (error) { console.error('加载工作流定义失败:', error); message.error('加载工作流定义失败'); @@ -217,6 +216,7 @@ const WorkflowDesign: React.FC = () => { const nodes = graph.getNodes().map(node => { const nodeDefinition = node.getProp('nodeDefinition'); const nodeType = node.getProp('type'); + const position = node.getPosition(); return { id: node.id, @@ -230,7 +230,11 @@ const WorkflowDesign: React.FC = () => { height: node.size().height }, style: nodeDefinition?.graphConfig.uiSchema.style, - ports: nodeDefinition?.graphConfig.uiSchema.ports + ports: nodeDefinition?.graphConfig.uiSchema.ports, + position: { + x: position.x, + y: position.y + } }, config: node.getProp('config') || {} }; diff --git a/frontend/src/pages/Workflow/Definition/Design/utils/layoutUtils.ts b/frontend/src/pages/Workflow/Definition/Design/utils/layoutUtils.ts index 4cf99530..c09d4d43 100644 --- a/frontend/src/pages/Workflow/Definition/Design/utils/layoutUtils.ts +++ b/frontend/src/pages/Workflow/Definition/Design/utils/layoutUtils.ts @@ -1,11 +1,40 @@ import { Graph } from '@antv/x6'; import dagre from 'dagre'; +interface NodeData { + graph: { + position?: { + x: number; + y: number; + }; + }; +} + +/** + * 检查是否所有节点都有位置信息 + * @param nodes 节点列表 + * @returns 是否所有节点都有位置信息 + */ +const hasAllNodesPosition = (nodes: NodeData[]) => { + return nodes.every(node => { + const graphData = node.graph; + return graphData && graphData.position && + typeof graphData.position.x === 'number' && + typeof graphData.position.y === 'number'; + }); +}; + /** * 应用自动布局 * @param graph 图形实例 + * @param nodes 节点数据列表 */ -export const applyAutoLayout = (graph: Graph) => { +export const applyAutoLayout = (graph: Graph, nodes: NodeData[] = []) => { + // 如果所有节点都有位置信息,则不应用自动布局 + if (nodes.length > 0 && hasAllNodesPosition(nodes)) { + return; + } + const g = new dagre.graphlib.Graph(); g.setGraph({ rankdir: 'LR', @@ -18,8 +47,8 @@ export const applyAutoLayout = (graph: Graph) => { g.setDefaultEdgeLabel(() => ({})); // 添加节点 - const nodes = graph.getNodes(); - nodes.forEach(node => { + const graphNodes = graph.getNodes(); + graphNodes.forEach(node => { g.setNode(node.id, { width: node.getSize().width, height: node.getSize().height, @@ -36,7 +65,7 @@ export const applyAutoLayout = (graph: Graph) => { dagre.layout(g); // 应用布局结果 - nodes.forEach(node => { + graphNodes.forEach(node => { const nodeWithPosition = g.node(node.id); node.position( nodeWithPosition.x - nodeWithPosition.width / 2, diff --git a/frontend/src/pages/Workflow/Definition/Design/utils/nodeUtils.ts b/frontend/src/pages/Workflow/Definition/Design/utils/nodeUtils.ts index 9e1ba52c..71b84ef8 100644 --- a/frontend/src/pages/Workflow/Definition/Design/utils/nodeUtils.ts +++ b/frontend/src/pages/Workflow/Definition/Design/utils/nodeUtils.ts @@ -3,20 +3,23 @@ import {convertPortConfig} from '../constants'; /** * 添加节点到图形 + * @param isNew 是否为新节点 * @param graph X6 Graph实例 - * @param node 节点数据 - * @param position 节点位置(可选) + * @param workflowDefinitionNode 工作流节点定义 + * @param workflowNodeDefinitionList 工作流节点定义列表 + * @param position 新节点的位置(可选) * @returns 创建的节点实例 */ export const addNodeToGraph = ( - isNew: Boolean, + isNew: boolean, graph: Graph, workflowDefinitionNode: any, workflowNodeDefinitionList: any, - position?: { x: number, y: number } + position?: { x: number; y: number } ) => { let nodeDefinition = workflowNodeDefinitionList.find(def => def.type === workflowDefinitionNode.type); let uiGraph = isNew ? nodeDefinition.graphConfig.uiSchema : workflowDefinitionNode.graph; + // 根据形状类型设置正确的 shape let shape = 'rect'; // 默认使用矩形 if (uiGraph.shape === 'circle') { @@ -24,8 +27,9 @@ export const addNodeToGraph = ( } else if (uiGraph.shape === 'diamond') { shape = 'polygon'; } - // 创建节点 - return graph.addNode({ + + // 创建节点配置 + const nodeConfig = { inherit: 'rect', width: uiGraph.size.width, height: uiGraph.size.height, @@ -40,14 +44,26 @@ export const addNodeToGraph = ( text: isNew ? nodeDefinition.name : workflowDefinitionNode.name }, }, - shape, // 使用映射后的形状 - ...(position && {x: position.x, y: position.y}), - // 如果是已保存的节点,使用其ID和位置 - ...(uiGraph.id && {id: uiGraph.id}), - ...(uiGraph.graph?.position && {position: uiGraph.graph.position}), + shape, type: isNew ? nodeDefinition.type : workflowDefinitionNode.type, code: uiGraph.code, ports: convertPortConfig(uiGraph.ports), nodeDefinition: nodeDefinition - }); + }; + + // 设置节点位置 + if (isNew && position) { + // 新节点:使用传入的position + Object.assign(nodeConfig, { x: position.x, y: position.y }); + } else if (!isNew && workflowDefinitionNode.graph?.position) { + // 已有节点:使用后端返回的position + Object.assign(nodeConfig, { position: workflowDefinitionNode.graph.position }); + } + + // 设置节点ID(如果有) + if (uiGraph.id) { + Object.assign(nodeConfig, { id: uiGraph.id }); + } + + return graph.addNode(nodeConfig); }; \ No newline at end of file