From 1d222da2dc508ba6f6345fca07de1395f5b7d1bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=88=9A=E8=BE=B0=E5=85=88=E7=94=9F?= Date: Thu, 5 Dec 2024 09:34:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=89=E6=96=B9=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/FlowDesigner/index.module.css | 69 +++++++++- .../Edit/components/FlowDesigner/index.tsx | 124 ++++++++++++++++-- .../Edit/components/FlowDesigner/nodes.ts | 71 +++++++--- .../Edit/components/FormDesigner/index.tsx | 8 +- 4 files changed, 239 insertions(+), 33 deletions(-) diff --git a/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.module.css b/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.module.css index 7af1a028..4df1be3a 100644 --- a/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.module.css +++ b/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.module.css @@ -1,6 +1,6 @@ .container { width: 100%; - height: 600px; + min-height: 600px; position: relative; background: #fff; border: 1px solid #e8e8e8; @@ -10,7 +10,24 @@ .canvas { flex: 1; - height: 100%; + min-height: 600px; + position: relative; + overflow: hidden; + background: #fafafa; +} + +.canvas::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: + linear-gradient(#e8e8e8 1px, transparent 1px), + linear-gradient(90deg, #e8e8e8 1px, transparent 1px); + background-size: 20px 20px; + opacity: 0.5; } .configPanel { @@ -18,10 +35,58 @@ height: 100%; border-left: 1px solid #e8e8e8; overflow-y: auto; + background: #fff; + box-shadow: -2px 0 8px rgba(0, 0, 0, 0.06); } :global(.lf-control) { position: absolute; right: 330px; top: 10px; + display: flex; + gap: 8px; + background: #fff; + padding: 4px; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + z-index: 2; +} + +:global(.lf-dnd-panel) { + position: absolute; + left: 10px; + top: 10px; + width: 160px; + background: #fff; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + padding: 12px; + border-radius: 4px; + display: flex; + flex-direction: column; + gap: 8px; + z-index: 2; +} + +:global(.lf-dnd-panel .lf-dnd-item) { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 35px; + border: 1px solid #e8e8e8; + border-radius: 4px; + cursor: grab; + user-select: none; + transition: all 0.3s; +} + +:global(.lf-dnd-panel .lf-dnd-item:hover) { + border-color: #1890ff; + color: #1890ff; +} + +:global(.lf-dnd-panel .lf-dnd-item img) { + width: 20px; + height: 20px; + margin-right: 8px; } \ No newline at end of file diff --git a/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.tsx b/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.tsx index 126c4773..63eaf167 100644 --- a/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.tsx +++ b/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useRef, useState } from 'react'; -import { Card, Space } from 'antd'; +import { Card } from 'antd'; import LogicFlow from '@logicflow/core'; -import { DndPanel, SelectionSelect, Control } from '@logicflow/extension'; +import { DndPanel, SelectionSelect, Control, Menu } from '@logicflow/extension'; import '@logicflow/core/es/index.css'; import '@logicflow/extension/es/style/index.css'; import styles from './index.module.css'; @@ -21,20 +21,95 @@ const FlowDesigner: React.FC = ({ const [lf, setLf] = useState(); const [selectedNode, setSelectedNode] = useState(null); - useEffect(() => { - if (containerRef.current) { - // 初始化 LogicFlow + // 初始化 LogicFlow + const initLogicFlow = () => { + if (!containerRef.current) { + console.error('Container ref is not ready'); + return; + } + + try { + // 注册插件 + LogicFlow.use(DndPanel); + LogicFlow.use(SelectionSelect); + LogicFlow.use(Control); + LogicFlow.use(Menu); + const logicflow = new LogicFlow({ container: containerRef.current, grid: true, - plugins: [DndPanel, SelectionSelect, Control] + nodeTextEdit: true, + edgeTextEdit: true, + width: containerRef.current.offsetWidth || 800, + height: containerRef.current.offsetHeight || 600, + style: { + circle: { + r: 30, + stroke: '#000000', + strokeWidth: 1, + }, + rect: { + width: 100, + height: 50, + stroke: '#000000', + strokeWidth: 1, + }, + diamond: { + rx: 20, + ry: 20, + stroke: '#000000', + strokeWidth: 1, + }, + nodeText: { + fontSize: 12, + color: '#000000', + }, + edgeText: { + fontSize: 12, + color: '#000000', + background: { + fill: '#FFFFFF', + }, + }, + }, }); + console.log('LogicFlow instance created'); + // 注册自定义节点 registerNodes(logicflow); + // 配置拖拽面板 + logicflow.setPatternItems([ + { + type: 'start', + text: '开始节点', + label: '开始节点', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVDhP7ZQxDsIwDEV7AcZeggOwMXAFJg7BxMgBuAcrZ2DiGuwcgImNA3AFBgaWDhUlqhKnKWFh4EtWaif2s/OdAn8VY8wE13jEHR6wjUu0eIoL7ODBa0zQzlrswQWvcOcLSpzhFu2sxRN0wTZKLnGOT7jgHXZQcoYuOMKvkFZQcoMuKOdTkH4RpBWUdNEFU0idOsE7bnBYEL0gScvnkE5LgS44wA4+UHY5R3vMMtqZxB4+UQZkl+1M4hTf0K7Szjp4wQztrIMXlEFZQUkXL5jBf4KygpIuXjCDsoLyXwQzKCsoSWGwD3Q5p3qCzuvYAAAAAElFTkSuQmCC' + }, + { + type: 'task', + text: '任务节点', + label: '任务节点', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAB5SURBVDhP7ZTBCYAwDEU7QkfxZi/eHMGbo3Rwhe7hCB3Bo+kHIZgmTUXQgw8k5Cd5tE34VUSkhw3ucYUTrNBiggUecYsNWkzwCyvc4QMtJviFJZ7wCS0mGIQVnvEFLSb4hRVe8A0tJhiEFd7wAy0m+IUVZmHxPyLZAYNFt+rXusajAAAAAElFTkSuQmCC' + }, + { + type: 'gateway', + text: '网关节点', + label: '网关节点', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACxSURBVDhP7ZQxDoMwDEU5AiM34AqMHXqFTj0EE2MP0L1bz8DENeicgYmNA3AFBgaWVrIiRyZRU1VIPPIT2Mb+NlYS8FeJMQ5wjkvc4QYnWKHFU5zjFtdeY4Z21mIHF7zAjS8ocYprtLMWj9AF2yi5wBk+4II32EHJKbrgAL9CWkHJFbqgnE9B+kWQVlDSRRdMIXXqCG+4wn5B9IIkLZ9DOi0FumAfW3hH2eUU7THLaGcSO/gBUb+nujmI/XsAAAAASUVORK5CYII=' + }, + { + type: 'end', + text: '结束节点', + label: '结束节点', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVDhP7ZQxDsIwDEV7AcZeggOwMXAFJg7BxMgBuAcrZ2DiGuwcgImNA3AFBgaWDhUlqhKnKWFh4EtWaif2s/OdAn8VY8wE13jEHR6wjUu0eIoL7ODBa0zQzlrswQWvcOcLSpzhFu2sxRN0wTZKLnGOT7jgHXZQcoYuOMKvkFZQcoMuKOdTkH4RpBWUdNEFU0idOsE7bnBYEL0gScvnkE5LgS44wA4+UHY5R3vMMtqZxB4+UQZkl+1M4hTf0K7Szjp4wQztrIMXlEFZQUkXL5jBf4KygpIuXjCDsoLyXwQzKCsoSWGwD3Q5p3qCzuvYAAAAAElFTkSuQmCC' + } + ]); + // 注册事件 logicflow.on('node:click', ({ data }) => { + console.log('Node clicked:', data); setSelectedNode(data); }); @@ -42,22 +117,45 @@ const FlowDesigner: React.FC = ({ setSelectedNode(null); }); - // 如果有初始值,渲染流程图 + logicflow.on('history:change', () => { + const graphData = logicflow.getGraphData(); + onChange?.(JSON.stringify(graphData)); + }); + + // 渲染初始数据 if (value) { try { const graphData = JSON.parse(value); logicflow.render(graphData); + console.log('Initial graph data rendered:', graphData); } catch (error) { console.error('Failed to parse graph data:', error); + logicflow.render({ nodes: [], edges: [] }); } + } else { + logicflow.render({ nodes: [], edges: [] }); + console.log('Empty graph rendered'); } setLf(logicflow); + console.log('LogicFlow initialization completed'); - return () => { - logicflow.destroy(); - }; + return logicflow; + } catch (error) { + console.error('Failed to initialize LogicFlow:', error); + return null; } + }; + + useEffect(() => { + const logicflow = initLogicFlow(); + + return () => { + if (logicflow) { + console.log('Destroying LogicFlow instance'); + logicflow.destroy(); + } + }; }, []); // 处理节点配置更新 @@ -74,7 +172,11 @@ const FlowDesigner: React.FC = ({ return (
-
+
{selectedNode && (
= ({ } }); + const form = useForm(); + const handleSubmit = (formData: any) => { console.log('Form data:', formData); }; @@ -44,6 +45,7 @@ const FormDesigner: React.FC = ({