1
This commit is contained in:
parent
f55b21d5ab
commit
45264109d0
@ -3,6 +3,7 @@ import { useParams, useNavigate } from 'react-router-dom';
|
|||||||
import { Button, Space, Card, Row, Col, message, Modal } from 'antd';
|
import { Button, Space, Card, Row, Col, message, Modal } from 'antd';
|
||||||
import { ArrowLeftOutlined, SaveOutlined, PlayCircleOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined, SaveOutlined, PlayCircleOutlined } from '@ant-design/icons';
|
||||||
import { Graph, Cell } from '@antv/x6';
|
import { Graph, Cell } from '@antv/x6';
|
||||||
|
import '@antv/x6-plugin-snapline';
|
||||||
import { getDefinitionDetail, saveDefinition } from '../service';
|
import { getDefinitionDetail, saveDefinition } from '../service';
|
||||||
import { getNodeDefinitionList } from './service';
|
import { getNodeDefinitionList } from './service';
|
||||||
import NodePanel from './components/NodePanel';
|
import NodePanel from './components/NodePanel';
|
||||||
@ -119,9 +120,9 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, [graphContainerRef, id, nodeDefinitions, isNodeDefinitionsLoaded]);
|
}, [graphContainerRef, id, nodeDefinitions, isNodeDefinitionsLoaded]);
|
||||||
|
|
||||||
const loadDefinitionDetail = async (graphInstance: Graph, definitionId: number) => {
|
const loadDefinitionDetail = async (graphInstance: Graph, definitionId: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await getDefinitionDetail(definitionId);
|
const response = await getDefinitionDetail(Number(definitionId));
|
||||||
setTitle(`工作流设计 - ${response.name}`);
|
setTitle(`工作流设计 - ${response.name}`);
|
||||||
setDefinitionData(response);
|
setDefinitionData(response);
|
||||||
|
|
||||||
@ -131,8 +132,6 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
|
|
||||||
// 创建节点
|
// 创建节点
|
||||||
response.graph?.nodes?.forEach((workflowDefinitionNode: any) => {
|
response.graph?.nodes?.forEach((workflowDefinitionNode: any) => {
|
||||||
console.log('Creating node with data:', workflowDefinitionNode); // 添加日志
|
|
||||||
|
|
||||||
const node = addNodeToGraph(false, graphInstance, workflowDefinitionNode, nodeDefinitions);
|
const node = addNodeToGraph(false, graphInstance, workflowDefinitionNode, nodeDefinitions);
|
||||||
// 保存节点配置
|
// 保存节点配置
|
||||||
node.setProp('config', workflowDefinitionNode.config);
|
node.setProp('config', workflowDefinitionNode.config);
|
||||||
@ -155,8 +154,8 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 应用自动布局
|
// 传入节点数据,只在没有位置信息时应用自动布局
|
||||||
applyAutoLayout(graphInstance);
|
applyAutoLayout(graphInstance, response.graph?.nodes || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载工作流定义失败:', error);
|
console.error('加载工作流定义失败:', error);
|
||||||
message.error('加载工作流定义失败');
|
message.error('加载工作流定义失败');
|
||||||
@ -217,6 +216,7 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
const nodes = graph.getNodes().map(node => {
|
const nodes = graph.getNodes().map(node => {
|
||||||
const nodeDefinition = node.getProp('nodeDefinition');
|
const nodeDefinition = node.getProp('nodeDefinition');
|
||||||
const nodeType = node.getProp('type');
|
const nodeType = node.getProp('type');
|
||||||
|
const position = node.getPosition();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: node.id,
|
id: node.id,
|
||||||
@ -230,7 +230,11 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
height: node.size().height
|
height: node.size().height
|
||||||
},
|
},
|
||||||
style: nodeDefinition?.graphConfig.uiSchema.style,
|
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') || {}
|
config: node.getProp('config') || {}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,11 +1,40 @@
|
|||||||
import { Graph } from '@antv/x6';
|
import { Graph } from '@antv/x6';
|
||||||
import dagre from 'dagre';
|
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 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();
|
const g = new dagre.graphlib.Graph();
|
||||||
g.setGraph({
|
g.setGraph({
|
||||||
rankdir: 'LR',
|
rankdir: 'LR',
|
||||||
@ -18,8 +47,8 @@ export const applyAutoLayout = (graph: Graph) => {
|
|||||||
g.setDefaultEdgeLabel(() => ({}));
|
g.setDefaultEdgeLabel(() => ({}));
|
||||||
|
|
||||||
// 添加节点
|
// 添加节点
|
||||||
const nodes = graph.getNodes();
|
const graphNodes = graph.getNodes();
|
||||||
nodes.forEach(node => {
|
graphNodes.forEach(node => {
|
||||||
g.setNode(node.id, {
|
g.setNode(node.id, {
|
||||||
width: node.getSize().width,
|
width: node.getSize().width,
|
||||||
height: node.getSize().height,
|
height: node.getSize().height,
|
||||||
@ -36,7 +65,7 @@ export const applyAutoLayout = (graph: Graph) => {
|
|||||||
dagre.layout(g);
|
dagre.layout(g);
|
||||||
|
|
||||||
// 应用布局结果
|
// 应用布局结果
|
||||||
nodes.forEach(node => {
|
graphNodes.forEach(node => {
|
||||||
const nodeWithPosition = g.node(node.id);
|
const nodeWithPosition = g.node(node.id);
|
||||||
node.position(
|
node.position(
|
||||||
nodeWithPosition.x - nodeWithPosition.width / 2,
|
nodeWithPosition.x - nodeWithPosition.width / 2,
|
||||||
|
|||||||
@ -3,20 +3,23 @@ import {convertPortConfig} from '../constants';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加节点到图形
|
* 添加节点到图形
|
||||||
|
* @param isNew 是否为新节点
|
||||||
* @param graph X6 Graph实例
|
* @param graph X6 Graph实例
|
||||||
* @param node 节点数据
|
* @param workflowDefinitionNode 工作流节点定义
|
||||||
* @param position 节点位置(可选)
|
* @param workflowNodeDefinitionList 工作流节点定义列表
|
||||||
|
* @param position 新节点的位置(可选)
|
||||||
* @returns 创建的节点实例
|
* @returns 创建的节点实例
|
||||||
*/
|
*/
|
||||||
export const addNodeToGraph = (
|
export const addNodeToGraph = (
|
||||||
isNew: Boolean,
|
isNew: boolean,
|
||||||
graph: Graph,
|
graph: Graph,
|
||||||
workflowDefinitionNode: any,
|
workflowDefinitionNode: any,
|
||||||
workflowNodeDefinitionList: any,
|
workflowNodeDefinitionList: any,
|
||||||
position?: { x: number, y: number }
|
position?: { x: number; y: number }
|
||||||
) => {
|
) => {
|
||||||
let nodeDefinition = workflowNodeDefinitionList.find(def => def.type === workflowDefinitionNode.type);
|
let nodeDefinition = workflowNodeDefinitionList.find(def => def.type === workflowDefinitionNode.type);
|
||||||
let uiGraph = isNew ? nodeDefinition.graphConfig.uiSchema : workflowDefinitionNode.graph;
|
let uiGraph = isNew ? nodeDefinition.graphConfig.uiSchema : workflowDefinitionNode.graph;
|
||||||
|
|
||||||
// 根据形状类型设置正确的 shape
|
// 根据形状类型设置正确的 shape
|
||||||
let shape = 'rect'; // 默认使用矩形
|
let shape = 'rect'; // 默认使用矩形
|
||||||
if (uiGraph.shape === 'circle') {
|
if (uiGraph.shape === 'circle') {
|
||||||
@ -24,8 +27,9 @@ export const addNodeToGraph = (
|
|||||||
} else if (uiGraph.shape === 'diamond') {
|
} else if (uiGraph.shape === 'diamond') {
|
||||||
shape = 'polygon';
|
shape = 'polygon';
|
||||||
}
|
}
|
||||||
// 创建节点
|
|
||||||
return graph.addNode({
|
// 创建节点配置
|
||||||
|
const nodeConfig = {
|
||||||
inherit: 'rect',
|
inherit: 'rect',
|
||||||
width: uiGraph.size.width,
|
width: uiGraph.size.width,
|
||||||
height: uiGraph.size.height,
|
height: uiGraph.size.height,
|
||||||
@ -40,14 +44,26 @@ export const addNodeToGraph = (
|
|||||||
text: isNew ? nodeDefinition.name : workflowDefinitionNode.name
|
text: isNew ? nodeDefinition.name : workflowDefinitionNode.name
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
shape, // 使用映射后的形状
|
shape,
|
||||||
...(position && {x: position.x, y: position.y}),
|
|
||||||
// 如果是已保存的节点,使用其ID和位置
|
|
||||||
...(uiGraph.id && {id: uiGraph.id}),
|
|
||||||
...(uiGraph.graph?.position && {position: uiGraph.graph.position}),
|
|
||||||
type: isNew ? nodeDefinition.type : workflowDefinitionNode.type,
|
type: isNew ? nodeDefinition.type : workflowDefinitionNode.type,
|
||||||
code: uiGraph.code,
|
code: uiGraph.code,
|
||||||
ports: convertPortConfig(uiGraph.ports),
|
ports: convertPortConfig(uiGraph.ports),
|
||||||
nodeDefinition: nodeDefinition
|
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);
|
||||||
};
|
};
|
||||||
Loading…
Reference in New Issue
Block a user