|
|
|
@ -520,92 +520,222 @@ export const useFlowCallbacks = (
|
|
|
|
// 这里可以实现边编辑逻辑
|
|
|
|
// 这里可以实现边编辑逻辑
|
|
|
|
console.log('编辑边:', edge);
|
|
|
|
console.log('编辑边:', edge);
|
|
|
|
}, []);
|
|
|
|
}, []);
|
|
|
|
// 复制节点
|
|
|
|
// 复制节点(支持多节点和多边)
|
|
|
|
const copyNode = useCallback((node: Node) => {
|
|
|
|
const copyNode = useCallback((node: Node) => {
|
|
|
|
// 将节点数据存储到localStorage中,以便粘贴时使用
|
|
|
|
// 获取所有选中的节点(包括当前节点)
|
|
|
|
const nodeData = {
|
|
|
|
const selectedNodes = nodes.filter(n => n.selected || n.id === node.id);
|
|
|
|
...node,
|
|
|
|
|
|
|
|
// 清除可能存在的运行时状态
|
|
|
|
// 过滤掉开始和结束节点
|
|
|
|
|
|
|
|
const nodesToCopy = selectedNodes.filter(n => n.type !== 'start' && n.type !== 'end');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (nodesToCopy.length === 0) {
|
|
|
|
|
|
|
|
console.warn('没有可复制的节点(开始和结束节点不能复制)');
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取这些节点之间的边
|
|
|
|
|
|
|
|
const nodeIds = new Set(nodesToCopy.map(n => n.id));
|
|
|
|
|
|
|
|
const edgesToCopy = edges.filter(e =>
|
|
|
|
|
|
|
|
nodeIds.has(e.source) && nodeIds.has(e.target)
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 清除运行时状态
|
|
|
|
|
|
|
|
const cleanedNodes = nodesToCopy.map(n => ({
|
|
|
|
|
|
|
|
...n,
|
|
|
|
selected: false,
|
|
|
|
selected: false,
|
|
|
|
dragging: false,
|
|
|
|
dragging: false
|
|
|
|
// 添加应用标识信息
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const cleanedEdges = edgesToCopy.map(e => ({
|
|
|
|
|
|
|
|
...e,
|
|
|
|
|
|
|
|
selected: false
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 存储复制的数据
|
|
|
|
|
|
|
|
const copiedData = {
|
|
|
|
|
|
|
|
nodes: cleanedNodes,
|
|
|
|
|
|
|
|
edges: cleanedEdges,
|
|
|
|
appId: initialData?.appId
|
|
|
|
appId: initialData?.appId
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
localStorage.setItem('copiedNode', JSON.stringify(nodeData));
|
|
|
|
localStorage.setItem('copiedFlowData', JSON.stringify(copiedData));
|
|
|
|
}, [initialData?.appId]);
|
|
|
|
console.log(`已复制 ${nodesToCopy.length} 个节点和 ${edgesToCopy.length} 条边`);
|
|
|
|
|
|
|
|
}, [nodes, edges, initialData?.appId]);
|
|
|
|
|
|
|
|
|
|
|
|
// 粘贴节点
|
|
|
|
// 粘贴节点(支持多节点和多边)
|
|
|
|
const pasteNode = useCallback((position: { x: number; y: number }) => {
|
|
|
|
const pasteNode = useCallback((position: { x: number; y: number }) => {
|
|
|
|
|
|
|
|
// 尝试读取新格式的复制数据(多节点+边)
|
|
|
|
|
|
|
|
const copiedFlowDataStr = localStorage.getItem('copiedFlowData');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 兼容旧格式的单节点复制
|
|
|
|
const copiedNodeStr = localStorage.getItem('copiedNode');
|
|
|
|
const copiedNodeStr = localStorage.getItem('copiedNode');
|
|
|
|
if (!copiedNodeStr) {
|
|
|
|
|
|
|
|
|
|
|
|
if (!copiedFlowDataStr && !copiedNodeStr) {
|
|
|
|
console.warn('没有找到复制的节点数据');
|
|
|
|
console.warn('没有找到复制的节点数据');
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const copiedNode = JSON.parse(copiedNodeStr);
|
|
|
|
// 处理新格式(多节点+边)
|
|
|
|
|
|
|
|
if (copiedFlowDataStr) {
|
|
|
|
|
|
|
|
const copiedData = JSON.parse(copiedFlowDataStr);
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否为同一应用,如果不是则不允许粘贴
|
|
|
|
// 检查是否为同一应用
|
|
|
|
if (copiedNode.appId && initialData?.appId && copiedNode.appId !== initialData.appId) {
|
|
|
|
if (copiedData.appId && initialData?.appId && copiedData.appId !== initialData.appId) {
|
|
|
|
console.warn('不能在不同应用之间粘贴节点');
|
|
|
|
console.warn('不能在不同应用之间粘贴节点');
|
|
|
|
// Message.warning('不能在不同应用之间粘贴节点');
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新节点,更新ID和位置
|
|
|
|
const { nodes: copiedNodes, edges: copiedEdges } = copiedData;
|
|
|
|
const newNode = {
|
|
|
|
|
|
|
|
...copiedNode,
|
|
|
|
if (!copiedNodes || copiedNodes.length === 0) {
|
|
|
|
id: `${copiedNode.type}-${Date.now()}`, // 生成新的唯一ID
|
|
|
|
console.warn('没有可粘贴的节点');
|
|
|
|
position, // 使用传入的位置
|
|
|
|
return;
|
|
|
|
selected: false,
|
|
|
|
}
|
|
|
|
dragging: false,
|
|
|
|
|
|
|
|
// 移除应用标识信息,避免存储在节点数据中
|
|
|
|
|
|
|
|
appId: undefined
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 特殊处理循环节点
|
|
|
|
// 计算所有节点的边界框中心点
|
|
|
|
if (copiedNode.type === 'LOOP') {
|
|
|
|
const minX = Math.min(...copiedNodes.map((n: Node) => n.position.x));
|
|
|
|
// 对于循环节点,我们需要特殊处理
|
|
|
|
const minY = Math.min(...copiedNodes.map((n: Node) => n.position.y));
|
|
|
|
|
|
|
|
const maxX = Math.max(...copiedNodes.map((n: Node) => n.position.x));
|
|
|
|
|
|
|
|
const maxY = Math.max(...copiedNodes.map((n: Node) => n.position.y));
|
|
|
|
|
|
|
|
const centerX = (minX + maxX) / 2;
|
|
|
|
|
|
|
|
const centerY = (minY + maxY) / 2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算偏移量,使粘贴的节点组以鼠标位置为中心
|
|
|
|
|
|
|
|
const offsetX = position.x - centerX;
|
|
|
|
|
|
|
|
const offsetY = position.y - centerY;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建旧ID到新ID的映射
|
|
|
|
|
|
|
|
const idMap = new Map<string, string>();
|
|
|
|
|
|
|
|
const timestamp = Date.now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新节点
|
|
|
|
|
|
|
|
const newNodes = copiedNodes.map((node: Node, index: number) => {
|
|
|
|
|
|
|
|
const newId = `${node.type}-${timestamp}-${index}`;
|
|
|
|
|
|
|
|
idMap.set(node.id, newId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const newNode = {
|
|
|
|
|
|
|
|
...node,
|
|
|
|
|
|
|
|
id: newId,
|
|
|
|
|
|
|
|
position: {
|
|
|
|
|
|
|
|
x: node.position.x + offsetX,
|
|
|
|
|
|
|
|
y: node.position.y + offsetY
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
selected: false,
|
|
|
|
|
|
|
|
dragging: false,
|
|
|
|
|
|
|
|
appId: undefined
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 注册节点类型
|
|
|
|
|
|
|
|
const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key));
|
|
|
|
|
|
|
|
if (!nodeMap.includes(newNode.type)) {
|
|
|
|
|
|
|
|
registerNodeType(newNode.type, getNodeComponent(newNode.type), newNode.data?.title as string || newNode.type);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return newNode;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新边,使用新的节点ID
|
|
|
|
|
|
|
|
const newEdges = copiedEdges.map((edge: Edge, index: number) => {
|
|
|
|
|
|
|
|
const newSourceId = idMap.get(edge.source);
|
|
|
|
|
|
|
|
const newTargetId = idMap.get(edge.target);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!newSourceId || !newTargetId) {
|
|
|
|
|
|
|
|
console.warn('边的源节点或目标节点未找到:', edge);
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
...edge,
|
|
|
|
|
|
|
|
id: `e${newSourceId}-${newTargetId}-${timestamp}-${index}`,
|
|
|
|
|
|
|
|
source: newSourceId,
|
|
|
|
|
|
|
|
target: newTargetId,
|
|
|
|
|
|
|
|
selected: false
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}).filter(Boolean); // 过滤掉null值
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 更新节点和边
|
|
|
|
setNodes((nds: Node[]) => {
|
|
|
|
setNodes((nds: Node[]) => {
|
|
|
|
const newNodes = [...nds, newNode];
|
|
|
|
const updatedNodes = [...nds, ...newNodes];
|
|
|
|
|
|
|
|
|
|
|
|
// 添加节点后记录历史
|
|
|
|
// 添加节点后记录历史
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
const event = new CustomEvent('takeSnapshot', {
|
|
|
|
const event = new CustomEvent('takeSnapshot', {
|
|
|
|
detail: { nodes: [...newNodes], edges: [...edges] }
|
|
|
|
detail: { nodes: [...updatedNodes], edges: [...edges, ...newEdges] }
|
|
|
|
});
|
|
|
|
});
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
}, 0);
|
|
|
|
}, 0);
|
|
|
|
|
|
|
|
|
|
|
|
return newNodes;
|
|
|
|
return updatedNodes;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将未定义的节点动态追加进nodeTypes
|
|
|
|
setEdges((eds: Edge[]) => {
|
|
|
|
const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key));
|
|
|
|
const updatedEdges = [...eds, ...newEdges];
|
|
|
|
if (!nodeMap.includes(newNode.type)) {
|
|
|
|
return updatedEdges;
|
|
|
|
registerNodeType(newNode.type, getNodeComponent(newNode.type), newNode.data?.title || newNode.type);
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`已粘贴 ${newNodes.length} 个节点和 ${newEdges.length} 条边`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 处理旧格式(单节点)- 保持向后兼容
|
|
|
|
|
|
|
|
else if (copiedNodeStr) {
|
|
|
|
|
|
|
|
const copiedNode = JSON.parse(copiedNodeStr);
|
|
|
|
|
|
|
|
|
|
|
|
setNodes((nds: Node[]) => {
|
|
|
|
// 检查是否为同一应用
|
|
|
|
const newNodes = [...nds, newNode];
|
|
|
|
if (copiedNode.appId && initialData?.appId && copiedNode.appId !== initialData.appId) {
|
|
|
|
|
|
|
|
console.warn('不能在不同应用之间粘贴节点');
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加节点后记录历史
|
|
|
|
// 创建新节点,更新ID和位置
|
|
|
|
setTimeout(() => {
|
|
|
|
const newNode = {
|
|
|
|
const event = new CustomEvent('takeSnapshot', {
|
|
|
|
...copiedNode,
|
|
|
|
detail: { nodes: [...newNodes], edges: [...edges] }
|
|
|
|
id: `${copiedNode.type}-${Date.now()}`,
|
|
|
|
|
|
|
|
position,
|
|
|
|
|
|
|
|
selected: false,
|
|
|
|
|
|
|
|
dragging: false,
|
|
|
|
|
|
|
|
appId: undefined
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 特殊处理循环节点
|
|
|
|
|
|
|
|
if (copiedNode.type === 'LOOP') {
|
|
|
|
|
|
|
|
setNodes((nds: Node[]) => {
|
|
|
|
|
|
|
|
const newNodes = [...nds, newNode];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
|
|
const event = new CustomEvent('takeSnapshot', {
|
|
|
|
|
|
|
|
detail: { nodes: [...newNodes], edges: [...edges] }
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
|
|
|
|
}, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return newNodes;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
return;
|
|
|
|
}, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return newNodes;
|
|
|
|
// 注册节点类型
|
|
|
|
});
|
|
|
|
const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key));
|
|
|
|
|
|
|
|
if (!nodeMap.includes(newNode.type)) {
|
|
|
|
|
|
|
|
registerNodeType(newNode.type, getNodeComponent(newNode.type), newNode.data?.title || newNode.type);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setNodes((nds: Node[]) => {
|
|
|
|
|
|
|
|
const newNodes = [...nds, newNode];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
|
|
const event = new CustomEvent('takeSnapshot', {
|
|
|
|
|
|
|
|
detail: { nodes: [...newNodes], edges: [...edges] }
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
|
|
|
|
}, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return newNodes;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
console.error('粘贴节点时出错:', error);
|
|
|
|
console.error('粘贴节点时出错:', error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [edges, initialData?.appId]);
|
|
|
|
}, [edges, initialData?.appId, nodes]);
|
|
|
|
// endregion
|
|
|
|
// endregion
|
|
|
|
|
|
|
|
|
|
|
|
// region 节点/边操作
|
|
|
|
// region 节点/边操作
|
|
|
|
@ -1195,7 +1325,7 @@ export const useFlowCallbacks = (
|
|
|
|
appEventDefinition,
|
|
|
|
appEventDefinition,
|
|
|
|
sceneId: info.id
|
|
|
|
sceneId: info.id
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const res: any = await setMainFlowNew(params, initialData.appId);
|
|
|
|
const res: any = await setMainFlowNew(params, initialData.appId);
|
|
|
|
if (res.code === 200) {
|
|
|
|
if (res.code === 200) {
|
|
|
|
Message.success('保存成功');
|
|
|
|
Message.success('保存成功');
|
|
|
|
@ -1450,17 +1580,20 @@ export const useFlowCallbacks = (
|
|
|
|
Message.success('应用已恢复');
|
|
|
|
Message.success('应用已恢复');
|
|
|
|
// 更新暂停状态为 false
|
|
|
|
// 更新暂停状态为 false
|
|
|
|
dispatch(updateIsPaused(false));
|
|
|
|
dispatch(updateIsPaused(false));
|
|
|
|
} else {
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
Message.error(res.msg || '恢复失败');
|
|
|
|
Message.error(res.msg || '恢复失败');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
// 当前正在运行,执行暂停操作
|
|
|
|
// 当前正在运行,执行暂停操作
|
|
|
|
const res: any = await pauseApp({ id: runId });
|
|
|
|
const res: any = await pauseApp({ id: runId });
|
|
|
|
if (res.code === 200) {
|
|
|
|
if (res.code === 200) {
|
|
|
|
Message.success('应用已暂停');
|
|
|
|
Message.success('应用已暂停');
|
|
|
|
// 更新暂停状态为 true
|
|
|
|
// 更新暂停状态为 true
|
|
|
|
dispatch(updateIsPaused(true));
|
|
|
|
dispatch(updateIsPaused(true));
|
|
|
|
} else {
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
Message.error(res.msg || '暂停失败');
|
|
|
|
Message.error(res.msg || '暂停失败');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -1505,7 +1638,8 @@ export const useFlowCallbacks = (
|
|
|
|
|
|
|
|
|
|
|
|
// 重置节点状态
|
|
|
|
// 重置节点状态
|
|
|
|
dispatch(resetNodeStatus());
|
|
|
|
dispatch(resetNodeStatus());
|
|
|
|
} else {
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
Message.error(res.msg || '重跑失败');
|
|
|
|
Message.error(res.msg || '重跑失败');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
|