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: {
shape: 'rounded-rect',
size: { width: 80, height: 48 },
shape: 'ellipse',
size: { width: 120, height: 50 },
icon: {
type: 'emoji',
content: '⏹️',
size: 32
size: 24
},
theme: {
primary: '#ef4444',

View File

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

View File

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