Merge branch 'refs/heads/master' into production

master
钟良源 3 months ago
commit 428dea25ca

@ -38,9 +38,10 @@ export function queryEventItem(name: string) {
export function queryEventItemBySceneIdOld(sceneId: string) {
return axios.get(`${urlPrefix}/event/${sceneId}/get`);
}
// 事件管理-使用场景ID查询事件新
export function queryEventItemBySceneId(sceneId: string) {
return axios.get(`${urlPrefix}/event/${sceneId}/getTopic`);
export function queryEventItemBySceneId(data: { sceneId: string, nodeId?: string, appId?: string }) {
return axios.post(`${urlPrefix}/event/getTopic`, data);
}
// 事件管理-获取工程下可用的topic

@ -41,9 +41,9 @@ const LocalNodeEditor: React.FC<NodeEditorProps> = ({
case 'CYCLE': // 周期
return <CycleEditor nodeData={nodeData} updateNodeData={updateNodeData} />;
case 'EVENTLISTENE': // 事件接收
return <EventListenEditor nodeData={nodeData} updateNodeData={updateNodeData} />;
return <EventListenEditor nodeData={nodeData} updateNodeData={updateNodeData} node={node} />;
case 'EVENTSEND': // 事件发送
return <EventSendEditor nodeData={nodeData} updateNodeData={updateNodeData} />;
return <EventSendEditor nodeData={nodeData} updateNodeData={updateNodeData} node={node} />;
case 'JSON2STR': // JSON转字符串
return <JsonToStringEditor nodeData={nodeData} updateNodeData={updateNodeData} />;
case 'STR2JSON': // 字符串转JSON

@ -7,12 +7,17 @@ import { useDispatch, useSelector } from 'react-redux';
import { queryEventItemBySceneId } from '@/api/event';
import ParamsTable from '@/components/FlowEditor/nodeEditors/components/ParamsTable';
const EventListenEditor: React.FC<NodeEditorProps> = ({ nodeData, updateNodeData }) => {
const EventListenEditor: React.FC<NodeEditorProps> = ({ node, nodeData, updateNodeData }) => {
const [eventList, setEventList] = useState<any[]>();
const { currentAppData } = useSelector(state => state.ideContainer);
const getEventList = async () => {
const res = await queryEventItemBySceneId(currentAppData.sceneId);
const params = {
nodeId: node.id,
appId: currentAppData.id,
sceneId: currentAppData.sceneId
};
const res = await queryEventItemBySceneId(params);
setEventList(res.data);
};

@ -27,25 +27,29 @@ const EventSelect: React.FC<EventSelectProps> = ({ nodeData, eventList, type, on
const [form] = Form.useForm();
const [showModal, setShowModal] = useState(false);
const [currentEvent, setCurrentEvent] = useState<any>(null);
const { currentAppData } = useSelector(state => state.ideContainer);
const { currentAppData, eventListOld } = useSelector(state => state.ideContainer);
useEffect(() => {
if (nodeData && eventList && eventList.length > 0) {
setOptions(eventList.filter(item => !item.topic.includes('**empty**')));
try {
// 数据是JSON字符串标识是接口回来的
const customDef = JSON.parse(nodeData.component?.customDef);
// 先判断topic是不是**empty**是就不设置currentevent
if (customDef.topic && customDef.topic.includes('**empty**')) {
setCurrentEvent(null);
}
if (customDef.topic && customDef.topic.includes('**empty**')) setCurrentEvent(null);
else {
setCurrentEvent(eventList.find(item => customDef.topic === item.topic));
if (!customDef.eventId) {
setCurrentEvent(eventList.find(item => customDef.topic === item.topic));
}
else {
const currentItem = eventListOld.find(item => customDef.eventId === item.eventId);
setCurrentEvent(currentItem.topic);
}
}
} catch (e) {
// 数据是普通对象标识是当前操作数据
// 先判断topic是不是**empty**是就不设置currentevent
if (nodeData.component?.customDef?.topic && nodeData.component?.customDef?.topic.includes('**empty**')) {
setCurrentEvent(null);
}
if (nodeData.component?.customDef?.topic && nodeData.component?.customDef?.topic.includes('**empty**')) setCurrentEvent(null);
else {
setCurrentEvent(eventList.find(item => nodeData.component?.customDef.topic === item.topic));
}
@ -80,10 +84,11 @@ const EventSelect: React.FC<EventSelectProps> = ({ nodeData, eventList, type, on
};
const handelSelect = (e) => {
console.log('e:', e);
const data = {
type: typeMap[type],
customDef: {
eventId: null,
eventId: e.eventId,
name: e.name,
topic: e.topic
}
@ -123,8 +128,8 @@ const EventSelect: React.FC<EventSelectProps> = ({ nodeData, eventList, type, on
)}
dropdownMenuStyle={{ maxHeight: 300 }}
>
{options.map((option) => (
<Option key={option.topicId} value={option}>
{options.map((option, index) => (
<Option key={option.topic} value={option}>
{option.name}
</Option>
))}

@ -12,7 +12,7 @@ const EventSendEditor: React.FC<NodeEditorProps> = ({ nodeData, updateNodeData }
const { currentAppData } = useSelector(state => state.ideContainer);
const getEventList = async () => {
const res = await queryEventItemBySceneId(currentAppData.sceneId);
const res = await queryEventItemBySceneId({sceneId:currentAppData.sceneId});
setEventList(res.data);
};

@ -34,17 +34,17 @@ const ParamsTable: React.FC<ParamsTableProps> = ({
}, [initialData]);
const dataTypeOptions = [
{ label: '字符串', value: 'string' },
{ label: '数字', value: 'number' },
{ label: '布尔值', value: 'boolean' },
{ label: '数组', value: 'array' },
{ label: '对象', value: 'object' }
{ label: '字符串', value: 'STRING' },
{ label: '数字', value: 'INTEGER' },
{ label: '布尔值', value: 'BOOLEAN' },
{ label: '数组', value: 'ARRAY' },
{ label: '对象', value: 'OBJECT' }
];
const arrayTypeOptions = [
{ label: '字符串数组', value: 'string' },
{ label: '数字数组', value: 'number' },
{ label: '布尔数组', value: 'boolean' }
{ label: '字符串数组', value: 'STRING' },
{ label: '数字数组', value: 'INTEGER' },
{ label: '布尔数组', value: 'BOOLEAN' }
];
const columns = [

@ -313,6 +313,15 @@ const validateCycleNode = (nodeData: any): string[] => {
const validateBasicParams = (nodeData: any): string[] => {
const errors: string[] = [];
// 检查BASIC节点是否有关联的组件实例
if (nodeData.type === 'BASIC') {
// 检查节点是否具有component属性和customDef属性
console.log('nodeData:', nodeData);
if (!nodeData.component || !nodeData.component.compIdentifier) {
errors.push('基础节点缺少组件实例配置');
}
}
// 检查输入参数的完整性
if (nodeData.parameters?.dataIns) {
nodeData.parameters.dataIns.forEach((param: any, index: number) => {

@ -15,7 +15,15 @@ import { localNodeData } from '@/pages/flowEditor/sideBar/config/localNodeData';
import { useAlignmentGuidelines } from '@/hooks/useAlignmentGuidelines';
import LocalNode from '@/components/FlowEditor/node/localNode/LocalNode';
import LoopNode from '@/components/FlowEditor/node/loopNode/LoopNode';
import { updateCanvasDataMap, resetNodeStatus, updateIsRunning, updateEventListOld } from '@/store/ideContainer';
import {
updateCanvasDataMap,
resetNodeStatus,
updateIsRunning,
updateEventListOld,
addRuntimeLog,
clearRuntimeLogs,
updateRuntimeId
} from '@/store/ideContainer';
import {
validateAllNodes,
showValidationErrors,
@ -122,7 +130,6 @@ export const useFlowCallbacks = (
if (!sourceNode || !targetNode) {
return;
}
// 获取源节点和目标节点的参数信息
const sourceParams: any = sourceNode.data?.parameters || {};
const targetParams: any = targetNode.data?.parameters || {};
@ -148,19 +155,43 @@ export const useFlowCallbacks = (
// 创建带有事件信息的连接
const edgeParams = { ...params, type: 'custom' };
// 添加lineType字段用于区分API连接和数据连接
edgeParams.data = {
...edgeParams.data,
lineType: sourceHandleType // 'api' 或 'data'
};
// 对于数据类型的边需要额外验证dataIns和dataOuts中的数据类型是否一致
if (sourceHandleType === 'data') {
// 查找源节点的dataOuts中对应的数据
const sourceDataOut = (sourceParams.dataOuts || []).find((dataOut: any) =>
dataOut.name === params.sourceHandle || dataOut.id === params.sourceHandle);
// 查找目标节点的dataIns中对应的数据
const targetDataIn = (targetParams.dataIns || []).find((dataIn: any) =>
dataIn.name === params.targetHandle || dataIn.id === params.targetHandle);
// 验证数据类型是否一致
if (sourceDataOut && targetDataIn && sourceDataOut.dataType !== targetDataIn.dataType) {
console.warn('数据类型不匹配,源节点数据类型:', sourceDataOut.dataType, '目标节点数据类型:', targetDataIn.dataType);
Message.warning(`数据类型不匹配,源节点数据类型: ${sourceDataOut.dataType},目标节点数据类型: ${targetDataIn.dataType}`);
return edgesSnapshot; // 不创建连接
}
}
// 检查源节点和目标节点是否都有事件信息
const sourceApi = (sourceParams.apiOuts || []).find((api: any) =>
api.name === params.sourceHandle || api.id === params.sourceHandle);
const targetApi = (targetParams.apiIns || []).find((api: any) =>
api.name === params.targetHandle || api.id === params.targetHandle);
const sourceApi = (sourceParams.apiOuts || []).find((api: any) => (api?.eventId || api.name || api.id) === params.sourceHandle);
const targetApi = (targetParams.apiIns || []).find((api: any) => (api?.eventId || api.name || api.id) === params.targetHandle);
// 如果源节点有事件topic信息
if (sourceApi && sourceApi.topic) {
// 如果目标节点的topic是**empty**或没有topic则使用源节点的事件信息
if (!targetApi || !targetApi.topic || targetApi.topic.includes('**empty**')) {
edgeParams.data = {
...edgeParams.data,
lineType: 'api',
displayData: {
name: sourceApi.name,
name: sourceApi.eventName,
eventId: sourceApi.eventId,
topic: sourceApi.topic
}
@ -171,8 +202,10 @@ export const useFlowCallbacks = (
!sourceApi.topic.includes('**empty**') &&
!targetApi.topic.includes('**empty**')) {
edgeParams.data = {
...edgeParams.data,
lineType: 'api',
displayData: {
name: sourceApi.name,
name: sourceApi.eventName,
eventId: sourceApi.eventId,
topic: sourceApi.topic
}
@ -182,8 +215,10 @@ export const useFlowCallbacks = (
// 如果源节点没有事件信息,但目标节点有
else if (targetApi && targetApi.topic && !targetApi.topic.includes('**empty**')) {
edgeParams.data = {
...edgeParams.data,
lineType: 'api',
displayData: {
name: targetApi.name,
name: targetApi.eventName,
eventId: targetApi.eventId,
topic: targetApi.topic
}
@ -559,6 +594,12 @@ export const useFlowCallbacks = (
// region 节点/边操作
// 删除节点函数
const deleteNode = useCallback((node: Node) => {
// 在应用编排模式下useDefault为false不允许删除节点
if (!useDefault) {
console.warn('在应用编排模式下不允许删除节点');
return;
}
// 开始和结束节点不允许删除
if (node.type === 'start' || node.type === 'end') {
console.warn('开始和结束节点不允许删除');
@ -646,9 +687,21 @@ export const useFlowCallbacks = (
});
document.dispatchEvent(event);
}, 0);
}, [nodes, edges]);
}, [nodes, edges, useDefault]);
// 删除边函数
const deleteEdge = useCallback((edge: Edge) => {
// 获取当前应用的运行状态
const { appRuntimeData, currentAppData } = store.getState().ideContainer;
const currentAppIsRunning = currentAppData?.id && appRuntimeData[currentAppData.id]
? appRuntimeData[currentAppData.id].isRunning
: false;
// 在运行时禁止删除边
if (currentAppIsRunning) {
console.warn('在运行时不允许删除边');
return;
}
setEdges((eds: Edge[]) => eds.filter((e) => e.id !== edge.id));
// 删除边后记录历史
@ -1024,7 +1077,6 @@ export const useFlowCallbacks = (
const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key));
// 目前默认添加的都是系统组件/本地组件
if (!nodeMap.includes(nodeType)) {
// console.log('getRegisterNodes():', getRegisterNodes(nodeType));
registerNodeType(nodeType, getNodeComponent(nodeType), nodeDefinition.nodeName);
}
@ -1135,6 +1187,92 @@ export const useFlowCallbacks = (
}
else return {};
};
const handelEventNodeList = (newRevertedData) => {
const { appRuntimeData, currentAppData } = store.getState().ideContainer;
const current = appRuntimeData[currentAppData.id];
const deleteEventSendNodeList = [];
const deleteEventlisteneList = [];
// 处理流程接口后的数据
const currentEventSendNodeList = current.eventSendNodeList;
const currentEventlisteneList = current.eventlisteneList;
// 画布数据
const nodeEntries: [string, any][] = Object.entries(newRevertedData);
// 分类事件节点
const eventSendNodes = [];
const eventListenerNodes = [];
// 从nodeEntries中提取事件节点
nodeEntries.forEach(([nodeId, nodeConfig]) => {
if (nodeConfig.component?.type === 'EVENTSEND') {
eventSendNodes.push({ nodeId, ...nodeConfig });
}
else if (nodeConfig.component?.type === 'EVENTLISTENE') {
eventListenerNodes.push({ nodeId, ...nodeConfig });
}
});
// 处理EVENTSEND节点
// 如果currentEventSendNodeList中有而nodeEntries中没有则加入删除列表
currentEventSendNodeList.forEach((eventNode) => {
const nodeId = eventNode;
const nodeInCanvas = eventSendNodes.find(node => node.nodeId === nodeId);
if (!nodeInCanvas) {
// 画布数据中没有该节点,加入删除列表
deleteEventSendNodeList.push(eventNode);
}
else {
// 节点存在于画布中,比较时间戳
// nodeId格式为"节点类型-时间戳"
const canvasNode = nodeInCanvas.nodeId.split('-');
const canvasTimestamp = canvasNode[canvasNode.length - 1];
const interfaceNode = nodeId.split('-');
const interfaceTimestamp = interfaceNode[interfaceNode.length - 1];
// 如果画布数据的时间戳是最新的那就是新增的事件节点不需要添加到deleteList中
// 反之需要进一步判断
if (canvasTimestamp < interfaceTimestamp) {
// 画布节点时间戳较旧,需要进一步判断
// 这里留空,等待后续实现
}
}
});
// 处理EVENTLISTENE节点
// 如果currentEventlisteneList中有而nodeEntries中没有则加入删除列表
currentEventlisteneList.forEach((eventNode) => {
const nodeId = eventNode;
const nodeInCanvas = eventListenerNodes.find(node => node.nodeId === nodeId);
if (!nodeInCanvas) {
// 画布数据中没有该节点,加入删除列表
deleteEventlisteneList.push(eventNode);
}
else {
// 节点存在于画布中,比较时间戳
// nodeId格式为"节点类型-时间戳"
const canvasNode = nodeInCanvas.nodeId.split('-');
const canvasTimestamp = canvasNode[canvasNode.length - 1];
const interfaceNode = nodeId.split('-');
const interfaceTimestamp = interfaceNode[interfaceNode.length - 1];
// 如果画布数据的时间戳是最新的那就是新增的事件节点不需要添加到deleteList中
// 反之需要进一步判断
if (canvasTimestamp < interfaceTimestamp) {
// 画布节点时间戳较旧,需要进一步判断
// 这里留空,等待后续实现
}
}
});
return {
deleteEventSendNodeList,
deleteEventlisteneList
};
};
const saveFlowDataToServer = useCallback(async () => {
if (useDefault) {
try {
@ -1156,6 +1294,9 @@ export const useFlowCallbacks = (
const upDatePublishCB = await upDatePublish(revertedData.nodeConfigs);
const newRevertedData = reverseConvertFlowData(nodes, edges, upDatePublishCB);
const { flowData, currentAppData, info } = store.getState().ideContainer;
const { deleteEventSendNodeList, deleteEventlisteneList } = handelEventNodeList(newRevertedData);
// console.log(deleteEventSendNodeList, deleteEventlisteneList);
// return;
let params = {};
// 更新复合组件/子流程
if (currentAppData.key.includes('sub')) {
@ -1192,6 +1333,12 @@ export const useFlowCallbacks = (
// 更新事件枚举表
const res1: any = await queryEventItemBySceneIdOld(info.id);
if (res1.code === 200) dispatch(updateEventListOld(res1.data));
// 更新缓存数据
dispatch(updateCanvasDataMap({
...canvasDataMap,
[currentAppData.id]: { nodes, edges }
}));
}
else {
Message.error(res.message);
@ -1243,48 +1390,63 @@ export const useFlowCallbacks = (
...item,
eventId: Array.from(new Set(item.eventId))
}));
updateAppFlowData(appFlowParams);
if (appEventParams.length > 0) {
for (const item of appEventParams) {
await sleep(500);
updateAppEventChannel(item);
try {
updateAppFlowData(appFlowParams);
if (appEventParams.length > 0) {
for (const item of appEventParams) {
await sleep(500);
await updateAppEventChannel(item);
}
}
Message.success('保存成功');
} catch (error) {
console.error('保存失败:', error);
Message.error('保存失败: ' + (error.message));
}
}
}, [nodes, edges, initialData?.appId]);
// 运行处理函数
const handleRun = useCallback(async (running: boolean) => {
const { currentAppData, socketId } = store.getState().ideContainer;
const { currentAppData, socketId, appRuntimeData } = store.getState().ideContainer;
if (running) {
// 启动运行
const params = {
appId: currentAppData.id,
socketId
};
runMainFlow(params);
// 设置运行状态为true
dispatch(updateIsRunning(true));
// 重置节点状态
dispatch(resetNodeStatus());
// 开始运行时动画
setEdges((eds) => eds.map(edge => ({
...edge,
data: {
...edge.data,
isRunning: true,
animationProgress: 0
}
})));
const res: any = await runMainFlow(params);
if (res.code === 200) {
// 设置运行状态为true
dispatch(updateIsRunning(true));
// 重置节点状态
dispatch(resetNodeStatus());
// 更新运行ID
dispatch(updateRuntimeId(res.data));
// 开始运行时动画
setEdges((eds) => eds.map(edge => ({
...edge,
data: {
...edge.data,
isRunning: true,
animationProgress: 0
}
})));
}
else {
Message.error(res.message);
}
}
else {
// 设置运行状态为false
dispatch(updateIsRunning(false));
stopApp(currentAppData.id);
await stopApp(appRuntimeData[currentAppData.id].runId);
// 更新运行ID
dispatch(updateRuntimeId(''));
// 停止运行
setEdges((eds) => eds.map(edge => ({
@ -1295,6 +1457,9 @@ export const useFlowCallbacks = (
animationProgress: 0
}
})));
// 清空当前应用的运行日志
dispatch(clearRuntimeLogs({ appId: currentAppData.id }));
}
}, [initialData?.appId]);

@ -9,9 +9,14 @@ import { Dispatch } from 'redux';
export const useFlowEditorState = (initialData?: any) => {
const [nodes, setNodes] = useState<Node[]>([]);
const [edges, setEdges] = useState<Edge[]>([]);
const { canvasDataMap, nodeStatusMap, isRunning } = useSelector((state: any) => state.ideContainer);
const { canvasDataMap, nodeStatusMap, isRunning, appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer);
const dispatch = useDispatch();
// 获取当前应用的运行状态
const currentAppIsRunning = currentAppData?.id && appRuntimeData[currentAppData.id]
? appRuntimeData[currentAppData.id].isRunning
: false;
// 添加编辑弹窗相关状态
const [editingNode, setEditingNode] = useState<Node | null>(null);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
@ -33,11 +38,11 @@ export const useFlowEditorState = (initialData?: any) => {
data: {
...node.data,
status: nodeStatusMap[node.id] || 'waiting',
isStatusVisible: isRunning // 只有在运行时才显示状态指示器
isStatusVisible: currentAppIsRunning // 只有在运行时才显示状态指示器
}
}))
);
}, [nodeStatusMap, isRunning]);
}, [nodeStatusMap, currentAppIsRunning]);
const updateCanvasDataMapDebounced = useRef(
debounce((dispatch: Dispatch<any>, canvasDataMap: any, id: string, nodes: Node[], edges: Edge[]) => {
@ -65,7 +70,7 @@ export const useFlowEditorState = (initialData?: any) => {
setEdgeForNodeAdd,
positionForNodeAdd,
setPositionForNodeAdd,
isRunning,
isRunning: currentAppIsRunning, // 使用当前应用的运行状态
historyInitialized,
setHistoryInitialized,
historyTimeoutRef,

@ -24,6 +24,7 @@ import ActionBar from './components/actionBar';
import { useAlignmentGuidelines } from '@/hooks/useAlignmentGuidelines';
import { useHistory } from './components/historyContext';
import { NodeTypes } from '@xyflow/react';
import { useSelector } from 'react-redux';
const edgeTypes = {
custom: CustomEdge
@ -138,6 +139,15 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
const { undo, redo, canUndo, canRedo } = useHistory();
const reactFlowId = useMemo(() => new Date().getTime().toString(), []);
// 从Redux store中获取当前应用的运行状态
const { appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer);
const currentAppIsRunning = currentAppData?.id && appRuntimeData[currentAppData.id]
? appRuntimeData[currentAppData.id].isRunning
: false;
// 在应用编排模式下useDefault为false禁用删除功能
const isDeleteDisabled = currentAppIsRunning;
// 监听键盘事件实现快捷键
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
@ -178,18 +188,24 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
onContextMenu={(e) => e.preventDefault()}>
<ReactFlow
id={reactFlowId}
nodes={nodes.map(node => ({ ...node, draggable: !isRunning }))} // 运行时禁用节点拖拽
nodes={nodes.map(node => ({ ...node, draggable: !currentAppIsRunning }))} // 运行时禁用节点拖拽
edges={edges}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
snapToGrid={true}
snapGrid={[2, 2]}
nodesConnectable={!isRunning} // 运行时禁用节点连接
nodesDraggable={!isRunning} // 运行时禁用节点拖拽
elementsSelectable={!isRunning} // 运行时禁用元素选择
connectOnClick={!isRunning} // 运行时禁用点击连接
disableKeyboardA11y={isRunning} // 运行时禁用键盘交互
onBeforeDelete={async ({ nodes }) => {
nodesConnectable={!currentAppIsRunning} // 运行时禁用节点连接
nodesDraggable={!currentAppIsRunning} // 运行时禁用节点拖拽
elementsSelectable={!currentAppIsRunning} // 运行时禁用元素选择
connectOnClick={!currentAppIsRunning} // 运行时禁用点击连接
disableKeyboardA11y={currentAppIsRunning} // 运行时禁用键盘交互
onBeforeDelete={async ({ nodes, edges }) => {
// 在应用编排模式下useDefault为false只允许删除边不允许删除节点
if (!useDefault && nodes.length > 0) {
console.warn('在应用编排模式下不允许删除节点');
return false; // 阻止删除节点操作
}
// 检查是否有开始或结束节点
const hasStartOrEndNode = nodes.some(node => node.type === 'start' || node.type === 'end');
if (hasStartOrEndNode) {
@ -203,9 +219,20 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
);
// 允许删除操作继续进行
return !isRunning; // 运行时禁止删除节点
return !currentAppIsRunning; // 在运行时禁止删除任何元素
}}
onNodesDelete={(deleted) => {
// 在应用编排模式下useDefault为false不允许删除节点
if (!useDefault) {
console.warn('在应用编排模式下不允许删除节点');
return;
}
// 如果在运行时,禁止删除
if (currentAppIsRunning) {
return;
}
// 检查是否有循环节点
const loopNodes = deleted.filter(node =>
node.data?.type === 'LOOP_START' || node.data?.type === 'LOOP_END'
@ -262,21 +289,21 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
setIsEditModalOpen(false);
}}
onNodesChange={!isRunning ? onNodesChange : undefined} // 运行时禁用节点变更
onEdgesChange={!isRunning ? onEdgesChange : undefined} // 运行时禁用边变更
onConnect={!isRunning ? onConnect : undefined} // 运行时禁用连接
onReconnect={!isRunning ? onReconnect : undefined} // 运行时禁用重新连接
onDragOver={!isRunning ? onDragOver : undefined} // 运行时禁用拖拽
onDrop={!isRunning ? onDrop : undefined} // 运行时禁用放置
onNodeDrag={!isRunning ? onNodeDrag : undefined} // 运行时禁用节点拖拽
onNodesChange={!currentAppIsRunning ? onNodesChange : undefined} // 运行时禁用节点变更
onEdgesChange={!currentAppIsRunning ? onEdgesChange : undefined} // 运行时禁用边变更
onConnect={!currentAppIsRunning ? onConnect : undefined} // 运行时禁用连接
onReconnect={!currentAppIsRunning ? onReconnect : undefined} // 运行时禁用重新连接
onDragOver={!currentAppIsRunning ? onDragOver : undefined} // 运行时禁用拖拽
onDrop={!currentAppIsRunning ? onDrop : undefined} // 运行时禁用放置
onNodeDrag={!currentAppIsRunning ? onNodeDrag : undefined} // 运行时禁用节点拖拽
connectionLineType={ConnectionLineType.SmoothStep}
connectionLineComponent={CustomConnectionLine}
onNodeDragStop={!isRunning ? onNodeDragStop : undefined} // 运行时禁用节点拖拽停止
onNodeContextMenu={!isRunning ? onNodeContextMenu : undefined} // 运行时禁用节点上下文菜单
onNodeDoubleClick={!isRunning ? onNodeDoubleClick : undefined} // 运行时禁用节点双击
onEdgeContextMenu={!isRunning ? onEdgeContextMenu : undefined} // 运行时禁用边上下文菜单
onPaneClick={!isRunning ? onPaneClick : undefined} // 运行时禁用面板点击
onPaneContextMenu={!isRunning ? onPaneContextMenu : undefined} // 运行时禁用面板上下文菜单
onNodeDragStop={!currentAppIsRunning ? onNodeDragStop : undefined} // 运行时禁用节点拖拽停止
onNodeContextMenu={(!currentAppIsRunning && useDefault) ? onNodeContextMenu : undefined} // 运行时或应用编排模式下禁用节点上下文菜单
onNodeDoubleClick={!currentAppIsRunning ? onNodeDoubleClick : undefined} // 运行时禁用节点双击
onEdgeContextMenu={(!currentAppIsRunning && useDefault) ? onEdgeContextMenu : undefined} // 运行时或应用编排模式下禁用边上下文菜单
onPaneClick={!currentAppIsRunning ? onPaneClick : undefined} // 运行时禁用面板点击
onPaneContextMenu={(!currentAppIsRunning && useDefault) ? onPaneContextMenu : undefined} // 运行时或应用编排模式下禁用面板上下文菜单
onEdgeMouseEnter={(_event, edge) => {
setEdges((eds) => eds.map(e => {
if (e.id === edge.id) {
@ -294,8 +321,8 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
}));
}}
fitView
selectionOnDrag={!isRunning} // 运行时禁用拖拽选择
selectionMode={!isRunning ? SelectionMode.Partial : undefined} // 运行时禁用选择模式
selectionOnDrag={!currentAppIsRunning} // 运行时禁用拖拽选择
selectionMode={!currentAppIsRunning ? SelectionMode.Partial : undefined} // 运行时禁用选择模式
>
<Background />
<Panel position="top-left">
@ -307,14 +334,14 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
canUndo={canUndo}
canRedo={canRedo}
onRun={handleRun}
isRunning={isRunning}
isRunning={currentAppIsRunning}
></ActionBar>
</Panel>
<AlignmentGuides />
</ReactFlow>
{/*节点右键上下文*/}
{!isRunning && menu && menu.type === 'node' && (
{/*节点右键上下文 - 仅在默认模式且非运行时显示*/}
{!currentAppIsRunning && useDefault && menu && menu.type === 'node' && (
<div
style={{
position: 'absolute',
@ -334,8 +361,8 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
</div>
)}
{/*边右键上下文*/}
{!isRunning && menu && menu.type === 'edge' && (
{/*边右键上下文 - 在非运行时显示,应用编排模式下也允许*/}
{!currentAppIsRunning && menu && menu.type === 'edge' && (
<div
style={{
position: 'absolute',
@ -357,8 +384,8 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
</div>
)}
{/*画布右键上下文*/}
{!isRunning && menu && menu.type === 'pane' && (
{/*画布右键上下文 - 仅在默认模式且非运行时显示*/}
{!currentAppIsRunning && useDefault && menu && menu.type === 'pane' && (
<div
style={{
position: 'absolute',
@ -382,14 +409,14 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
<NodeEditModal
popupContainer={reactFlowWrapper}
node={editingNode}
isOpen={isEditModalOpen && !isRunning}
isOpen={isEditModalOpen && !currentAppIsRunning}
isDelete={isDelete}
onSave={saveNodeEdit}
onClose={closeEditModal}
/>
{/*统一的添加节点菜单*/}
{!isRunning && (edgeForNodeAdd || positionForNodeAdd) && (
{/*统一的添加节点菜单 - 仅在默认模式且非运行时显示*/}
{!currentAppIsRunning && useDefault && (edgeForNodeAdd || positionForNodeAdd) && (
<div
style={{
position: 'absolute',

@ -27,15 +27,20 @@ const ActionBar: React.FC<ActionBarProps> = ({
onRun,
isRunning = false
}) => {
const { logBarStatus } = useSelector((state) => state.ideContainer);
const { logBarStatus, appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer);
const dispatch = useDispatch();
// 获取当前应用的运行状态
const currentAppIsRunning = currentAppData?.id && appRuntimeData[currentAppData.id]
? appRuntimeData[currentAppData.id].isRunning
: false;
const changeLogBarStatus = () => {
dispatch(updateLogBarStatus(!logBarStatus));
};
const handleRun = () => {
onRun?.(!isRunning);
onRun?.(!currentAppIsRunning);
};
return (
@ -50,9 +55,9 @@ const ActionBar: React.FC<ActionBarProps> = ({
icon={<IconPlayArrow />}
onClick={() => handleRun()}
style={{ padding: '0 8px', backgroundColor: '#fff' }}
status={isRunning ? 'danger' : undefined}
status={currentAppIsRunning ? 'danger' : undefined}
>
{isRunning ? '停止' : '运行'}
{currentAppIsRunning ? '停止' : '运行'}
</Button>
<Button
type="outline"

@ -1,19 +1,29 @@
import React from 'react';
import { getBezierPath } from '@xyflow/react';
import { getHandleType } from '@/utils/flowCommon';
const CustomConnectionLine = ({ fromNode, fromX, fromY, toX, toY }) => {
const CustomConnectionLine = ({ fromNode, fromX, fromY, toX, toY, fromHandle }) => {
// 查找源节点的事件信息
let eventData = null;
let isApiHandle = false;
if (fromNode && fromNode.data) {
const params = fromNode.data.parameters || {};
const apiOuts = params.apiOuts || [];
// 查找匹配的输出API
for (const api of apiOuts) {
// 这里假设fromNode存在selectedHandle属性表示当前拖拽的句柄
if (api.name || api.id) {
eventData = api;
break;
// 判断是否为API类型的句柄
isApiHandle = getHandleType(fromHandle.id, params) === 'api';
// 只有在API句柄上才查找事件信息
if (isApiHandle) {
const apiOuts = params.apiOuts || [];
// 查找匹配的输出API
for (const api of apiOuts) {
// 检查api的name或id是否与fromHandle匹配
if (api.name || api.id) {
eventData = api;
break;
}
}
}
}
@ -35,10 +45,10 @@ const CustomConnectionLine = ({ fromNode, fromX, fromY, toX, toY }) => {
className="animated-stroke"
fill="none"
/>
{/* 当存在事件信息的时候展示连接提示 */}
{eventData && eventData.topic && !eventData.topic.includes('**empty**') && (
{/* 只有在API类型的句柄上拖拽时才显示事件提示 */}
{isApiHandle && eventData && eventData.topic && !eventData.topic.includes('**empty**') && (
<text x={(fromX + toX) / 2} y={(fromY + toY) / 2 - 10} fill="#000" fontSize={12}>
{`连接事件: ${eventData.name}`}
{`连接事件: ${eventData.eventName}`}
</text>
)}
</g>

@ -11,6 +11,7 @@ type DataDisplayEdgeData = {
name?: string;
topic?: string;
eventId?: string;
lineType?: string; // 添加lineType属性
};
const DataDisplayEdge: React.FC<EdgeProps> = ({
@ -46,11 +47,21 @@ const DataDisplayEdge: React.FC<EdgeProps> = ({
// 从数据中获取要显示的信息
const displayData: DataDisplayEdgeData = data?.displayData || {};
// 获取lineType
const lineType = data?.lineType || 'data'; // 默认为data类型
// 使用useReactFlow钩子获取setEdges方法
const { setEdges } = useReactFlow();
// 边点击处理函数
const handleEdgeAddNode = (e) => {
// 如果是数据类型边线,不允许添加节点
if (lineType === 'data') {
console.warn('数据类型边线上不允许添加节点');
Message.warning('数据类型连接线上不允许添加节点');
return;
}
// 更新边的数据,触发边上添加节点的流程
setEdges(eds => eds.map(edge => {
if (edge.id === id) {
@ -190,7 +201,7 @@ const DataDisplayEdge: React.FC<EdgeProps> = ({
</div>
)}
{hovered && Object.keys(displayData).length === 0 && Object.keys(displayData).length === 0 && (
{hovered && Object.keys(displayData).length === 0 && lineType !== 'data' && (
<EdgeAddNodeButton
onClick={(e) => handleEdgeAddNode(e)}
/>

@ -286,7 +286,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
color: group ? group.color : '#000'
}}
>
{isValidData(input) ? (input.name || input.id || `输入${index + 1}`) : ''}
{isValidData(input) ? `${input.name || input.id} ${input?.dataType}` : ''}
</div>
);
})}
@ -307,7 +307,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
color: group ? group.color : '#000'
}}
>
{isValidData(output) ? (output.name || output.id || `输出${index + 1}`) : ''}
{isValidData(output) ? `${output.dataType} ${output.name || output.id}` : ''}
</div>
);
})}

@ -155,7 +155,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-inputs']}>
{dataIns.map((input, index) => (
<div key={input.id || `input-${index}`} className={styles['node-input-label']}>
{input.id || `输入${index + 1}`}
{input.id || `输入${index + 1}`} {input.dataType}
</div>
))}
</div>
@ -165,7 +165,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-outputs']}>
{dataOuts.map((output, index) => (
<div key={output.id || `output-${index}`} className={styles['node-input-label']}>
{`${output.id} ${output.dataType}` || `输出${index + 1}`}
{output.dataType} {`${output.id}` || `输出${index + 1}`}
</div>
))}
</div>

@ -161,7 +161,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-outputs']}>
{dataOuts.map((output, index) => (
<div key={output.id || `output-${index}`} className={styles['node-input-label']}>
{output.id || `输出${index + 1}`}
{output.dataType} {output.id || `输出${index + 1}`}
</div>
))}
</div>

@ -150,7 +150,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-inputs']}>
{dataIns.map((input, index) => (
<div key={input.id || `input-${index}`} className={styles['node-input-label']}>
{input.id || `输入${index + 1}`}
{input.id || `输入${index + 1}`} {input.dataType}
</div>
))}
</div>
@ -160,7 +160,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-outputs']}>
{dataOuts.map((output, index) => (
<div key={output.id || `output-${index}`} className={styles['node-input-label']}>
{output.id || `输出${index + 1}`}
{output.dataType} {output.id || `输出${index + 1}`}
</div>
))}
</div>

@ -193,10 +193,18 @@ const formatFooter = (data: any, eventListOld = []) => {
return cronstrue.toString(intervalSeconds, { locale: 'zh_CN' });
case 'EVENTSEND':
case 'EVENTLISTENE':
const { eventId, topic, name } = isJSON(data.customDef) ? JSON.parse(data.customDef) : data.customDef;
if (topic.includes('**empty**')) return '';
const currentEvent = eventListOld.length > 0 ? eventListOld.find(item => item.eventId === eventId) : { name: '无' };
return `事件: ${name || currentEvent.name}`;
const parsedData = isJSON(data.customDef) ? JSON.parse(data.customDef) : null;
// 数据是JSON字符串标识是接口回来的数据是普通对象标识是当前操作
if (parsedData) {
const { eventId, topic, name } = parsedData;
if (topic.includes('**empty**')) return '';
const currentEvent = eventListOld.length > 0 ? eventListOld.find(item => item.eventId === eventId) : { name: '无' };
return `事件: ${currentEvent.name}`;
}
else {
const { name } = data.customDef;
return `事件: ${name}`;
}
case 'BASIC':
return data.compIdentifier ? `当前实例:${data.compIdentifier}` : '';
default:
@ -215,7 +223,6 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
const dataOuts = data.parameters?.dataOuts || [];
const showFooter = formatFooter(data.component) || false;
const footerData = (showFooter && data.component) || {};
// console.log(apiIns, apiOuts, dataIns, dataOuts);
// 判断节点类型
const isStartNode = data.type === 'start';
@ -241,7 +248,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-outputs-api']}>
{apiOuts.map((output, index) => (
<div key={output.id || `output-${index}`} className={styles['node-input-label']}>
{output.desc}
{output.dataType} {output.desc}
</div>
))}
</div>
@ -264,7 +271,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-inputs']}>
{dataIns.map((input, index) => (
<div key={input.id || `input-${index}`} className={styles['node-input-label']}>
{input.id || `输入${index + 1}`}
{input.id || `输入${index + 1}`} {input.dataType}
</div>
))}
</div>
@ -274,7 +281,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-outputs']}>
{dataOuts.map((output, index) => (
<div key={output.id || `output-${index}`} className={styles['node-input-label']}>
{output.id || `输出${index + 1}`}
{output.dataType} {output.id || `输出${index + 1}`}
</div>
))}
</div>

@ -152,7 +152,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-inputs']}>
{dataIns.map((input, index) => (
<div key={input.id || `input-${index}`} className={styles['node-input-label']}>
{input.id || `输入${index + 1}`}
{input.id || `输入${index + 1}`} {input.dataType}
</div>
))}
</div>
@ -162,7 +162,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-outputs']}>
{dataOuts.map((output, index) => (
<div key={output.id || `output-${index}`} className={styles['node-input-label']}>
{`${output.id} ${output.dataType}` || `输出${index + 1}`}
{output.dataType} {`${output.id}` || `输出${index + 1}`}
</div>
))}
</div>

@ -148,7 +148,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-inputs']}>
{dataIns.map((input, index) => (
<div key={input.id || `input-${index}`} className={styles['node-input-label']}>
{input.id || `输入${index + 1}`}
{input.id || `输入${index + 1}`} {input.dataType}
</div>
))}
</div>
@ -158,7 +158,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<div className={styles['node-outputs']}>
{dataOuts.map((output, index) => (
<div key={output.id || `output-${index}`} className={styles['node-input-label']}>
{output.id || `输出${index + 1}`}
{output.dataType} {output.id || `输出${index + 1}`}
</div>
))}
</div>

@ -219,7 +219,7 @@ function IDEContainer() {
};
const getEvent = async () => {
const res: any = await queryEventItemBySceneId(urlParams.id);
const res: any = await queryEventItemBySceneId({ sceneId: urlParams.id });
const res1: any = await queryEventItemBySceneIdOld(urlParams.id);
if (res.code === 200) dispatch(updateEventList(res.data));
if (res1.code === 200) dispatch(updateEventListOld(res1.data));

@ -44,11 +44,12 @@ const LogBar: React.FC<LogBarProps> = () => {
const [tabs] = useState(data);
const [activeTab, setActiveTab] = useState('1');
const resizeBoxRef = useRef<HTMLDivElement>(null); // 引用 ResizeBox 容器
const { logBarStatus } = useSelector((state) => state.ideContainer);
const { logBarStatus, appRuntimeData } = useSelector((state: any) => state.ideContainer);
const dispatch = useDispatch();
const [validationLogs, setValidationLogs] = useState<LogMessage[]>([]);
const [runtimeLogs, setRuntimeLogs] = useState<LogMessage[]>([]); // 添加运行时日志状态
const [logContainerHeight, setLogContainerHeight] = useState('250px'); // 添加日志容器高度状态
const { currentAppData } = useSelector((state: any) => state.ideContainer);
// 处理 Tab 点击事件
const handleTabClick = (key: string) => {
@ -120,6 +121,18 @@ const LogBar: React.FC<LogBarProps> = () => {
// 自动切换到运行日志tab并展开logBar
setActiveTab('1');
dispatch(updateLogBarStatus(true));
// 同时将日志添加到当前应用的运行日志中
const appId = currentAppData?.id;
if (appId) {
dispatch({
type: 'ideContainer/addRuntimeLog',
payload: {
log: newLog,
appId: appId
}
});
}
}
};
@ -130,7 +143,7 @@ const LogBar: React.FC<LogBarProps> = () => {
return () => {
document.removeEventListener('logMessage', handleLogMessage as EventListener);
};
}, [dispatch]);
}, [dispatch, currentAppData?.id]);
// 渲染校验日志内容
const renderValidationLogs = () => {
@ -156,12 +169,17 @@ const LogBar: React.FC<LogBarProps> = () => {
// 渲染运行时日志内容
const renderRuntimeLogs = () => {
// 获取当前应用的运行日志
const currentAppLogs = currentAppData?.id && appRuntimeData[currentAppData.id]
? appRuntimeData[currentAppData.id].logs || []
: [];
return (
<div style={{ padding: '10px', height: 'calc(100% - 40px)', overflowY: 'auto' }}>
{runtimeLogs.length === 0 ? (
{currentAppLogs.length === 0 ? (
<p></p>
) : (
runtimeLogs.map(log => (
currentAppLogs.map((log: LogMessage) => (
<div key={log.id} style={{ marginBottom: '8px', padding: '4px', borderBottom: '1px solid #eee' }}>
<div style={{ fontSize: '12px', color: '#999' }}>
{new Date(log.timestamp).toLocaleString()}

@ -16,7 +16,7 @@ const ApplicationContainer = () => {
};
const getEventList = async () => {
const res: any = await queryEventItemBySceneId(info.id);
const res: any = await queryEventItemBySceneId({ sceneId: info.id });
if (res.code === 200) {
dispatch(updateEventTopicList(res.data

@ -235,7 +235,7 @@ const EventContainer = () => {
];
const fetchEventData = async () => {
const res: any = await queryEventItemBySceneId(info.id);
const res: any = await queryEventItemBySceneId({sceneId:info.id});
if (res && res.code === 200) setEventData(res.data.filter(item => !item.topic.includes('**empty**')));
};

@ -15,6 +15,15 @@ interface IDEContainerState {
socketId: string;
nodeStatusMap: Record<string, string>; // 节点状态映射
isRunning: boolean; // 是否正在运行
// 应用运行状态和日志数据按应用ID隔离存储
appRuntimeData: Record<string, {
nodeStatusMap: Record<string, string>;
isRunning: boolean;
logs: any[];
runId: string;
eventSendNodeList: string[],
eventlisteneList: string[]
}>;
}
// 初始状态
@ -31,7 +40,8 @@ const initialState: IDEContainerState = {
logBarStatus: false,
socketId: '', // 工程的socketId
nodeStatusMap: {}, // 初始化节点状态映射
isRunning: false // 默认未运行
isRunning: false, // 默认未运行
appRuntimeData: {} // 按应用ID隔离的应用运行状态和日志数据
};
// 创建切片
@ -78,18 +88,109 @@ const ideContainerSlice = createSlice({
// 如果是运行时更新,不记录到历史记录中
if (actionType !== 'RUNTIME_UPDATE') {
state.nodeStatusMap[nodeId] = status;
} else {
}
else {
// 对于运行时更新,我们仍然更新状态但标记为运行时状态
state.nodeStatusMap[nodeId] = status;
}
// 同时更新当前应用的节点状态
const appId = state.currentAppData?.id;
if (appId) {
if (!state.appRuntimeData[appId]) {
state.appRuntimeData[appId] = {
nodeStatusMap: {},
isRunning: false,
logs: [],
runId: '',
eventSendNodeList: [],
eventlisteneList: []
};
}
state.appRuntimeData[appId].nodeStatusMap[nodeId] = status;
}
},
// 重置节点状态
resetNodeStatus: (state) => {
state.nodeStatusMap = {};
// 同时重置当前应用的节点状态
const appId = state.currentAppData?.id;
if (appId && state.appRuntimeData[appId]) {
state.appRuntimeData[appId].nodeStatusMap = {};
}
},
// 更新运行状态
updateIsRunning: (state, { payload }) => {
state.isRunning = payload;
// 同时更新当前应用的运行状态
const appId = state.currentAppData?.id;
if (appId) {
if (!state.appRuntimeData[appId]) {
state.appRuntimeData[appId] = {
nodeStatusMap: {},
isRunning: false,
logs: [],
runId: '',
eventSendNodeList: [],
eventlisteneList: []
};
}
state.appRuntimeData[appId].isRunning = payload;
}
},
// 添加运行id
updateRuntimeId: (state, { payload }) => {
const appId = state.currentAppData?.id;
if (!state.appRuntimeData[appId]) {
state.appRuntimeData[appId] = {
nodeStatusMap: {},
isRunning: false,
logs: [],
runId: '',
eventSendNodeList: [],
eventlisteneList: []
};
}
state.appRuntimeData[appId].runId = payload;
},
// 更新事件节点列表
updateEventNodeList: (state, { payload }) => {
const appId = state.currentAppData?.id;
if (!state.appRuntimeData[appId]) {
state.appRuntimeData[appId] = {
nodeStatusMap: {},
isRunning: false,
logs: [],
runId: '',
eventSendNodeList: [],
eventlisteneList: []
};
}
state.appRuntimeData[appId] = { ...state.appRuntimeData[appId], ...payload };
},
// 添加运行日志
addRuntimeLog: (state, { payload }) => {
const { log, appId } = payload;
if (!state.appRuntimeData[appId]) {
state.appRuntimeData[appId] = {
nodeStatusMap: {},
isRunning: false,
logs: [],
runId: '',
eventSendNodeList: [],
eventlisteneList: []
};
}
state.appRuntimeData[appId].logs.push(log);
},
// 清空指定应用的运行日志
clearRuntimeLogs: (state, { payload }) => {
const { appId } = payload;
if (state.appRuntimeData[appId]) {
state.appRuntimeData[appId].logs = [];
}
}
}
});
@ -109,7 +210,11 @@ export const {
updateSocketId,
updateNodeStatus,
resetNodeStatus,
updateIsRunning
updateIsRunning,
updateRuntimeId,
updateEventNodeList,
addRuntimeLog,
clearRuntimeLogs
} = ideContainerSlice.actions;
// 默认导出 reducer

@ -33,21 +33,23 @@ export const convertAppFlowData = (appFlowData: any[]) => {
parameters: {
// eventListenes 作为 apiIns输入
apiIns: app.eventListenes ? app.eventListenes.map((event: any) => ({
name: event.eventName,
name: event.nodeName,
desc: event.description || '',
dataType: '',
defaultValue: '',
topic: event.topic,
eventId: event.eventId
eventId: event.eventId,
eventName: event.eventName
})) : [],
// eventSends 作为 apiOuts输出
apiOuts: app.eventSends ? app.eventSends.map((event: any) => ({
name: event.eventName,
name: event.nodeName,
desc: event.description || '',
dataType: '',
defaultValue: '',
topic: event.topic,
eventId: event.eventId
eventId: event.eventId,
eventName: event.eventName
})) : [],
// 提取 dataIns 和 dataOuts 属性
dataIns: [],
@ -118,6 +120,7 @@ export const convertAppFlowData = (appFlowData: any[]) => {
targetHandle: targetEvent.eventId,
type: 'custom',
data: {
lineType: 'api', // 应用间连接始终是API类型
displayData: {
name: sourceEvent.eventName,
eventId: sourceEvent.eventId,

@ -7,6 +7,8 @@ import BasicNode from '@/components/FlowEditor/node/basicNode/BasicNode';
import ImageNode from '@/components/FlowEditor/node/imageNode/ImageNode';
import CodeNode from '@/components/FlowEditor/node/codeNode/CodeNode';
import RestNode from '@/components/FlowEditor/node/restNode/RestNode';
import { updateEventNodeList } from '@/store/ideContainer';
/**
* flow editor nodes edges
@ -17,6 +19,8 @@ import RestNode from '@/components/FlowEditor/node/restNode/RestNode';
export const convertFlowData = (flowData: any, useDefault = true) => {
const nodes: any[] = [];
const edges: any[] = [];
const eventSendNodeList = [];
const eventlisteneList = [];
const currentProjectCompData = getCurrentProjectStoreData();
if (!flowData || Object.keys(flowData).length === 0) {
@ -70,6 +74,15 @@ export const convertFlowData = (flowData: any, useDefault = true) => {
const nodeId: string = entry[0];
const nodeConfig: any = entry[1];
// 更新应用中的事件节点列表
if (nodeId.includes('EVENTLISTENE')) eventlisteneList.push(nodeId);
else if (nodeId.includes('EVENTSEND')) eventSendNodeList.push(nodeId);
if (eventlisteneList.length > 0 || eventSendNodeList.length > 0) store.dispatch(updateEventNodeList({
eventSendNodeList,
eventlisteneList
}));
// 确定节点类型
let nodeType = 'BASIC';
if (nodeId === 'start') {
@ -492,7 +505,6 @@ export const reverseConvertFlowData = (nodes: any[], edges: any[], complexKV: an
position: node.position || { x: 0, y: 0 }
};
console.log('node:', node);
// 处理 component 信息
if (node.type === 'SUB' && !node.customDef) {
nodeConfig.component = {

@ -12,8 +12,8 @@ const getHandleType = (handleId: string, nodeParams: any) => {
const apiOuts = nodeParams.apiOuts || [];
const apiIns = nodeParams.apiIns || [];
if (apiOuts.some((api: any) => (api.name || api.id) === handleId) ||
apiIns.some((api: any) => (api.name || api.id) === handleId) || (handleId.includes('loop'))) {
if (apiOuts.some((api: any) => (api?.eventId || api.name || api.id) === handleId) ||
apiIns.some((api: any) => (api?.eventId || api.name || api.id) === handleId) || (handleId.includes('loop'))) {
return 'api';
}

Loading…
Cancel
Save