feat(flowEditor): 添加应用暂停和重跑功能

master
钟良源 2 weeks ago
parent cf5c0fc8ad
commit 49b1e04205

@ -80,7 +80,11 @@ export function runSubFlow(data: any) {
} }
// 重运行 // 重运行
export function reRunApp(data: any) { export function reRunApp(data: {
appId: string,
instanceId: string,
socketId: string
}) {
return axios.post<apiResData>(`${runPrefix}/apps/rerun`, data); return axios.post<apiResData>(`${runPrefix}/apps/rerun`, data);
} }

@ -19,6 +19,7 @@ import {
updateCanvasDataMap, updateCanvasDataMap,
resetNodeStatus, resetNodeStatus,
updateIsRunning, updateIsRunning,
updateIsPaused,
updateEventListOld, updateEventListOld,
addRuntimeLog, addRuntimeLog,
clearRuntimeLogs, clearRuntimeLogs,
@ -39,7 +40,7 @@ import { appFLowHandle } from '@/pages/flowEditor/utils/appFlowhandle';
import { handelEventNodeList, updateEvent, upDatePublish } from '@/pages/flowEditor/utils/common'; import { handelEventNodeList, updateEvent, upDatePublish } from '@/pages/flowEditor/utils/common';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { getAppListBySceneId, runMainFlow, runSubFlow, stopApp } from '@/api/apps'; import { getAppListBySceneId, runMainFlow, runSubFlow, stopApp, pauseApp, resumeApp, reRunApp } from '@/api/apps';
import store from '@/store'; import store from '@/store';
import { updateAppEvent, updateAppEventChannel, updateAppFlowData } from '@/api/appEvent'; import { updateAppEvent, updateAppEventChannel, updateAppFlowData } from '@/api/appEvent';
import { getUrlParams, sleep } from '@/utils/common'; import { getUrlParams, sleep } from '@/utils/common';
@ -1423,6 +1424,96 @@ export const useFlowCallbacks = (
} }
}, [getCurrentAppKey]); }, [getCurrentAppKey]);
// 暂停/恢复应用
const handlePause = useCallback(async (isPaused: boolean) => {
const { currentAppData, appRuntimeData } = store.getState().ideContainer;
const appKey = getCurrentAppKey();
if (!currentAppData) {
Message.warning('请先选择一个应用');
return;
}
// 获取runId
const runId = appKey && appRuntimeData[appKey] ? appRuntimeData[appKey].runId : '';
if (!runId) {
Message.warning('应用未运行');
return;
}
try {
if (isPaused) {
// 当前已暂停,执行恢复操作
const res: any = await resumeApp({ id: runId });
if (res.code === 200) {
Message.success('应用已恢复');
// 更新暂停状态为 false
dispatch(updateIsPaused(false));
} else {
Message.error(res.msg || '恢复失败');
}
} else {
// 当前正在运行,执行暂停操作
const res: any = await pauseApp({ id: runId });
if (res.code === 200) {
Message.success('应用已暂停');
// 更新暂停状态为 true
dispatch(updateIsPaused(true));
} else {
Message.error(res.msg || '暂停失败');
}
}
} catch (error) {
console.error('暂停/恢复失败:', error);
Message.error('操作失败');
}
}, [getCurrentAppKey]);
// 重跑应用
const handleReRun = useCallback(async () => {
const { currentAppData, appRuntimeData, socketId } = store.getState().ideContainer;
const appKey = getCurrentAppKey();
if (!currentAppData) {
Message.warning('请先选择一个应用');
return;
}
// 获取runId (instanceId)
const instanceId = appKey && appRuntimeData[appKey] ? appRuntimeData[appKey].runId : '';
if (!instanceId) {
Message.warning('应用未运行');
return;
}
try {
// 判断是主流程还是子流程
const appId = currentAppData.key && currentAppData.key.includes('sub')
? currentAppData.parentAppId
: currentAppData.id;
const res: any = await reRunApp({
appId,
instanceId,
socketId
});
if (res.code === 200) {
Message.success('应用重跑成功');
// 重置节点状态
dispatch(resetNodeStatus());
} else {
Message.error(res.msg || '重跑失败');
}
} catch (error) {
console.error('重跑失败:', error);
Message.error('重跑失败');
}
}, [getCurrentAppKey]);
return { return {
// Event handlers // Event handlers
onNodesChange, onNodesChange,
@ -1456,7 +1547,9 @@ export const useFlowCallbacks = (
// Actions // Actions
saveFlowDataToServer, saveFlowDataToServer,
handleRun handleRun,
handlePause,
handleReRun
}; };
}; };
export default useFlowCallbacks; export default useFlowCallbacks;

