This commit is contained in:
asp_ly 2024-12-13 20:46:05 +08:00
parent f55b21d5ab
commit 45264109d0
3 changed files with 72 additions and 23 deletions

View File

@ -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') || {}
};

View File

@ -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,

View File

@ -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);
};