feat: 在画布里展示图片

feature
鱼星 3 weeks ago
parent bea38a7e36
commit b01b9b5f69

@ -5,6 +5,9 @@ import DynamicIcon from '@/components/DynamicIcon';
import NodeContentImage from '@/pages/flowEditor/components/nodeContentImage'; import NodeContentImage from '@/pages/flowEditor/components/nodeContentImage';
import NodeStatusIndicator, { NodeStatus } from '@/components/FlowEditor/NodeStatusIndicator'; import NodeStatusIndicator, { NodeStatus } from '@/components/FlowEditor/NodeStatusIndicator';
import { useStore as useFlowStore } from '@xyflow/react'; import { useStore as useFlowStore } from '@xyflow/react';
import { useSelector } from 'react-redux';
import { getCurrentAppKey } from '@/utils/flow/runtime';
import { getRuntimeImageUrl } from '@/utils/flow/imageRuntimeData';
const setIcon = () => { const setIcon = () => {
return <DynamicIcon type="IconImage" style={{ fontSize: '16px', marginRight: '5px' }} />; return <DynamicIcon type="IconImage" style={{ fontSize: '16px', marginRight: '5px' }} />;
@ -12,6 +15,12 @@ const setIcon = () => {
const ImageNode = ({ data, id }: { data: any; id: string }) => { const ImageNode = ({ data, id }: { data: any; id: string }) => {
const title = data.title || '图片展示'; const title = data.title || '图片展示';
const { currentAppData, appRuntimeData } = useSelector((state: any) => state.ideContainer);
const currentAppKey = getCurrentAppKey(currentAppData);
const runtimeNodeData = currentAppKey
? appRuntimeData[currentAppKey]?.nodeData
: [];
const imageUrl = getRuntimeImageUrl(runtimeNodeData, id);
// 获取节点选中状态 - 适配React Flow v12 API // 获取节点选中状态 - 适配React Flow v12 API
const isSelected = useStore((state) => const isSelected = useStore((state) =>
@ -35,9 +44,9 @@ const ImageNode = ({ data, id }: { data: any; id: string }) => {
{title} {title}
<NodeStatusIndicator status={nodeStatus} isVisible={isStatusVisible} /> <NodeStatusIndicator status={nodeStatus} isVisible={isStatusVisible} />
</div> </div>
<NodeContentImage data={data} /> <NodeContentImage data={data} imageUrl={imageUrl} />
</div> </div>
); );
}; };
export default ImageNode; export default ImageNode;

@ -105,6 +105,26 @@
min-height: 10px; min-height: 10px;
} }
.node-image-box {
width: 150px;
padding: 6px;
margin: -1px auto 0;
background-color: #ffffff;
border: 1px solid #cccccc;
border-radius: 3px;
:global(.arco-image) {
display: block;
max-width: 100%;
}
:global(.arco-image-img) {
max-width: 100%;
max-height: 120px;
object-fit: contain;
}
}
.node-content-box { .node-content-box {
padding: 10px; padding: 10px;
margin: -1px auto; margin: -1px auto;
@ -182,4 +202,4 @@
100% { 100% {
box-shadow: 0 0 0 0 rgba(24, 144, 255, 0); box-shadow: 0 0 0 0 rgba(24, 144, 255, 0);
} }
} }

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import styles from '@/components/FlowEditor/node/style/baseOther.module.less'; import styles from '@/components/FlowEditor/node/style/baseOther.module.less';
import { Handle, Position, useStore } from '@xyflow/react'; import { Handle, Position } from '@xyflow/react';
import { Image } from '@arco-design/web-react'; import { Image } from '@arco-design/web-react';
import { formatDataType } from '@/utils/common'; import { formatDataType } from '@/utils/common';
@ -103,7 +103,7 @@ const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[]
}; };
const NodeContent = ({ data }: { data: NodeContentData }) => { const NodeContent = ({ data, imageUrl = '' }: { data: NodeContentData; imageUrl?: string }) => {
const apiIns = data.parameters?.apiIns || []; const apiIns = data.parameters?.apiIns || [];
const apiOuts = data.parameters?.apiOuts || []; const apiOuts = data.parameters?.apiOuts || [];
const dataIns = data.parameters?.dataIns || []; const dataIns = data.parameters?.dataIns || [];
@ -178,14 +178,16 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
</> </>
)} )}
{/*图片展示 TODO 需要对接接口*/} {imageUrl && (
{/*<div className={styles['node-image-box']}>*/} <div className={styles['node-image-box']}>
{/* <Image*/} <Image
{/* width={150}*/} width={150}
{/* src=""*/} src={imageUrl}
{/* alt="lamp"*/} alt="图片展示"
{/* />*/} style={{ display: 'block' }}
{/*</div>*/} />
</div>
)}
{/* 根据节点类型渲染不同的句柄 */} {/* 根据节点类型渲染不同的句柄 */}
{renderRegularNodeHandles(dataIns, dataOuts, apiIns, apiOuts)} {renderRegularNodeHandles(dataIns, dataOuts, apiIns, apiOuts)}
@ -193,4 +195,4 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
); );
}; };
export default NodeContent; export default NodeContent;

