This commit is contained in:
dengqichen 2025-10-21 10:31:09 +08:00
parent a8c512eee6
commit 051709ed2a
3 changed files with 135 additions and 146 deletions

View File

@ -26,6 +26,7 @@ interface FlowCanvasProps {
onEdgeClick?: (event: React.MouseEvent, edge: any) => void; onEdgeClick?: (event: React.MouseEvent, edge: any) => void;
onDrop?: (event: React.DragEvent) => void; onDrop?: (event: React.DragEvent) => void;
onDragOver?: (event: React.DragEvent) => void; onDragOver?: (event: React.DragEvent) => void;
onViewportChange?: () => void;
className?: string; className?: string;
} }
@ -38,6 +39,7 @@ const FlowCanvas: React.FC<FlowCanvasProps> = ({
onEdgeClick, onEdgeClick,
onDrop, onDrop,
onDragOver, onDragOver,
onViewportChange,
className = '' className = ''
}) => { }) => {
const [nodes, , onNodesStateChange] = useNodesState(initialNodes); const [nodes, , onNodesStateChange] = useNodesState(initialNodes);
@ -133,6 +135,7 @@ const FlowCanvas: React.FC<FlowCanvasProps> = ({
onEdgeClick={onEdgeClick} onEdgeClick={onEdgeClick}
onDrop={handleDrop} onDrop={handleDrop}
onDragOver={handleDragOver} onDragOver={handleDragOver}
onMove={onViewportChange}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
isValidConnection={isValidConnection} isValidConnection={isValidConnection}
fitView fitView

View File

@ -1,11 +1,10 @@
import React from 'react'; import React from 'react';
import { Button, Space, Divider, Tooltip } from 'antd'; import { Button, Divider, Tooltip } from 'antd';
import { import {
SaveOutlined, SaveOutlined,
UndoOutlined, UndoOutlined,
RedoOutlined, RedoOutlined,
CopyOutlined, CopyOutlined,
ScissorOutlined,
DeleteOutlined, DeleteOutlined,
ZoomInOutlined, ZoomInOutlined,
ZoomOutOutlined, ZoomOutOutlined,
@ -20,8 +19,6 @@ interface WorkflowToolbarProps {
onUndo?: () => void; onUndo?: () => void;
onRedo?: () => void; onRedo?: () => void;
onCopy?: () => void; onCopy?: () => void;
onCut?: () => void;
onPaste?: () => void;
onDelete?: () => void; onDelete?: () => void;
onZoomIn?: () => void; onZoomIn?: () => void;
onZoomOut?: () => void; onZoomOut?: () => void;
@ -40,8 +37,6 @@ const WorkflowToolbar: React.FC<WorkflowToolbarProps> = ({
onUndo, onUndo,
onRedo, onRedo,
onCopy, onCopy,
onCut,
onPaste,
onDelete, onDelete,
onZoomIn, onZoomIn,
onZoomOut, onZoomOut,
@ -67,7 +62,7 @@ const WorkflowToolbar: React.FC<WorkflowToolbarProps> = ({
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)' boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)'
}} }}
> >
{/* 左侧:标题和返回按钮 */} {/* 左侧:返回按钮和标题 */}
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}> <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
<Tooltip title="返回列表"> <Tooltip title="返回列表">
<Button <Button
@ -78,7 +73,6 @@ const WorkflowToolbar: React.FC<WorkflowToolbarProps> = ({
/> />
</Tooltip> </Tooltip>
<Divider type="vertical" /> <Divider type="vertical" />
<div>
<h2 style={{ <h2 style={{
margin: 0, margin: 0,
fontSize: '16px', fontSize: '16px',
@ -87,33 +81,10 @@ const WorkflowToolbar: React.FC<WorkflowToolbarProps> = ({
}}> }}>
{title} {title}
</h2> </h2>
<div style={{
fontSize: '12px',
color: '#6b7280',
marginTop: '2px'
}}>
React Flow
</div>
</div>
</div> </div>
{/* 中间:主要操作按钮 */} {/* 右侧:操作按钮区域 */}
<div style={{ display: 'flex', alignItems: 'center' }}> <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Space size={4}>
{/* 保存 */}
<Tooltip title="保存工作流">
<Button
type="primary"
icon={<SaveOutlined />}
onClick={onSave}
size="small"
>
</Button>
</Tooltip>
<Divider type="vertical" />
{/* 撤销/重做 */} {/* 撤销/重做 */}
<Tooltip title="撤销"> <Tooltip title="撤销">
<Button <Button
@ -145,14 +116,6 @@ const WorkflowToolbar: React.FC<WorkflowToolbarProps> = ({
size="small" size="small"
/> />
</Tooltip> </Tooltip>
<Tooltip title="剪切">
<Button
type="text"
icon={<ScissorOutlined />}
onClick={onCut}
size="small"
/>
</Tooltip>
<Tooltip title="删除选中"> <Tooltip title="删除选中">
<Button <Button
type="text" type="text"
@ -198,34 +161,36 @@ const WorkflowToolbar: React.FC<WorkflowToolbarProps> = ({
size="small" size="small"
/> />
</Tooltip> </Tooltip>
</Space>
</div>
{/* 右侧:状态信息 */} <Divider type="vertical" />
<div style={{
display: 'flex', {/* 缩放比例显示 */}
alignItems: 'center',
gap: '12px',
fontSize: '12px',
color: '#6b7280'
}}>
<div style={{ <div style={{
background: '#f3f4f6', background: '#f3f4f6',
padding: '4px 8px', padding: '4px 8px',
borderRadius: '4px', borderRadius: '4px',
fontFamily: 'monospace' fontSize: '12px',
color: '#6b7280',
fontFamily: 'monospace',
minWidth: '70px',
textAlign: 'center'
}}> }}>
: {Math.round(zoom * 100)}% {Math.round(zoom * 100)}%
</div>
<div style={{
background: '#ecfdf5',
color: '#065f46',
padding: '4px 8px',
borderRadius: '4px',
fontWeight: '500'
}}>
React Flow
</div> </div>
<Divider type="vertical" />
{/* 保存按钮(最右侧) */}
<Tooltip title="保存工作流">
<Button
type="primary"
icon={<SaveOutlined />}
onClick={onSave}
size="small"
>
</Button>
</Tooltip>
</div> </div>
</div> </div>
); );

View File

@ -34,6 +34,7 @@ const WorkflowDesignInner: React.FC = () => {
} = useReactFlow(); } = useReactFlow();
const [workflowTitle, setWorkflowTitle] = useState('新建工作流'); const [workflowTitle, setWorkflowTitle] = useState('新建工作流');
const [currentZoom, setCurrentZoom] = useState(1); // 当前缩放比例
const reactFlowWrapper = useRef<HTMLDivElement>(null); const reactFlowWrapper = useRef<HTMLDivElement>(null);
// 当前工作流ID // 当前工作流ID
@ -67,6 +68,11 @@ const WorkflowDesignInner: React.FC = () => {
loadData(); loadData();
}, [currentWorkflowId, loadWorkflow, setNodes, setEdges]); }, [currentWorkflowId, loadWorkflow, setNodes, setEdges]);
// 初始化缩放比例
useEffect(() => {
setCurrentZoom(getZoom());
}, [getZoom]);
// 自动适应视图 // 自动适应视图
useEffect(() => { useEffect(() => {
// 延迟执行fitView以确保节点已渲染 // 延迟执行fitView以确保节点已渲染
@ -77,10 +83,12 @@ const WorkflowDesignInner: React.FC = () => {
minZoom: 1.0, // 最小缩放100% minZoom: 1.0, // 最小缩放100%
maxZoom: 1.0 // 最大缩放100%确保默认100% maxZoom: 1.0 // 最大缩放100%确保默认100%
}); });
// 更新zoom显示
setTimeout(() => setCurrentZoom(getZoom()), 850);
}, 100); }, 100);
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [fitView]); }, [fitView, getZoom]);
// 初始化示例节点 - 优化位置和布局 // 初始化示例节点 - 优化位置和布局
const initialNodes: FlowNode[] = [ const initialNodes: FlowNode[] = [
@ -217,15 +225,21 @@ const WorkflowDesignInner: React.FC = () => {
const handleFitView = useCallback(() => { const handleFitView = useCallback(() => {
fitView({ padding: 0.2, duration: 800 }); fitView({ padding: 0.2, duration: 800 });
}, [fitView]); // 延迟更新zoom值以获取最新的缩放比例
setTimeout(() => setCurrentZoom(getZoom()), 850);
}, [fitView, getZoom]);
const handleZoomIn = useCallback(() => { const handleZoomIn = useCallback(() => {
zoomIn({ duration: 300 }); zoomIn({ duration: 300 });
}, [zoomIn]); // 延迟更新zoom值以获取最新的缩放比例
setTimeout(() => setCurrentZoom(getZoom()), 350);
}, [zoomIn, getZoom]);
const handleZoomOut = useCallback(() => { const handleZoomOut = useCallback(() => {
zoomOut({ duration: 300 }); zoomOut({ duration: 300 });
}, [zoomOut]); // 延迟更新zoom值以获取最新的缩放比例
setTimeout(() => setCurrentZoom(getZoom()), 350);
}, [zoomOut, getZoom]);
// 处理节点拖拽放置 - 使用官方推荐的screenToFlowPosition方法 // 处理节点拖拽放置 - 使用官方推荐的screenToFlowPosition方法
const handleDrop = useCallback((event: React.DragEvent) => { const handleDrop = useCallback((event: React.DragEvent) => {
@ -352,6 +366,12 @@ const WorkflowDesignInner: React.FC = () => {
setConfigEdge(null); setConfigEdge(null);
}, []); }, []);
// 监听视图变化(缩放、平移等)
const handleViewportChange = useCallback(() => {
const zoom = getZoom();
setCurrentZoom(zoom);
}, [getZoom]);
return ( return (
<div <div
className="workflow-design-container" className="workflow-design-container"
@ -375,7 +395,7 @@ const WorkflowDesignInner: React.FC = () => {
onFitView={handleFitView} onFitView={handleFitView}
canUndo={false} canUndo={false}
canRedo={false} canRedo={false}
zoom={getZoom()} zoom={currentZoom}
/> />
{/* 主要内容区域 */} {/* 主要内容区域 */}
@ -394,6 +414,7 @@ const WorkflowDesignInner: React.FC = () => {
onNodeClick={handleNodeClick} onNodeClick={handleNodeClick}
onEdgeClick={handleEdgeClick} onEdgeClick={handleEdgeClick}
onDrop={handleDrop} onDrop={handleDrop}
onViewportChange={handleViewportChange}
className="workflow-canvas" className="workflow-canvas"
/> />
</div> </div>