This commit is contained in:
dengqichen 2025-10-21 18:03:47 +08:00
parent 40d0ba918f
commit a59bd47cd0
4 changed files with 101 additions and 80 deletions

View File

@ -13,12 +13,12 @@ export const EndEventNodeDefinition: BaseNodeDefinition = {
// 渲染配置(配置驱动) // 渲染配置(配置驱动)
renderConfig: { renderConfig: {
shape: 'rounded-rect', shape: 'ellipse',
size: { width: 80, height: 48 }, size: { width: 120, height: 50 },
icon: { icon: {
type: 'emoji', type: 'emoji',
content: '⏹️', content: '⏹️',
size: 32 size: 24
}, },
theme: { theme: {
primary: '#ef4444', primary: '#ef4444',

View File

@ -13,12 +13,12 @@ export const StartEventNodeDefinition: BaseNodeDefinition = {
// 渲染配置(配置驱动) // 渲染配置(配置驱动)
renderConfig: { renderConfig: {
shape: 'rounded-rect', shape: 'ellipse',
size: { width: 80, height: 48 }, size: { width: 120, height: 50 },
icon: { icon: {
type: 'emoji', type: 'emoji',
content: '▶️', content: '▶️',
size: 32 size: 24
}, },
theme: { theme: {
primary: '#10b981', primary: '#10b981',

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { Handle, Position, NodeProps } from '@xyflow/react'; import { Handle, Position, NodeProps } from '@xyflow/react';
import type { FlowNodeData } from '../../types'; import type { FlowNodeData } from '../../types';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
/** /**
* BaseNode - shadcn/ui * BaseNode - shadcn/ui
@ -16,32 +17,29 @@ const BaseNode: React.FC<NodeProps> = ({ data, selected }) => {
const config = definition.renderConfig; const config = definition.renderConfig;
// 获取输出字段简要信息 // 渲染输出字段标签
const getOutputsSummary = () => { const renderOutputSection = () => {
if (!nodeData.outputs || nodeData.outputs.length === 0) { if (!nodeData.outputs || nodeData.outputs.length === 0) {
return null; return null;
} }
return nodeData.outputs.map(output => output.name).join(', '); return (
}; <div className="space-y-2">
{/* 输出标签 - 靠左 */}
// shadcn 风格容器类名 <div className="text-xs text-muted-foreground font-medium"></div>
const getContainerClass = () => { {/* 输出字段标签 */}
// 基础样式 - shadcn card 风格 <div className="flex flex-wrap gap-1.5">
// 使用 items-start 让图标和文本顶部对齐 {nodeData.outputs.map((output, index) => (
const baseClass = ` <code
relative inline-flex items-start gap-2 px-3 py-2 key={index}
bg-background border border-border className="text-xs px-2 py-1 bg-secondary text-secondary-foreground rounded-md font-mono inline-flex items-center"
rounded-lg shadow-sm title={output.title || output.description}
transition-all duration-200 >
hover:shadow-md hover:border-primary/50 {output.name}
`; </code>
))}
// 选中状态 - shadcn 的 ring 效果 </div>
const selectedClass = selected </div>
? 'border-primary shadow-md ring-2 ring-ring ring-offset-2 ring-offset-background' );
: '';
return `${baseClass} ${selectedClass}`;
}; };
// 图标容器 - shadcn 风格 // 图标容器 - shadcn 风格
@ -51,20 +49,20 @@ const BaseNode: React.FC<NodeProps> = ({ data, selected }) => {
<div <div
className=" className="
flex items-center justify-center flex items-center justify-center
rounded-md rounded-lg
transition-transform duration-200 transition-all duration-200
group-hover:scale-110 group-hover:scale-105
flex-shrink-0 flex-shrink-0
mt-0.5 shadow-sm
" "
style={{ style={{
width: '32px', width: '36px',
height: '32px', height: '36px',
background: `linear-gradient(135deg, ${config.theme.primary}, ${config.theme.secondary})`, background: `linear-gradient(135deg, ${config.theme.primary}, ${config.theme.secondary})`,
}} }}
> >
<span <span
className="text-white text-base leading-none" className="text-white text-lg leading-none"
> >
{config.icon.content} {config.icon.content}
</span> </span>
@ -76,9 +74,9 @@ const BaseNode: React.FC<NodeProps> = ({ data, selected }) => {
// shadcn 风格连接点 // shadcn 风格连接点
const handleClass = ` const handleClass = `
!w-2.5 !h-2.5 !rounded-full !border-2 !border-background !w-3 !h-3 !rounded-full !border-2 !border-background
transition-all duration-200 transition-all duration-200
hover:!w-3 hover:!h-3 hover:!w-3.5 hover:!h-3.5 hover:!shadow-md
`; `;
const getHandleStyle = () => ({ const getHandleStyle = () => ({
@ -107,50 +105,73 @@ const BaseNode: React.FC<NodeProps> = ({ data, selected }) => {
); );
}; };
// 椭圆形节点(开始/结束)使用特殊布局
const isEllipse = config.shape === 'ellipse';
return ( return (
<div className="group"> <div className="group relative">
<div className={getContainerClass()}> {/* 输入连接点 */}
{/* 输入连接点 */} {config.handles.input && (
{config.handles.input && ( <Handle
<Handle type="target"
type="target" position={Position.Left}
position={Position.Left} className={handleClass}
className={handleClass} style={getHandleStyle()}
style={getHandleStyle()} />
/> )}
)}
{/* 图标 */} {/* 使用 shadcn Card 组件 */}
{renderIcon()} <Card
className={`
{/* 标签和输出信息 - 垂直布局 */} ${isEllipse ? 'rounded-full px-6 py-3' : 'min-w-[240px] max-w-[420px]'}
<div className="flex flex-col gap-0.5 min-w-0"> ${selected ? 'ring-2 ring-primary ring-offset-2 ring-offset-background' : ''}
{/* 节点名称 */} transition-all duration-200
<div className="text-sm font-medium text-foreground whitespace-nowrap"> hover:shadow-lg hover:border-primary/50
{nodeData.label || definition.nodeName} `}
>
{isEllipse ? (
// 椭圆形节点:紧凑布局,图标+文字居中
<div className="flex items-center justify-center gap-3 px-2">
{renderIcon()}
<span className="text-sm font-semibold whitespace-nowrap">
{nodeData.label || definition.nodeName}
</span>
</div> </div>
) : (
// 普通节点:使用 Card 的标准布局
<>
<CardHeader className="pb-3">
{/* 图标 + 标题 */}
<div className="flex items-center gap-3">
{renderIcon()}
<CardTitle className="text-base">
{nodeData.label || definition.nodeName}
</CardTitle>
</div>
</CardHeader>
{/* 输出能力简要信息 */} {/* 输出部分 */}
{getOutputsSummary() && ( {renderOutputSection() && (
<div className="text-[10px] text-muted-foreground"> <CardContent className="pt-0">
{getOutputsSummary()} {renderOutputSection()}
</div> </CardContent>
)} )}
</div> </>
{/* 输出连接点 */}
{config.handles.output && (
<Handle
type="source"
position={Position.Right}
className={handleClass}
style={getHandleStyle()}
/>
)} )}
</Card>
{/* 配置徽章 */} {/* 输出连接点 */}
{renderBadge()} {config.handles.output && (
</div> <Handle
type="source"
position={Position.Right}
className={handleClass}
style={getHandleStyle()}
/>
)}
{/* 配置徽章 */}
{renderBadge()}
</div> </div>
); );
}; };

View File

@ -72,7 +72,7 @@ export interface NodeSize {
} }
// 节点形状 // 节点形状
export type NodeShape = 'circle' | 'rounded-rect' | 'rect' | 'diamond'; export type NodeShape = 'circle' | 'rounded-rect' | 'rect' | 'diamond' | 'ellipse';
// 图标配置 // 图标配置
export interface IconConfig { export interface IconConfig {