From 85d4c581788edefb10a1b3ec5e45451eaaaae6aa Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 09:21:10 +0800 Subject: [PATCH 01/16] =?UTF-8?q?feat(flow):=20=E6=B7=BB=E5=8A=A0=E8=BE=B9?= =?UTF-8?q?=E7=BA=BF=E7=B1=BB=E5=9E=8B=E5=92=8C=E6=95=B0=E6=8D=AE=E5=B1=9E?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -为 API 类型边线添加 lineType 和 data 属性- 为数据类型边线添加 lineType 和 data 属性 --- src/utils/convertFlowData.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/utils/convertFlowData.ts b/src/utils/convertFlowData.ts index 25deb98..ad70d94 100644 --- a/src/utils/convertFlowData.ts +++ b/src/utils/convertFlowData.ts @@ -304,7 +304,11 @@ export const convertFlowData = (flowData: any, useDefault = true) => { target: target, sourceHandle: finalSourceHandle, targetHandle: finalTargetHandle, - type: 'custom' + type: 'custom', + lineType: 'api', + data: { + lineType: 'api' + } }); } } @@ -340,7 +344,11 @@ export const convertFlowData = (flowData: any, useDefault = true) => { target: targetNodeId, sourceHandle: sourceHandle, targetHandle: targetHandle, - type: 'custom' + type: 'custom', + lineType: 'data', + data: { + lineType: 'data' + } }); } } From a4184ea1a8b27cb0e7d93739f85be4c4e8a08dcb Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 09:23:42 +0800 Subject: [PATCH 02/16] =?UTF-8?q?fix(flowEditor):=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E5=86=85=E5=AE=B9=E6=98=BE=E7=A4=BA=E9=80=BB?= =?UTF-8?q?=E8=BE=91=20-=20=E4=BB=8Edata.customDef=E4=B8=AD=E8=A7=A3?= =?UTF-8?q?=E6=9E=84=E5=87=BAtopic=E5=AD=97=E6=AE=B5=20-=20=E5=BD=93topic?= =?UTF-8?q?=E5=8C=85=E5=90=AB'**empty**'=E6=97=B6=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E7=A9=BA=E5=AD=97=E7=AC=A6=E4=B8=B2=20-=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E5=90=8D=E7=A7=B0=E7=9A=84=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/flowEditor/components/nodeContentOther.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/flowEditor/components/nodeContentOther.tsx b/src/pages/flowEditor/components/nodeContentOther.tsx index fd66f09..ce1ba86 100644 --- a/src/pages/flowEditor/components/nodeContentOther.tsx +++ b/src/pages/flowEditor/components/nodeContentOther.tsx @@ -202,7 +202,8 @@ const formatFooter = (data: any, eventListOld = []) => { return `事件: ${currentEvent.name}`; } else { - const { name } = data.customDef; + const { name, topic } = data.customDef; + if (topic.includes('**empty**')) return ''; return `事件: ${name}`; } case 'BASIC': From 8b9bf0095264971b8b7acc7c324cdf4571b25450 Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 10:39:53 +0800 Subject: [PATCH 03/16] =?UTF-8?q?feat(flow):=20=E4=BC=98=E5=8C=96=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E8=8A=82=E7=82=B9=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构事件节点列表处理函数,提升事件节点识别准确性 - 更新事件发送和接收节点的数据结构,支持节点ID与主题映射 - 完善事件节点变动检测机制,精确识别新增、删除和修改的事件节点- 增加事件节点主题变更时的自动删除逻辑 - 优化事件节点数据解析过程,增强对自定义定义的处理能力 - 调整API接口参数结构,统一使用topics字段替代eventIds - 改进流程数据保存逻辑,确保事件节点变动及时同步到服务端 - 修复事件节点时间戳比较逻辑,避免误判节点版本 - 增强错误处理机制,提升事件节点解析的稳定性 - 更新Redux存储结构,支持更丰富的事件节点信息存储 --- src/api/interface/index.ts | 2 +- src/hooks/useFlowCallbacks.ts | 204 ++++----------------- src/pages/flowEditor/utils/common.ts | 255 +++++++++++++++++++++++++++ src/store/ideContainer.ts | 4 +- src/utils/convertFlowData.ts | 24 ++- 5 files changed, 314 insertions(+), 175 deletions(-) create mode 100644 src/pages/flowEditor/utils/common.ts diff --git a/src/api/interface/index.ts b/src/api/interface/index.ts index e3455d1..5c5ba29 100644 --- a/src/api/interface/index.ts +++ b/src/api/interface/index.ts @@ -205,7 +205,7 @@ export interface DeleteEventParams { export interface DeleteEventForAppParams { appId: string, - eventIds: string[] + topics: string[] } // runtime diff --git a/src/hooks/useFlowCallbacks.ts b/src/hooks/useFlowCallbacks.ts index 90b0121..b856faf 100644 --- a/src/hooks/useFlowCallbacks.ts +++ b/src/hooks/useFlowCallbacks.ts @@ -36,13 +36,14 @@ import { } from '@/utils/flowCommon'; import { projectFlowHandle } from '@/pages/flowEditor/utils/projectFlowHandle'; import { appFLowHandle } from '@/pages/flowEditor/utils/appFlowhandle'; +import { handelEventNodeList, updateEvent, upDatePublish } from '@/pages/flowEditor/utils/common'; import { Dispatch } from 'redux'; import { runMainFlow, stopApp } from '@/api/apps'; import store from '@/store'; import { updateAppEvent, updateAppEventChannel, updateAppFlowData } from '@/api/appEvent'; import { sleep } from '@/utils/common'; -import { queryEventItemBySceneIdOld } from '@/api/event'; +import { queryEventItemBySceneIdOld, deleteEventSub, deleteEventPub } from '@/api/event'; export const useFlowCallbacks = ( nodes: Node[], @@ -810,7 +811,11 @@ export const useFlowCallbacks = ( target: loopStartNode.id, sourceHandle: edgeForNodeAdd.sourceHandle, targetHandle: 'start', // 循环开始节点的输入句柄 - type: 'custom' + type: 'custom', + lineType: 'api', + data: { + lineType: 'api' + } }, { id: `e${loopEndNode.id}-${edgeForNodeAdd.target}`, @@ -818,7 +823,11 @@ export const useFlowCallbacks = ( target: edgeForNodeAdd.target, sourceHandle: 'break', // 循环结束节点的输出句柄 targetHandle: edgeForNodeAdd.targetHandle, - type: 'custom' + type: 'custom', + lineType: 'api', + data: { + lineType: 'api' + } } ]; @@ -977,7 +986,11 @@ export const useFlowCallbacks = ( target: newNode.id, sourceHandle: edgeForNodeAdd.sourceHandle, targetHandle: newNodeTargetHandle, - type: 'custom' + type: 'custom', + lineType: 'api', + data: { + lineType: 'api' + } }, { id: `e${newNode.id}-${edgeForNodeAdd.target}`, @@ -985,7 +998,11 @@ export const useFlowCallbacks = ( target: edgeForNodeAdd.target, sourceHandle: newNodeSourceHandle, targetHandle: edgeForNodeAdd.targetHandle, - type: 'custom' + type: 'custom', + lineType: 'api', + data: { + lineType: 'api' + } } ]; @@ -1111,168 +1128,7 @@ export const useFlowCallbacks = ( }, [edgeForNodeAdd, positionForNodeAdd, addNodeOnEdge, addNodeOnPane]); // endregion - // 保存所有节点和边数据到服务器,更新事件相关数据 - const updateEvent = (revertedData, appid) => { - // 初始化参数对象 - const params: any = { - eventListenes: [], - eventSends: [] - }; - - // 遍历所有节点,查找事件相关节点 - Object.entries(revertedData).forEach(([nodeId, nodeConfig]: [string, any]) => { - // 检查节点是否有component属性和type定义 - if (nodeConfig.component?.type) { - const nodeType = nodeConfig.component.type; - const customDef = nodeConfig.component.customDef; - - // 解析customDef,可能是字符串也可能是对象 - let eventConfig; - if (typeof customDef === 'string') { - try { - eventConfig = JSON.parse(customDef); - } catch (e) { - console.error('Failed to parse customDef:', e); - return; - } - } - else { - eventConfig = customDef; - } - // 根据节点类型处理事件节点 - if (nodeType === 'EVENTLISTENE') { - // 事件接收节点 - params.eventListenes.push({ - nodeName: nodeConfig.nodeName || '', - eventId: eventConfig?.eventId || null, - topic: eventConfig?.topic || '', - eventName: eventConfig?.name || '', - dataOuts: nodeConfig.dataOuts || [], - nodeId: nodeConfig.nodeId - }); - } - else if (nodeType === 'EVENTSEND') { - // 事件发送节点 - params.eventSends.push({ - nodeName: nodeConfig.nodeName || '', - eventId: eventConfig?.eventId || null, - topic: eventConfig?.topic || '', - eventName: eventConfig?.name || '', - dataIns: nodeConfig.dataIns || [], - nodeId: nodeConfig.nodeId - }); - } - } - }); - // 调用更新事件的API - if (params.eventListenes.length > 0 || params.eventSends.length > 0) { - return params; - } - else return null; - }; - const upDatePublish = async (revertedData) => { - const { currentAppData } = store.getState().ideContainer; - const params = { - appId: currentAppData.id, - publishAppId: [] - }; - revertedData.forEach(item => { - if (item?.component && item.component.type === 'SUB' && !item.component.customDef) params.publishAppId.push(item.component.compId); // 复合组件的这个compId实际上就是flowHousVO里面的id - }); - if (params.publishAppId.length > 0) { - const res: any = await refPublish(params); - if (res.code === 200) return res.data; - else return {}; - } - 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 { @@ -1295,7 +1151,7 @@ export const useFlowCallbacks = ( const newRevertedData = reverseConvertFlowData(nodes, edges, upDatePublishCB); const { flowData, currentAppData, info } = store.getState().ideContainer; const { deleteEventSendNodeList, deleteEventlisteneList } = handelEventNodeList(newRevertedData); - // console.log(deleteEventSendNodeList, deleteEventlisteneList); + console.log('事件处理数据', deleteEventSendNodeList, deleteEventlisteneList); // return; let params = {}; // 更新复合组件/子流程 @@ -1344,6 +1200,18 @@ export const useFlowCallbacks = ( Message.error(res.message); } } + + // 事件节点变动数据有长度就通知后端,主流程和子流程(复合节点)通用 + if (deleteEventSendNodeList.length > 0 || deleteEventlisteneList.length > 0) { + deleteEventSendNodeList.length > 0 && deleteEventPub({ + appId: currentAppData.id, + topics: deleteEventSendNodeList + }); + deleteEventlisteneList.length > 0 && deleteEventSub({ + appId: currentAppData.id, + topics: deleteEventlisteneList + }); + } } catch (error) { console.error('Error saving flow data:', error); Message.error('保存失败'); @@ -1391,7 +1259,7 @@ export const useFlowCallbacks = ( eventId: Array.from(new Set(item.eventId)) })); try { - updateAppFlowData(appFlowParams); + updateAppFlowData(appEventParams); if (appEventParams.length > 0) { for (const item of appEventParams) { await sleep(500); diff --git a/src/pages/flowEditor/utils/common.ts b/src/pages/flowEditor/utils/common.ts new file mode 100644 index 0000000..e01145a --- /dev/null +++ b/src/pages/flowEditor/utils/common.ts @@ -0,0 +1,255 @@ +import store from '@/store'; +import { refPublish } from '@/api/appRes'; + +export 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); + + // 分类事件节点 [{nodeId:topic}] + const eventSendNodes = []; + const eventListenerNodes = []; + + // 从nodeEntries中提取事件节点 + nodeEntries.forEach(([nodeId, nodeConfig]) => { + if (nodeConfig.component?.type === 'EVENTSEND') { + try { + const customDef = JSON.parse(nodeConfig.component.customDef); + eventSendNodes.push({ nodeId, topic: customDef.topic }); + } catch (e) { + eventSendNodes.push({ nodeId, topic: nodeConfig.component.customDef.topic }); + } + } + else if (nodeConfig.component?.type === 'EVENTLISTENE') { + try { + const customDef = JSON.parse(nodeConfig.component.customDef); + eventListenerNodes.push({ nodeId, topic: customDef.topic }); + } catch (e) { + eventListenerNodes.push({ nodeId, topic: nodeConfig.component.customDef.topic }); + } + } + }); + + // 处理EVENTSEND节点 + // 1. 原始数据中有但新数据中没有的节点,直接加入删除列表(节点被删除) + currentEventSendNodeList.forEach((eventNode) => { + const nodeId = Object.keys(eventNode)[0]; + const nodeInCanvas = eventSendNodes.find(node => node.nodeId === nodeId); + + if (!nodeInCanvas) { + // 画布数据中没有该节点,说明节点被删除了,直接加入删除列表 + deleteEventSendNodeList.push(eventNode[nodeId]); + } + }); + + // 2. 遍历新数据中的每个事件节点 + eventSendNodes.forEach((newNode) => { + const newNodeId = newNode.nodeId; + // nodeId格式为"节点类型-时间戳" + const newNodeParts = newNodeId.split('-'); + const newNodeTimestamp = parseInt(newNodeParts[newNodeParts.length - 1]) || 0; + + // 在原始数据中查找相同类型的节点 + const sameTypeNodes = currentEventSendNodeList.filter(node => { + const nodeIdKey = Object.keys(node)[0]; + const nodeIdParts = nodeIdKey.split('-'); + // 比较节点类型部分(除了时间戳) + return nodeIdParts.slice(0, -1).join('-') === newNodeParts.slice(0, -1).join('-'); + }); + + if (sameTypeNodes.length > 0) { + // 获取这些同类型节点中的最大时间戳 + const maxTimestamp = Math.max(...sameTypeNodes.map(node => { + const nodeIdKey = Object.keys(node)[0]; + return parseInt(nodeIdKey.split('-')[nodeIdKey.split('-').length - 1]) || 0; + })); + + // 查找新节点在原始数据中的对应项 + const correspondingNode = currentEventSendNodeList.find(node => { + const nodeIdKey = Object.keys(node)[0]; + return nodeIdKey === newNodeId; + }); + + // 如果找到了对应的节点 + if (correspondingNode) { + const correspondingNodeId = Object.keys(correspondingNode)[0]; + const correspondingTopic = correspondingNode[correspondingNodeId]; + + // 比较topic是否一致 + if (newNode.topic !== correspondingTopic) { + // topic不一致,将原始数据的topic加入删除列表 + deleteEventSendNodeList.push(correspondingTopic); + } + } + // 如果新节点的时间戳小于最大时间戳,则说明它是一个旧版本,应该被删除 + else if (newNodeTimestamp < maxTimestamp) { + // 查找该节点在currentEventSendNodeList中的完整对象 + const nodeToDelete = currentEventSendNodeList.find(node => { + const nodeIdKey = Object.keys(node)[0]; + return nodeIdKey === newNodeId; + }); + + if (nodeToDelete) { + const nodeIdKey = Object.keys(nodeToDelete)[0]; + deleteEventSendNodeList.push(nodeToDelete[nodeIdKey]); + } + } + } + // 如果sameTypeNodes为空,说明这是一个全新的节点类型,不需要处理 + }); + + // 处理EVENTLISTENE节点 + // 1. 原始数据中有但新数据中没有的节点,直接加入删除列表(节点被删除) + currentEventlisteneList.forEach((eventNode) => { + const nodeId = Object.keys(eventNode)[0]; + const nodeInCanvas = eventListenerNodes.find(node => node.nodeId === nodeId); + + if (!nodeInCanvas) { + // 画布数据中没有该节点,说明节点被删除了,直接加入删除列表 + deleteEventlisteneList.push(eventNode[nodeId]); + } + }); + + // 2. 遍历新数据中的每个事件节点 + eventListenerNodes.forEach((newNode) => { + const newNodeId = newNode.nodeId; + // nodeId格式为"节点类型-时间戳" + const newNodeParts = newNodeId.split('-'); + const newNodeTimestamp = parseInt(newNodeParts[newNodeParts.length - 1]) || 0; + + // 在原始数据中查找相同类型的节点 + const sameTypeNodes = currentEventlisteneList.filter(node => { + const nodeIdKey = Object.keys(node)[0]; + const nodeIdParts = nodeIdKey.split('-'); + // 比较节点类型部分(除了时间戳) + return nodeIdParts.slice(0, -1).join('-') === newNodeParts.slice(0, -1).join('-'); + }); + + if (sameTypeNodes.length > 0) { + // 获取这些同类型节点中的最大时间戳 + const maxTimestamp = Math.max(...sameTypeNodes.map(node => { + const nodeIdKey = Object.keys(node)[0]; + return parseInt(nodeIdKey.split('-')[nodeIdKey.split('-').length - 1]) || 0; + })); + + // 查找新节点在原始数据中的对应项 + const correspondingNode = currentEventlisteneList.find(node => { + const nodeIdKey = Object.keys(node)[0]; + return nodeIdKey === newNodeId; + }); + + // 如果找到了对应的节点 + if (correspondingNode) { + const correspondingNodeId = Object.keys(correspondingNode)[0]; + const correspondingTopic = correspondingNode[correspondingNodeId]; + + // 比较topic是否一致 + if (newNode.topic !== correspondingTopic) { + // topic不一致,将原始数据的topic加入删除列表 + deleteEventlisteneList.push(correspondingTopic); + } + } + // 如果新节点的时间戳小于最大时间戳,则说明它是一个旧版本,应该被删除 + else if (newNodeTimestamp < maxTimestamp) { + // 查找该节点在currentEventlisteneList中的完整对象 + const nodeToDelete = currentEventlisteneList.find(node => { + const nodeIdKey = Object.keys(node)[0]; + return nodeIdKey === newNodeId; + }); + + if (nodeToDelete) { + const nodeIdKey = Object.keys(nodeToDelete)[0]; + deleteEventlisteneList.push(nodeToDelete[nodeIdKey]); + } + } + } + // 如果sameTypeNodes为空,说明这是一个全新的节点类型,不需要处理 + }); + + return { + deleteEventSendNodeList, + deleteEventlisteneList + }; +}; + +export const updateEvent = (revertedData, appid) => { + // 初始化参数对象 + const params: any = { + eventListenes: [], + eventSends: [] + }; + + // 遍历所有节点,查找事件相关节点 + Object.entries(revertedData).forEach(([nodeId, nodeConfig]: [string, any]) => { + // 检查节点是否有component属性和type定义 + if (nodeConfig.component?.type) { + const nodeType = nodeConfig.component.type; + const customDef = nodeConfig.component.customDef; + + // 解析customDef,可能是字符串也可能是对象 + let eventConfig; + if (typeof customDef === 'string') { + try { + eventConfig = JSON.parse(customDef); + } catch (e) { + console.error('Failed to parse customDef:', e); + return; + } + } + else { + eventConfig = customDef; + } + + // 根据节点类型处理事件节点 + if (nodeType === 'EVENTLISTENE') { + // 事件接收节点 + params.eventListenes.push({ + nodeName: nodeConfig.nodeName || '', + eventId: eventConfig?.eventId || null, + topic: eventConfig?.topic || '', + eventName: eventConfig?.name || '', + dataOuts: nodeConfig.dataOuts || [], + nodeId: nodeConfig.nodeId + }); + } + else if (nodeType === 'EVENTSEND') { + // 事件发送节点 + params.eventSends.push({ + nodeName: nodeConfig.nodeName || '', + eventId: eventConfig?.eventId || null, + topic: eventConfig?.topic || '', + eventName: eventConfig?.name || '', + dataIns: nodeConfig.dataIns || [], + nodeId: nodeConfig.nodeId + }); + } + } + }); + // 调用更新事件的API + if (params.eventListenes.length > 0 || params.eventSends.length > 0) { + return params; + } + else return null; +}; +export const upDatePublish = async (revertedData) => { + const { currentAppData } = store.getState().ideContainer; + const params = { + appId: currentAppData.id, + publishAppId: [] + }; + revertedData.forEach(item => { + if (item?.component && item.component.type === 'SUB' && !item.component.customDef) params.publishAppId.push(item.component.compId); // 复合组件的这个compId实际上就是flowHousVO里面的id + }); + if (params.publishAppId.length > 0) { + const res: any = await refPublish(params); + if (res.code === 200) return res.data; + else return {}; + } + else return {}; +}; \ No newline at end of file diff --git a/src/store/ideContainer.ts b/src/store/ideContainer.ts index 2981138..5c9e9e4 100644 --- a/src/store/ideContainer.ts +++ b/src/store/ideContainer.ts @@ -21,8 +21,8 @@ interface IDEContainerState { isRunning: boolean; logs: any[]; runId: string; - eventSendNodeList: string[], - eventlisteneList: string[] + eventSendNodeList: any[], // [{nodeID:topic}] + eventlisteneList: any[] // [{nodeID:topic}] }>; } diff --git a/src/utils/convertFlowData.ts b/src/utils/convertFlowData.ts index ad70d94..cdbc223 100644 --- a/src/utils/convertFlowData.ts +++ b/src/utils/convertFlowData.ts @@ -75,12 +75,28 @@ export const convertFlowData = (flowData: any, useDefault = true) => { const nodeConfig: any = entry[1]; // 更新应用中的事件节点列表 - if (nodeId.includes('EVENTLISTENE')) eventlisteneList.push(nodeId); - else if (nodeId.includes('EVENTSEND')) eventSendNodeList.push(nodeId); + if (nodeId.includes('EVENTLISTENE')) { + try { + const customDef = JSON.parse(nodeConfig.component.customDef); + // 使用展开运算符创建新数组,避免修改冻结对象 + eventlisteneList.splice(eventlisteneList.length, 0, { [nodeId]: customDef.topic }); + } catch (error) { + console.log(error); + } + } + else if (nodeId.includes('EVENTSEND')) { + try { + const customDef = JSON.parse(nodeConfig.component.customDef); + // 使用展开运算符创建新数组,避免修改冻结对象 + eventSendNodeList.splice(eventSendNodeList.length, 0, { [nodeId]: customDef.topic }); + } catch (error) { + console.log(error); + } + } if (eventlisteneList.length > 0 || eventSendNodeList.length > 0) store.dispatch(updateEventNodeList({ - eventSendNodeList, - eventlisteneList + eventSendNodeList: [...eventSendNodeList], + eventlisteneList: [...eventlisteneList] })); // 确定节点类型 From 21526a68317dbb8ddfbe1c0f0c6c66a0155a3982 Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 11:05:30 +0800 Subject: [PATCH 04/16] =?UTF-8?q?pref(flow):=20=E6=95=B0=E6=8D=AE=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E9=BB=98=E8=AE=A4=E6=B7=BB=E5=8A=A0appRuntim?= =?UTF-8?q?eData?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/convertFlowData.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/utils/convertFlowData.ts b/src/utils/convertFlowData.ts index cdbc223..142e5ba 100644 --- a/src/utils/convertFlowData.ts +++ b/src/utils/convertFlowData.ts @@ -98,6 +98,12 @@ export const convertFlowData = (flowData: any, useDefault = true) => { eventSendNodeList: [...eventSendNodeList], eventlisteneList: [...eventlisteneList] })); + else { + store.dispatch(updateEventNodeList({ + eventSendNodeList: [], + eventlisteneList: [] + })); + } // 确定节点类型 let nodeType = 'BASIC'; From 01e9b2a5c45ad0aae6931f9f589113ded28932dd Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 11:05:58 +0800 Subject: [PATCH 05/16] =?UTF-8?q?fix(flow):=20=E4=BF=AE=E5=A4=8D=E5=BE=AA?= =?UTF-8?q?=E7=8E=AF=E8=8A=82=E7=82=B9=E5=88=A0=E9=99=A4=E5=90=8E=E9=81=97?= =?UTF-8?q?=E7=95=99=E9=93=BE=E6=8E=A5=E7=BA=BF=E6=95=B0=E6=8D=AE=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/flowEditor/FlowEditorMain.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pages/flowEditor/FlowEditorMain.tsx b/src/pages/flowEditor/FlowEditorMain.tsx index fb35c24..4b72d44 100644 --- a/src/pages/flowEditor/FlowEditorMain.tsx +++ b/src/pages/flowEditor/FlowEditorMain.tsx @@ -279,8 +279,14 @@ const FlowEditorMain: React.FC = (props) => { index === self.findIndex(n => n.id === node.id) ); - // 删除所有相关节点 + // 删除所有相关节点和边 setNodes((nds) => nds.filter((n) => !nodesToRemove.find((d) => d.id === n.id))); + + // 删除与这些节点相关的所有边 + const nodeIdsToRemove = nodesToRemove.map(node => node.id); + setEdges((eds) => eds.filter((e) => + !nodeIdsToRemove.includes(e.source) && !nodeIdsToRemove.includes(e.target) + )); } else { // 普通节点删除 From fe0b85919eb40c3771ef01e5e084fbfa3a36e814 Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 11:06:17 +0800 Subject: [PATCH 06/16] =?UTF-8?q?pref(flow):=E4=BC=98=E5=8C=96=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=9B=9E=E8=B0=83=E9=80=BB=E8=BE=91=E5=B9=B6=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E8=B0=83=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useFlowCallbacks.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/hooks/useFlowCallbacks.ts b/src/hooks/useFlowCallbacks.ts index b856faf..c278bbe 100644 --- a/src/hooks/useFlowCallbacks.ts +++ b/src/hooks/useFlowCallbacks.ts @@ -1151,8 +1151,7 @@ export const useFlowCallbacks = ( 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')) { @@ -1169,6 +1168,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); @@ -1218,10 +1223,6 @@ export const useFlowCallbacks = ( } } else { - const params = { - nodes, - edges - }; const appFlowParams = { appEventList: {} }; From cbceb5a101bdbd12dbf9a7b8e7d5a0e3629b15e2 Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 11:09:37 +0800 Subject: [PATCH 07/16] =?UTF-8?q?pref(ideContainer):=20=E9=9A=90=E8=97=8F?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E8=8A=82=E7=82=B9=E4=B8=8D=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/ideContainer/sideBar.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/ideContainer/sideBar.tsx b/src/pages/ideContainer/sideBar.tsx index 6b14f5b..1adbb5b 100644 --- a/src/pages/ideContainer/sideBar.tsx +++ b/src/pages/ideContainer/sideBar.tsx @@ -475,6 +475,7 @@ const SideBar: React.FC = ({ ); } else { + if (item.title.includes('隐藏事件')) return null; return ( Date: Mon, 3 Nov 2025 14:26:30 +0800 Subject: [PATCH 08/16] =?UTF-8?q?feat(flow):=20=E6=B7=BB=E5=8A=A0=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E8=BF=9E=E7=BA=BF=E5=A4=84=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useFlowCallbacks.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/hooks/useFlowCallbacks.ts b/src/hooks/useFlowCallbacks.ts index c278bbe..737beae 100644 --- a/src/hooks/useFlowCallbacks.ts +++ b/src/hooks/useFlowCallbacks.ts @@ -1224,7 +1224,8 @@ export const useFlowCallbacks = ( } else { const appFlowParams = { - appEventList: {} + appEventList: {}, + eventEdges: [] }; nodes.forEach(node => { @@ -1236,6 +1237,21 @@ export const useFlowCallbacks = ( const eventMap = new Map(); edges.forEach((edge: any) => { + console.log('edge:', edge); + + // 处理事件连线 + appFlowParams.eventEdges.push({ + id: edge.id, + source: edge.source, + target: edge.target, + lineType: 'data', + data: { + displayData: { + ...edge.data.displayData + } + } + }); + // 应用组件的桩点id就是事件id const sourceId = edge.sourceHandle; const targetId = edge.targetHandle; @@ -1260,7 +1276,7 @@ export const useFlowCallbacks = ( eventId: Array.from(new Set(item.eventId)) })); try { - updateAppFlowData(appEventParams); + updateAppFlowData(appFlowParams); if (appEventParams.length > 0) { for (const item of appEventParams) { await sleep(500); From 5778383add900bf746476507a4776fa8e0fcc052 Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 14:28:28 +0800 Subject: [PATCH 09/16] =?UTF-8?q?chroe:=20=E7=A7=BB=E9=99=A4=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useFlowCallbacks.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hooks/useFlowCallbacks.ts b/src/hooks/useFlowCallbacks.ts index 737beae..71f938d 100644 --- a/src/hooks/useFlowCallbacks.ts +++ b/src/hooks/useFlowCallbacks.ts @@ -1237,7 +1237,6 @@ export const useFlowCallbacks = ( const eventMap = new Map(); edges.forEach((edge: any) => { - console.log('edge:', edge); // 处理事件连线 appFlowParams.eventEdges.push({ From 7237faaebdc61330ce10e17455b70ab4f8e956b3 Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 15:23:54 +0800 Subject: [PATCH 10/16] =?UTF-8?q?feat(flow-editor):=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E5=86=85=E5=AE=B9=E6=98=BE=E7=A4=BA=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整节点输入输出样式布局,使用justify-content: space-between - 注释掉旧的节点输入输出类名引用 - 新增标题格式化函数,过滤start/end节点标题显示 - 修改输入输出描述展示逻辑,优先使用desc字段 - 调整数据输入框渲染条件,移除对dataIns长度判断 --- .../FlowEditor/node/style/baseOther.module.less | 6 ++++-- src/pages/flowEditor/components/nodeContentOther.tsx | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/components/FlowEditor/node/style/baseOther.module.less b/src/components/FlowEditor/node/style/baseOther.module.less index e3f2ca2..e0a4444 100644 --- a/src/components/FlowEditor/node/style/baseOther.module.less +++ b/src/components/FlowEditor/node/style/baseOther.module.less @@ -49,6 +49,7 @@ padding: 0 5px; border: 1px solid #cccccc; border-radius: 3px; + justify-content: space-between; .node-inputs { padding-right: 10px; @@ -58,8 +59,9 @@ padding-left: 10px; } - .node-inputs, - .node-outputs, + + //.node-inputs, + //.node-outputs, .node-inputs-api, .node-outputs-api { flex: 1; diff --git a/src/pages/flowEditor/components/nodeContentOther.tsx b/src/pages/flowEditor/components/nodeContentOther.tsx index ce1ba86..b78785a 100644 --- a/src/pages/flowEditor/components/nodeContentOther.tsx +++ b/src/pages/flowEditor/components/nodeContentOther.tsx @@ -216,6 +216,10 @@ const formatFooter = (data: any, eventListOld = []) => { } }; +const formatTitle = (text) => { + return text === 'start' || text === 'end' ? '' : text; +}; + const NodeContent = ({ data }: { data: NodeContentData }) => { const { eventListOld } = useSelector((state) => state.ideContainer); const apiIns = data.parameters?.apiIns || []; @@ -239,17 +243,17 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
{apiIns.map((input, index) => (
- {input.desc} + {formatTitle(input.desc || input.id || input.name)}
))}
)} {apiOuts.length > 0 && ( -
+
{apiOuts.map((output, index) => (
- {output.dataType} {output.desc} + {output.desc}
))}
@@ -268,7 +272,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => { {/*content栏-data部分*/}
- {dataIns.length > 0 && !isStartNode && ( + {!isStartNode && (
{dataIns.map((input, index) => (
From 797487aaea1366652cc576ae1ec67bb13a45b5db Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 15:34:27 +0800 Subject: [PATCH 11/16] =?UTF-8?q?style(flowEditor):=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E5=86=85=E5=AE=B9=E6=B8=B2=E6=9F=93=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowEditor/components/nodeContentApp.tsx | 77 +++++++++---------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/src/pages/flowEditor/components/nodeContentApp.tsx b/src/pages/flowEditor/components/nodeContentApp.tsx index d43d04b..cc355a8 100644 --- a/src/pages/flowEditor/components/nodeContentApp.tsx +++ b/src/pages/flowEditor/components/nodeContentApp.tsx @@ -2,7 +2,6 @@ import React, { useMemo } from 'react'; import styles from '@/components/FlowEditor/node/style/baseOther.module.less'; import { Handle, Position, useStore } from '@xyflow/react'; import { deserializeValue, isJSON } from '@/utils/common'; -import cronstrue from 'cronstrue/i18n'; interface NodeContentData { parameters?: { @@ -224,25 +223,25 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
- {apiIns.length > 0 && ( -
- {apiIns.map((input, index) => { - // 查找关联的事件分组 - const group = findApiGroupByTopic(input, eventGroups); - return ( -
- {isValidApi(input) ? input.name : ''} -
- ); - })} -
- )} + {/*{apiIns.length > 0 && (*/} +
+ {apiIns.map((input, index) => { + // 查找关联的事件分组 + const group = findApiGroupByTopic(input, eventGroups); + return ( +
+ {isValidApi(input) ? input.name : ''} +
+ ); + })} +
+ {/*)}*/} {apiOuts.length > 0 && (
@@ -273,25 +272,25 @@ const NodeContent = ({ data }: { data: NodeContentData }) => { {/*content栏-data部分*/}
- {dataIns.length > 0 && ( -
- {dataIns.map((input, index) => { - // 查找关联的事件分组 - const group = findRelatedEventGroup(input, eventGroups, 'eventSends'); - return ( -
- {isValidData(input) ? `${input.name || input.id} ${input?.dataType}` : ''} -
- ); - })} -
- )} + {/*{dataIns.length > 0 && (*/} +
+ {dataIns.map((input, index) => { + // 查找关联的事件分组 + const group = findRelatedEventGroup(input, eventGroups, 'eventSends'); + return ( +
+ {isValidData(input) ? `${input.name || input.id} ${input?.dataType}` : ''} +
+ ); + })} +
+ {/*)}*/} {dataOuts.length > 0 && (
From 0b26c2385d313a800bccf5a0f3bcb1811b87061b Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 15:38:15 +0800 Subject: [PATCH 12/16] =?UTF-8?q?feat(flowEditor):=20=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E9=A2=9C=E8=89=B2=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/flowEditor/components/nodeContentApp.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/pages/flowEditor/components/nodeContentApp.tsx b/src/pages/flowEditor/components/nodeContentApp.tsx index cc355a8..c5d9d06 100644 --- a/src/pages/flowEditor/components/nodeContentApp.tsx +++ b/src/pages/flowEditor/components/nodeContentApp.tsx @@ -120,7 +120,16 @@ const getGroupColor = (groupId: number) => { '#faad14', // 黄色 '#f5222d', // 红色 '#722ed1', // 紫色 - '#13c2c2' // 青色 + '#13c2c2', // 青色 + '#eb2f96', // 粉色 + '#a0d911', // 青绿色 + '#ff7a45', // 橙色 + '#9254de', // 深紫色 + '#40a9ff', // 浅蓝色 + '#ff4d4f', // 亮红色 + '#36cfc9', // 深青色 + '#fadb14', // 亮黄色 + '#c41d7f' // 深粉色 ]; return colors[groupId % colors.length]; }; From dd6fc780a30b1754c0be417beba8e6974c210096 Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 15:57:53 +0800 Subject: [PATCH 13/16] =?UTF-8?q?feat(flowEditor):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E9=9A=90=E8=97=8F/=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=B9=B6=E5=BA=94=E7=94=A8=E9=80=8F=E6=98=8E?= =?UTF-8?q?=E5=BA=A6=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 FlowEditorMain 组件中新增 hiddenNodes 状态管理隐藏节点 - 监听 toggleNodeVisibility 自定义事件更新节点可见性 -为隐藏的节点和边应用 opacity 样式 - 在 customEdge 组件中为标签、下拉框和按钮添加透明度支持 - 在 sideBar 组件中新增眼睛图标按钮控制节点显示/隐藏 - 引入 IconEye 和 IconEyeInvisible 图标用于切换可见性状态 - 更新节点树额外操作按钮渲染逻辑以支持可见性切换 --- src/pages/flowEditor/FlowEditorMain.tsx | 90 ++++++++++++++++++- .../flowEditor/components/customEdge.tsx | 10 ++- src/pages/ideContainer/sideBar.tsx | 83 +++++++++++++++-- 3 files changed, 172 insertions(+), 11 deletions(-) diff --git a/src/pages/flowEditor/FlowEditorMain.tsx b/src/pages/flowEditor/FlowEditorMain.tsx index 4b72d44..2769ae1 100644 --- a/src/pages/flowEditor/FlowEditorMain.tsx +++ b/src/pages/flowEditor/FlowEditorMain.tsx @@ -139,6 +139,34 @@ const FlowEditorMain: React.FC = (props) => { const { undo, redo, canUndo, canRedo } = useHistory(); const reactFlowId = useMemo(() => new Date().getTime().toString(), []); + // 用于存储隐藏的节点ID + const [hiddenNodes, setHiddenNodes] = React.useState>(new Set()); + + // 监听自定义事件以隐藏/显示节点 + useEffect(() => { + const handleToggleNodeVisibility = (event: CustomEvent) => { + const { appId, isVisible } = event.detail; + + if (isVisible) { + // 显示节点 - 从隐藏节点集合中移除 + setHiddenNodes(prev => { + const newSet = new Set(prev); + newSet.delete(appId); + return newSet; + }); + } else { + // 隐藏节点 - 添加到隐藏节点集合 + setHiddenNodes(prev => new Set(prev).add(appId)); + } + }; + + document.addEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener); + + return () => { + document.removeEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener); + }; + }, []); + // 从Redux store中获取当前应用的运行状态 const { appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer); const currentAppIsRunning = currentAppData?.id && appRuntimeData[currentAppData.id] @@ -148,6 +176,31 @@ const FlowEditorMain: React.FC = (props) => { // 在应用编排模式下(useDefault为false)禁用删除功能 const isDeleteDisabled = currentAppIsRunning; + // 监听自定义事件以隐藏/显示节点 + useEffect(() => { + const handleToggleNodeVisibility = (event: CustomEvent) => { + const { appId, isVisible } = event.detail; + + if (isVisible) { + // 显示节点 - 从隐藏节点集合中移除 + setHiddenNodes(prev => { + const newSet = new Set(prev); + newSet.delete(appId); + return newSet; + }); + } else { + // 隐藏节点 - 添加到隐藏节点集合 + setHiddenNodes(prev => new Set(prev).add(appId)); + } + }; + + document.addEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener); + + return () => { + document.removeEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener); + }; + }, []); + // 监听键盘事件实现快捷键 useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { @@ -188,8 +241,41 @@ const FlowEditorMain: React.FC = (props) => { onContextMenu={(e) => e.preventDefault()}> ({ ...node, draggable: !currentAppIsRunning }))} // 运行时禁用节点拖拽 - edges={edges} + nodes={nodes.map(node => { + // 检查节点是否应该被隐藏 + const isHidden = hiddenNodes.has(node.id); + // 应用透明度样式 + const style = isHidden ? { opacity: 0.3 } : {}; + + return { + ...node, + draggable: !currentAppIsRunning, + style: { + ...node.style, + ...style + } + }; + })} + edges={edges.map(edge => { + // 检查边连接的节点是否被隐藏 + const isSourceHidden = hiddenNodes.has(edge.source); + const isTargetHidden = hiddenNodes.has(edge.target); + // 如果源节点或目标节点被隐藏,则边也应用透明度 + const style = (isSourceHidden || isTargetHidden) ? { opacity: 0.3 } : {}; + + // 更新边的数据,确保选择框也应用透明度 + return { + ...edge, + style: { + ...edge.style, + ...style + }, + data: { + ...edge.data, + ...(isSourceHidden || isTargetHidden ? { hidden: true, opacity: 0.3 } : { opacity: 1 }) + } + }; + })} nodeTypes={nodeTypes} edgeTypes={edgeTypes} snapToGrid={true} diff --git a/src/pages/flowEditor/components/customEdge.tsx b/src/pages/flowEditor/components/customEdge.tsx index df19e51..c7705c2 100644 --- a/src/pages/flowEditor/components/customEdge.tsx +++ b/src/pages/flowEditor/components/customEdge.tsx @@ -135,7 +135,8 @@ const DataDisplayEdge: React.FC = ({ position: 'absolute', transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`, fontSize: 12, - pointerEvents: 'all' + pointerEvents: 'all', + opacity: style?.opacity || 1 // 应用透明度样式到标签容器 }} className="nodrag nopan" > @@ -148,7 +149,8 @@ const DataDisplayEdge: React.FC = ({ border: isOpen ? '1px solid #1890ff' : '1px solid #d9d9d9', borderRadius: 4, backgroundColor: '#fff', - position: 'relative' + position: 'relative', + opacity: style?.opacity || 1 // 应用透明度样式到下拉框 }} >
= ({ backgroundColor: '#fff', zIndex: 1000, maxHeight: 200, - overflowY: 'auto' + overflowY: 'auto', + opacity: style?.opacity || 1 // 应用透明度样式到下拉列表 }} > {eventTopicList.map((option: { value: string; label: string }) => ( @@ -204,6 +207,7 @@ const DataDisplayEdge: React.FC = ({ {hovered && Object.keys(displayData).length === 0 && lineType !== 'data' && ( handleEdgeAddNode(e)} + style={{ opacity: style?.opacity || 1 }} // 应用透明度样式到添加节点按钮 /> )}
diff --git a/src/pages/ideContainer/sideBar.tsx b/src/pages/ideContainer/sideBar.tsx index 1adbb5b..4074abe 100644 --- a/src/pages/ideContainer/sideBar.tsx +++ b/src/pages/ideContainer/sideBar.tsx @@ -12,9 +12,17 @@ import { Menu, Popconfirm } from '@arco-design/web-react'; -import { IconApps, IconMore, IconDelete, IconEdit } from '@arco-design/web-react/icon'; +import { + IconApps, + IconMore, + IconDelete, + IconEdit, + IconEye, + IconSearch, + IconPlus, + IconEyeInvisible +} from '@arco-design/web-react/icon'; import { menuData1, menuData2 } from './config/menuData'; -import { IconSearch, IconPlus } from '@arco-design/web-react/icon'; import { Selected } from '@/pages/ideContainer/types'; import { useDispatch, useSelector } from 'react-redux'; import { @@ -197,6 +205,8 @@ const SideBar: React.FC = ({ y: 0, nodeData: null }); // 添加右键菜单状态 + // 用于存储隐藏的节点ID + const [hiddenNodes, setHiddenNodes] = useState>(new Set()); const resizeBoxRef = useRef(null); // 引用第一个 ResizeBox 容器 const contextMenuRef = useRef(null); // 右键菜单引用 const { menuData, info, canvasDataMap } = useSelector(state => state.ideContainer); @@ -551,12 +561,35 @@ const SideBar: React.FC = ({ }; }, []); + // 监听自定义事件以更新隐藏节点状态 + useEffect(() => { + const handleToggleNodeVisibility = (event: CustomEvent) => { + const { appId, isVisible } = event.detail; + + if (isVisible) { + // 显示节点 - 从隐藏节点集合中移除 + setHiddenNodes(prev => { + const newSet = new Set(prev); + newSet.delete(appId); + return newSet; + }); + } else { + // 隐藏节点 - 添加到隐藏节点集合 + setHiddenNodes(prev => new Set(prev).add(appId)); + } + }; + + document.addEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener); + + return () => { + document.removeEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener); + }; + }, []); + // 渲染节点的额外操作按钮 const renderNodeExtra = (node) => { // 只有当 node.dataRef.id 存在时才渲染操作按钮 - if (!node.dataRef?.id) { - return null; - } + if (!node.dataRef?.id) return null; const dropList = ( @@ -622,6 +655,44 @@ const SideBar: React.FC = ({ ); }; + const renderNodeExtraEye = (node) => { + // console.log('node:', node); + // 只有当 node.dataRef.id 存在时才渲染操作按钮 + if (!node.dataRef?.id) return null; + + // 检查节点当前是否被隐藏 + const isNodeHidden = hiddenNodes?.has(node.dataRef.id); + + function handleEyeClick(e) { + e.stopPropagation(); + // 发送自定义事件,通知流程图组件隐藏/显示节点 + const event = new CustomEvent('toggleNodeVisibility', { + detail: { + appId: node.dataRef.id, + isVisible: isNodeHidden // 如果当前是隐藏的,点击后应该显示 + } + }); + document.dispatchEvent(event); + } + + return ( +
+ {isNodeHidden ? : } +
+ ); + }; + return (
= ({ } }} style={{ background: 'transparent' }} // 移除背景色 - renderExtra={selected?.parentKey === 'appList' ? renderNodeExtra : null} + renderExtra={selected?.parentKey === 'appList' ? renderNodeExtra : renderNodeExtraEye} > {renderMenuItems(filteredMenu[activeKey]?.children)} From a340e1ff4b893023dc0a8b853b69005b9186d078 Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 3 Nov 2025 16:08:34 +0800 Subject: [PATCH 14/16] =?UTF-8?q?feat(ideContainer):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=95=B0=E6=8D=AE=E5=B1=95=E7=A4=BA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/ideContainer/logBar.tsx | 78 +++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/src/pages/ideContainer/logBar.tsx b/src/pages/ideContainer/logBar.tsx index e233d01..a36aeef 100644 --- a/src/pages/ideContainer/logBar.tsx +++ b/src/pages/ideContainer/logBar.tsx @@ -3,6 +3,7 @@ import { ResizeBox, Tabs } from '@arco-design/web-react'; import styles from './style/logBar.module.less'; import { updateLogBarStatus } from '@/store/ideContainer'; import { useSelector, useDispatch } from 'react-redux'; +import { getNodeData } from '@/api/appIns'; // 添加导入 const TabPane = Tabs.TabPane; @@ -13,6 +14,11 @@ interface LogMessage { timestamp: string; } +// 添加运行数据接口 +interface RuntimeData { + [appId: string]: any; +} + interface LogBarProps { a?: string; } @@ -50,6 +56,9 @@ const LogBar: React.FC = () => { const [runtimeLogs, setRuntimeLogs] = useState([]); // 添加运行时日志状态 const [logContainerHeight, setLogContainerHeight] = useState('250px'); // 添加日志容器高度状态 const { currentAppData } = useSelector((state: any) => state.ideContainer); + // 添加运行数据状态 + const [runtimeData, setRuntimeData] = useState({}); + const [loading, setLoading] = useState(false); // 处理 Tab 点击事件 const handleTabClick = (key: string) => { @@ -121,13 +130,13 @@ const LogBar: React.FC = () => { // 自动切换到运行日志tab并展开logBar setActiveTab('1'); dispatch(updateLogBarStatus(true)); - + // 同时将日志添加到当前应用的运行日志中 const appId = currentAppData?.id; if (appId) { dispatch({ type: 'ideContainer/addRuntimeLog', - payload: { + payload: { log: newLog, appId: appId } @@ -145,6 +154,42 @@ const LogBar: React.FC = () => { }; }, [dispatch, currentAppData?.id]); + // 实现轮询获取运行数据 + useEffect(() => { + let intervalId: NodeJS.Timeout | null = null; + + // 只有在当前tab是运行数据且有当前应用时才开始轮询 + if (activeTab === '3' && currentAppData?.id && logBarStatus) { + const fetchRuntimeData = async () => { + try { + setLoading(true); + const response = await getNodeData(currentAppData.id); + setRuntimeData(prev => ({ + ...prev, + [currentAppData.id]: response.data + })); + } catch (error) { + console.error('获取运行数据失败:', error); + } finally { + setLoading(false); + } + }; + + // 立即获取一次数据 + fetchRuntimeData(); + + // 设置轮询,每5秒获取一次数据 + intervalId = setInterval(fetchRuntimeData, 3000); + } + + // 清理函数,组件卸载或条件不满足时清除定时器 + return () => { + if (intervalId) { + clearInterval(intervalId); + } + }; + }, [activeTab, currentAppData?.id, logBarStatus]); + // 渲染校验日志内容 const renderValidationLogs = () => { return ( @@ -170,10 +215,10 @@ const LogBar: React.FC = () => { // 渲染运行时日志内容 const renderRuntimeLogs = () => { // 获取当前应用的运行日志 - const currentAppLogs = currentAppData?.id && appRuntimeData[currentAppData.id] + const currentAppLogs = currentAppData?.id && appRuntimeData[currentAppData.id] ? appRuntimeData[currentAppData.id].logs || [] : []; - + return (
{currentAppLogs.length === 0 ? ( @@ -194,6 +239,28 @@ const LogBar: React.FC = () => { ); }; + // 渲染运行数据内容 + const renderRuntimeData = () => { + const currentAppDataContent = currentAppData?.id ? runtimeData[currentAppData.id] : null; + + return ( +
+ {loading ? ( +

加载中...

+ ) : !currentAppData?.id ? ( +

请选择应用

+ ) : !currentAppDataContent ? ( +

暂无运行数据

+ ) : ( +
+

应用: {currentAppData.name}

+
{JSON.stringify(currentAppDataContent, null, 2)}
+
+ )} +
+ ); + }; + return ( <> = () => { {x.key === '1' ? renderRuntimeLogs() : x.key === '2' ? renderValidationLogs() : - x.content} + x.key === '3' ? renderRuntimeData() : // 添加运行数据渲染 + x.content} ))} From d7ca2dbd3b1df4f12eda435e6f8e4b0939fbba5a Mon Sep 17 00:00:00 2001 From: ZLY Date: Tue, 4 Nov 2025 09:32:03 +0800 Subject: [PATCH 15/16] =?UTF-8?q?pref(flow):=20=E4=BC=98=E5=8C=96=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E4=BF=9D=E5=AD=98=E5=90=8E=E7=9A=84=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=80=BB=E8=BE=91=E5=92=8C=E5=AD=90=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E7=9A=84=E6=95=B0=E6=8D=AE=E5=A4=84=E7=90=86=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useFlowCallbacks.ts | 70 +++++++++++++++++++++++++++-------- src/utils/convertFlowData.ts | 2 +- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/hooks/useFlowCallbacks.ts b/src/hooks/useFlowCallbacks.ts index 71f938d..8d8b294 100644 --- a/src/hooks/useFlowCallbacks.ts +++ b/src/hooks/useFlowCallbacks.ts @@ -7,7 +7,7 @@ import { Node, Edge } from '@xyflow/react'; -import { refPublish, setMainFlow, setMainFlowNew, setSubFlowNew } from '@/api/appRes'; +import { getAppInfoNew, setMainFlowNew, setSubFlowNew } from '@/api/appRes'; import { Message } from '@arco-design/web-react'; import { nodeTypeMap, registerNodeType } from '@/components/FlowEditor/node'; import { convertFlowData, reverseConvertFlowData, revertFlowData } from '@/utils/convertFlowData'; @@ -22,7 +22,7 @@ import { updateEventListOld, addRuntimeLog, clearRuntimeLogs, - updateRuntimeId + updateRuntimeId, updateFlowData } from '@/store/ideContainer'; import { validateAllNodes, @@ -912,18 +912,24 @@ export const useFlowCallbacks = ( }; } else if (nodeType === 'SUB') { - const flowMainData = flowData[currentAppData.id]?.main?.components; - const sameData: any = Object.values(flowMainData).filter((item: any) => { - if (item?.component?.compId === newNode.data.compId) return item; - }); - if (sameData.length) { + const flowSubMap = flowData[currentAppData.id]?.subMap || {}; + const sameData: any = flowSubMap[newNode.data.compId]; + if (sameData) { newNode.data.component = { - ...sameData[0].component + type: nodeType, + compId: newNode.data.compId, + customDef: JSON.stringify({ + dataIns: newNode.data.parameters.dataIns, + dataOuts: newNode.data.parameters.dataOuts, + subflowId: sameData, + name: newNode.data.title + }) }; } else { newNode.data.component = { - type: nodeType + type: nodeType, + compId: newNode.data.compId }; } } @@ -1061,18 +1067,24 @@ export const useFlowCallbacks = ( }; } else if (nodeType === 'SUB') { - const flowMainData = flowData[currentAppData.id]?.main?.components; - const sameData: any = Object.values(flowMainData).filter((item: any) => { - if (item?.component?.compId === newNode.data.compId) return item; - }); - if (sameData.length) { + const flowSubMap = flowData[currentAppData.id]?.subMap || {}; + const sameData: any = flowSubMap[newNode.data.compId]; + if (sameData) { newNode.data.component = { - ...sameData[0].component + type: nodeType, + compId: newNode.data.compId, + customDef: JSON.stringify({ + dataIns: newNode.data.parameters.dataIns, + dataOuts: newNode.data.parameters.dataOuts, + subflowId: sameData, + name: newNode.data.title + }) }; } else { newNode.data.component = { - type: nodeType + type: nodeType, + compId: newNode.data.compId }; } } @@ -1174,6 +1186,19 @@ export const useFlowCallbacks = ( ...canvasDataMap, [currentAppData.id]: { nodes, edges } })); + const appRes: any = await getAppInfoNew(currentAppData.id); + // 更新 flowData 中的数据 + dispatch(updateFlowData({ [currentAppData.id]: appRes.data })); + // 同步更新到 canvasDataMap + if (appRes.data.main?.components) { + const { nodes, edges } = convertFlowData(appRes.data.main.components, true); + setNodes(nodes); + setEdges(edges); + dispatch(updateCanvasDataMap({ + ...canvasDataMap, + [currentAppData.id]: { nodes, edges } + })); + } } else { Message.error(res.message); @@ -1200,6 +1225,19 @@ export const useFlowCallbacks = ( ...canvasDataMap, [currentAppData.id]: { nodes, edges } })); + const appRes: any = await getAppInfoNew(currentAppData.id); + // 更新 flowData 中的数据 + dispatch(updateFlowData({ [currentAppData.id]: appRes.data })); + // 同步更新到 canvasDataMap + if (appRes.data.main?.components) { + const { nodes, edges } = convertFlowData(appRes.data.main.components, true); + setNodes(nodes); + setEdges(edges); + dispatch(updateCanvasDataMap({ + ...canvasDataMap, + [currentAppData.id]: { nodes, edges } + })); + } } else { Message.error(res.message); diff --git a/src/utils/convertFlowData.ts b/src/utils/convertFlowData.ts index 142e5ba..1ee4ab4 100644 --- a/src/utils/convertFlowData.ts +++ b/src/utils/convertFlowData.ts @@ -536,7 +536,7 @@ export const reverseConvertFlowData = (nodes: any[], edges: any[], complexKV: an }; // 处理 component 信息 - if (node.type === 'SUB' && !node.customDef) { + if (node.type === 'SUB' && !node.data.component.customDef) { nodeConfig.component = { type: 'SUB', compId: node.data.compId, From 7047ef0ba566e8efdc0565bdaa4bd177520ce8e2 Mon Sep 17 00:00:00 2001 From: ZLY Date: Tue, 4 Nov 2025 09:32:51 +0800 Subject: [PATCH 16/16] =?UTF-8?q?feat(sideBar):=20=E4=BE=A7=E8=BE=B9?= =?UTF-8?q?=E6=A0=8F=E7=82=B9=E5=87=BB=E5=BA=94=E7=94=A8=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E9=BB=98=E8=AE=A4=E5=A2=9E=E5=8A=A0appRuntimeData?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E4=BA=8B=E4=BB=B6=E5=88=97=E8=A1=A8=E6=95=B0?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/ideContainer/sideBar.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pages/ideContainer/sideBar.tsx b/src/pages/ideContainer/sideBar.tsx index 4074abe..f53bbc0 100644 --- a/src/pages/ideContainer/sideBar.tsx +++ b/src/pages/ideContainer/sideBar.tsx @@ -30,7 +30,8 @@ import { updateFlowData, updateCanvasDataMap, updateCurrentAppData, - updateEventListOld + updateEventListOld, + updateEventNodeList } from '@/store/ideContainer'; import { addApp, getProjectEnv, editApp, deleteApp } from '@/api/apps'; import _ from 'lodash'; @@ -440,6 +441,10 @@ const SideBar: React.FC = ({ dispatch(updateFlowData({ [data.id]: res.data })); // 更新 currentAppData 中的数据 dispatch(updateCurrentAppData({ ...findMenuItem(menuData[identity], children.key) })); + dispatch(updateEventNodeList({ + eventSendNodeList: [], + eventlisteneList: [] + })); // 同步更新到 canvasDataMap if (res.data.main?.components) { @@ -565,7 +570,7 @@ const SideBar: React.FC = ({ useEffect(() => { const handleToggleNodeVisibility = (event: CustomEvent) => { const { appId, isVisible } = event.detail; - + if (isVisible) { // 显示节点 - 从隐藏节点集合中移除 setHiddenNodes(prev => { @@ -573,14 +578,15 @@ const SideBar: React.FC = ({ newSet.delete(appId); return newSet; }); - } else { + } + else { // 隐藏节点 - 添加到隐藏节点集合 setHiddenNodes(prev => new Set(prev).add(appId)); } }; document.addEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener); - + return () => { document.removeEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener); };