feat(flowEditor): 实现节点粘贴功能并优化事件列表获取逻辑

master
钟良源 3 months ago
parent 37ead877ec
commit 052f454bef

@ -26,7 +26,6 @@ const DataDisplayEdge: React.FC<EdgeProps> = ({
selected, selected,
data data
}) => { }) => {
const [options, setOptions] = useState([]);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [selectedValue, setSelectedValue] = useState(''); const [selectedValue, setSelectedValue] = useState('');
const dropdownRef = useRef<HTMLDivElement>(null); const dropdownRef = useRef<HTMLDivElement>(null);
@ -39,7 +38,7 @@ const DataDisplayEdge: React.FC<EdgeProps> = ({
targetPosition, targetPosition,
borderRadius: 8 // 设置圆角半径 borderRadius: 8 // 设置圆角半径
}); });
const { info } = useSelector((state: any) => state.ideContainer); const { info, eventTopicList } = useSelector((state: any) => state.ideContainer);
// 从数据中获取悬停状态 // 从数据中获取悬停状态
const hovered = data?.hovered || false; const hovered = data?.hovered || false;
@ -69,19 +68,6 @@ const DataDisplayEdge: React.FC<EdgeProps> = ({
})); }));
}; };
const getEventList = async () => {
const res: any = await getTopicList(info.id);
if (res.code === 200) {
setOptions(res.data.map(v => {
return { label: v.eventName, value: v.topic };
}));
}
};
useEffect(() => {
getEventList();
}, [displayData]);
useEffect(() => { useEffect(() => {
const handleClickOutside = (event: MouseEvent) => { const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
@ -163,7 +149,7 @@ const DataDisplayEdge: React.FC<EdgeProps> = ({
overflowY: 'auto' overflowY: 'auto'
}} }}
> >
{options.map((option: { value: string; label: string }) => ( {eventTopicList.map((option: { value: string; label: string }) => (
<div <div
key={option.value} key={option.value}
onClick={() => handleSelect(option)} onClick={() => handleSelect(option)}

@ -86,6 +86,7 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini
editNode, editNode,
editEdge, editEdge,
copyNode, copyNode,
pasteNode, // 添加粘贴节点功能
// Node operations // Node operations
addNodeOnEdge, addNodeOnEdge,
@ -200,11 +201,46 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini
// 点击画布其他区域关闭菜单 // 点击画布其他区域关闭菜单
const onPaneClick = useCallback(() => { const onPaneClick = useCallback(() => {
setMenu(null); setMenu(null);
// 关闭添加节点菜单
setEdgeForNodeAdd(null);
setPositionForNodeAdd(null);
}, [setMenu]); }, [setMenu]);
// 添加处理粘贴事件的函数
const handlePasteNode = useCallback((event: ClipboardEvent) => {
// 检查是否是粘贴操作且在画布区域内
if (event.clipboardData?.getData('text') === 'paste-node' && menu?.type === 'pane' && menu.position) {
pasteNode(menu.position);
setMenu(null);
}
}, [menu, pasteNode]);
// 添加键盘事件监听
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
// 检查是否按下了 Ctrl+V (Windows/Linux) 或 Cmd+V (Mac)
if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
// 触发自定义粘贴事件
const pasteEvent = new ClipboardEvent('paste', {
clipboardData: new DataTransfer()
});
pasteEvent.clipboardData?.setData('text', 'paste-node');
document.dispatchEvent(pasteEvent);
}
};
// 为画布添加键盘事件监听
const canvasElement = reactFlowWrapper.current;
if (canvasElement) {
canvasElement.addEventListener('keydown', handleKeyDown);
document.addEventListener('paste', handlePasteNode);
}
return () => {
if (canvasElement) {
canvasElement.removeEventListener('keydown', handleKeyDown);
}
document.removeEventListener('paste', handlePasteNode);
};
}, [handlePasteNode]);
// 监听边的变化,处理添加节点的触发 // 监听边的变化,处理添加节点的触发
useEffect(() => { useEffect(() => {
const edgeToAddNode = edges.find(edge => edge.data?.addNodeTrigger); const edgeToAddNode = edges.find(edge => edge.data?.addNodeTrigger);
@ -243,6 +279,12 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini
} }
return ( return (
<div
ref={reactFlowWrapper}
style={{ width: '100%', height: '100%' }}
onContextMenu={(e) => e.preventDefault()}
tabIndex={0} // 使div可获得焦点以接收键盘事件
>
<HistoryProvider <HistoryProvider
initialNodes={nodes} initialNodes={nodes}
initialEdges={edges} initialEdges={edges}
@ -310,6 +352,7 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini
handleRun={handleRun} handleRun={handleRun}
/> />
</HistoryProvider> </HistoryProvider>
</div>
); );
}; };

@ -1,18 +1,33 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import FlowEditor from '@/pages/flowEditor/index'; import FlowEditor from '@/pages/flowEditor/index';
import { useSelector } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import { getAppEventData, getAppEventList } from '@/api/appEvent'; import { getAppEventList } from '@/api/appEvent';
import { getTopicList } from '@/api/event';
import { updateEventTopicList } from '@/store/ideContainer';
const ApplicationContainer = () => { const ApplicationContainer = () => {
const { info } = useSelector((state: any) => state.ideContainer); const { info } = useSelector((state: any) => state.ideContainer);
const [appFlowList, setAppFlowList] = useState([]); const [appFlowList, setAppFlowList] = useState([]);
const dispatch = useDispatch();
const getAppFlowList = async () => { const getAppFlowList = async () => {
const res: any = await getAppEventList(info.id); const res: any = await getAppEventList(info.id);
if (res.code === 200) setAppFlowList(res.data); if (res.code === 200) setAppFlowList(res.data);
}; };
const getEventList = async () => {
const res: any = await getTopicList(info.id);
if (res.code === 200) {
dispatch(updateEventTopicList(res.data.map(v => {
return { label: v.eventName, value: v.topic };
})));
}
};
useEffect(() => { useEffect(() => {
getEventList();
getAppFlowList(); getAppFlowList();
}, []); }, []);

@ -9,6 +9,7 @@ interface IDEContainerState {
projectComponentData: any; projectComponentData: any;
currentAppData: any; currentAppData: any;
eventList: any; eventList: any;
eventTopicList: any;
logBarStatus?: boolean; logBarStatus?: boolean;
socketId: string; socketId: string;
nodeStatusMap: Record<string, string>; // 节点状态映射 nodeStatusMap: Record<string, string>; // 节点状态映射
@ -24,6 +25,7 @@ const initialState: IDEContainerState = {
projectComponentData: {}, // 工程下的组件列表 projectComponentData: {}, // 工程下的组件列表
currentAppData: {}, // 当前选中的应用数据 currentAppData: {}, // 当前选中的应用数据
eventList: [], // 工程下的事件列表 eventList: [], // 工程下的事件列表
eventTopicList: [], // 应用编排使用的事件名和topic列表
logBarStatus: false, logBarStatus: false,
socketId: '', // 工程的socketId socketId: '', // 工程的socketId
nodeStatusMap: {}, // 初始化节点状态映射 nodeStatusMap: {}, // 初始化节点状态映射
@ -56,6 +58,9 @@ const ideContainerSlice = createSlice({
updateEventList(state, action) { updateEventList(state, action) {
state.eventList = action.payload; state.eventList = action.payload;
}, },
updateEventTopicList(state, action) {
state.eventTopicList = action.payload;
},
updateLogBarStatus(state, action) { updateLogBarStatus(state, action) {
state.logBarStatus = action.payload; state.logBarStatus = action.payload;
}, },
@ -87,6 +92,7 @@ export const {
updateProjectComponentData, updateProjectComponentData,
updateCurrentAppData, updateCurrentAppData,
updateEventList, updateEventList,
updateEventTopicList,
updateLogBarStatus, updateLogBarStatus,
updateSocketId, updateSocketId,
updateNodeStatus, updateNodeStatus,

Loading…
Cancel
Save