From 81b76f150b64a77a80a2cf44434d4e3c2b72d641 Mon Sep 17 00:00:00 2001 From: ZivvW <1194291667@qq.com> Date: Fri, 9 May 2025 15:56:59 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix(extension):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=88=86=E7=BB=84=E5=85=8B=E9=9A=86=E6=97=B6=EF=BC=8C=E8=BE=B9?= =?UTF-8?q?=E4=B8=A2=E5=A4=B1=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/extension/src/dynamic-group/index.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/extension/src/dynamic-group/index.ts b/packages/extension/src/dynamic-group/index.ts index ed3192a68..514f55cd7 100644 --- a/packages/extension/src/dynamic-group/index.ts +++ b/packages/extension/src/dynamic-group/index.ts @@ -496,7 +496,7 @@ export class DynamicGroup { // 1. 存储 children 内部节点相关的输入边(incoming) allRelatedEdges.push( - ...[...tempChildNode.incoming.edges, ...tempChildNode.outgoing.edges], + ...[...childNode.incoming.edges, ...childNode.outgoing.edges], ) if (childNodeChildren instanceof Set) { @@ -545,11 +545,8 @@ export class DynamicGroup { const sourceId = nodeIdMap[sourceNodeId] ?? sourceNodeId const targetId = nodeIdMap[targetNodeId] ?? targetNodeId - // 如果是有 id 且 text 是对象的边,需要重新计算位置,否则直接用 edgeConfig 生成边 let newEdgeConfig = cloneDeep(edge) - if (edge.id && typeof edge.text === 'object' && edge.text !== null) { - newEdgeConfig = transformEdgeData(edge as EdgeData, distance) - } + newEdgeConfig = transformEdgeData(edge as EdgeData, distance) return this.lf.graphModel.addEdge({ ...newEdgeConfig, From 31a9a7c16f3dac9d4cf7514690d847e6f15b9db3 Mon Sep 17 00:00:00 2001 From: ZivvW <1194291667@qq.com> Date: Sat, 10 May 2025 17:38:21 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix(extension):=20=E5=88=86=E7=BB=84?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=E6=97=B6edge=E9=87=8D=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/extension/src/dynamic-group/index.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/extension/src/dynamic-group/index.ts b/packages/extension/src/dynamic-group/index.ts index 514f55cd7..a9df87072 100644 --- a/packages/extension/src/dynamic-group/index.ts +++ b/packages/extension/src/dynamic-group/index.ts @@ -7,7 +7,16 @@ import LogicFlow, { transformNodeData, transformEdgeData, } from '@logicflow/core' -import { assign, cloneDeep, filter, forEach, has, map, sortBy } from 'lodash-es' +import { + assign, + cloneDeep, + filter, + forEach, + has, + map, + sortBy, + uniqBy, +} from 'lodash-es' import { DynamicGroupNode } from './node' import { DynamicGroupNodeModel } from './model' import { isAllowMoveTo, isBoundsInGroup } from './utils' @@ -719,7 +728,7 @@ export class DynamicGroup { model as DynamicGroupNodeModel, distance, ) - edgesInnerGroup.push(...edgesData) + edgesInnerGroup.push(...uniqBy(edgesData, 'id')) } }) From b775d596647f256b540ed6117b5ee0810d530f20 Mon Sep 17 00:00:00 2001 From: ZivvW <1194291667@qq.com> Date: Tue, 13 May 2025 15:43:24 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat(core):=20=E5=A4=8D=E5=88=B6=E7=B2=98?= =?UTF-8?q?=E8=B4=B4=E5=BF=AB=E6=8D=B7=E9=94=AE=E4=BD=BF=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E7=8B=AC=E7=AB=8B=EF=BC=88addElements=20->?= =?UTF-8?q?=20cloneElements=EF=BC=89=EF=BC=8C=E5=B9=B6=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=E7=B2=98=E8=B4=B4=E8=BE=B9=E6=97=B6=E5=85=B6?= =?UTF-8?q?=20anchorId=20=E6=9C=AA=E6=9B=B4=E6=96=B0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/LogicFlow.tsx | 72 ++++++++++++++++--- packages/core/src/keyboard/shortcut.ts | 14 +--- packages/extension/src/dynamic-group/index.ts | 69 +++++++++++++++--- .../extension/src/materials/group/index.ts | 2 +- 4 files changed, 121 insertions(+), 36 deletions(-) diff --git a/packages/core/src/LogicFlow.tsx b/packages/core/src/LogicFlow.tsx index cfa3b181c..85ddaf8b2 100644 --- a/packages/core/src/LogicFlow.tsx +++ b/packages/core/src/LogicFlow.tsx @@ -19,7 +19,11 @@ import { formatData } from './util' import { Dnd, snapline } from './view/behavior' import Tool from './tool' import History from './history' -import Keyboard, { initDefaultShortcut } from './keyboard' +import Keyboard, { + initDefaultShortcut, + transformEdgeData, + transformNodeData, +} from './keyboard' import { EventCallback, CallbackArgs, EventArgs } from './event/eventEmitter' import { ElementType, EventType, SegmentDirection } from './constant' @@ -691,25 +695,17 @@ export class LogicFlow { * @param edges * @param distance */ - addElements({ nodes, edges }: GraphConfigData, distance = 40): GraphElements { - // TODO: 1. 解决下面方法中 distance 传参缺未使用的问题;该方法在快捷键中有调用 - // TODO: 2. review 一下本函数代码逻辑,确认 nodeIdMap 的作用,看是否有优化的空间 - console.log('distance', distance) - const nodeIdMap: Record = {} + addElements({ nodes, edges }: GraphConfigData): GraphElements { const elements: GraphElements = { nodes: [], edges: [], } forEach(nodes, (node) => { - const nodeId = node.id const nodeModel = this.addNode(node) - if (nodeId) nodeIdMap[nodeId] = nodeModel.id elements.nodes.push(nodeModel) }) forEach(edges, (edge) => { - let { sourceNodeId, targetNodeId } = edge - if (nodeIdMap[sourceNodeId]) sourceNodeId = nodeIdMap[sourceNodeId] - if (nodeIdMap[targetNodeId]) targetNodeId = nodeIdMap[targetNodeId] + const { sourceNodeId, targetNodeId } = edge const edgeModel = this.graphModel.addEdge({ ...edge, sourceNodeId, @@ -720,6 +716,60 @@ export class LogicFlow { return elements } + cloneElements({ nodes, edges }: GraphData, distance = 40): GraphElements { + const nodeIdMap: Record = {} + const anchorIdMap: Record = {} + const elements: GraphElements = { + nodes: [], + edges: [], + } + forEach(nodes, (node) => { + const originNodeId = node.id + const nodeModel = this.addNode(transformNodeData(node, distance)) + if (originNodeId) { + nodeIdMap[originNodeId] = nodeModel.id + const originNodeModel = this.getNodeModelById(originNodeId) + if (originNodeModel) { + // 建立源节点和新节点的锚点映射 + const newNodeAnchors = nodeModel.anchors + originNodeModel.anchors.forEach((originNodeAnchor, index) => { + if (originNodeAnchor.id && newNodeAnchors[index].id) { + // @ts-ignore + anchorIdMap[originNodeAnchor.id] = newNodeAnchors[index].id + } + }) + } + } + elements.nodes.push(nodeModel) + }) + forEach(edges, (edge) => { + let { sourceNodeId, targetNodeId, sourceAnchorId, targetAnchorId } = edge + if (nodeIdMap[sourceNodeId]) sourceNodeId = nodeIdMap[sourceNodeId] + if (nodeIdMap[targetNodeId]) targetNodeId = nodeIdMap[targetNodeId] + if (sourceAnchorId && targetAnchorId) { + if (anchorIdMap[sourceAnchorId]) + sourceAnchorId = anchorIdMap[sourceAnchorId] + if (anchorIdMap[targetAnchorId]) + targetAnchorId = anchorIdMap[targetAnchorId] + } + + const edgeModel = this.graphModel.addEdge( + transformEdgeData( + { + ...edge, + sourceNodeId, + targetNodeId, + sourceAnchorId, + targetAnchorId, + }, + distance, + ), + ) + elements.edges.push(edgeModel) + }) + return elements + } + /** * 将图形选中 * @param id 选择元素ID diff --git a/packages/core/src/keyboard/shortcut.ts b/packages/core/src/keyboard/shortcut.ts index b749cbb40..f9b105fc8 100644 --- a/packages/core/src/keyboard/shortcut.ts +++ b/packages/core/src/keyboard/shortcut.ts @@ -135,12 +135,6 @@ export function initDefaultShortcut(lf: LogicFlow, graph: GraphModel) { return true } selected = elements - selected.nodes.forEach((node) => - translateNodeData(node, TRANSLATION_DISTANCE), - ) - selected.edges.forEach((edge) => - translateEdgeData(edge, TRANSLATION_DISTANCE), - ) return false }) // 粘贴 @@ -149,19 +143,13 @@ export function initDefaultShortcut(lf: LogicFlow, graph: GraphModel) { if (graph.textEditElement) return true if (selected && (selected.nodes || selected.edges)) { lf.clearSelectElements() - const addElements = lf.addElements( + const addElements = lf.cloneElements( selected, CHILDREN_TRANSLATION_DISTANCE, ) if (!addElements) return true addElements.nodes.forEach((node) => lf.selectElementById(node.id, true)) addElements.edges.forEach((edge) => lf.selectElementById(edge.id, true)) - selected.nodes.forEach((node) => - translateNodeData(node, TRANSLATION_DISTANCE), - ) - selected.edges.forEach((edge) => - translateEdgeData(edge, TRANSLATION_DISTANCE), - ) CHILDREN_TRANSLATION_DISTANCE = CHILDREN_TRANSLATION_DISTANCE + TRANSLATION_DISTANCE } diff --git a/packages/extension/src/dynamic-group/index.ts b/packages/extension/src/dynamic-group/index.ts index a9df87072..b806779ee 100644 --- a/packages/extension/src/dynamic-group/index.ts +++ b/packages/extension/src/dynamic-group/index.ts @@ -21,7 +21,7 @@ import { DynamicGroupNode } from './node' import { DynamicGroupNodeModel } from './model' import { isAllowMoveTo, isBoundsInGroup } from './utils' -import GraphConfigData = LogicFlow.GraphConfigData +import GraphData = LogicFlow.GraphData import GraphElements = LogicFlow.GraphElements import EdgeConfig = LogicFlow.EdgeConfig import EdgeData = LogicFlow.EdgeData @@ -475,6 +475,7 @@ export class DynamicGroup { */ initGroupChildNodes( nodeIdMap: Record, + anchorIdMap: Record, children: Set, curGroup: DynamicGroupNodeModel, distance: number, @@ -501,6 +502,13 @@ export class DynamicGroup { curGroup.addChild(tempChildNode.id) nodeIdMap[childId] = tempChildNode.id // id 同 childId,做映射存储 + childNode.anchors.forEach((anchor, index) => { + if (anchor.id && tempChildNode.anchors[index].id) { + // @ts-ignore + anchorIdMap[anchor.id] = tempChildNode.anchors[index].id + } + }) + allChildNodes.push(tempChildNode) // 1. 存储 children 内部节点相关的输入边(incoming) @@ -511,6 +519,7 @@ export class DynamicGroup { if (childNodeChildren instanceof Set) { const { childNodes, edgesData } = this.initGroupChildNodes( nodeIdMap, + anchorIdMap, childNodeChildren, tempChildNode as DynamicGroupNodeModel, distance, @@ -548,11 +557,25 @@ export class DynamicGroup { createEdge( edge: EdgeConfig | EdgeData, nodeIdMap: Record, + anchorIdMap: Record, distance: number, ) { - const { sourceNodeId, targetNodeId } = edge + const { + sourceNodeId, + targetNodeId, + sourceAnchorId: originSourceAnchorId, + targetAnchorId: originTargetAnchorId, + } = edge const sourceId = nodeIdMap[sourceNodeId] ?? sourceNodeId const targetId = nodeIdMap[targetNodeId] ?? targetNodeId + let sourceAnchorId = originSourceAnchorId + if (originSourceAnchorId) { + sourceAnchorId = anchorIdMap[originSourceAnchorId] ?? originSourceAnchorId + } + let targetAnchorId = originTargetAnchorId + if (originTargetAnchorId) { + targetAnchorId = anchorIdMap[originTargetAnchorId] ?? originTargetAnchorId + } let newEdgeConfig = cloneDeep(edge) newEdgeConfig = transformEdgeData(edge as EdgeData, distance) @@ -561,6 +584,8 @@ export class DynamicGroup { ...newEdgeConfig, sourceNodeId: sourceId, targetNodeId: targetId, + sourceAnchorId: sourceAnchorId, + targetAnchorId: targetAnchorId, }) } @@ -695,14 +720,16 @@ export class DynamicGroup { lf.on('group:add-node', this.onGroupAddNode) // https://github.com/didi/LogicFlow/issues/1346 - // 重写 addElements() 方法,在 addElements() 原有基础上增加对 group 内部所有 nodes 和 edges 的复制功能 - // 使用场景:addElements api 项目内部目前只在快捷键粘贴时使用(此处解决的也应该是粘贴场景的问题) - lf.addElements = ( - { nodes: selectedNodes, edges: selectedEdges }: GraphConfigData, + // 重写 cloneElements() 方法,在 cloneElements() 原有基础上增加对 group 内部所有 nodes 和 edges 的复制功能 + // 使用场景: cloneElements api 项目内部目前只在快捷键粘贴时使用(此处解决的也应该是粘贴场景的问题) + lf.cloneElements = ( + { nodes: selectedNodes, edges: selectedEdges }: GraphData, distance = 40, ): GraphElements => { // oldNodeId -> newNodeId 映射 Map const nodeIdMap: Record = {} + // oldAnchorId -> newAnchorId 映射 Map + const anchorIdMap: Record = {} // 本次添加的所有节点和边 const elements: GraphElements = { nodes: [], @@ -715,15 +742,33 @@ export class DynamicGroup { const originId = node.id const children = node.properties?.children ?? node.children - const model = lf.addNode(this.removeChildrenInGroupNodeData(node)) - - if (originId) nodeIdMap[originId] = model.id + const newNodeConfig = transformNodeData( + this.removeChildrenInGroupNodeData(node), + distance, + ) + const model = lf.addNode(newNodeConfig) + + if (originId) { + nodeIdMap[originId] = model.id + const originNodeModel = lf.getNodeModelById(originId) + if (originNodeModel) { + // 建立源节点和新节点的锚点映射 + const newNodeAnchors = model.anchors + originNodeModel.anchors.forEach((originNodeAnchor, index) => { + if (originNodeAnchor.id && newNodeAnchors[index].id) { + // @ts-ignore + anchorIdMap[originNodeAnchor.id] = newNodeAnchors[index].id + } + }) + } + } elements.nodes.push(model) // 此时为 group 的 nodeModel // TODO: 递归创建 group 的 nodeModel 的 children if (model.isGroup) { const { edgesData } = this.initGroupChildNodes( nodeIdMap, + anchorIdMap, children, model as DynamicGroupNodeModel, distance, @@ -733,10 +778,12 @@ export class DynamicGroup { }) forEach(edgesInnerGroup, (edge) => { - this.createEdge(edge, nodeIdMap, distance) + this.createEdge(edge, nodeIdMap, anchorIdMap, distance) }) forEach(selectedEdges, (edge) => { - elements.edges.push(this.createEdge(edge, nodeIdMap, distance)) + elements.edges.push( + this.createEdge(edge, nodeIdMap, anchorIdMap, distance), + ) }) // 返回 elements 进行选中效果,即触发 element.selectElementById() diff --git a/packages/extension/src/materials/group/index.ts b/packages/extension/src/materials/group/index.ts index 032c54a5a..31245d93e 100644 --- a/packages/extension/src/materials/group/index.ts +++ b/packages/extension/src/materials/group/index.ts @@ -66,7 +66,7 @@ export class Group { // https://github.com/didi/LogicFlow/issues/1346 // 重写 addElements() 方法,在 addElements() 原有基础上增加对 group 内部所有 nodes 和 edges 的复制功能 - lf.addElements = ( + lf.cloneElements = ( { nodes: selectedNodes, edges: selectedEdges }: GraphConfigData, distance: number, ): {