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,34 +17,31 @@ 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">
{/* 输出标签 - 靠左 */}
<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 风格容器类名
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}`;
};
// 图标容器 - shadcn 风格 // 图标容器 - shadcn 风格
const renderIcon = () => { const renderIcon = () => {
if (config.icon.type === 'emoji') { if (config.icon.type === 'emoji') {
@ -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 组件 */}
{/* 图标 */} <Card
{renderIcon()} 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' : ''}
<div className="flex flex-col gap-0.5 min-w-0"> transition-all duration-200
{/* 节点名称 */} hover:shadow-lg hover:border-primary/50
<div className="text-sm font-medium text-foreground whitespace-nowrap"> `}
{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 的标准布局
{getOutputsSummary() && ( <>
<div className="text-[10px] text-muted-foreground"> <CardHeader className="pb-3">
{getOutputsSummary()} {/* 图标 + 标题 */}
</div> <div className="flex items-center gap-3">
)} {renderIcon()}
</div> <CardTitle className="text-base">
{nodeData.label || definition.nodeName}
{/* 输出连接点 */} </CardTitle>
{config.handles.output && ( </div>
<Handle </CardHeader>
type="source"
position={Position.Right} {/* 输出部分 */}
className={handleClass} {renderOutputSection() && (
style={getHandleStyle()} <CardContent className="pt-0">
/> {renderOutputSection()}
</CardContent>
)}
</>
)} )}
</Card>
{/* 配置徽章 */}
{renderBadge()} {/* 输出连接点 */}
</div> {config.handles.output && (
<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 {