deploy-ease-platform/frontend/src/pages/Workflow2/Design/components/NodePanel.tsx
dengqichen c08c99f422 1
2025-10-21 10:17:32 +08:00

189 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from 'react';
import { NodeCategory } from '../types';
import { NODE_DEFINITIONS } from '../nodes';
import type { WorkflowNodeDefinition } from '../nodes/types';
// 图标映射函数
const getNodeIcon = (iconName: string): string => {
const iconMap: Record<string, string> = {
'play-circle': '▶️',
'stop-circle': '⏹️',
'user': '👤',
'api': '⚙️',
'code': '📜',
'build': '🚀',
'jenkins': '🔨',
'gateway': '💎'
};
return iconMap[iconName] || '📋';
};
interface NodePanelProps {
className?: string;
}
const NodePanel: React.FC<NodePanelProps> = ({ className = '' }) => {
// 按分类分组节点
const nodesByCategory = NODE_DEFINITIONS.reduce((acc, node) => {
if (!acc[node.category]) {
acc[node.category] = [];
}
acc[node.category].push(node);
return acc;
}, {} as Record<NodeCategory, WorkflowNodeDefinition[]>);
// 拖拽开始处理
const handleDragStart = (event: React.DragEvent, nodeDefinition: WorkflowNodeDefinition) => {
event.dataTransfer.setData('application/reactflow', JSON.stringify({
nodeType: nodeDefinition.nodeType,
nodeDefinition
}));
event.dataTransfer.effectAllowed = 'move';
};
// 渲染节点项
const renderNodeItem = (nodeDefinition: WorkflowNodeDefinition) => (
<div
key={nodeDefinition.nodeCode}
draggable
style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
padding: '8px 12px',
borderRadius: '6px',
border: '1px solid #e5e7eb',
background: 'white',
cursor: 'grab',
transition: 'all 0.2s ease-in-out',
marginBottom: '6px'
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = '#f9fafb';
e.currentTarget.style.borderColor = nodeDefinition.uiConfig.style.fill;
e.currentTarget.style.transform = 'translateX(2px)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'white';
e.currentTarget.style.borderColor = '#e5e7eb';
e.currentTarget.style.transform = 'translateX(0)';
}}
onDragStart={(e) => {
e.currentTarget.style.cursor = 'grabbing';
handleDragStart(e, nodeDefinition);
}}
onDragEnd={(e) => {
e.currentTarget.style.cursor = 'grab';
}}
>
<div style={{
fontSize: '16px',
width: '20px',
textAlign: 'center',
color: nodeDefinition.uiConfig.style.fill
}}>
{getNodeIcon(nodeDefinition.uiConfig.style.icon)}
</div>
<div>
<div style={{
fontSize: '13px',
fontWeight: '500',
color: '#374151',
lineHeight: '1.2'
}}>
{nodeDefinition.nodeName}
</div>
<div style={{
fontSize: '11px',
color: '#6b7280',
lineHeight: '1.2',
marginTop: '2px'
}}>
{nodeDefinition.description}
</div>
</div>
</div>
);
// 分类标题映射
const categoryTitles = {
[NodeCategory.EVENT]: '🎯 事件节点',
[NodeCategory.TASK]: '📋 任务节点',
[NodeCategory.GATEWAY]: '🔀 网关节点',
[NodeCategory.CONTAINER]: '📦 容器节点'
};
return (
<div className={`node-panel ${className}`} style={{
width: '260px',
height: '100%',
background: '#f8fafc',
borderRight: '1px solid #e5e7eb',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column'
}}>
<div style={{
padding: '16px',
background: 'white',
borderBottom: '1px solid #e5e7eb'
}}>
<h3 style={{
margin: 0,
fontSize: '14px',
fontWeight: '600',
color: '#374151'
}}>
</h3>
<p style={{
margin: '4px 0 0 0',
fontSize: '12px',
color: '#6b7280'
}}>
</p>
</div>
<div style={{
flex: '1',
overflow: 'auto',
padding: '12px'
}}>
{Object.entries(nodesByCategory).map(([category, nodes]) => (
<div key={category} style={{ marginBottom: '16px' }}>
<div style={{
fontSize: '12px',
fontWeight: '600',
color: '#4b5563',
marginBottom: '8px',
padding: '4px 0',
borderBottom: '1px solid #e5e7eb'
}}>
{categoryTitles[category as NodeCategory]}
</div>
{nodes.map(renderNodeItem)}
</div>
))}
</div>
{/* 使用提示 */}
<div style={{
padding: '12px',
background: '#f1f5f9',
borderTop: '1px solid #e5e7eb',
fontSize: '11px',
color: '#64748b',
lineHeight: '1.4'
}}>
💡
<br />
<br />
<br />
</div>
</div>
);
};
export default NodePanel;