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

@ -19,6 +19,7 @@ import {
updateCanvasDataMap,
resetNodeStatus,
updateIsRunning,
updateIsPaused,
updateEventListOld,
addRuntimeLog,
clearRuntimeLogs,
@ -39,7 +40,7 @@ import { appFLowHandle } from '@/pages/flowEditor/utils/appFlowhandle';
import { handelEventNodeList, updateEvent, upDatePublish } from '@/pages/flowEditor/utils/common';
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 { updateAppEvent, updateAppEventChannel, updateAppFlowData } from '@/api/appEvent';
import { getUrlParams, sleep } from '@/utils/common';
@ -1423,6 +1424,96 @@ export const useFlowCallbacks = (
}
}, [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 {
// Event handlers
onNodesChange,
@ -1456,7 +1547,9 @@ export const useFlowCallbacks = (
// Actions
saveFlowDataToServer,
handleRun
handleRun,
handlePause,
handleReRun
};
};
export default useFlowCallbacks;

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

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

@ -1,6 +1,14 @@
import React from '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 { useSelector, useDispatch } from 'react-redux';
@ -14,6 +22,8 @@ interface ActionBarProps {
canUndo?: boolean;
canRedo?: boolean;
onRun?: (isRunning: boolean) => void;
onPause?: (isPaused: boolean) => void;
onReRun?: () => void;
isRunning?: boolean;
}
@ -25,6 +35,8 @@ const ActionBar: React.FC<ActionBarProps> = ({
canUndo = false,
canRedo = false,
onRun,
onPause,
onReRun,
isRunning = false
}) => {
const { logBarStatus, appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer);
@ -47,6 +59,11 @@ const ActionBar: React.FC<ActionBarProps> = ({
? appRuntimeData[currentAppKey].isRunning
: false;
// 获取当前应用的暂停状态(如果有的话)
const currentAppIsPaused = currentAppKey && appRuntimeData[currentAppKey]
? appRuntimeData[currentAppKey].isPaused
: false;
const changeLogBarStatus = () => {
dispatch(updateLogBarStatus(!logBarStatus));
};
@ -55,6 +72,16 @@ const ActionBar: React.FC<ActionBarProps> = ({
onRun?.(!currentAppIsRunning);
};
// 暂停/恢复应用
const handlePause = () => {
onPause?.(currentAppIsPaused);
};
// 重跑应用
const handleReRun = () => {
onReRun?.();
};
return (
<div className="action-bar">
<Button onClick={onSave} type="primary" shape="round" icon={<IconSave />}></Button>
@ -71,6 +98,27 @@ const ActionBar: React.FC<ActionBarProps> = ({
>
{currentAppIsRunning ? '停止' : '运行'}
</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
type="outline"
shape="round"

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

@ -19,6 +19,7 @@ interface IDEContainerState {
appRuntimeData: Record<string, {
nodeStatusMap: Record<string, string>;
isRunning: boolean;
isPaused: boolean;
logs: any[];
runId: string;
eventSendNodeList: any[], // [{nodeID:topic}]
@ -142,6 +143,7 @@ const ideContainerSlice = createSlice({
state.appRuntimeData[targetAppKey] = {
nodeStatusMap: {},
isRunning: false,
isPaused: false,
logs: [],
runId: '',
eventSendNodeList: [],
@ -172,6 +174,7 @@ const ideContainerSlice = createSlice({
state.appRuntimeData[appKey] = {
nodeStatusMap: {},
isRunning: false,
isPaused: false,
logs: [],
runId: '',
eventSendNodeList: [],
@ -181,6 +184,24 @@ const ideContainerSlice = createSlice({
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
updateRuntimeId: (state, { payload }) => {
const appKey = getCurrentAppKey(state.currentAppData);
@ -203,6 +224,7 @@ const ideContainerSlice = createSlice({
state.appRuntimeData[appKey] = {
nodeStatusMap: {},
isRunning: false,
isPaused: false,
logs: [],
runId: '',
eventSendNodeList: [],
@ -218,6 +240,7 @@ const ideContainerSlice = createSlice({
state.appRuntimeData[appId] = {
nodeStatusMap: {},
isRunning: false,
isPaused: false,
logs: [],
runId: '',
eventSendNodeList: [],
@ -257,6 +280,7 @@ export const {
updateNodeStatus,
resetNodeStatus,
updateIsRunning,
updateIsPaused,
updateRuntimeId,
updateEventNodeList,
addRuntimeLog,

Loading…
Cancel
Save