112 lines
3.4 KiB
TypeScript
112 lines
3.4 KiB
TypeScript
import React, { useState, useMemo } from 'react';
|
|
import { BaseEdge, EdgeLabelRenderer, EdgeProps, getSmoothStepPath, useReactFlow } from '@xyflow/react';
|
|
import type { FlowNode } from '../types';
|
|
import { convertToDisplayName } from '@/utils/workflow/variableConversion';
|
|
|
|
/**
|
|
* 自定义边组件
|
|
* 支持hover高亮和样式优化
|
|
*/
|
|
const CustomEdge: React.FC<EdgeProps> = ({
|
|
id,
|
|
sourceX,
|
|
sourceY,
|
|
targetX,
|
|
targetY,
|
|
sourcePosition,
|
|
targetPosition,
|
|
style = {},
|
|
markerEnd,
|
|
label,
|
|
selected,
|
|
}) => {
|
|
const [isHovered, setIsHovered] = useState(false);
|
|
const { getNodes } = useReactFlow();
|
|
|
|
// 将 label 中的 UUID 转换为节点名
|
|
const displayLabel = useMemo(() => {
|
|
if (!label || typeof label !== 'string') {
|
|
return label;
|
|
}
|
|
|
|
const allNodes = getNodes() as FlowNode[];
|
|
return convertToDisplayName(label, allNodes);
|
|
}, [label, getNodes]);
|
|
|
|
const [edgePath, labelX, labelY] = getSmoothStepPath({
|
|
sourceX,
|
|
sourceY,
|
|
sourcePosition,
|
|
targetX,
|
|
targetY,
|
|
targetPosition,
|
|
});
|
|
|
|
// 根据状态确定样式
|
|
const edgeStyle = {
|
|
...style,
|
|
stroke: selected ? '#3b82f6' : isHovered ? '#64748b' : '#94a3b8',
|
|
strokeWidth: selected ? 3 : isHovered ? 2.5 : 2,
|
|
transition: 'all 0.2s ease',
|
|
};
|
|
|
|
const markerEndStyle = (() => {
|
|
if (!markerEnd || typeof markerEnd !== 'object') return markerEnd;
|
|
if (selected) return { ...markerEnd, color: '#3b82f6' };
|
|
if (isHovered) return { ...markerEnd, color: '#64748b' };
|
|
return markerEnd;
|
|
})();
|
|
|
|
return (
|
|
<>
|
|
<BaseEdge
|
|
id={id}
|
|
path={edgePath}
|
|
style={edgeStyle}
|
|
markerEnd={markerEndStyle}
|
|
/>
|
|
|
|
{/* 增加点击区域 */}
|
|
<path
|
|
d={edgePath}
|
|
fill="none"
|
|
stroke="transparent"
|
|
strokeWidth={20}
|
|
style={{ cursor: 'pointer' }}
|
|
onMouseEnter={() => setIsHovered(true)}
|
|
onMouseLeave={() => setIsHovered(false)}
|
|
/>
|
|
|
|
{/* 边标签 */}
|
|
{displayLabel && (
|
|
<EdgeLabelRenderer>
|
|
<div
|
|
style={{
|
|
position: 'absolute',
|
|
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
|
fontSize: '12px',
|
|
fontWeight: '500',
|
|
color: selected ? '#3b82f6' : '#64748b',
|
|
background: 'white',
|
|
padding: '4px 10px',
|
|
borderRadius: '6px',
|
|
border: `1px solid ${selected ? '#3b82f6' : '#e5e7eb'}`,
|
|
boxShadow: '0 2px 6px rgba(0,0,0,0.1)',
|
|
pointerEvents: 'all',
|
|
cursor: 'pointer',
|
|
transition: 'all 0.2s ease',
|
|
}}
|
|
onMouseEnter={() => setIsHovered(true)}
|
|
onMouseLeave={() => setIsHovered(false)}
|
|
>
|
|
{displayLabel}
|
|
</div>
|
|
</EdgeLabelRenderer>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default CustomEdge;
|
|
|