diff --git a/src/api/appEvent.ts b/src/api/appEvent.ts index e22fb24..bd02d95 100644 --- a/src/api/appEvent.ts +++ b/src/api/appEvent.ts @@ -17,4 +17,14 @@ export function getAppEventList(id: any) { // 更新事件 export function updateAppEvent(id: any, data: any) { return axios.post(`${urlPrefix}/appEvent/${id}/update`, data); +} + +// 更新应用编排数据 +export function updateAppFlowData(data: any) { + return axios.post(`${urlPrefix}/appEvent/update`, data); +} + +// 修改应用编排中的事件通道 +export function updateAppEventChannel(data: any) { + return axios.post(`${urlPrefix}/appEvent/updateTopic`, data); } \ No newline at end of file diff --git a/src/api/appRes.ts b/src/api/appRes.ts index 3781f29..09833f6 100644 --- a/src/api/appRes.ts +++ b/src/api/appRes.ts @@ -21,7 +21,7 @@ export function setMainFlow(data: FlowDefinition, appId: string) { } // 更新主流程-新数据结构 -export function setMainFlowNew(data: FlowDefinition, appId: string) { +export function setMainFlowNew(data, appId: string) { return axios.post(`${urlPrefix}/appRes/${appId}/updateMainNew`, data); } @@ -51,6 +51,10 @@ export function setSubFlow(data: FlowDefinition, appId: string) { return axios.post(`${urlPrefix}/appRes/${appId}/updateSub`, data); } +export function setSubFlowNew(data, appId: string) { + return axios.post(`${urlPrefix}/appRes/${appId}/updateSubNew`, data); +} + // 导入子流程 export function addSubComp(data: appFlowModel, appId: string) { return axios.post(`${urlPrefix}/appRes/${appId}/addSubComp`, data); diff --git a/src/api/apps.ts b/src/api/apps.ts index 9755f07..b437b82 100644 --- a/src/api/apps.ts +++ b/src/api/apps.ts @@ -86,8 +86,8 @@ export function resumeApp(data: any) { } // 停止 -export function stopApp(data: any) { - return axios.post(`${runPrefix}/apps/${data.id}/stop`); +export function stopApp(appid: any) { + return axios.post(`${runPrefix}/apps/${appid}/stop`); } // APi发布 diff --git a/src/api/event.ts b/src/api/event.ts index 34db91a..a84104e 100644 --- a/src/api/event.ts +++ b/src/api/event.ts @@ -35,9 +35,13 @@ export function queryEventItem(name: string) { } // 事件管理-使用场景ID查询事件 -export function queryEventItemBySceneId(sceneId: 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`); +} // 事件管理-获取工程下可用的topic export function getTopicList(id: string) { diff --git a/src/components/FlowEditor/node/style/baseOther.module.less b/src/components/FlowEditor/node/style/baseOther.module.less index 7732144..e3f2ca2 100644 --- a/src/components/FlowEditor/node/style/baseOther.module.less +++ b/src/components/FlowEditor/node/style/baseOther.module.less @@ -50,6 +50,13 @@ border: 1px solid #cccccc; border-radius: 3px; + .node-inputs { + padding-right: 10px; + } + + .node-outputs { + padding-left: 10px; + } .node-inputs, .node-outputs, @@ -58,6 +65,7 @@ flex: 1; } + .node-outputs-api { .node-input-label { font-size: 12px; diff --git a/src/components/FlowEditor/nodeEditors/components/EventSelect.tsx b/src/components/FlowEditor/nodeEditors/components/EventSelect.tsx index 1c3a1f2..749721f 100644 --- a/src/components/FlowEditor/nodeEditors/components/EventSelect.tsx +++ b/src/components/FlowEditor/nodeEditors/components/EventSelect.tsx @@ -24,7 +24,6 @@ const typeMap = { const EventSelect: React.FC = ({ nodeData, eventList, type, onRefresh, onUpdateData }) => { const [options, setOptions] = useState([]); - const [specialOptions, setSpecialOptions] = useState({}); const [form] = Form.useForm(); const [showModal, setShowModal] = useState(false); const [currentEvent, setCurrentEvent] = useState(null); @@ -32,7 +31,6 @@ const EventSelect: React.FC = ({ nodeData, eventList, type, on useEffect(() => { if (nodeData && eventList && eventList.length > 0) { - setSpecialOptions(eventList.find(item => item.topic.includes('**empty**'))); setOptions(eventList.filter(item => !item.topic.includes('**empty**'))); try { const customDef = JSON.parse(nodeData.component?.customDef); @@ -41,7 +39,7 @@ const EventSelect: React.FC = ({ nodeData, eventList, type, on setCurrentEvent(null); } else { - setCurrentEvent(eventList.find(item => customDef.eventId === item.eventId)); + setCurrentEvent(eventList.find(item => customDef.topic === item.topic)); } } catch (e) { // 先判断topic是不是**empty**,是就不设置currentevent @@ -49,7 +47,7 @@ const EventSelect: React.FC = ({ nodeData, eventList, type, on setCurrentEvent(null); } else { - setCurrentEvent(eventList.find(item => nodeData.component?.customDef.eventId === item.eventId)); + setCurrentEvent(eventList.find(item => nodeData.component?.customDef.topic === item.topic)); } } } @@ -85,7 +83,7 @@ const EventSelect: React.FC = ({ nodeData, eventList, type, on const data = { type: typeMap[type], customDef: { - eventId: e.eventId, + eventId: null, name: e.name, topic: e.topic } @@ -126,7 +124,7 @@ const EventSelect: React.FC = ({ nodeData, eventList, type, on dropdownMenuStyle={{ maxHeight: 300 }} > {options.map((option) => ( - ))} diff --git a/src/hooks/useFlowCallbacks.ts b/src/hooks/useFlowCallbacks.ts index 5006a19..7111c32 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 } from '@/api/appRes'; +import { refPublish, setMainFlow, 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'; @@ -15,7 +15,7 @@ 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 } from '@/store/ideContainer'; +import { updateCanvasDataMap, resetNodeStatus, updateIsRunning, updateEventListOld } from '@/store/ideContainer'; import { validateAllNodes, showValidationErrors, @@ -30,9 +30,11 @@ import { projectFlowHandle } from '@/pages/flowEditor/utils/projectFlowHandle'; import { appFLowHandle } from '@/pages/flowEditor/utils/appFlowhandle'; import { Dispatch } from 'redux'; -import { runMainFlow } from '@/api/apps'; +import { runMainFlow, stopApp } from '@/api/apps'; import store from '@/store'; -import { updateAppEvent } from '@/api/appEvent'; +import { updateAppEvent, updateAppEventChannel, updateAppFlowData } from '@/api/appEvent'; +import { sleep } from '@/utils/common'; +import { queryEventItemBySceneIdOld } from '@/api/event'; export const useFlowCallbacks = ( nodes: Node[], @@ -93,6 +95,7 @@ export const useFlowCallbacks = ( }, 100); } }, [nodes, edges]); + // 边变更处理 const onEdgesChange = useCallback((changes: any) => { const newEdges = applyEdgeChanges(changes, edges); @@ -121,8 +124,8 @@ export const useFlowCallbacks = ( } // 获取源节点和目标节点的参数信息 - const sourceParams = sourceNode.data?.parameters || {}; - const targetParams = targetNode.data?.parameters || {}; + const sourceParams: any = sourceNode.data?.parameters || {}; + const targetParams: any = targetNode.data?.parameters || {}; // 获取源handle和目标handle的类型 (api或data) const sourceHandleType = getHandleType(params.sourceHandle, sourceParams); @@ -142,7 +145,52 @@ export const useFlowCallbacks = ( // 如果验证通过,创建连接 setEdges((edgesSnapshot: Edge[]) => { - const newEdges = addEdge({ ...params, type: 'custom' }, edgesSnapshot); + // 创建带有事件信息的连接 + const edgeParams = { ...params, type: 'custom' }; + + // 检查源节点和目标节点是否都有事件信息 + 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); + + // 如果源节点有事件topic信息 + if (sourceApi && sourceApi.topic) { + // 如果目标节点的topic是**empty**或没有topic,则使用源节点的事件信息 + if (!targetApi || !targetApi.topic || targetApi.topic.includes('**empty**')) { + edgeParams.data = { + displayData: { + name: sourceApi.name, + eventId: sourceApi.eventId, + topic: sourceApi.topic + } + }; + } + // 如果两个节点都有非empty的topic,则以源节点为准 + else if (sourceApi.topic && targetApi.topic && + !sourceApi.topic.includes('**empty**') && + !targetApi.topic.includes('**empty**')) { + edgeParams.data = { + displayData: { + name: sourceApi.name, + eventId: sourceApi.eventId, + topic: sourceApi.topic + } + }; + } + } + // 如果源节点没有事件信息,但目标节点有 + else if (targetApi && targetApi.topic && !targetApi.topic.includes('**empty**')) { + edgeParams.data = { + displayData: { + name: targetApi.name, + eventId: targetApi.eventId, + topic: targetApi.topic + } + }; + } + + const newEdges = addEdge(edgeParams, edgesSnapshot); // 连接建立后记录历史 setTimeout(() => { @@ -433,7 +481,6 @@ export const useFlowCallbacks = ( }; localStorage.setItem('copiedNode', JSON.stringify(nodeData)); - console.log('复制节点:', node); }, [initialData?.appId]); // 粘贴节点 @@ -958,7 +1005,7 @@ export const useFlowCallbacks = ( const emptyEvent = eventList.find(item => item.topic.includes('**empty**')); newNode.data.component = { type: nodeType, - customDef: { eventId: emptyEvent.id, name: emptyEvent.name, topic: emptyEvent.topic } + customDef: { eventId: emptyEvent?.eventId ?? null, name: emptyEvent.name, topic: emptyEvent.topic } }; } // 将未定义的节点动态追加进nodeTypes @@ -1001,7 +1048,7 @@ export const useFlowCallbacks = ( // endregion // 保存所有节点和边数据到服务器,更新事件相关数据 - const updateEvent = (revertedData) => { + const updateEvent = (revertedData, appid) => { // 初始化参数对象 const params: any = { eventListenes: [], @@ -1034,28 +1081,31 @@ export const useFlowCallbacks = ( // 事件接收节点 params.eventListenes.push({ nodeName: nodeConfig.nodeName || '', - eventId: eventConfig?.eventId || '', + eventId: eventConfig?.eventId || null, topic: eventConfig?.topic || '', eventName: eventConfig?.name || '', - dataOuts: nodeConfig.dataOuts || [] + dataOuts: nodeConfig.dataOuts || [], + nodeId: nodeConfig.nodeId }); } else if (nodeType === 'EVENTSEND') { // 事件发送节点 params.eventSends.push({ nodeName: nodeConfig.nodeName || '', - eventId: eventConfig?.eventId || '', + eventId: eventConfig?.eventId || null, topic: eventConfig?.topic || '', eventName: eventConfig?.name || '', - dataIns: nodeConfig.dataIns || [] + dataIns: nodeConfig.dataIns || [], + nodeId: nodeConfig.nodeId }); } } }); // 调用更新事件的API - if (params.eventListenes.lengrh > 0 || params.eventSends.length > 0) { - updateAppEvent(initialData.appId, params); + if (params.eventListenes.length > 0 || params.eventSends.length > 0) { + return params; } + else return null; }; const upDatePublish = async (revertedData) => { const { currentAppData } = store.getState().ideContainer; @@ -1093,24 +1143,47 @@ export const useFlowCallbacks = ( const revertedData = revertFlowData(nodes, edges); const upDatePublishCB = await upDatePublish(revertedData.nodeConfigs); const newRevertedData = reverseConvertFlowData(nodes, edges, upDatePublishCB); - const { flowData, currentAppData } = store.getState().ideContainer; - // console.log('revertedData:', revertedData); - // console.log('newRevertedData:', newRevertedData); - const params = { - ...flowData[currentAppData.id]?.main || {}, - components: newRevertedData - }; - // return; - updateEvent(revertedData.nodeConfigs); - // 旧版数据结构 - // const res: any = await setMainFlow(revertedData, initialData.appId); - // 新版数据结构 - const res: any = await setMainFlowNew(params, initialData.appId); - if (res.code === 200) { - Message.success('保存成功'); + const { flowData, currentAppData, info } = store.getState().ideContainer; + let params = {}; + // 更新复合组件/子流程 + if (currentAppData.key.includes('sub')) { + const appEventDefinition = updateEvent(revertedData.nodeConfigs, initialData.appId); + params = { + ...currentAppData?.compData || {}, + components: newRevertedData, + appEventDefinition, + sceneId: info.id + }; + const res: any = await setSubFlowNew(params, currentAppData.parentAppId); + if (res.code === 200) { + Message.success('保存成功'); + // 更新事件枚举表 + const res1: any = await queryEventItemBySceneIdOld(info.id); + if (res1.code === 200) dispatch(updateEventListOld(res1.data)); + } + else { + Message.error(res.message); + } } + // 更新主流程 else { - Message.error(res.message); + const appEventDefinition = updateEvent(revertedData.nodeConfigs, initialData.appId); + params = { + ...flowData[currentAppData.id]?.main || {}, + components: newRevertedData, + appEventDefinition, + sceneId: info.id + }; + const res: any = await setMainFlowNew(params, initialData.appId); + if (res.code === 200) { + Message.success('保存成功'); + // 更新事件枚举表 + const res1: any = await queryEventItemBySceneIdOld(info.id); + if (res1.code === 200) dispatch(updateEventListOld(res1.data)); + } + else { + Message.error(res.message); + } } } catch (error) { console.error('Error saving flow data:', error); @@ -1122,13 +1195,56 @@ export const useFlowCallbacks = ( nodes, edges }; - console.log('params:', params); + const appFlowParams = { + appEventList: {} + }; + + nodes.forEach(node => { + appFlowParams.appEventList[node.id] = { + x: node.position.x, + y: node.position.y + }; + }); + + const eventMap = new Map(); + edges.forEach((edge: any) => { + // 应用组件的桩点id就是事件id + const sourceId = edge.sourceHandle; + const targetId = edge.targetHandle; + const topic = edge.data.displayData.topic; + + if (eventMap.has(topic)) { + // 如果topic已存在,将eventId添加到数组中 + eventMap.get(topic).eventId.push(sourceId); + eventMap.get(topic).eventId.push(targetId); + } + else { + // 如果topic不存在,创建新的条目 + eventMap.set(topic, { + eventId: [sourceId, targetId], + topic: topic + }); + } + }); + // 对eventId数组进行去重处理 + const appEventParams = Array.from(eventMap.values()).map(item => ({ + ...item, + eventId: Array.from(new Set(item.eventId)) + })); + + updateAppFlowData(appFlowParams); + if (appEventParams.length > 0) { + for (const item of appEventParams) { + await sleep(500); + updateAppEventChannel(item); + } + } } }, [nodes, edges, initialData?.appId]); // 运行处理函数 const handleRun = useCallback(async (running: boolean) => { + const { currentAppData, socketId } = store.getState().ideContainer; if (running) { - const { currentAppData, socketId } = store.getState().ideContainer; // 启动运行 const params = { appId: currentAppData.id, @@ -1156,6 +1272,8 @@ export const useFlowCallbacks = ( // 设置运行状态为false dispatch(updateIsRunning(false)); + stopApp(currentAppData.id); + // 停止运行 setEdges((eds) => eds.map(edge => ({ ...edge, diff --git a/src/pages/flowEditor/FlowEditorMain.tsx b/src/pages/flowEditor/FlowEditorMain.tsx index 8f178cf..2573032 100644 --- a/src/pages/flowEditor/FlowEditorMain.tsx +++ b/src/pages/flowEditor/FlowEditorMain.tsx @@ -178,12 +178,17 @@ const FlowEditorMain: React.FC = (props) => { onContextMenu={(e) => e.preventDefault()}> ({ ...node, draggable: !isRunning }))} // 运行时禁用节点拖拽 edges={edges} nodeTypes={nodeTypes} edgeTypes={edgeTypes} snapToGrid={true} snapGrid={[2, 2]} + nodesConnectable={!isRunning} // 运行时禁用节点连接 + nodesDraggable={!isRunning} // 运行时禁用节点拖拽 + elementsSelectable={!isRunning} // 运行时禁用元素选择 + connectOnClick={!isRunning} // 运行时禁用点击连接 + disableKeyboardA11y={isRunning} // 运行时禁用键盘交互 onBeforeDelete={async ({ nodes }) => { // 检查是否有开始或结束节点 const hasStartOrEndNode = nodes.some(node => node.type === 'start' || node.type === 'end'); @@ -198,11 +203,9 @@ const FlowEditorMain: React.FC = (props) => { ); // 允许删除操作继续进行 - return true; + return !isRunning; // 运行时禁止删除节点 }} onNodesDelete={(deleted) => { - console.log('deleted:', deleted); - // 检查是否有循环节点 const loopNodes = deleted.filter(node => node.data?.type === 'LOOP_START' || node.data?.type === 'LOOP_END' @@ -259,21 +262,21 @@ const FlowEditorMain: React.FC = (props) => { setIsEditModalOpen(false); }} - onNodesChange={onNodesChange} - onEdgesChange={onEdgesChange} - onConnect={onConnect} - onReconnect={onReconnect} - onDrop={onDrop} - onDragOver={onDragOver} - onNodeDrag={onNodeDrag} + 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} // 运行时禁用节点拖拽 connectionLineType={ConnectionLineType.SmoothStep} connectionLineComponent={CustomConnectionLine} - onNodeDragStop={onNodeDragStop} - onNodeContextMenu={onNodeContextMenu} - onEdgeContextMenu={onEdgeContextMenu} - onNodeClick={onNodeDoubleClick} - onPaneClick={onPaneClick} - onPaneContextMenu={onPaneContextMenu} + onNodeDragStop={!isRunning ? onNodeDragStop : undefined} // 运行时禁用节点拖拽停止 + onNodeContextMenu={!isRunning ? onNodeContextMenu : undefined} // 运行时禁用节点上下文菜单 + onNodeDoubleClick={!isRunning ? onNodeDoubleClick : undefined} // 运行时禁用节点双击 + onEdgeContextMenu={!isRunning ? onEdgeContextMenu : undefined} // 运行时禁用边上下文菜单 + onPaneClick={!isRunning ? onPaneClick : undefined} // 运行时禁用面板点击 + onPaneContextMenu={!isRunning ? onPaneContextMenu : undefined} // 运行时禁用面板上下文菜单 onEdgeMouseEnter={(_event, edge) => { setEdges((eds) => eds.map(e => { if (e.id === edge.id) { @@ -291,12 +294,8 @@ const FlowEditorMain: React.FC = (props) => { })); }} fitView - selectionKeyCode={['Meta', 'Control']} - selectionMode={SelectionMode.Partial} - panOnDrag={[0, 1, 2]} // 支持多点触控平移 - zoomOnScroll={true} - zoomOnPinch={true} - panOnScrollSpeed={0.5} + selectionOnDrag={!isRunning} // 运行时禁用拖拽选择 + selectionMode={!isRunning ? SelectionMode.Partial : undefined} // 运行时禁用选择模式 > @@ -315,7 +314,7 @@ const FlowEditorMain: React.FC = (props) => { {/*节点右键上下文*/} - {menu && menu.type === 'node' && ( + {!isRunning && menu && menu.type === 'node' && (
= (props) => { )} {/*边右键上下文*/} - {menu && menu.type === 'edge' && ( + {!isRunning && menu && menu.type === 'edge' && (
= (props) => { )} {/*画布右键上下文*/} - {menu && menu.type === 'pane' && ( + {!isRunning && menu && menu.type === 'pane' && (
= (props) => { {/*统一的添加节点菜单*/} - {(edgeForNodeAdd || positionForNodeAdd) && ( + {!isRunning && (edgeForNodeAdd || positionForNodeAdd) && (
= ({ 日志 - - - - + {/**/} + {/* }*/} + {/* onClick={onUndo}*/} + {/* disabled={!canUndo}*/} + {/* status="danger"*/} + {/* style={{ padding: '0 8px', backgroundColor: '#fff' }}*/} + {/* >*/} + {/* 撤销*/} + {/* */} + {/* }*/} + {/* onClick={onRedo}*/} + {/* disabled={!canRedo}*/} + {/* style={{ padding: '0 8px', backgroundColor: '#fff' }}*/} + {/* >*/} + {/* 重做*/} + {/* */} + {/**/} )}
diff --git a/src/pages/flowEditor/components/customConnectionLine.tsx b/src/pages/flowEditor/components/customConnectionLine.tsx index ba43697..cb9638b 100644 --- a/src/pages/flowEditor/components/customConnectionLine.tsx +++ b/src/pages/flowEditor/components/customConnectionLine.tsx @@ -1,10 +1,23 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import { getBezierPath } from '@xyflow/react'; const CustomConnectionLine = ({ fromNode, fromX, fromY, toX, toY }) => { - useEffect(() => { - console.log('fromNode', fromNode); - }, [fromNode]); + // 查找源节点的事件信息 + let eventData = null; + 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; + } + } + } + const [path] = getBezierPath({ sourceX: fromX, sourceY: fromY, @@ -22,10 +35,10 @@ const CustomConnectionLine = ({ fromNode, fromX, fromY, toX, toY }) => { className="animated-stroke" fill="none" /> - {/*TODO 临时定义,后续等接口字段。 存在事件信息的时候才展示连接提示*/} - {fromNode.data.eventData && ( + {/* 当存在事件信息的时候展示连接提示 */} + {eventData && eventData.topic && !eventData.topic.includes('**empty**') && ( - 正在连接... + {`连接事件: ${eventData.name}`} )} diff --git a/src/pages/flowEditor/components/customEdge.tsx b/src/pages/flowEditor/components/customEdge.tsx index 6975803..f4f8154 100644 --- a/src/pages/flowEditor/components/customEdge.tsx +++ b/src/pages/flowEditor/components/customEdge.tsx @@ -84,6 +84,27 @@ const DataDisplayEdge: React.FC = ({ const handleSelect = (option) => { setSelectedValue(option.label); setIsOpen(false); + + // 查找所有具有相同事件ID和连接点的边并同步更新 + setEdges(eds => eds.map((edge: any) => { + // 检查边是否有displayData且与当前选择的事件匹配 + if (edge.data?.displayData && + edge.data.displayData.eventId === displayData.eventId && + edge.data.displayData.topic === displayData.topic) { + return { + ...edge, + data: { + ...edge.data, + displayData: { + ...edge.data.displayData, + name: option.label, + topic: option.value + } + } + }; + } + return edge; + })); }; return ( @@ -169,7 +190,7 @@ const DataDisplayEdge: React.FC = ({
)} - {hovered && Object.keys(displayData).length === 0 && ( + {hovered && Object.keys(displayData).length === 0 && Object.keys(displayData).length === 0 && ( handleEdgeAddNode(e)} /> diff --git a/src/pages/flowEditor/components/nodeContentApp.tsx b/src/pages/flowEditor/components/nodeContentApp.tsx index 8ee00fb..635b82c 100644 --- a/src/pages/flowEditor/components/nodeContentApp.tsx +++ b/src/pages/flowEditor/components/nodeContentApp.tsx @@ -43,82 +43,6 @@ const handleStyles = { } }; -// 渲染特殊节点(开始/结束节点)的句柄 -const renderSpecialNodeHandles = (isStartNode: boolean, isEndNode: boolean, dataIns: any[], dataOuts: any[], apiIns: any[], apiOuts: any[]) => { - const renderStartNodeHandles = () => { - if (!isStartNode) return null; - - return ( - <> - {apiOuts.map((_, index) => ( - - ))} - {dataOuts.length > 0 && dataOuts.map((_, index) => ( - - ))} - - ); - }; - - const renderEndNodeHandles = () => { - if (!isEndNode) return null; - - return ( - <> - {apiIns.map((_, index) => ( - - ))} - {dataIns.length > 0 && dataIns.map((_, index) => ( - - ))} - - ); - }; - - return ( - <> - {renderStartNodeHandles()} - {renderEndNodeHandles()} - - ); -}; - // 渲染普通节点的句柄 const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[], apiOuts: any[]) => { return ( @@ -128,7 +52,7 @@ const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[] key={`api-output-handle-${index}`} type="source" position={Position.Right} - id={apiOuts[index].name || apiOuts[index].id || `output-${index}`} + id={apiOuts[index].eventId || `output-${index}`} style={{ ...handleStyles.mainSource, top: `${35 + index * 20}px` @@ -140,7 +64,7 @@ const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[] key={`api-input-handle-${index}`} type="target" position={Position.Left} - id={apiIns[index].name || apiIns[index].id || `input-${index}`} + id={apiIns[index].eventId || `input-${index}`} style={{ ...handleStyles.mainTarget, top: `${35 + index * 20}px` @@ -240,7 +164,6 @@ const useEventGroups = (component: any) => { }; }); } - console.log('groups:', groups); return groups; } catch (e) { console.error('解析customDef时出错:', e); @@ -295,15 +218,11 @@ const NodeContent = ({ data }: { data: NodeContentData }) => { // 获取事件分组信息 const eventGroups = useEventGroups(data.component); - // 判断节点类型 - const isStartNode = data.type === 'start'; - const isEndNode = data.type === 'end'; - const isSpecialNode = isStartNode || isEndNode; - return ( <> {/*content栏-api部分*/}
+
{apiIns.length > 0 && (
@@ -312,7 +231,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => { const group = findApiGroupByTopic(input, eventGroups); return (
{ )} {apiOuts.length > 0 && ( -
+
{apiOuts.map((output, index) => { // 查找关联的事件分组 const group = findApiGroupByTopic(output, eventGroups); return (
{ {/*content栏-data部分*/}
- {dataIns.length > 0 && !isStartNode && ( + {dataIns.length > 0 && (
{dataIns.map((input, index) => { // 查找关联的事件分组 @@ -374,7 +293,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
)} - {dataOuts.length > 0 && !isEndNode && ( + {dataOuts.length > 0 && (
{dataOuts.map((output, index) => { // 查找关联的事件分组 @@ -407,9 +326,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => { {/*)}*/} {/* 根据节点类型渲染不同的句柄 */} - {isSpecialNode - ? renderSpecialNodeHandles(isStartNode, isEndNode, dataIns, dataOuts, apiIns, apiOuts) - : renderRegularNodeHandles(dataIns, dataOuts, apiIns, apiOuts)} + {renderRegularNodeHandles(dataIns, dataOuts, apiIns, apiOuts)} ); }; diff --git a/src/pages/flowEditor/components/nodeContentOther.tsx b/src/pages/flowEditor/components/nodeContentOther.tsx index c5b086a..456c1ff 100644 --- a/src/pages/flowEditor/components/nodeContentOther.tsx +++ b/src/pages/flowEditor/components/nodeContentOther.tsx @@ -3,6 +3,7 @@ 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'; +import { useSelector } from 'react-redux'; interface NodeContentData { parameters?: { @@ -178,7 +179,7 @@ const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[] ); }; -const formatFooter = (data: any) => { +const formatFooter = (data: any, eventListOld = []) => { try { switch (data?.type) { case 'WAIT': @@ -192,9 +193,10 @@ const formatFooter = (data: any) => { return cronstrue.toString(intervalSeconds, { locale: 'zh_CN' }); case 'EVENTSEND': case 'EVENTLISTENE': - const { name, topic } = isJSON(data.customDef) ? JSON.parse(data.customDef) : data.customDef; + const { eventId, topic, name } = isJSON(data.customDef) ? JSON.parse(data.customDef) : data.customDef; if (topic.includes('**empty**')) return ''; - return `事件: ${name}`; + const currentEvent = eventListOld.length > 0 ? eventListOld.find(item => item.eventId === eventId) : { name: '无' }; + return `事件: ${name || currentEvent.name}`; case 'BASIC': return data.compIdentifier ? `当前实例:${data.compIdentifier}` : ''; default: @@ -206,6 +208,7 @@ const formatFooter = (data: any) => { }; const NodeContent = ({ data }: { data: NodeContentData }) => { + const { eventListOld } = useSelector((state) => state.ideContainer); const apiIns = data.parameters?.apiIns || []; const apiOuts = data.parameters?.apiOuts || []; const dataIns = data.parameters?.dataIns || []; @@ -284,7 +287,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => { {/*footer栏*/} {showFooter && (
- {formatFooter(footerData)} + {formatFooter(footerData, eventListOld)}
)} diff --git a/src/pages/flowEditor/components/nodeContextMenu.tsx b/src/pages/flowEditor/components/nodeContextMenu.tsx index a739018..3849e18 100644 --- a/src/pages/flowEditor/components/nodeContextMenu.tsx +++ b/src/pages/flowEditor/components/nodeContextMenu.tsx @@ -33,8 +33,18 @@ const NodeContextMenu: React.FC = ({ }; const handleEdit = () => { - onEdit && onEdit(node); - onCloseOpenModal(true); + // 判断节点类型,如果是SUB类型则打开新标签页 + if (node.type === 'SUB') { + // 创建自定义事件来通知打开新标签页 + const openTabEvent = new CustomEvent('openSubNodeTab', { + detail: { node } + }); + document.dispatchEvent(openTabEvent); + } + else { + onEdit && onEdit(node); + onCloseOpenModal(true); + } onCloseMenu(null); }; diff --git a/src/pages/flowEditor/utils/appFlowhandle.ts b/src/pages/flowEditor/utils/appFlowhandle.ts index 5dddb09..030d7ea 100644 --- a/src/pages/flowEditor/utils/appFlowhandle.ts +++ b/src/pages/flowEditor/utils/appFlowhandle.ts @@ -12,8 +12,6 @@ export const appFLowHandle = (initialData, useDefault, setNodes, setEdges, dispa nodes: convertedNodes, edges: convertedEdges } = convertAppFlowData(initialData); - console.log('nodes:', convertedNodes); - console.log('edges:', convertedEdges); // 为所有边添加类型 diff --git a/src/pages/flowEditor/utils/projectFlowHandle.ts b/src/pages/flowEditor/utils/projectFlowHandle.ts index 6f86781..0da732d 100644 --- a/src/pages/flowEditor/utils/projectFlowHandle.ts +++ b/src/pages/flowEditor/utils/projectFlowHandle.ts @@ -10,7 +10,7 @@ export const projectFlowHandle = (initialData, useDefault, setNodes, setEdges, d const { nodes: convertedNodes, edges: convertedEdges - } = convertFlowData(initialData?.main?.components || initialData?.compData?.components, useDefault); + } = convertFlowData(initialData?.main?.components || initialData?.components, useDefault); // 为所有边添加类型 const initialEdges: Edge[] = convertedEdges.map(edge => ({ diff --git a/src/pages/ideContainer/index.tsx b/src/pages/ideContainer/index.tsx index 4395fdb..ef0fff0 100644 --- a/src/pages/ideContainer/index.tsx +++ b/src/pages/ideContainer/index.tsx @@ -1,5 +1,9 @@ -import React, { useEffect, useState, useRef } from 'react'; -import { getUrlParams } from '@/utils/common'; +import React, { useState, useEffect, useRef } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { updateSocketId, updateNodeStatus, updateEventListOld } from '@/store/ideContainer'; +import useWebSocket from '@/hooks/useWebSocket'; +import { isJSON, getUrlParams } from '@/utils/common'; + import styles from './style/index.module.less'; import SideBar from './sideBar'; import LogBar from './logBar'; @@ -10,10 +14,8 @@ import { updateCurrentAppData, updateInfo, updateProjectComponentData, - updateSocketId, updateEventList } from '@/store/ideContainer'; -import { useDispatch, useSelector } from 'react-redux'; import { getAppListBySceneId } from '@/api/apps'; import { getProjectComp } from '@/api/scene'; @@ -28,10 +30,8 @@ import ComponentCoding from '@/pages/componentDevelopment/componentCoding'; import ComponentDeployment from '@/pages/componentDevelopment/componentDeployment'; import ComponentTest from '@/pages/componentDevelopment/componentTest'; import { getUserToken } from '@/api/user'; -import useWebSocket from '@/hooks/useWebSocket'; import { Message } from '@arco-design/web-react'; -import { queryEventItemBySceneId } from '@/api/event'; -import { updateNodeStatus } from '@/store/ideContainer'; +import { queryEventItemBySceneId, queryEventItemBySceneIdOld } from '@/api/event'; type UrlParamsOptions = { identity?: string; @@ -59,7 +59,7 @@ function IDEContainer() { // 用于跟踪已打开的tab,保持组件状态 const [openedTabs, setOpenedTabs] = useState>(new Set()); const [subMenuData, setSubMenuData] = useState({}); - const { menuData } = useSelector((state) => state.ideContainer); + const { menuData, flowData, currentAppData } = useSelector((state) => state.ideContainer); const dispatch = useDispatch(); const navBarRef = useRef(null); @@ -75,14 +75,13 @@ function IDEContainer() { console.error('WebSocket错误:', event); }, onMessage: (event) => { - console.log('收到WebSocket消息:', event.data); - // 这里可以处理从后端收到的消息,例如日志更新等 + // console.log('收到WebSocket消息:', event.data); const socketMessage = JSON.parse(event.data); if (socketMessage?.socketId) dispatch(updateSocketId(socketMessage.socketId)); // 处理节点状态更新 if (socketMessage?.nodeLog) { - const { nodeId, state } = socketMessage.nodeLog; + const { nodeId, state, runLog } = socketMessage.nodeLog; // 将状态映射为前端使用的状态 let status = 'waiting'; switch (state) { @@ -99,12 +98,96 @@ function IDEContainer() { status = 'waiting'; break; } - // 更新节点状态 - dispatch(updateNodeStatus({ nodeId, status })); + // 更新节点状态,使用特殊的actionType标记这是运行时状态更新 + dispatch(updateNodeStatus({ nodeId, status, actionType: 'RUNTIME_UPDATE' })); + + // 只有当存在runLog时才发送日志到logBar + if (runLog) { + const logEvent = new CustomEvent('logMessage', { + detail: { + type: 'runtime', + message: runLog, + timestamp: new Date().toISOString() + } + }); + document.dispatchEvent(logEvent); + } } } }); + // 监听自定义事件,处理打开子节点标签页的逻辑 + useEffect(() => { + const handleOpenSubNodeTab = (event: CustomEvent) => { + const { node } = event.detail; + const subCompList = flowData[currentAppData.id].subs; + const customDef = isJSON(node.data.component.customDef) ? JSON.parse(node.data.component.customDef) : {}; + const currentSubComp = subCompList.find((item) => item.flowId === customDef.subflowId); + // 根据节点信息创建新的标签页 + if (currentSubComp && Object.keys(currentSubComp).length > 0) { + const newNodeKey = currentSubComp.flowId; + + // 查找菜单项 + const findMenuItem = (menuItems: any[]): any => { + for (const item of menuItems) { + if (item.key === newNodeKey) { + return item; + } + if (item.children) { + const found = findMenuItem(item.children); + if (found) return found; + } + } + return null; + }; + + const menuItems = menuData[urlParams.identity]; + const menuItem = findMenuItem(menuItems); + + if (menuItem) { + // 更新当前应用数据 + dispatch(updateCurrentAppData({ ...menuItem })); + + // 设置选中项,触发tab创建 + setSelected({ + ...menuItem, + key: menuItem.key, + parentKey: menuItem.parentKey + }); + } + else { + // 如果在菜单中找不到对应项,创建一个临时的菜单项 + const tempMenuItem = { + key: newNodeKey, + title: currentSubComp.flowName, + pathTitle: `${currentAppData.name} / ${currentSubComp.flowName}`, + path: 'complexFlow', + parentKey: 'appList', + parentAppId: currentAppData.id, + compData: currentSubComp, + children: null + }; + + dispatch(updateCurrentAppData({ ...tempMenuItem })); + + setSelected({ + ...tempMenuItem, + key: tempMenuItem.key, + parentKey: tempMenuItem.parentKey + }); + } + } + }; + + // 添加事件监听器 + document.addEventListener('openSubNodeTab', handleOpenSubNodeTab as EventListener); + + // 清理事件监听器 + return () => { + document.removeEventListener('openSubNodeTab', handleOpenSubNodeTab as EventListener); + }; + }, [menuData, flowData, currentAppData]); + const connectWS = async () => { const res = await getUserToken(); const token = res.data; @@ -137,7 +220,9 @@ function IDEContainer() { const getEvent = async () => { const res: any = await queryEventItemBySceneId(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)); }; useEffect(() => { @@ -288,6 +373,9 @@ function IDEContainer() { key: menuItem.key, parentKey: menuItem.parentKey }); + + // 将选中的tab添加到已打开的tabs集合中 + setOpenedTabs(prev => new Set(prev).add(menuItem.key)); } } else { diff --git a/src/pages/ideContainer/logBar.tsx b/src/pages/ideContainer/logBar.tsx index d089bf9..e345749 100644 --- a/src/pages/ideContainer/logBar.tsx +++ b/src/pages/ideContainer/logBar.tsx @@ -30,8 +30,8 @@ const data = [ }, { key: '3', - title: '流程运行日志', - content: '流程运行时日志...' + title: '运行数据', + content: '运行数据日志...' }, { key: '4', @@ -47,6 +47,8 @@ const LogBar: React.FC = () => { const { logBarStatus } = useSelector((state) => state.ideContainer); const dispatch = useDispatch(); const [validationLogs, setValidationLogs] = useState([]); + const [runtimeLogs, setRuntimeLogs] = useState([]); // 添加运行时日志状态 + const [logContainerHeight, setLogContainerHeight] = useState('250px'); // 添加日志容器高度状态 // 处理 Tab 点击事件 const handleTabClick = (key: string) => { @@ -64,9 +66,9 @@ const LogBar: React.FC = () => { // 当 collapsed 状态改变时,直接更新元素的样式 useEffect(() => { if (resizeBoxRef.current) { - resizeBoxRef.current.style.height = logBarStatus ? '250px' : '0px'; + resizeBoxRef.current.style.height = logBarStatus ? logContainerHeight : '0px'; } - }, [logBarStatus]); + }, [logBarStatus, logContainerHeight]); // 处理 ResizeBox 手动调整大小事件 const handleResize = (e: MouseEvent, size: { @@ -79,6 +81,8 @@ const LogBar: React.FC = () => { } else { dispatch(updateLogBarStatus(true)); + // 更新日志容器高度状态 + setLogContainerHeight(`${size.height}px`); } }; @@ -86,7 +90,7 @@ const LogBar: React.FC = () => { useEffect(() => { const handleLogMessage = (event: CustomEvent) => { const { type, message, timestamp } = event.detail; - + // 如果是校验类型的消息且当前校验日志tab可见,则添加到校验日志中 if (type === 'validation') { const newLog: LogMessage = { @@ -95,18 +99,33 @@ const LogBar: React.FC = () => { message, timestamp }; - + setValidationLogs(prev => [...prev, newLog]); - + // 自动切换到校验日志tab并展开logBar setActiveTab('2'); dispatch(updateLogBarStatus(true)); } + // 如果是运行时日志 + else if (type === 'runtime') { + const newLog: LogMessage = { + id: Date.now(), + type, + message, + timestamp + }; + + setRuntimeLogs(prev => [...prev, newLog]); + + // 自动切换到运行日志tab并展开logBar + setActiveTab('1'); + dispatch(updateLogBarStatus(true)); + } }; // 添加事件监听器 document.addEventListener('logMessage', handleLogMessage as EventListener); - + // 清理事件监听器 return () => { document.removeEventListener('logMessage', handleLogMessage as EventListener); @@ -116,7 +135,7 @@ const LogBar: React.FC = () => { // 渲染校验日志内容 const renderValidationLogs = () => { return ( -
+
{validationLogs.length === 0 ? (

暂无校验日志

) : ( @@ -135,6 +154,28 @@ const LogBar: React.FC = () => { ); }; + // 渲染运行时日志内容 + const renderRuntimeLogs = () => { + return ( +
+ {runtimeLogs.length === 0 ? ( +

暂无运行时日志

+ ) : ( + runtimeLogs.map(log => ( +
+
+ {new Date(log.timestamp).toLocaleString()} +
+
+ {log.message} +
+
+ )) + )} +
+ ); + }; + return ( <> = () => { className={styles.logBar} directions={['top']} style={{ - height: logBarStatus ? '250px' : '0px' + height: logBarStatus ? logContainerHeight : '0px' }} onMoving={handleResize} > @@ -150,10 +191,13 @@ const LogBar: React.FC = () => { type="card-gutter" activeTab={activeTab} onClickTab={handleTabClick} + justify={true} > {tabs.map((x) => ( - {x.key === '2' ? renderValidationLogs() : x.content} + {x.key === '1' ? renderRuntimeLogs() : + x.key === '2' ? renderValidationLogs() : + x.content} ))} diff --git a/src/pages/ideContainer/navBar.tsx b/src/pages/ideContainer/navBar.tsx index 0461c2e..2f8c7ea 100644 --- a/src/pages/ideContainer/navBar.tsx +++ b/src/pages/ideContainer/navBar.tsx @@ -8,8 +8,8 @@ const TabPane = Tabs.TabPane; interface NavBarProps { selected?: Selected; menuData?: any[]; - onTabChange?: (path: string) => void; - onTabClose?: (path: string) => void; + onTabChange?: (key: string) => void; + onTabClose?: (key: string) => void; } // 添加ref类型定义 @@ -55,7 +55,7 @@ const NavBar: React.ForwardRefRenderFunction = ({ const title = selected.title; const pathTitle = selected?.pathTitle || null; // 检查tab是否已存在 - const existingTab = tabs.find(tab => tab.key === (key) && tab.title === title); + const existingTab = tabs.find(tab => tab.key === key); if (!existingTab) { // 创建新tab @@ -76,20 +76,31 @@ const NavBar: React.ForwardRefRenderFunction = ({ setActiveTab(key); } } + else if (!selected?.path && tabs.length > 0) { + // 如果没有选中的路径但有tabs存在,保持当前激活的tab + // 这样可以避免在切换tab时意外清除activeTab + } }, [selected, menuData]); const handleDeleteTab = (key: string) => { const newTabs = tabs.filter(tab => tab.key !== key); setTabs(newTabs); - // 如果删除的是当前激活的tab,激活第一个tab(如果存在) - if (key === activeTab && newTabs.length > 0) { - setActiveTab(newTabs[0].key); - onTabChange?.(newTabs[0].key); - } - else if (key === activeTab && newTabs.length === 0) { - setActiveTab(''); - onTabChange?.(''); + // 如果删除的是当前激活的tab,激活下一个tab(如果存在) + if (key === activeTab) { + if (newTabs.length > 0) { + // 尝试激活相邻的tab + const currentIndex = tabs.findIndex(tab => tab.key === key); + const nextIndex = currentIndex > 0 ? currentIndex - 1 : 0; + const nextTab = newTabs[nextIndex]; + + setActiveTab(nextTab.key); + onTabChange?.(nextTab.key); + } else { + // 如果没有更多tabs,重置状态 + setActiveTab(''); + onTabChange?.(''); + } } // 通知父组件tab已关闭 @@ -143,4 +154,4 @@ const NavBar: React.ForwardRefRenderFunction = ({ ); }; -export default React.forwardRef(NavBar); +export default React.forwardRef(NavBar); \ No newline at end of file diff --git a/src/pages/ideContainer/sideBar.tsx b/src/pages/ideContainer/sideBar.tsx index 1fdb3fb..6792e45 100644 --- a/src/pages/ideContainer/sideBar.tsx +++ b/src/pages/ideContainer/sideBar.tsx @@ -17,10 +17,12 @@ 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 { updateMenuData, updateFlowData, updateCurrentAppData } from '@/store/ideContainer'; +import { updateMenuData, updateFlowData, updateCurrentAppData, updateEventListOld } from '@/store/ideContainer'; import { addApp, getProjectEnv, editApp, deleteApp } from '@/api/apps'; import _ from 'lodash'; import { getAppInfoNew } from '@/api/appRes'; +import { getAppEventData } from '@/api/appEvent'; +import { queryEventItemBySceneIdOld } from '@/api/event'; const TreeNode = Tree.Node; const FormItem = Form.Item; @@ -190,7 +192,7 @@ const SideBar: React.FC = ({ }); // 添加右键菜单状态 const resizeBoxRef = useRef(null); // 引用第一个 ResizeBox 容器 const contextMenuRef = useRef(null); // 右键菜单引用 - const { menuData } = useSelector(state => state.ideContainer); + const { menuData, info } = useSelector(state => state.ideContainer); const dispatch = useDispatch(); function getMenuData(): MenuItemType[] { @@ -241,6 +243,20 @@ const SideBar: React.FC = ({ } } + // 监听 subMenuData 和 identity 变化,更新菜单数据 + useEffect(() => { + if (identity === 'scene') { + if (Object.keys(subMenuData).length > 0) { + const newMenu = getMenuData(); + setMenu(newMenu); + } + } + else if (identity === 'componentDevelopment') { + const newMenu = getMenuData(); + setMenu(newMenu); + } + }, [subMenuData, identity]); + // 处理子菜单区域的拖拽调整大小 const handleSubMenuResize = (e: MouseEvent, { width }: { width: number }) => { resizeBoxRef.current.style.width = `${width}px`; @@ -275,6 +291,42 @@ const SideBar: React.FC = ({ onMenuSelect?.({ ...item }); }; + // 监听 selected 状态变化,更新 activeKey + useEffect(() => { + if (menu.length > 0 && selected?.parentKey) { + // 查找匹配的菜单项索引 + const index = menu.findIndex(item => item.key === selected.parentKey); + if (index !== -1) { + setActiveKey(index); + } + } + }, [selected, menu]); + + // 监听 selected 状态变化,确保子菜单正确显示 + useEffect(() => { + if (selected?.key && menuData[identity]) { + // 确保子菜单区域根据selected状态正确展开或收起 + if (['appList', 'appFlow'].includes(selected.parentKey)) { + // 如果应该显示子菜单但当前是收起状态,则展开 + if (isSubMenuCollapsed) { + setIsSubMenuCollapsed(false); + resizeBoxRef.current.style.width = `300px`; + } + } + + // 强制更新filteredMenu以确保显示正确的子菜单 + setSearchValue(''); // 清空搜索值以显示所有项 + } + }, [selected, menuData, identity]); + + // 当菜单数据变化时更新本地状态 + useEffect(() => { + if (identity && menuData[identity]) { + setMenu(menuData[identity]); + } + }, [menuData, identity]); + + // 根据搜索值过滤菜单数据 const filteredMenu = useMemo(() => { if (!searchValue || activeKey === undefined || !menu[activeKey]?.children) { @@ -315,6 +367,7 @@ const SideBar: React.FC = ({ const currentMenu = _.cloneDeep(menuData[identity]); const index = currentMenu.findIndex(v => v.key === parentKey); const res: any = await getAppInfoNew(data.id); + getAppEventData(data.id); if (res.code === 200) { const children = currentMenu[index].children.find(v => v.id === data.id); children.children[0].children = res.data.events.map(item => { @@ -343,7 +396,8 @@ const SideBar: React.FC = ({ path: 'complexFlow', key: info.flowId, pathTitle: `${data.title} / ${info.flowName}`, - parentKey: 'appList' + parentKey: 'appList', + parentAppId: data.id }; }) }; @@ -376,6 +430,9 @@ const SideBar: React.FC = ({ newMenu[activeKey] = { ...newMenu[activeKey], children: currentMenu[index].children }; return newMenu; }); + // 获取最新的事件枚举表 + const res1: any = await queryEventItemBySceneIdOld(info.id); + if (res1.code === 200) dispatch(updateEventListOld(res1.data)); } }; diff --git a/src/pages/orchestration/application/index.tsx b/src/pages/orchestration/application/index.tsx index d169cb2..c9c1f2e 100644 --- a/src/pages/orchestration/application/index.tsx +++ b/src/pages/orchestration/application/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import FlowEditor from '@/pages/flowEditor/index'; import { useSelector, useDispatch } from 'react-redux'; import { getAppEventList } from '@/api/appEvent'; -import { getTopicList } from '@/api/event'; +import { getTopicList, queryEventItemBySceneId } from '@/api/event'; import { updateEventTopicList } from '@/store/ideContainer'; const ApplicationContainer = () => { @@ -16,12 +16,14 @@ const ApplicationContainer = () => { }; const getEventList = async () => { - const res: any = await getTopicList(info.id); + const res: any = await queryEventItemBySceneId(info.id); if (res.code === 200) { - dispatch(updateEventTopicList(res.data.map(v => { - return { label: v.eventName, value: v.topic }; - }))); + dispatch(updateEventTopicList(res.data + .filter(v => !v.topic.includes('**empty**')) // 过滤掉topic包含**empty**的项目 + .map(v => { + return { label: v.name, value: v.topic }; + }))); } }; diff --git a/src/pages/orchestration/event/index.tsx b/src/pages/orchestration/event/index.tsx index 879e468..dd0d51a 100644 --- a/src/pages/orchestration/event/index.tsx +++ b/src/pages/orchestration/event/index.tsx @@ -106,7 +106,7 @@ const HandleModal = ({ visible, onChangeVisible, onRefresh }) => { { return eventData.filter(item => { const name = item.name || ''; const topic = item.topic || ''; - const desc = item.description || ''; + const description = item.description || ''; const searchLower = searchValue.toLowerCase(); return ( name.toLowerCase().includes(searchLower) || topic.toLowerCase().includes(searchLower) || - desc.toLowerCase().includes(searchLower) + description.toLowerCase().includes(searchLower) ); }); }, [eventData, searchValue]); @@ -217,7 +217,7 @@ const EventContainer = () => { title="删除事件" content="事件删除后无法恢复,请谨慎删除!" onOk={async () => { - const res: any = await deleteEventItem(record.id); + const res: any = await deleteEventItem(record.eventId); if (res && res.code === 200) { Message.success('删除成功'); fetchEventData(); diff --git a/src/store/ideContainer.ts b/src/store/ideContainer.ts index b49f5a2..bfd9c99 100644 --- a/src/store/ideContainer.ts +++ b/src/store/ideContainer.ts @@ -8,6 +8,7 @@ interface IDEContainerState { canvasDataMap: any; projectComponentData: any; currentAppData: any; + eventListOld: any; eventList: any; eventTopicList: any; logBarStatus?: boolean; @@ -24,6 +25,7 @@ const initialState: IDEContainerState = { canvasDataMap: {}, // 每个画布的缓存信息 projectComponentData: {}, // 工程下的组件列表 currentAppData: {}, // 当前选中的应用数据 + eventListOld: [], // 工程下的事件列表 eventList: [], // 工程下的事件列表 eventTopicList: [], // 应用编排使用的事件名和topic列表 logBarStatus: false, @@ -58,6 +60,9 @@ const ideContainerSlice = createSlice({ updateEventList(state, action) { state.eventList = action.payload; }, + updateEventListOld(state, action) { + state.eventListOld = action.payload; + }, updateEventTopicList(state, action) { state.eventTopicList = action.payload; }, @@ -69,8 +74,14 @@ const ideContainerSlice = createSlice({ }, // 更新节点状态 updateNodeStatus: (state, { payload }) => { - const { nodeId, status } = payload; - state.nodeStatusMap[nodeId] = status; + const { nodeId, status, actionType } = payload; + // 如果是运行时更新,不记录到历史记录中 + if (actionType !== 'RUNTIME_UPDATE') { + state.nodeStatusMap[nodeId] = status; + } else { + // 对于运行时更新,我们仍然更新状态但标记为运行时状态 + state.nodeStatusMap[nodeId] = status; + } }, // 重置节点状态 resetNodeStatus: (state) => { @@ -92,6 +103,7 @@ export const { updateProjectComponentData, updateCurrentAppData, updateEventList, + updateEventListOld, updateEventTopicList, updateLogBarStatus, updateSocketId, diff --git a/src/utils/common.ts b/src/utils/common.ts index 9f279f1..1a804e6 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -229,4 +229,8 @@ export const deserializeValue = (value: any): any => { console.warn('Failed to deserialize value:', value, error); return value; } -}; \ No newline at end of file +}; + +export const sleep = (ms: number): Promise => { + return new Promise(resolve => setTimeout(resolve, ms)); +}; diff --git a/src/utils/convertAppFlowData.ts b/src/utils/convertAppFlowData.ts index 9eac62c..7fc6e0d 100644 --- a/src/utils/convertAppFlowData.ts +++ b/src/utils/convertAppFlowData.ts @@ -14,11 +14,20 @@ export const convertAppFlowData = (appFlowData: any[]) => { // 处理每个应用流程数据项(每个应用作为一个节点) appFlowData.forEach((app: any, index: number) => { + // 添加过滤逻辑:如果 eventListenes 和 eventSends 都为空,则不生成节点 + const hasEventListenes = app.eventListenes && app.eventListenes.length > 0; + const hasEventSends = app.eventSends && app.eventSends.length > 0; + + // 如果两者都为空,则跳过当前应用节点的创建 + if (!hasEventListenes && !hasEventSends) { + return; + } + // 构造节点数据 const node: any = { id: app.appId || `app_${index}`, type: 'APP', - position: { x: 200 + index * 300, y: 200 }, + position: app.position || { x: 200 + index * 300, y: 200 }, data: { title: app.name || `应用${index + 1}`, parameters: { @@ -28,7 +37,8 @@ export const convertAppFlowData = (appFlowData: any[]) => { desc: event.description || '', dataType: '', defaultValue: '', - topic: event.topic + topic: event.topic, + eventId: event.eventId })) : [], // eventSends 作为 apiOuts(输出) apiOuts: app.eventSends ? app.eventSends.map((event: any) => ({ @@ -36,7 +46,8 @@ export const convertAppFlowData = (appFlowData: any[]) => { desc: event.description || '', dataType: '', defaultValue: '', - topic: event.topic + topic: event.topic, + eventId: event.eventId })) : [], // 提取 dataIns 和 dataOuts 属性 dataIns: [], @@ -103,15 +114,14 @@ export const convertAppFlowData = (appFlowData: any[]) => { id: `e-${sourceNode.id}-${targetNode.id}-${outIndex}-${inIndex}`, source: sourceNode.id, target: targetNode.id, - sourceHandle: sourceEvent.eventName, - targetHandle: targetEvent.eventName, + sourceHandle: sourceEvent.eventId, + targetHandle: targetEvent.eventId, type: 'custom', - lineType: 'lineType', data: { displayData: { - name: '事件1', - eventId: 'eventId', - topic: 'topic' + name: sourceEvent.eventName, + eventId: sourceEvent.eventId, + topic: sourceEvent.topic } } });