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

View File

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