fix: 修复条件节点收起后分支连线丢失

- 在 tinyflow 条件节点收起态保留分支 handle,避免连线因 DOM 卸载消失

- 在节点折叠切换后刷新 xyflow 内部信息,确保连线锚点位置同步
This commit is contained in:
2026-03-07 21:23:01 +08:00
parent c3e3ba505d
commit 0031c71594
2 changed files with 64 additions and 9 deletions

View File

@@ -1,5 +1,12 @@
<script lang="ts">
import {Handle, type NodeProps, NodeToolbar, Position, useSvelteFlow} from '@xyflow/svelte';
import {
Handle,
type NodeProps,
NodeToolbar,
Position,
useSvelteFlow,
useUpdateNodeInternals
} from '@xyflow/svelte';
import {Button, Collapse, FloatingTrigger, Input, Textarea} from '../base';
import {type Snippet} from 'svelte';
import {useDeleteNode} from '../utils/useDeleteNode.svelte';
@@ -41,6 +48,7 @@
let activeKeys = data.expand ? ['key'] : [];
const { updateNodeData, getNode } = useSvelteFlow();
const updateNodeInternals = useUpdateNodeInternals();
const items = $derived.by(() => {
return [{
@@ -244,6 +252,7 @@
<div class="tf-node-wrapper-body">
<Collapse {items} activeKeys={activeKeys} onChange={(_,actionKeys) => {
updateNodeData(id, {expand: actionKeys?.includes('key')})
updateNodeInternals(id);
onCollapse?.(actionKeys?.includes('key') ? 'key' : '')
}} />
</div>

View File

@@ -1,6 +1,13 @@
<script lang="ts">
import NodeWrapper from '../core/NodeWrapper.svelte';
import {type Edge, Handle, type NodeProps, Position, useSvelteFlow} from '@xyflow/svelte';
import {
type Edge,
Handle,
type NodeProps,
Position,
useSvelteFlow,
useUpdateNodeInternals
} from '@xyflow/svelte';
import {Button, Input, MixedInput, Select} from '../base';
import ParamTokenEditor from '../core/ParamTokenEditor.svelte';
import {getCurrentNodeId} from '#components/utils/NodeUtils';
@@ -75,6 +82,7 @@
const currentNodeId = getCurrentNodeId();
const { updateNodeData } = useSvelteFlow();
const updateNodeInternals = useUpdateNodeInternals();
const refOptions = useRefOptions(false);
let activeBranchId = $state('');
@@ -214,6 +222,18 @@
return label || `条件分支${index + 1}`;
};
const isExpanded = () => Boolean(data?.expand);
const collapsedHandleTop = (index: number, count: number) => {
if (count <= 1) {
return '54px';
}
const start = 38;
const end = 74;
const step = (end - start) / (count - 1);
return `${start + index * step}px`;
};
const syncManagedEdges = (branches: ConditionBranch[]) => {
const branchMap = new Map(branches.map((branch) => [branch.id, branch]));
const currentEdges = store.getEdges();
@@ -568,6 +588,12 @@
syncManagedEdges(normalized.branches);
});
$effect(() => {
branches.map((branch) => branch.id).join('|');
isExpanded();
updateNodeInternals(currentNodeId);
});
</script>
<NodeWrapper {data} {...rest} showSourceHandle={false} allowSettingOfCondition={false}>
@@ -577,6 +603,20 @@
</svg>
{/snippet}
{#snippet handle()}
{#if !isExpanded()}
{#each branches as branch, index}
<Handle
type="source"
position={Position.Right}
id={`branch_${branch.id}`}
class="condition-branch-handle condition-branch-handle-collapsed"
style={`right: -28px; top: ${collapsedHandleTop(index, branches.length)};`}
/>
{/each}
{/if}
{/snippet}
<div class="condition-node-content">
<div class="condition-rail-head">
<div class="condition-rail-title">分支出口</div>
@@ -644,6 +684,7 @@
</svg>
</Button>
{/if}
{#if isExpanded()}
<Handle
type="source"
position={Position.Right}
@@ -651,6 +692,7 @@
class="condition-branch-handle"
style="right: -28px; top: 50%; transform: translateY(-50%);"
/>
{/if}
</div>
{/each}
</div>
@@ -876,6 +918,10 @@
box-shadow: none;
}
:global(.condition-branch-handle-collapsed) {
transform: translateY(-50%);
}
.condition-editor-actions-inline {
display: flex;
align-items: center;