fix(flow): stabilize preview import and dynamic-group rendering

- hide dynamic-group containers in preview graph sanitization

- keep dynamic-group plugin registered in render-only/interactive presets

- refresh canvas immediately after JSON import
This commit is contained in:
2026-02-28 00:38:53 +08:00
parent 92e482e854
commit 1b7596954a
3 changed files with 19 additions and 9 deletions

View File

@@ -111,12 +111,15 @@ const sanitizeLabelProperty = (properties: unknown): Record<string, any> | undef
return nextProperties return nextProperties
} }
const sanitizeGraphData = (input?: GraphData | null): GraphData => { const sanitizeGraphData = (
input?: GraphData | null,
options?: { hideDynamicGroups?: boolean }
): GraphData => {
if (!input || !Array.isArray(input.nodes) || !Array.isArray(input.edges)) { if (!input || !Array.isArray(input.nodes) || !Array.isArray(input.edges)) {
return { nodes: [], edges: [] } return { nodes: [], edges: [] }
} }
const nodes = input.nodes const rawNodes = input.nodes
.filter((node): node is NodeData => isPlainObject(node)) .filter((node): node is NodeData => isPlainObject(node))
.map((node) => { .map((node) => {
const nextNode: NodeData = { ...node } const nextNode: NodeData = { ...node }
@@ -127,6 +130,12 @@ const sanitizeGraphData = (input?: GraphData | null): GraphData => {
return nextNode return nextNode
}) })
const hiddenDynamicGroup = options?.hideDynamicGroups === true
const nodes = hiddenDynamicGroup
? rawNodes.filter((node) => node.type !== 'dynamic-group')
: rawNodes
const nodeIdSet = new Set(nodes.map((node) => node.id))
const edges = input.edges const edges = input.edges
.filter((edge): edge is EdgeData => isPlainObject(edge)) .filter((edge): edge is EdgeData => isPlainObject(edge))
.map((edge) => { .map((edge) => {
@@ -137,6 +146,7 @@ const sanitizeGraphData = (input?: GraphData | null): GraphData => {
} }
return nextEdge return nextEdge
}) })
.filter((edge) => !hiddenDynamicGroup || (nodeIdSet.has(edge.sourceNodeId) && nodeIdSet.has(edge.targetNodeId)))
return { nodes, edges } return { nodes, edges }
} }
@@ -328,7 +338,7 @@ const initPreviewMode = () => {
// 渲染数据 // 渲染数据
if (props.data) { if (props.data) {
previewLf.value.render(sanitizeGraphData(props.data)) previewLf.value.render(sanitizeGraphData(props.data, { hideDynamicGroups: true }))
} }
} }
@@ -362,7 +372,7 @@ const getGraphData = (): GraphData | null => {
} }
const setGraphData = (data: GraphData) => { const setGraphData = (data: GraphData) => {
const safeData = sanitizeGraphData(data) const safeData = sanitizeGraphData(data, { hideDynamicGroups: props.mode === 'preview' })
if (props.mode === 'edit') { if (props.mode === 'edit') {
const lfInstance = getLogicFlowInstance() const lfInstance = getLogicFlowInstance()
if (lfInstance) { if (lfInstance) {

View File

@@ -928,7 +928,7 @@ const handleImport = () => {
const target = e.target as FileReader; const target = e.target as FileReader;
const data = JSON.parse(target.result as string); const data = JSON.parse(target.result as string);
filesStore.importData(data); filesStore.importData(data);
// refreshLogicFlowCanvas('LogicFlow 画布已重新渲染(导入数据)'); refreshLogicFlowCanvas('LogicFlow 画布已重新渲染(导入数据)');
} catch (error) { } catch (error) {
console.error('Failed to import file', error); console.error('Failed to import file', error);
showMessage('error', '文件格式错误'); showMessage('error', '文件格式错误');

View File

@@ -1,5 +1,5 @@
import type LogicFlow from '@logicflow/core' import type LogicFlow from '@logicflow/core'
import { Menu, Label, Snapshot, SelectionSelect, MiniMap, Control } from '@logicflow/extension' import { Menu, Label, Snapshot, SelectionSelect, MiniMap, Control, DynamicGroup } from '@logicflow/extension'
import { register } from '@logicflow/vue-node-registry' import { register } from '@logicflow/vue-node-registry'
import ImageNode from './components/flow/nodes/common/ImageNode.vue' import ImageNode from './components/flow/nodes/common/ImageNode.vue'
@@ -27,8 +27,9 @@ const DEFAULT_FLOW_NODES: FlowNodeRegistration[] = [
] ]
const FLOW_PLUGIN_PRESETS: Record<FlowCapabilityLevel, FlowPlugin[]> = { const FLOW_PLUGIN_PRESETS: Record<FlowCapabilityLevel, FlowPlugin[]> = {
'render-only': [Snapshot], // 预览模式也需要 DynamicGroup避免包含 dynamic-group 节点的图在只读渲染时报错
interactive: [Menu, Label, Snapshot, SelectionSelect, MiniMap, Control] 'render-only': [DynamicGroup, Snapshot],
interactive: [DynamicGroup, Menu, Label, Snapshot, SelectionSelect, MiniMap, Control]
} }
export function getFlowPluginsByCapability(capability: FlowCapabilityLevel): FlowPlugin[] { export function getFlowPluginsByCapability(capability: FlowCapabilityLevel): FlowPlugin[] {
@@ -60,4 +61,3 @@ export function registerFlowNodes(lfInstance: LogicFlow, nodes?: FlowNodeRegistr
const registrations = resolveFlowNodes(nodes) const registrations = resolveFlowNodes(nodes)
registrations.forEach((registration) => register(registration, lfInstance)) registrations.forEach((registration) => register(registration, lfInstance))
} }