From 052f454bef5898ec7acffeddd9f7e026432a0889 Mon Sep 17 00:00:00 2001 From: ZLY Date: Fri, 24 Oct 2025 10:54:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(flowEditor):=20=E5=AE=9E=E7=8E=B0=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=B2=98=E8=B4=B4=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BA=8B=E4=BB=B6=E5=88=97=E8=A1=A8=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowEditor/components/customEdge.tsx | 18 +- src/pages/flowEditor/index.tsx | 181 +++++++++++------- src/pages/orchestration/application/index.tsx | 19 +- src/store/ideContainer.ts | 6 + 4 files changed, 137 insertions(+), 87 deletions(-) diff --git a/src/pages/flowEditor/components/customEdge.tsx b/src/pages/flowEditor/components/customEdge.tsx index 3d111d7..6975803 100644 --- a/src/pages/flowEditor/components/customEdge.tsx +++ b/src/pages/flowEditor/components/customEdge.tsx @@ -26,7 +26,6 @@ const DataDisplayEdge: React.FC = ({ selected, data }) => { - const [options, setOptions] = useState([]); const [isOpen, setIsOpen] = useState(false); const [selectedValue, setSelectedValue] = useState(''); const dropdownRef = useRef(null); @@ -39,7 +38,7 @@ const DataDisplayEdge: React.FC = ({ targetPosition, borderRadius: 8 // 设置圆角半径 }); - const { info } = useSelector((state: any) => state.ideContainer); + const { info, eventTopicList } = useSelector((state: any) => state.ideContainer); // 从数据中获取悬停状态 const hovered = data?.hovered || false; @@ -69,19 +68,6 @@ const DataDisplayEdge: React.FC = ({ })); }; - 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(() => { const handleClickOutside = (event: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { @@ -163,7 +149,7 @@ const DataDisplayEdge: React.FC = ({ overflowY: 'auto' }} > - {options.map((option: { value: string; label: string }) => ( + {eventTopicList.map((option: { value: string; label: string }) => (
handleSelect(option)} diff --git a/src/pages/flowEditor/index.tsx b/src/pages/flowEditor/index.tsx index 247cc6d..5aed36b 100644 --- a/src/pages/flowEditor/index.tsx +++ b/src/pages/flowEditor/index.tsx @@ -86,6 +86,7 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini editNode, editEdge, copyNode, + pasteNode, // 添加粘贴节点功能 // Node operations addNodeOnEdge, @@ -200,11 +201,46 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini // 点击画布其他区域关闭菜单 const onPaneClick = useCallback(() => { setMenu(null); - // 关闭添加节点菜单 - setEdgeForNodeAdd(null); - setPositionForNodeAdd(null); }, [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(() => { const edgeToAddNode = edges.find(edge => edge.data?.addNodeTrigger); @@ -243,73 +279,80 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini } return ( - { - setNodes(newNodes); - setEdges(newEdges); - }} +
e.preventDefault()} + tabIndex={0} // 使div可获得焦点以接收键盘事件 > - - + { + setNodes(newNodes); + setEdges(newEdges); + }} + > + + +
); }; diff --git a/src/pages/orchestration/application/index.tsx b/src/pages/orchestration/application/index.tsx index d147529..d169cb2 100644 --- a/src/pages/orchestration/application/index.tsx +++ b/src/pages/orchestration/application/index.tsx @@ -1,18 +1,33 @@ import React, { useEffect, useState } from 'react'; import FlowEditor from '@/pages/flowEditor/index'; -import { useSelector } from 'react-redux'; -import { getAppEventData, getAppEventList } from '@/api/appEvent'; +import { useSelector, useDispatch } from 'react-redux'; +import { getAppEventList } from '@/api/appEvent'; +import { getTopicList } from '@/api/event'; +import { updateEventTopicList } from '@/store/ideContainer'; const ApplicationContainer = () => { const { info } = useSelector((state: any) => state.ideContainer); const [appFlowList, setAppFlowList] = useState([]); + const dispatch = useDispatch(); const getAppFlowList = async () => { const res: any = await getAppEventList(info.id); 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(() => { + getEventList(); getAppFlowList(); }, []); diff --git a/src/store/ideContainer.ts b/src/store/ideContainer.ts index 62b98ff..b49f5a2 100644 --- a/src/store/ideContainer.ts +++ b/src/store/ideContainer.ts @@ -9,6 +9,7 @@ interface IDEContainerState { projectComponentData: any; currentAppData: any; eventList: any; + eventTopicList: any; logBarStatus?: boolean; socketId: string; nodeStatusMap: Record; // 节点状态映射 @@ -24,6 +25,7 @@ const initialState: IDEContainerState = { projectComponentData: {}, // 工程下的组件列表 currentAppData: {}, // 当前选中的应用数据 eventList: [], // 工程下的事件列表 + eventTopicList: [], // 应用编排使用的事件名和topic列表 logBarStatus: false, socketId: '', // 工程的socketId nodeStatusMap: {}, // 初始化节点状态映射 @@ -56,6 +58,9 @@ const ideContainerSlice = createSlice({ updateEventList(state, action) { state.eventList = action.payload; }, + updateEventTopicList(state, action) { + state.eventTopicList = action.payload; + }, updateLogBarStatus(state, action) { state.logBarStatus = action.payload; }, @@ -87,6 +92,7 @@ export const { updateProjectComponentData, updateCurrentAppData, updateEventList, + updateEventTopicList, updateLogBarStatus, updateSocketId, updateNodeStatus,