@ -1,7 +1,7 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import { ResizeBox, Tabs } from '@arco-design/web-react'; import { ResizeBox, Tabs } from '@arco-design/web-react';
import styles from './style/logBar.module.less'; import styles from './style/logBar.module.less';
import { updateLogBarStatus } from '@/store/ideContainer'; import { updateLogBarStatus, updateRuntimeNodeData } from '@/store/ideContainer';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import { getNodeData } from '@/api/appIns'; import { getNodeData } from '@/api/appIns';
import RunTimeData from './components/runTimeData'; import RunTimeData from './components/runTimeData';
@ -165,27 +165,32 @@ const LogBar: React.FC<LogBarProps> = () => {
// 获取当前应用的运行状态 // 获取当前应用的运行状态
const currentAppKey = getCurrentAppKey(currentAppData); const currentAppKey = getCurrentAppKey(currentAppData);
const isRunning = currentAppKey && appRuntimeData[currentAppKey]?.isRunning; const currentAppIsRunning = currentAppKey && appRuntimeData[currentAppKey]?.isRunning;
const currentRunId = currentAppKey ? appRuntimeData[currentAppKey]?.runId : '';
// 实现轮询获取运行数据 - 只在应用运行时轮询 // 实现轮询获取运行数据 - 只在应用运行时轮询
useEffect(() => { useEffect(() => {
let intervalId: NodeJS.Timeout | null = null; let intervalId: NodeJS.Timeout | null = null;
const appKey = getCurrentAppKey(currentAppData); const appKey = currentAppKey;
// 只有在应用正在运行且有 runId 时才开始轮询 // 只有在应用正在运行且有 runId 时才开始轮询
if ( if (
appKey && appKey &&
appRuntimeData[appKey]?.isRunning && currentAppIsRunning &&
appRuntimeData[appKey]?.runId currentRunId
) { ) {
const fetchRuntimeData = async () => { const fetchRuntimeData = async () => {
try { try {
setLoading(true); setLoading(true);
const response = await getNodeData(appRuntimeData[appKey].runId); const response = await getNodeData(currentRunId);
setRuntimeData((prev) => ({ setRuntimeData((prev) => ({
...prev, ...prev,
[appKey]: response.data, [appKey]: response.data,
})); }));
dispatch(updateRuntimeNodeData({
appId: appKey,
nodeData: response.data
}));
} catch (error) { } catch (error) {
console.error('获取运行数据失败:', error); console.error('获取运行数据失败:', error);
} finally { } finally {
@ -206,12 +211,12 @@ const LogBar: React.FC<LogBarProps> = () => {
clearInterval(intervalId); clearInterval(intervalId);
} }
}; };
}, [currentAppData, appRuntimeData]); }, [currentAppKey, currentAppIsRunning, currentRunId, dispatch]);
// 当应用停止运行时,清除运行数据 // 当应用停止运行时,清除运行数据
useEffect(() => { useEffect(() => {
const appKey = getCurrentAppKey(currentAppData); const appKey = currentAppKey;
if (appKey && !appRuntimeData[appKey]?.isRunning) { if (appKey && !currentAppIsRunning) {
// 清除当前应用的运行数据 // 清除当前应用的运行数据
setRuntimeData((prev) => { setRuntimeData((prev) => {
const newData = { ...prev }; const newData = { ...prev };
@ -219,7 +224,7 @@ const LogBar: React.FC<LogBarProps> = () => {
return newData; return newData;
}); });
} }
}, [isRunning, currentAppData]); }, [currentAppKey, currentAppIsRunning]);
// 渲染校验日志内容 // 渲染校验日志内容
const renderValidationLogs = () => { const renderValidationLogs = () => {

@ -28,6 +28,7 @@ interface IDEContainerState {
isPaused: boolean; isPaused: boolean;
logs: any[]; logs: any[];
runId: string; runId: string;
nodeData: any[];
eventSendNodeList: any[]; // [{nodeID:topic}] eventSendNodeList: any[]; // [{nodeID:topic}]
eventlisteneList: any[]; // [{nodeID:topic}] eventlisteneList: any[]; // [{nodeID:topic}]
} }
@ -212,6 +213,19 @@ const ideContainerSlice = createSlice({
state.appRuntimeData[appId].logs = []; state.appRuntimeData[appId].logs = [];
} }
}, },
// 更新运行节点数据
updateRuntimeNodeData: (state, { payload }) => {
const { appId, nodeData } = payload;
if (!appId) {
return;
}
if (!state.appRuntimeData[appId]) {
state.appRuntimeData[appId] = createDefaultAppRuntimeState();
}
state.appRuntimeData[appId].nodeData = Array.isArray(nodeData)
? nodeData
: [];
},
// 更新组件编码路径 // 更新组件编码路径
updateComponentCodingPath(state, action) { updateComponentCodingPath(state, action) {
state.componentCoding = { ...action.payload }; state.componentCoding = { ...action.payload };
@ -241,6 +255,7 @@ export const {
updateEventNodeList, updateEventNodeList,
addRuntimeLog, addRuntimeLog,
clearRuntimeLogs, clearRuntimeLogs,
updateRuntimeNodeData,
updateComponentCodingPath, updateComponentCodingPath,
} = ideContainerSlice.actions; } = ideContainerSlice.actions;

@ -0,0 +1,40 @@
export interface RuntimeNodeData {
nodeId?: string;
input?: Record<string, any>;
[key: string]: any;
}
const isRuntimeNode = (item: any): item is RuntimeNodeData => {
return !!item && typeof item === 'object' && typeof item.nodeId === 'string';
};
export const flattenRuntimeNodeData = (runtimeData: any): RuntimeNodeData[] => {
if (!Array.isArray(runtimeData)) {
return [];
}
return runtimeData.reduce<RuntimeNodeData[]>((nodes, item) => {
if (isRuntimeNode(item)) {
nodes.push(item);
return nodes;
}
if (Array.isArray(item?.nodes)) {
nodes.push(...item.nodes.filter(isRuntimeNode));
}
return nodes;
}, []);
};
export const getRuntimeImageUrl = (
runtimeData: any,
nodeId: string
): string => {
const nodeData = flattenRuntimeNodeData(runtimeData).find(
(item) => item.nodeId === nodeId
);
const imageUrl = nodeData?.input?.in;
return typeof imageUrl === 'string' ? imageUrl.trim() : '';
};

@ -9,6 +9,7 @@ export interface AppRuntimeState {
isPaused: boolean; isPaused: boolean;
logs: any[]; logs: any[];
runId: string; runId: string;
nodeData: any[];
eventSendNodeList: any[]; eventSendNodeList: any[];
eventlisteneList: any[]; eventlisteneList: any[];
} }
@ -29,6 +30,7 @@ export const createDefaultAppRuntimeState = (): AppRuntimeState => ({
isPaused: false, isPaused: false,
logs: [], logs: [],
runId: '', runId: '',
nodeData: [],
eventSendNodeList: [], eventSendNodeList: [],
eventlisteneList: [], eventlisteneList: [],
}); });

Loading…
Cancel
Save