重构消息通知弹窗

This commit is contained in:
dengqichen 2025-11-14 18:08:31 +08:00
parent 5e23c7b17f
commit 2dd4e11191
3 changed files with 22 additions and 106 deletions

View File

@ -52,6 +52,7 @@
"@react-form-builder/designer-bundle": "^7.4.0",
"@reduxjs/toolkit": "^2.0.1",
"@tanstack/react-virtual": "^3.13.12",
"@tisoap/react-flow-smart-edge": "^4.0.1",
"@types/recharts": "^1.8.29",
"@types/uuid": "^10.0.0",
"@xyflow/react": "^12.8.6",

View File

@ -131,6 +131,9 @@ importers:
'@tanstack/react-virtual':
specifier: ^3.13.12
version: 3.13.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tisoap/react-flow-smart-edge':
specifier: ^4.0.1
version: 4.0.1(@xyflow/react@12.9.0(@types/react@18.3.18)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2)
'@types/recharts':
specifier: ^1.8.29
version: 1.8.29
@ -2045,6 +2048,14 @@ packages:
'@tanstack/virtual-core@3.13.12':
resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==}
'@tisoap/react-flow-smart-edge@4.0.1':
resolution: {integrity: sha512-tqyQyaQFDc4QIL3Kw9UL9QVWId4cSuVNqKsDRzdPH1Mf8YMrwB8/dq/BMFEbuGaln9B3wlgeyhQx3vn6E9lpJA==}
peerDependencies:
'@xyflow/react': '>=12'
react: '>=18'
react-dom: '>=18'
typescript: '>=5'
'@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@ -6409,6 +6420,13 @@ snapshots:
'@tanstack/virtual-core@3.13.12': {}
'@tisoap/react-flow-smart-edge@4.0.1(@xyflow/react@12.9.0(@types/react@18.3.18)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2)':
dependencies:
'@xyflow/react': 12.9.0(@types/react@18.3.18)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
typescript: 5.7.2
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.26.3

View File

