Merge branch 'refs/heads/master' into production

master
钟良源 3 months ago
commit 09be740906

@ -18,3 +18,13 @@ export function getAppEventList(id: any) {
export function updateAppEvent(id: any, data: any) {
return axios.post<apiResData>(`${urlPrefix}/appEvent/${id}/update`, data);
}
// 更新应用编排数据
export function updateAppFlowData(data: any) {
return axios.post<apiResData>(`${urlPrefix}/appEvent/update`, data);
}
// 修改应用编排中的事件通道
export function updateAppEventChannel(data: any) {
return axios.post<apiResData>(`${urlPrefix}/appEvent/updateTopic`, data);
}

@ -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);

@ -86,8 +86,8 @@ export function resumeApp(data: any) {
}
// 停止
export function stopApp(data: any) {
return axios.post<apiResData>(`${runPrefix}/apps/${data.id}/stop`);
export function stopApp(appid: any) {
return axios.post<apiResData>(`${runPrefix}/apps/${appid}/stop`);
}
// APi发布

@ -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) {

@ -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;

@ -24,7 +24,6 @@ const typeMap = {
const EventSelect: React.FC<EventSelectProps> = ({ nodeData, eventList, type, onRefresh, onUpdateData }) => {
const [options, setOptions] = useState<any[]>([]);
const [specialOptions, setSpecialOptions] = useState<any>({});
const [form] = Form.useForm();
const [showModal, setShowModal] = useState(false);
const [currentEvent, setCurrentEvent] = useState<any>(null);
@ -32,7 +31,6 @@ const EventSelect: React.FC<EventSelectProps> = ({ 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<EventSelectProps> = ({ 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<EventSelectProps> = ({ 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<EventSelectProps> = ({ 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<EventSelectProps> = ({ nodeData, eventList, type, on
dropdownMenuStyle={{ maxHeight: 300 }}
>
{options.map((option) => (
<Option key={option.eventId} value={option}>
<Option key={option.topicId} value={option}>
{option.name}
</Option>
))}

@ -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,25 +1143,48 @@ 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 = {
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 {
const appEventDefinition = updateEvent(revertedData.nodeConfigs, initialData.appId);
params = {
...flowData[currentAppData.id]?.main || {},
components: newRevertedData
components: newRevertedData,
appEventDefinition,
sceneId: info.id
};
// 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 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);
Message.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) => {
if (running) {
const { currentAppData, socketId } = store.getState().ideContainer;
if (running) {
// 启动运行
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,

@ -178,12 +178,17 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
onContextMenu={(e) => e.preventDefault()}>
<ReactFlow
id={reactFlowId}
nodes={nodes}
nodes={nodes.map(node => ({ ...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<FlowEditorMainProps> = (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<FlowEditorMainProps> = (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<FlowEditorMainProps> = (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} // 运行时禁用选择模式
>
<Background />
<Panel position="top-left">
@ -315,7 +314,7 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
</ReactFlow>
{/*节点右键上下文*/}
{menu && menu.type === 'node' && (
{!isRunning && menu && menu.type === 'node' && (
<div
style={{
position: 'absolute',
@ -336,7 +335,7 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
)}
{/*边右键上下文*/}
{menu && menu.type === 'edge' && (
{!isRunning && menu && menu.type === 'edge' && (
<div
style={{
position: 'absolute',
@ -359,7 +358,7 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
)}
{/*画布右键上下文*/}
{menu && menu.type === 'pane' && (
{!isRunning && menu && menu.type === 'pane' && (
<div
style={{
position: 'absolute',
@ -383,14 +382,14 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
<NodeEditModal
popupContainer={reactFlowWrapper}
node={editingNode}
isOpen={isEditModalOpen}
isOpen={isEditModalOpen && !isRunning}
isDelete={isDelete}
onSave={saveNodeEdit}
onClose={closeEditModal}
/>
{/*统一的添加节点菜单*/}
{(edgeForNodeAdd || positionForNodeAdd) && (
{!isRunning && (edgeForNodeAdd || positionForNodeAdd) && (
<div
style={{
position: 'absolute',

@ -64,29 +64,29 @@ const ActionBar: React.FC<ActionBarProps> = ({
</Button>
</ButtonGroup>
<ButtonGroup style={{ marginLeft: 15 }}>
<Button
type="outline"
shape="round"
icon={<IconUndo />}
onClick={onUndo}
disabled={!canUndo}
status="danger"
style={{ padding: '0 8px', backgroundColor: '#fff' }}
>
</Button>
<Button
type="outline"
shape="round"
icon={<IconRedo />}
onClick={onRedo}
disabled={!canRedo}
style={{ padding: '0 8px', backgroundColor: '#fff' }}
>
</Button>
</ButtonGroup>
{/*<ButtonGroup style={{ marginLeft: 15 }}>*/}
{/* <Button*/}
{/* type="outline"*/}
{/* shape="round"*/}
{/* icon={<IconUndo />}*/}
{/* onClick={onUndo}*/}
{/* disabled={!canUndo}*/}
{/* status="danger"*/}
{/* style={{ padding: '0 8px', backgroundColor: '#fff' }}*/}
{/* >*/}
{/* 撤销*/}
{/* </Button>*/}
{/* <Button*/}
{/* type="outline"*/}
{/* shape="round"*/}
{/* icon={<IconRedo />}*/}
{/* onClick={onRedo}*/}
{/* disabled={!canRedo}*/}
{/* style={{ padding: '0 8px', backgroundColor: '#fff' }}*/}
{/* >*/}
{/* 重做*/}
{/* </Button>*/}
{/*</ButtonGroup>*/}
</>
)}
</div>

@ -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**') && (
<text x={(fromX + toX) / 2} y={(fromY + toY) / 2 - 10} fill="#000" fontSize={12}>
...
{`连接事件: ${eventData.name}`}
</text>
)}
</g>

@ -84,6 +84,27 @@ const DataDisplayEdge: React.FC<EdgeProps> = ({
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<EdgeProps> = ({
</div>
)}
{hovered && Object.keys(displayData).length === 0 && (
{hovered && Object.keys(displayData).length === 0 && Object.keys(displayData).length === 0 && (
<EdgeAddNodeButton
onClick={(e) => handleEdgeAddNode(e)}
/>

@ -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) => (
<Handle
key={`start-output-handle-${index}`}
type="source"
position={Position.Right}
id={apiOuts[index].name || `start-output-${index}`}
style={{
...handleStyles.mainSource,
top: `${35 + index * 20}px`
}}
/>
))}
{dataOuts.length > 0 && dataOuts.map((_, index) => (
<Handle
key={`output-handle-${index}`}
type="source"
position={Position.Right}
id={dataOuts[index].name || `output-${index}`}
style={{
...handleStyles.data,
top: `${70 + apiOuts.length * 20 + index * 20}px`
}}
/>
))}
</>
);
};
const renderEndNodeHandles = () => {
if (!isEndNode) return null;
return (
<>
{apiIns.map((_, index) => (
<Handle
key={`end-input-handle-${index}`}
type="target"
position={Position.Left}
id={apiIns[index].name || `end-input-${index}`}
style={{
...handleStyles.mainTarget,
top: `${35 + index * 20}px`
}}
/>
))}
{dataIns.length > 0 && dataIns.map((_, index) => (
<Handle
key={`input-handle-${index}`}
type="target"
position={Position.Left}
id={dataIns[index].name || `input-${index}`}
style={{
...handleStyles.data,
top: `${70 + apiIns.length * 20 + index * 20}px`
}}
/>
))}
</>
);
};
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部分*/}
<div className={styles['node-api-box']}>
<div className={styles['node-content-api']}>
{apiIns.length > 0 && (
<div className={styles['node-inputs']}>
@ -312,7 +231,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
const group = findApiGroupByTopic(input, eventGroups);
return (
<div
key={input.id || `input-${index}`}
key={input.eventId || `input-${index}`}
className={styles['node-input-label']}
style={{
color: group ? group.color : '#000'
@ -326,13 +245,13 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
)}
{apiOuts.length > 0 && (
<div className={styles['node-outputs-api']}>
<div className={styles['node-outputs']}>
{apiOuts.map((output, index) => {
// 查找关联的事件分组
const group = findApiGroupByTopic(output, eventGroups);
return (
<div
key={output.id || `output-${index}`}
key={output.eventId || `output-${index}`}
className={styles['node-input-label']}
style={{
color: group ? group.color : '#000'
@ -354,7 +273,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
{/*content栏-data部分*/}
<div className={styles['node-data-box']}>
<div className={styles['node-content']}>
{dataIns.length > 0 && !isStartNode && (
{dataIns.length > 0 && (
<div className={styles['node-inputs']}>
{dataIns.map((input, index) => {
// 查找关联的事件分组
@ -374,7 +293,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
</div>
)}
{dataOuts.length > 0 && !isEndNode && (
{dataOuts.length > 0 && (
<div className={styles['node-outputs']}>
{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)}
</>
);
};

@ -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 && (
<div className={styles['node-footer']}>
{formatFooter(footerData)}
{formatFooter(footerData, eventListOld)}
</div>
)}

@ -33,8 +33,18 @@ const NodeContextMenu: React.FC<NodeContextMenuProps> = ({
};
const handleEdit = () => {
// 判断节点类型如果是SUB类型则打开新标签页
if (node.type === 'SUB') {
// 创建自定义事件来通知打开新标签页
const openTabEvent = new CustomEvent('openSubNodeTab', {
detail: { node }
});
document.dispatchEvent(openTabEvent);
}
else {
onEdit && onEdit(node);
onCloseOpenModal(true);
}
onCloseMenu(null);
};

@ -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);
// 为所有边添加类型

@ -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 => ({

@ -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<Set<string>>(new Set());
const [subMenuData, setSubMenuData] = useState<any>({});
const { menuData } = useSelector((state) => state.ideContainer);
const { menuData, flowData, currentAppData } = useSelector((state) => state.ideContainer);
const dispatch = useDispatch();
const navBarRef = useRef<NavBarRef>(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,11 +98,95 @@ 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();
@ -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 {

@ -30,8 +30,8 @@ const data = [
},
{
key: '3',
title: '流程运行日志',
content: '流程运行时日志...'
title: '运行数据',
content: '运行数据日志...'
},
{
key: '4',
@ -47,6 +47,8 @@ const LogBar: React.FC<LogBarProps> = () => {
const { logBarStatus } = useSelector((state) => state.ideContainer);
const dispatch = useDispatch();
const [validationLogs, setValidationLogs] = useState<LogMessage[]>([]);
const [runtimeLogs, setRuntimeLogs] = useState<LogMessage[]>([]); // 添加运行时日志状态
const [logContainerHeight, setLogContainerHeight] = useState('250px'); // 添加日志容器高度状态
// 处理 Tab 点击事件
const handleTabClick = (key: string) => {
@ -64,9 +66,9 @@ const LogBar: React.FC<LogBarProps> = () => {
// 当 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<LogBarProps> = () => {
}
else {
dispatch(updateLogBarStatus(true));
// 更新日志容器高度状态
setLogContainerHeight(`${size.height}px`);
}
};
@ -102,6 +106,21 @@ const LogBar: React.FC<LogBarProps> = () => {
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));
}
};
// 添加事件监听器
@ -116,7 +135,7 @@ const LogBar: React.FC<LogBarProps> = () => {
// 渲染校验日志内容
const renderValidationLogs = () => {
return (
<div style={{ padding: '10px', maxHeight: '200px', overflowY: 'auto' }}>
<div style={{ padding: '10px', height: 'calc(100% - 40px)', overflowY: 'auto' }}>
{validationLogs.length === 0 ? (
<p></p>
) : (
@ -135,6 +154,28 @@ const LogBar: React.FC<LogBarProps> = () => {
);
};
// 渲染运行时日志内容
const renderRuntimeLogs = () => {
return (
<div style={{ padding: '10px', height: 'calc(100% - 40px)', overflowY: 'auto' }}>
{runtimeLogs.length === 0 ? (
<p></p>
) : (
runtimeLogs.map(log => (
<div key={log.id} style={{ marginBottom: '8px', padding: '4px', borderBottom: '1px solid #eee' }}>
<div style={{ fontSize: '12px', color: '#999' }}>
{new Date(log.timestamp).toLocaleString()}
</div>
<div style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
{log.message}
</div>
</div>
))
)}
</div>
);
};
return (
<>
<ResizeBox
@ -142,7 +183,7 @@ const LogBar: React.FC<LogBarProps> = () => {
className={styles.logBar}
directions={['top']}
style={{
height: logBarStatus ? '250px' : '0px'
height: logBarStatus ? logContainerHeight : '0px'
}}
onMoving={handleResize}
>
@ -150,10 +191,13 @@ const LogBar: React.FC<LogBarProps> = () => {
type="card-gutter"
activeTab={activeTab}
onClickTab={handleTabClick}
justify={true}
>
{tabs.map((x) => (
<TabPane destroyOnHide key={x.key} title={x.title}>
{x.key === '2' ? renderValidationLogs() : x.content}
{x.key === '1' ? renderRuntimeLogs() :
x.key === '2' ? renderValidationLogs() :
x.content}
</TabPane>
))}
</Tabs>

@ -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<NavBarRef, NavBarProps> = ({
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,21 +76,32 @@ const NavBar: React.ForwardRefRenderFunction<NavBarRef, NavBarProps> = ({
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) {
// 如果删除的是当前激活的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已关闭
onTabClose?.(key);

@ -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<SideBarProps> = ({
}); // 添加右键菜单状态
const resizeBoxRef = useRef<HTMLDivElement>(null); // 引用第一个 ResizeBox 容器
const contextMenuRef = useRef<HTMLDivElement>(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<SideBarProps> = ({
}
}
// 监听 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<SideBarProps> = ({
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<SideBarProps> = ({
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<SideBarProps> = ({
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<SideBarProps> = ({
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));
}
};

@ -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,11 +16,13 @@ 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 };
})));
}
};

@ -106,7 +106,7 @@ const HandleModal = ({ visible, onChangeVisible, onRefresh }) => {
<Input placeholder="请输入事件标识" />
</FormItem>
<FormItem
field="desc"
field="description"
label="事件描述"
required
rules={[
@ -142,13 +142,13 @@ const EventContainer = () => {
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();

@ -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;
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,

@ -230,3 +230,7 @@ export const deserializeValue = (value: any): any => {
return value;
}
};
export const sleep = (ms: number): Promise<void> => {
return new Promise(resolve => setTimeout(resolve, ms));
};

@ -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<api|data>',
data: {
displayData: {
name: '事件1',
eventId: 'eventId',
topic: 'topic'
name: sourceEvent.eventName,
eventId: sourceEvent.eventId,
topic: sourceEvent.topic
}
}
});

Loading…
Cancel
Save