diff --git a/src/api/appEvent.ts b/src/api/appEvent.ts new file mode 100644 index 0000000..e22fb24 --- /dev/null +++ b/src/api/appEvent.ts @@ -0,0 +1,20 @@ +import axios from 'axios'; +import { apiResData } from '@/api/interface/index'; + +// 公共路径 +const urlPrefix = '/api/v1/bpms-workbench'; + +// 获取应用事件 +export function getAppEventData(id: any) { + return axios.get(`${urlPrefix}/appEvent/${id}`); +} + +// 获取工程下的所有应用事件 +export function getAppEventList(id: any) { + return axios.get(`${urlPrefix}/appEvent/${id}/list`); +} + +// 更新事件 +export function updateAppEvent(id: any, data: any) { + return axios.post(`${urlPrefix}/appEvent/${id}/update`, data); +} \ No newline at end of file diff --git a/src/components/FlowEditor/nodeEditors/components/EventListenEditor.tsx b/src/components/FlowEditor/nodeEditors/components/EventListenEditor.tsx index 59a050b..e055ad2 100644 --- a/src/components/FlowEditor/nodeEditors/components/EventListenEditor.tsx +++ b/src/components/FlowEditor/nodeEditors/components/EventListenEditor.tsx @@ -5,6 +5,7 @@ import { IconUnorderedList } from '@arco-design/web-react/icon'; import EventSelect from './EventSelect'; import { useDispatch, useSelector } from 'react-redux'; import { queryEventItemBySceneId } from '@/api/event'; +import ParamsTable from '@/components/FlowEditor/nodeEditors/components/ParamsTable'; const EventListenEditor: React.FC = ({ nodeData, updateNodeData }) => { const [eventList, setEventList] = useState(); @@ -31,7 +32,17 @@ const EventListenEditor: React.FC = ({ nodeData, updateNodeData updateNodeData('component', { ...data }); - }}> + }} /> + 输入参数 + { + updateNodeData('parameters', { + ...nodeData.parameters, + dataIns: data + }); + }} + /> ); }; diff --git a/src/components/FlowEditor/nodeEditors/components/EventSelect.tsx b/src/components/FlowEditor/nodeEditors/components/EventSelect.tsx index fe82848..3858e42 100644 --- a/src/components/FlowEditor/nodeEditors/components/EventSelect.tsx +++ b/src/components/FlowEditor/nodeEditors/components/EventSelect.tsx @@ -24,6 +24,7 @@ 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); @@ -31,7 +32,8 @@ const EventSelect: React.FC = ({ nodeData, eventList, type, on useEffect(() => { if (eventList && eventList.length > 0) { - setOptions(eventList); + setSpecialOptions(eventList.find(item => item.topic.includes('**empty**'))); + setOptions(eventList.filter(item => !item.topic.includes('**empty**'))); try { setCurrentEvent(eventList.find(item => JSON.parse(nodeData.component?.customDef).eventId === item.id)); } catch (e) { @@ -50,8 +52,7 @@ const EventSelect: React.FC = ({ nodeData, eventList, type, on const formData = form.getFields(); const params = { ...formData, - sceneId: currentAppData.sceneId, - topic: formData.topic += `/${currentAppData.identify}` + sceneId: currentAppData.sceneId }; const res: any = await addEventItem(params as AddEventParams); @@ -76,7 +77,6 @@ const EventSelect: React.FC = ({ nodeData, eventList, type, on topic: e.topic } }; - console.log('data:', data); onUpdateData(data); }; diff --git a/src/components/FlowEditor/nodeEditors/components/EventSendEditor.tsx b/src/components/FlowEditor/nodeEditors/components/EventSendEditor.tsx index c3dc9e5..6d909e4 100644 --- a/src/components/FlowEditor/nodeEditors/components/EventSendEditor.tsx +++ b/src/components/FlowEditor/nodeEditors/components/EventSendEditor.tsx @@ -5,6 +5,7 @@ import { IconUnorderedList } from '@arco-design/web-react/icon'; import { useDispatch, useSelector } from 'react-redux'; import EventSelect from '@/components/FlowEditor/nodeEditors/components/EventSelect'; import { queryEventItemBySceneId } from '@/api/event'; +import ParamsTable from '@/components/FlowEditor/nodeEditors/components/ParamsTable'; const EventSendEditor: React.FC = ({ nodeData, updateNodeData }) => { const [eventList, setEventList] = useState(); @@ -31,7 +32,17 @@ const EventSendEditor: React.FC = ({ nodeData, updateNodeData } updateNodeData('component', { ...data }); - }}> + }} /> + 输出参数 + { + updateNodeData('parameters', { + ...nodeData.parameters, + dataOuts: data + }); + }} + /> ); }; diff --git a/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts b/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts index 38dc53c..2c15525 100644 --- a/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts +++ b/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts @@ -35,10 +35,10 @@ export const validateNodeData = (nodeData: any, nodeType: string): ValidationRes case 'LOOP_END': errors.push(...validateLoopNode(nodeData, nodeType)); break; - case 'EVENTSEND': - case 'EVENTLISTENE': - errors.push(...validateEventNode(nodeData)); - break; + // case 'EVENTSEND': + // case 'EVENTLISTENE': + // errors.push(...validateEventNode(nodeData)); + // break; case 'WAIT': errors.push(...validateWaitNode(nodeData)); break; diff --git a/src/hooks/useFlowCallbacks.ts b/src/hooks/useFlowCallbacks.ts index e198bfd..d5bc9ed 100644 --- a/src/hooks/useFlowCallbacks.ts +++ b/src/hooks/useFlowCallbacks.ts @@ -31,6 +31,9 @@ import { } from '@/components/FlowEditor/nodeEditors/validators/nodeValidators'; import { Dispatch } from 'redux'; +import { runMainFlow } from '@/api/apps'; +import store from '@/store'; +import { updateAppEvent } from '@/api/appEvent'; // 根据节点类型获取对应的节点组件 const getNodeComponent = (nodeType: string) => { @@ -774,6 +777,14 @@ export const useFlowCallbacks = ( }) }; } + else if (nodeType === 'EVENTSEND' || nodeType === 'EVENTLISTENE') { + const { eventList } = store.getState().ideContainer; + const emptyEvent = eventList.find(item => item.topic.includes('**empty**')); + newNode.data.component = { + type: nodeType, + customDef: { eventId: emptyEvent.id, name: emptyEvent.name, topic: emptyEvent.topic } + }; + } // 将未定义的节点动态追加进nodeTypes const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key)); @@ -863,7 +874,6 @@ export const useFlowCallbacks = ( return; } - // 创建新节点 const newNode = { id: `${nodeType}-${Date.now()}`, @@ -885,6 +895,14 @@ export const useFlowCallbacks = ( }) }; } + else if (nodeType === 'EVENTSEND' || nodeType === 'EVENTLISTENE') { + const { eventList } = store.getState().ideContainer; + const emptyEvent = eventList.find(item => item.topic.includes('**empty**')); + newNode.data.component = { + type: nodeType, + customDef: { eventId: emptyEvent.id, name: emptyEvent.name, topic: emptyEvent.topic } + }; + } // 将未定义的节点动态追加进nodeTypes const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key)); @@ -945,8 +963,9 @@ export const useFlowCallbacks = ( // 转换会原始数据类型 const revertedData = revertFlowData(nodes, edges); console.log('revertedData(中断):', revertedData); - // return; + + updateEvent(revertedData.nodeConfigs); const res: any = await setMainFlow(revertedData, initialData.appId); if (res.code === 200) { Message.success('保存成功'); @@ -960,49 +979,78 @@ export const useFlowCallbacks = ( } }, [nodes, edges, initialData?.appId]); - // 初始化WebSocket hook - const ws = useWebSocket({ - onOpen: () => { - console.log('WebSocket连接已建立'); - Message.success('运行已启动'); - }, - onClose: () => { - console.log('WebSocket连接已关闭'); - setIsRunning(false); - Message.info('运行已停止'); - }, - onError: (event) => { - console.error('WebSocket错误:', event); - setIsRunning(false); - Message.error('运行连接出错'); - }, - onMessage: (event) => { - console.log('收到WebSocket消息:', event.data); - // 这里可以处理从后端收到的消息,例如日志更新等 + const updateEvent = (revertedData) => { + // 初始化参数对象 + const params: any = { + eventListenes: [], + eventSends: [] + }; + + // 遍历所有节点,查找事件相关节点 + Object.entries(revertedData).forEach(([nodeId, nodeConfig]: [string, any]) => { + // 检查节点是否有component属性和type定义 + if (nodeConfig.component?.type) { + const nodeType = nodeConfig.component.type; + const customDef = nodeConfig.component.customDef; + + // 解析customDef,可能是字符串也可能是对象 + let eventConfig; + if (typeof customDef === 'string') { + try { + eventConfig = JSON.parse(customDef); + } catch (e) { + console.error('Failed to parse customDef:', e); + return; + } + } + else { + eventConfig = customDef; + } + + // 根据节点类型处理事件节点 + if (nodeType === 'EVENTLISTENE') { + // 事件接收节点 + params.eventListenes.push({ + eventId: eventConfig?.eventId || '', + topic: eventConfig?.topic || '', + eventName: eventConfig?.name || '', + dataOuts: nodeConfig.dataOuts || [] + }); + } + else if (nodeType === 'EVENTSEND') { + // 事件发送节点 + params.eventSends.push({ + eventId: eventConfig?.eventId || '', + topic: eventConfig?.topic || '', + eventName: eventConfig?.name || '', + dataIns: nodeConfig.dataIns || [] + }); + } + } + }); + + // 调用更新事件的API + if (initialData?.appId) { + updateAppEvent(initialData.appId, params); } - }); + }; + // 运行处理函数 const handleRun = useCallback(async (running: boolean) => { if (running) { + const { currentAppData, socketId } = store.getState().ideContainer; // 启动运行 - const res = await getUserToken(); - const token = res.data; - const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; - let wsApi = `${protocol}://${window.location.host}/ws/v1/bpms-runtime`; - if (window.location.host.includes('localhost')) { - wsApi = `ws://api.myserver.com:4121/ws/v1/bpms-runtime`; - } - const uri = `${wsApi}?x-auth0-token=${token}`; - ws.connect(uri); - setIsRunning(true); + const params = { + appId: currentAppData.id, + socketId + }; + runMainFlow(params); } else { // 停止运行 - ws.disconnect(); - setIsRunning(false); } - }, [initialData?.appId, ws]); + }, [initialData?.appId]); return { // Event handlers diff --git a/src/pages/flowEditor/components/nodeContentOther.tsx b/src/pages/flowEditor/components/nodeContentOther.tsx index 84a48b3..1d93681 100644 --- a/src/pages/flowEditor/components/nodeContentOther.tsx +++ b/src/pages/flowEditor/components/nodeContentOther.tsx @@ -180,7 +180,7 @@ const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[] const formatFooter = (data: any) => { try { - switch (data.type) { + switch (data?.type) { case 'WAIT': const { duration } = deserializeValue(data.customDef); const hours = Math.floor(duration / 3600); @@ -192,10 +192,11 @@ const formatFooter = (data: any) => { return cronstrue.toString(intervalSeconds, { locale: 'zh_CN' }); case 'EVENTSEND': case 'EVENTLISTENE': - const { name } = isJSON(data.customDef) ? JSON.parse(data.customDef) : data.customDef; + const { name, topic } = isJSON(data.customDef) ? JSON.parse(data.customDef) : data.customDef; + if (topic.includes('**empty**')) return ''; return `事件: ${name}`; default: - return '这个类型还没开发'; + return ''; } } catch (e) { console.log(e); @@ -207,7 +208,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => { const apiOuts = data.parameters?.apiOuts || []; const dataIns = data.parameters?.dataIns || []; const dataOuts = data.parameters?.dataOuts || []; - const showFooter = data?.component?.customDef || false; + const showFooter = formatFooter(data.component) || false; const footerData = (showFooter && data.component) || {}; // console.log(apiIns, apiOuts, dataIns, dataOuts); diff --git a/src/pages/ideContainer/index.tsx b/src/pages/ideContainer/index.tsx index ea3324c..061b0c9 100644 --- a/src/pages/ideContainer/index.tsx +++ b/src/pages/ideContainer/index.tsx @@ -6,7 +6,13 @@ import LogBar from './logBar'; import RightSideBar from './rightSideBar'; import NavBar, { NavBarRef } from './navBar'; import { Selected } from '@/pages/ideContainer/types'; -import { updateCurrentAppData, updateInfo, updateProjectComponentData } from '@/store/ideContainer'; +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'; @@ -20,6 +26,10 @@ import ComponentList from '@/pages/componentDevelopment/componentList'; 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'; type UrlParamsOptions = { @@ -52,6 +62,38 @@ function IDEContainer() { const dispatch = useDispatch(); const navBarRef = useRef(null); + // 初始化WebSocket hook + const ws = useWebSocket({ + onOpen: () => { + console.log('WebSocket连接已建立'); + }, + onClose: () => { + console.log('WebSocket连接已关闭'); + }, + onError: (event) => { + console.error('WebSocket错误:', event); + }, + onMessage: (event) => { + console.log('收到WebSocket消息:', event.data); + // 这里可以处理从后端收到的消息,例如日志更新等 + const socketMessage = JSON.parse(event.data); + if (socketMessage?.socketId) dispatch(updateSocketId(socketMessage.socketId)); + + } + }); + + const connectWS = async () => { + const res = await getUserToken(); + const token = res.data; + const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; + let wsApi = `${protocol}://${window.location.host}/ws/v1/bpms-runtime`; + // if (window.location.host.includes('localhost')) { + wsApi = `ws://192.168.5.119/ws/v1/bpms-runtime`; + // } + const uri = `${wsApi}?x-auth0-token=${token}`; + ws.connect(uri); + }; + const getAppList = async () => { const res: any = await getAppListBySceneId({ pageSize: 999, @@ -68,10 +110,14 @@ function IDEContainer() { } }; + const getEvent = async () => { + const res: any = await queryEventItemBySceneId(urlParams.id); + if (res.code === 200) dispatch(updateEventList(res.data)); + }; + useEffect(() => { setUrlParams(getUrlParams(window.location.href) as UrlParamsOptions); dispatch(updateInfo(getUrlParams(window.location.href) as UrlParamsOptions)); - }, []); useEffect(() => { @@ -81,6 +127,13 @@ function IDEContainer() { } }, [urlParams.id]); + useEffect(() => { + if (urlParams.identity && urlParams.identity === 'scene') { + connectWS(); + getEvent(); + } + }, [urlParams.identity]); + // 当selected.path变化时,添加到已打开的tab集合中 useEffect(() => { if (selected.key) { diff --git a/src/pages/orchestration/event/index.tsx b/src/pages/orchestration/event/index.tsx index 92e2ef4..8cd9ee9 100644 --- a/src/pages/orchestration/event/index.tsx +++ b/src/pages/orchestration/event/index.tsx @@ -29,7 +29,7 @@ const HandleModal = ({ visible, onChangeVisible, onRefresh }) => { const params = { name: formData.name, - topic: `${formData.topic}/${info.identity}`, + topic: `${formData.topic}`, description: formData.description, sceneId: info.id }; @@ -183,7 +183,7 @@ const EventContainer = () => { const fetchEventData = async () => { const res: any = await queryEventItemBySceneId(info.id); - if (res && res.code === 200) setEventData(res.data); + if (res && res.code === 200) setEventData(res.data.filter(item => !item.topic.includes('**empty**'))); }; useEffect(() => { diff --git a/src/store/ideContainer.ts b/src/store/ideContainer.ts index 1bc7502..ce32245 100644 --- a/src/store/ideContainer.ts +++ b/src/store/ideContainer.ts @@ -7,7 +7,9 @@ interface IDEContainerState { canvasDataMap: any; projectComponentData: any; currentAppData: any; + eventList: any; logBarStatus?: boolean; + socketId: string; } const initialState: IDEContainerState = { @@ -17,7 +19,9 @@ const initialState: IDEContainerState = { canvasDataMap: {}, // 每个画布的缓存信息 projectComponentData: {}, // 工程下的组件列表 currentAppData: {}, // 当前选中的应用数据 - logBarStatus: false + eventList:[], // 工程下的事件列表 + logBarStatus: false, + socketId: '' // 工程的socketId }; const ideContainerSlice = createSlice({ @@ -42,8 +46,14 @@ const ideContainerSlice = createSlice({ updateCurrentAppData(state, action) { state.currentAppData = action.payload; }, + updateEventList(state, action) { + state.eventList = action.payload; + }, updateLogBarStatus(state, action) { state.logBarStatus = action.payload; + }, + updateSocketId(state, action) { + state.socketId = action.payload; } } }); @@ -55,7 +65,9 @@ export const { updateCanvasDataMap, updateProjectComponentData, updateCurrentAppData, - updateLogBarStatus + updateEventList, + updateLogBarStatus, + updateSocketId } = ideContainerSlice.actions; export default ideContainerSlice.reducer; \ No newline at end of file diff --git a/src/utils/convertFlowData.ts b/src/utils/convertFlowData.ts index 92b06ee..ccfd912 100644 --- a/src/utils/convertFlowData.ts +++ b/src/utils/convertFlowData.ts @@ -579,7 +579,6 @@ const getNodeApiOuts = (nodeId: string, nodeConfig: any, currentProjectCompData: // 获取节点的API输出句柄名称 const getNodeApiOutHandle = (nodeId: string, nodeConfig: any) => { - console.log('nodeConfig:', nodeConfig); if (nodeConfig.component?.type === 'LOOP_START') { return 'done'; }