@ -758,32 +758,32 @@ const GlobalVarContainer = () => {
</Space> </Space>
</div> </div>
<div className={styles['comp-list-handle']}> <div className={styles['comp-list-handle']}>
{/*<Radio.Group*/} <Radio.Group
{/* defaultValue={''}*/} defaultValue={''}
{/* name="button-radio-group"*/} name="button-radio-group"
{/* value={componentStatus}*/} value={componentStatus}
{/* onChange={handleStatusChange}*/} onChange={handleStatusChange}
{/* style={{ marginRight: 30 }}*/} style={{ marginRight: 30 }}
{/*>*/} >
{/* {[{ label: '全部', value: '' }, ...componentStatusDict].map((item) => {*/} {[{ label: '全部', value: '' }, ...componentStatusDict].map((item) => {
{/* return (*/} return (
{/* <Radio key={item.value} value={item.value}>*/} <Radio key={item.value} value={item.value}>
{/* {({ checked }) => {*/} {({ checked }) => {
{/* return (*/} return (
{/* <Button*/} <Button
{/* tabIndex={-1}*/} tabIndex={-1}
{/* key={item.value}*/} key={item.value}
{/* shape="round"*/} shape="round"
{/* type={checked ? 'primary' : 'default'}*/} type={checked ? 'primary' : 'default'}
{/* >*/} >
{/* {item.label}*/} {item.label}
{/* </Button>*/} </Button>
{/* );*/} );
{/* }}*/} }}
{/* </Radio>*/} </Radio>
{/* );*/} );
{/* })}*/} })}
{/*</Radio.Group>*/} </Radio.Group>
{selectedItem === '我的组件' && <Space split={<Divider type="vertical" />}> {selectedItem === '我的组件' && <Space split={<Divider type="vertical" />}>
{/*<Button type="secondary" status="success" style={{ borderRadius: 4 }}>生成组件</Button>*/} {/*<Button type="secondary" status="success" style={{ borderRadius: 4 }}>生成组件</Button>*/}
<Button <Button

@ -84,6 +84,8 @@ interface FlowEditorMainProps {
handleAddNode: (nodeType: string, node: any) => void; handleAddNode: (nodeType: string, node: any) => void;
saveFlowDataToServer: () => void; saveFlowDataToServer: () => void;
handleRun: (running: boolean) => void; handleRun: (running: boolean) => void;
handlePause: (isPaused: boolean) => void;
handleReRun: () => void;
} }
const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => { const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
@ -136,7 +138,9 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
addNodeOnPane, addNodeOnPane,
handleAddNode, handleAddNode,
saveFlowDataToServer, saveFlowDataToServer,
handleRun handleRun,
handlePause,
handleReRun
} = props; } = props;
const { getGuidelines, clearGuidelines, AlignmentGuides } = useAlignmentGuidelines(); const { getGuidelines, clearGuidelines, AlignmentGuides } = useAlignmentGuidelines();
@ -529,6 +533,8 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
canUndo={canUndo} canUndo={canUndo}
canRedo={canRedo} canRedo={canRedo}
onRun={handleRun} onRun={handleRun}
onPause={handlePause}
onReRun={handleReRun}
isRunning={currentAppIsRunning} isRunning={currentAppIsRunning}
></ActionBar> ></ActionBar>
</Panel> </Panel>

@ -1,6 +1,14 @@
import React from 'react'; import React from 'react';
import { Button, Message } from '@arco-design/web-react'; import { Button, Message } from '@arco-design/web-react';
import { IconSave, IconPlayArrow, IconCodeSquare, IconUndo, IconRedo } from '@arco-design/web-react/icon'; import {
IconSave,
IconPlayArrow,
IconCodeSquare,
IconPause,
IconSync,
IconUndo,
IconRedo
} from '@arco-design/web-react/icon';
import { updateLogBarStatus } from '@/store/ideContainer'; import { updateLogBarStatus } from '@/store/ideContainer';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
@ -14,6 +22,8 @@ interface ActionBarProps {
canUndo?: boolean; canUndo?: boolean;
canRedo?: boolean; canRedo?: boolean;
onRun?: (isRunning: boolean) => void; onRun?: (isRunning: boolean) => void;
onPause?: (isPaused: boolean) => void;
onReRun?: () => void;
isRunning?: boolean; isRunning?: boolean;
} }
@ -25,6 +35,8 @@ const ActionBar: React.FC<ActionBarProps> = ({
canUndo = false, canUndo = false,
canRedo = false, canRedo = false,
onRun, onRun,
onPause,
onReRun,
isRunning = false isRunning = false
}) => { }) => {
const { logBarStatus, appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer); const { logBarStatus, appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer);
@ -47,6 +59,11 @@ const ActionBar: React.FC<ActionBarProps> = ({
? appRuntimeData[currentAppKey].isRunning ? appRuntimeData[currentAppKey].isRunning
: false; : false;
// 获取当前应用的暂停状态(如果有的话)
const currentAppIsPaused = currentAppKey && appRuntimeData[currentAppKey]
? appRuntimeData[currentAppKey].isPaused
: false;
const changeLogBarStatus = () => { const changeLogBarStatus = () => {
dispatch(updateLogBarStatus(!logBarStatus)); dispatch(updateLogBarStatus(!logBarStatus));
}; };
@ -55,6 +72,16 @@ const ActionBar: React.FC<ActionBarProps> = ({
onRun?.(!currentAppIsRunning); onRun?.(!currentAppIsRunning);
}; };
// 暂停/恢复应用
const handlePause = () => {
onPause?.(currentAppIsPaused);
};
// 重跑应用
const handleReRun = () => {
onReRun?.();
};
return ( return (
<div className="action-bar"> <div className="action-bar">
<Button onClick={onSave} type="primary" shape="round" icon={<IconSave />}></Button> <Button onClick={onSave} type="primary" shape="round" icon={<IconSave />}></Button>
@ -71,6 +98,27 @@ const ActionBar: React.FC<ActionBarProps> = ({
> >
{currentAppIsRunning ? '停止' : '运行'} {currentAppIsRunning ? '停止' : '运行'}
</Button> </Button>
<Button
type="outline"
shape="round"
icon={<IconPause />}
onClick={() => handlePause()}
style={{ padding: '0 8px', backgroundColor: '#fff' }}
disabled={!currentAppIsRunning}
status={currentAppIsPaused ? 'warning' : undefined}
>
{currentAppIsPaused ? '恢复' : '暂停'}
</Button>
{/*<Button*/}
{/* type="outline"*/}
{/* shape="round"*/}
{/* icon={<IconSync />}*/}
{/* onClick={() => handleReRun()}*/}
{/* style={{ padding: '0 8px', backgroundColor: '#fff' }}*/}
{/* disabled={!currentAppIsRunning}*/}
{/*>*/}
{/* 重跑*/}
{/*</Button>*/}
<Button <Button
type="outline" type="outline"
shape="round" shape="round"

@ -99,7 +99,9 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini
// Actions // Actions
saveFlowDataToServer, saveFlowDataToServer,
handleRun handleRun,
handlePause,
handleReRun
} = useFlowCallbacks( } = useFlowCallbacks(
nodes, nodes,
setNodes, setNodes,
@ -351,6 +353,8 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini
// Actions // Actions
saveFlowDataToServer={saveFlowDataToServer} saveFlowDataToServer={saveFlowDataToServer}
handleRun={handleRun} handleRun={handleRun}
handlePause={handlePause}
handleReRun={handleReRun}
/> />
</HistoryProvider> </HistoryProvider>
</div> </div>

@ -19,6 +19,7 @@ interface IDEContainerState {
appRuntimeData: Record<string, { appRuntimeData: Record<string, {
nodeStatusMap: Record<string, string>; nodeStatusMap: Record<string, string>;
isRunning: boolean; isRunning: boolean;
isPaused: boolean;
logs: any[]; logs: any[];
runId: string; runId: string;
eventSendNodeList: any[], // [{nodeID:topic}] eventSendNodeList: any[], // [{nodeID:topic}]
@ -142,6 +143,7 @@ const ideContainerSlice = createSlice({
state.appRuntimeData[targetAppKey] = { state.appRuntimeData[targetAppKey] = {
nodeStatusMap: {}, nodeStatusMap: {},
isRunning: false, isRunning: false,
isPaused: false,
logs: [], logs: [],
runId: '', runId: '',
eventSendNodeList: [], eventSendNodeList: [],
@ -172,6 +174,7 @@ const ideContainerSlice = createSlice({
state.appRuntimeData[appKey] = { state.appRuntimeData[appKey] = {
nodeStatusMap: {}, nodeStatusMap: {},
isRunning: false, isRunning: false,
isPaused: false,
logs: [], logs: [],
runId: '', runId: '',
eventSendNodeList: [], eventSendNodeList: [],
@ -181,6 +184,24 @@ const ideContainerSlice = createSlice({
state.appRuntimeData[appKey].isRunning = payload; state.appRuntimeData[appKey].isRunning = payload;
} }
}, },
// 更新暂停状态
updateIsPaused: (state, { payload }) => {
const appKey = getCurrentAppKey(state.currentAppData);
if (appKey) {
if (!state.appRuntimeData[appKey]) {
state.appRuntimeData[appKey] = {
nodeStatusMap: {},
isRunning: false,
isPaused: false,
logs: [],
runId: '',
eventSendNodeList: [],
eventlisteneList: []
};
}
state.appRuntimeData[appKey].isPaused = payload;
}
},
// 添加运行id // 添加运行id
updateRuntimeId: (state, { payload }) => { updateRuntimeId: (state, { payload }) => {
const appKey = getCurrentAppKey(state.currentAppData); const appKey = getCurrentAppKey(state.currentAppData);
@ -203,6 +224,7 @@ const ideContainerSlice = createSlice({
state.appRuntimeData[appKey] = { state.appRuntimeData[appKey] = {
nodeStatusMap: {}, nodeStatusMap: {},
isRunning: false, isRunning: false,
isPaused: false,
logs: [], logs: [],
runId: '', runId: '',
eventSendNodeList: [], eventSendNodeList: [],
@ -218,6 +240,7 @@ const ideContainerSlice = createSlice({
state.appRuntimeData[appId] = { state.appRuntimeData[appId] = {
nodeStatusMap: {}, nodeStatusMap: {},
isRunning: false, isRunning: false,
isPaused: false,
logs: [], logs: [],
runId: '', runId: '',
eventSendNodeList: [], eventSendNodeList: [],
@ -257,6 +280,7 @@ export const {
updateNodeStatus, updateNodeStatus,
resetNodeStatus, resetNodeStatus,
updateIsRunning, updateIsRunning,
updateIsPaused,
updateRuntimeId, updateRuntimeId,
updateEventNodeList, updateEventNodeList,
addRuntimeLog, addRuntimeLog,

Loading…
Cancel
Save