@ -17,7 +17,7 @@ import '@xyflow/react/dist/style.css';
import type { FlowNode, FlowEdge } from '../types';
import { nodeTypes } from '../nodes';
import CustomEdge from './CustomEdge';
import SmartEdge from './SmartEdge';
import { generateEdgeId } from '../utils/idGenerator';
interface FlowCanvasProps {
@ -48,109 +48,6 @@ const FlowCanvas: React.FC<FlowCanvasProps> = ({
const [nodes, , onNodesStateChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesStateChange] = useEdgesState(initialEdges);
// --- Auto adjust edge vertices when nodes move (preserve shape, avoid excessive length) ---
useEffect(() => {
// helper: get node position by id
const getNodePos = (id: string) => {
const n = nodes.find((nn) => nn.id === id);
return n?.position;
};
// geometry helpers
const recomputeVertices = (
oldVerts: Array<{ x: number; y: number }> | undefined,
oldA: { x: number; y: number } | undefined,
oldB: { x: number; y: number } | undefined,
newA: { x: number; y: number },
newB: { x: number; y: number }
) => {
// default three vertices placed along the baseline
const defaultThree = () => [
{ x: newA.x + (newB.x - newA.x) * 0.25, y: newA.y + (newB.y - newA.y) * 0.25 },
{ x: (newA.x + newB.x) / 2, y: (newA.y + newB.y) / 2 },
{ x: newA.x + (newB.x - newA.x) * 0.75, y: newA.y + (newB.y - newA.y) * 0.75 },
];
// if no old endpoints or old vertices, return default
if (!oldA || !oldB || !oldVerts || oldVerts.length !== 3) {
return defaultThree();
}
const projOnLine = (p: { x: number; y: number }, a: { x: number; y: number }, b: { x: number; y: number }) => {
const dx = b.x - a.x, dy = b.y - a.y;
const len2 = dx * dx + dy * dy || 1;
const t = Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2));
const px = a.x + t * dx, py = a.y + t * dy;
const offx = p.x - px, offy = p.y - py;
// sign: which side of baseline (using cross product sign)
const cross = dx * (p.y - a.y) - dy * (p.x - a.x);
const sign = cross >= 0 ? 1 : -1;
const offMag = Math.hypot(offx, offy);
return { t, offMag, sign };
};
const mapToNew = (t: number, offMag: number, sign: number, a: { x: number; y: number }, b: { x: number; y: number }) => {
const dx = b.x - a.x, dy = b.y - a.y;
const len = Math.hypot(dx, dy) || 1;
// unit normal for new baseline
const nx = -dy / len, ny = dx / len;
// clamp offset to avoid huge bulges (auto shrink if nodes far apart)
const maxOffset = Math.max(20, 0.35 * len);
const useOff = Math.min(offMag, maxOffset);
const baseX = a.x + t * dx, baseY = a.y + t * dy;
return { x: baseX + sign * useOff * nx, y: baseY + sign * useOff * ny };
};
// Compute mapping (t, offset) from old baseline, then apply to new baseline
const tOff = oldVerts.map((v) => projOnLine(v, oldA, oldB));
// enforce monotonic t order to keep vertex order stable
tOff.sort((a, b) => a.t - b.t);
const mapped = tOff.map((m) => mapToNew(m.t, m.offMag, m.sign, newA, newB));
// ensure exactly 3 vertices
if (mapped.length === 3) return mapped as Array<{ x: number; y: number }>;
// fallback
return defaultThree();
};
setEdges((eds) => {
return eds.map((ed) => {
const s = getNodePos(ed.source);
const t = getNodePos(ed.target);
if (!s || !t) return ed;
const dataAny = (ed as any).data || {};
const last = dataAny._lastEndpoints as { sx: number; sy: number; tx: number; ty: number } | undefined;
const current = { sx: s.x, sy: s.y, tx: t.x, ty: t.y };
// Initialize vertices if missing
const curVerts = (dataAny.vertices as any) as Array<{ x: number; y: number }> | undefined;
let vertices = curVerts;
let changed = false;
if (!Array.isArray(vertices) || vertices.length !== 3) {
vertices = recomputeVertices(undefined, undefined, undefined, { x: current.sx, y: current.sy }, { x: current.tx, y: current.ty });
changed = true;
}
// Recompute when endpoints moved
if (!last || last.sx !== current.sx || last.sy !== current.sy || last.tx !== current.tx || last.ty !== current.ty) {
vertices = recomputeVertices(vertices, last ? { x: last.sx, y: last.sy } : undefined, last ? { x: last.tx, y: last.ty } : undefined, { x: current.sx, y: current.sy }, { x: current.tx, y: current.ty });
changed = true;
}
if (!changed) return ed;
return {
...ed,
data: {
...ed.data,
vertices,
_lastEndpoints: current,
},
} as any;
});
});
}, [nodes, setEdges]);
// 处理边重连
const onReconnect = useCallback((oldEdge: Edge, newConnection: Connection) => {
setEdges((els) => reconnectEdge(oldEdge, newConnection, els));
@ -164,7 +61,7 @@ const FlowCanvas: React.FC<FlowCanvasProps> = ({
source: params.source!,
target: params.target!,
type: 'smoothstep',
animated: true,
animated: false,
style: {
stroke: '#94a3b8',
strokeWidth: 2,
@ -282,7 +179,7 @@ const FlowCanvas: React.FC<FlowCanvasProps> = ({
onDragOver={handleDragOver}
onMove={onViewportChange}
nodeTypes={nodeTypes}
edgeTypes={{ smoothstep: CustomEdge }}
edgeTypes={{ smoothstep: SmartEdge }}
isValidConnection={isValidConnection}
defaultEdgeOptions={{
type: 'smoothstep',