From 0235db527952086f62fcf5ff884d9fdfabf6ef2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=AF=E5=B0=91=E5=BA=B7?= <2496371772@qq.com> Date: Mon, 10 Aug 2026 16:26:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=90=8C=E6=AD=A5bug?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/sjtb/configTask/dataTable/antvX6.ts | 244 +++++++++++------- src/pages/sjtb/configTask/dataTable/index.vue | 149 +++++++---- 2 files changed, 249 insertions(+), 144 deletions(-) diff --git a/src/pages/sjtb/configTask/dataTable/antvX6.ts b/src/pages/sjtb/configTask/dataTable/antvX6.ts index f631260f..8b73a887 100644 --- a/src/pages/sjtb/configTask/dataTable/antvX6.ts +++ b/src/pages/sjtb/configTask/dataTable/antvX6.ts @@ -88,7 +88,7 @@ export class X6Graph { private createMenuElement() { this.menuElement = document.getElementById("ant-x6-menu4"); - if(this.menuElement){ + if (this.menuElement) { this.menuElement.remove(); } this.menuElement = document.createElement("div"); @@ -304,7 +304,7 @@ export class X6Graph { label: targetNode.attr("label/text") || "", level: nodeLevel, allParents: allParentsOrdered, // 包含父级+当前节点(均有label) - isTbsl:false, + isTbsl: false, }); } this.hideMenu(); @@ -346,7 +346,7 @@ export class X6Graph { id: nodeId, label: targetNode.attr("label/text") || "", level: nodeLevel, - isTbsl:true, + isTbsl: true, }); } this.hideMenu(); @@ -634,6 +634,7 @@ export class X6Graph { addChildNode( parentId: string, config: ChildNodeConfig, + skipRearrange = false, ): { node: Node; edge: Edge } { const graph = this.getGraph(); @@ -682,11 +683,23 @@ export class X6Graph { throw new Error(`创建连线(${parentId}→${childNode.id})失败`); } - // 重排同层级节点 - this.rearrangeLevelNodes(config.level); - // 新增:如果添加的是第三层级,同步重排第二层级 - if (config.level === "level3") { - this.rearrangeLevelNodes("level2"); + // // 重排同层级节点 + // this.rearrangeLevelNodes(config.level); + // // 新增:如果添加的是第三层级,同步重排第二层级 + // if (config.level === "level3") { + // this.rearrangeLevelNodes("level2"); + // } + + if (!skipRearrange) { + this.rearrangeLevelNodes(config.level); + // 新增:如果添加的是第三层级,同步重排第二层级 + if (config.level === "level3") { + this.rearrangeLevelNodes("level2"); + } + // 如果新增level4,要重排level3(父层级),父节点位置可能变化 + if (config.level === "level4") { + this.rearrangeLevelNodes("level3"); + } } return { node: childNode, edge: edge as Edge }; } @@ -697,9 +710,8 @@ export class X6Graph { private rearrangeLevelNodes(level: NodeLevel): void { const graph = this.getGraph(); const containerHeight = graph.container.clientHeight; - const centerY = (containerHeight - this.nodeStyle.height) / 2; // 画布中心点 + const centerY = (containerHeight - this.nodeStyle.height) / 2; - // 获取当前层级的所有节点(按标签排序) const levelNodes = graph .getNodes() .filter((node) => node.getData().level === level) @@ -709,16 +721,12 @@ export class X6Graph { return labelA.localeCompare(labelB, undefined, { numeric: true }); }); const totalNodes = levelNodes.length; - if (totalNodes === 0) return; - // ======================== 第二层级:间距根据自身第三层级子节点数量动态调整 ======================== if (level === "level2") { - // 1. 统计每个第二层级节点的第三层级子节点数量 const level2ChildCount = new Map(); levelNodes.forEach((node) => { const childIds = this.getChildNodeIds(node.id); - // 筛选出属于第三层级的子节点 const level3ChildCount = childIds.filter((childId) => { const childNode = graph.getCellById(childId) as Node; return childNode?.getData().level === "level3"; @@ -726,45 +734,34 @@ export class X6Graph { level2ChildCount.set(node.id, level3ChildCount); }); - // 2. 计算每个第二层级节点的动态间距(自身子节点越多,间距越大) - const baseGap = 100; // 基础间距 - const factor = 30; // 比例因子(每个子节点增加30px间距) - let currentY = centerY; // 起始Y坐标(后续会调整为整体居中) - const nodePositions = new Map(); // 记录每个节点的Y坐标 + const baseGap = 100; + const factor = 30; + let currentY = centerY; + const nodePositions = new Map(); - // 先计算第一个节点的位置(以画布中心为基准) const firstNode = levelNodes[0]; nodePositions.set(firstNode.id, currentY); - // 计算后续节点的位置(根据前一个节点的子节点数量动态调整间距) for (let i = 1; i < totalNodes; i++) { const prevNode = levelNodes[i - 1]; const prevChildCount = level2ChildCount.get(prevNode.id) || 0; - // 间距 = 基础间距 + 前一个节点的子节点数 × 因子 const dynamicGap = baseGap + prevChildCount * factor; - // 下一个节点Y坐标 = 当前Y + 节点高度 + 动态间距 currentY += this.nodeStyle.height + dynamicGap; nodePositions.set(levelNodes[i].id, currentY); } - // 3. 计算整体偏移量,让第二层级节点整体垂直居中 - const lastNodeY = nodePositions.get(levelNodes[totalNodes - 1].id) || 0; - const totalHeight = - lastNodeY - nodePositions.get(levelNodes[0].id) || - 0 + this.nodeStyle.height; - const offset = - centerY - (nodePositions.get(levelNodes[0].id) || 0) - totalHeight / 2; + // ✅修复括号优先级 + const firstY = nodePositions.get(levelNodes[0].id) ?? 0; + const lastNodeY = nodePositions.get(levelNodes[totalNodes - 1].id) ?? 0; + const totalHeight = lastNodeY - firstY + this.nodeStyle.height; + const offset = centerY - firstY - totalHeight / 2; - // 4. 应用最终位置(加上偏移量,确保整体居中) levelNodes.forEach((node) => { const nodeY = (nodePositions.get(node.id) || 0) + offset; const nodeX = this.levelXMap.get("level2") || 300; node.setPosition(nodeX, nodeY); }); - } - - // ======================== 第三层级:跟随父节点垂直分布 ======================== - else if (level === "level3") { + } else if (level === "level3") { const parentGroups = new Map(); levelNodes.forEach((node) => { const parentEdge = graph @@ -785,7 +782,7 @@ export class X6Graph { const parentY = parentNode.getPosition().y; const childCount = children.length; - const verticalGap = 20; // 第三层级内部固定间距 + const verticalGap = 20; const totalChildHeight = this.nodeStyle.height * childCount + verticalGap * (childCount - 1); @@ -801,46 +798,53 @@ export class X6Graph { }); }); } else if (level === "level4") { - // 1. 按父节点(level3)分组(每个level3只对应1个level4) - const parentGroups = new Map(); - levelNodes.forEach((node) => { - const parentEdge = graph - .getEdges() - .find((edge) => edge.getTargetCellId() === node.id); - if (parentEdge) { - const parentId = parentEdge.getSourceCellId(); - // 只保留level3的父节点(确保父节点层级正确) - const parentNode = graph.getCellById(parentId) as Node; - if (parentNode?.getData().level === "level3") { - parentGroups.set(parentId, [ - ...(parentGroups.get(parentId) || []), - node, - ]); + const levelNodes = this.graph.getCells().filter((cell) => { + return cell.isNode() && cell.getData()?.level === level; + }) as Node[]; + if (!levelNodes.length) return; + + const allEdges = this.graph.getEdges(); + //按照父节点Y排序,保证顺序和左侧level3从上到下保持一致 + levelNodes.sort((a, b) => { + let ay = 0, + by = 0; + for (const e of allEdges) { + if (e.getTargetCellId() === a.id) { + const p = this.graph.getCellById(e.getSourceCellId()) as Node; + ay = p.getPosition().y; + } + if (e.getTargetCellId() === b.id) { + const p = this.graph.getCellById(e.getSourceCellId()) as Node; + by = p.getPosition().y; } } + return ay - by; }); - // 2. 每个level3父节点的level4子节点:放在父节点右侧(固定间距) - parentGroups.forEach((children, parentId) => { - const parentNode = graph.getCellById(parentId) as Node; - if (!parentNode) return; + const nodeH = this.nodeStyle.height; + const verticalGap = 20; + const totalHeight = + levelNodes.length * nodeH + (levelNodes.length - 1) * verticalGap; - // level4固定在level3父节点右侧,X坐标=level3X + 节点宽度 + 间距(20px) - const parentPos = parentNode.getPosition(); - const level4X = this.levelXMap.get("level4") || 800; - // Y坐标与父节点完全对齐(上下居中) - const level4Y = parentPos.y; + //参考level3整体的中心点 + const level3Nodes = this.graph + .getCells() + .filter((c) => c.isNode() && c.getData()?.level === "level3") as Node[]; + let refCenterY = 300; + if (level3Nodes.length > 0) { + const topY = Math.min(...level3Nodes.map((n) => n.getPosition().y)); + const botY = + Math.max(...level3Nodes.map((n) => n.getPosition().y)) + nodeH; + refCenterY = (topY + botY) / 2; + } - // 只保留第一个level4节点(确保仅一个子节点) - const targetChild = children[0]; - if (targetChild) { - targetChild.setPosition(level4X, level4Y); - // 多余节点直接删除(避免多个level4) - if (children.length > 1) { - children.slice(1).forEach((excessNode) => excessNode.remove()); - this.resetLevelCount("level4"); // 重置数量统计 - } - } + const startY = refCenterY - totalHeight / 2; + const level4X = this.levelXMap.get("level4") || 800; + + let currentY = startY; + levelNodes.forEach((node) => { + node.setPosition(level4X, currentY); + currentY += nodeH + verticalGap; }); } } @@ -853,6 +857,37 @@ export class X6Graph { const cell = graph.getCellById(nodeId); if (!cell || !graph.isNode(cell)) return; + // if (targetNode.getData().tbslFlag) { + // // 1、拿到被删节点的直接父节点 + // const inEdges = graph.getIncomingEdges(graph.getCellById(nodeId)); + // inEdges[0].setData({ ...inEdges[0].getData(), syncCount: null }); + + // inEdges[0].setAttrs({ + // label: { + // text: 22222, + // }, + // }); + // } + + if (cell.data.tbslFlag) { + graph.getEdges().forEach((edge) => { + const sourceNode = edge.getSourceNode(); + const targetNode = edge.getTargetNode(); + if (targetNode.id == cell.id) { + sourceNode.setData({ syncCount: null }); + } + }); + + const inEdges = graph.getIncomingEdges(cell); + inEdges[0].setData({ ...inEdges[0].getData(), syncCount: null }); + + inEdges[0].setAttrs({ + label: { + text: 22222, + }, + }); + } + const node = cell as Node; const nodeLevel = node.getData().level as NodeLevel; @@ -880,28 +915,61 @@ export class X6Graph { // 3. 遍历灌库数据,创建 level2→level3→level4 层级 importData.forEach((item) => { // 3.1 创建 level2 节点(userName 作为 label,父节点是根节点) - const { node: level2Node } = this.addChildNode(rootId, { - id: uuidv4(), - label: item.userName, - level: "level2", - }); + const { node: level2Node } = this.addChildNode( + rootId, + { + id: uuidv4(), + label: item.userName, + level: "level2", + }, + true, + ); // 3.2 遍历当前 level2 的 tables,创建 level3 和 level4 节点 item.tables.forEach((table) => { // 3.2.1 创建 level3 节点(tableName 作为 label,父节点是当前 level2) - const { node: level3Node } = this.addChildNode(level2Node.id, { - id: uuidv4(), - label: table.tableName, - level: "level3", - }); + const { node: level3Node } = this.addChildNode( + level2Node.id, + { + id: uuidv4(), + label: table.tableName, + level: "level3", + }, + true, + ); // 3.2.2 创建 level4 节点(columnName 作为 label,父节点是当前 level3) - this.addChildNode(level3Node.id, { - id: uuidv4(), - label: table.columnName, - level: "level4", - }); - }); + this.addChildNode( + level3Node.id, + { + id: uuidv4(), + label: table.columnName, + level: "level4", + }, + true, + ); + + if (table.syncCount) { + level3Node.data.syncCount = table.syncCount; + console.log(level3Node, "3333"); + this.addChildNode(level3Node.id, { + id: uuidv4(), + label: table.syncCount.toString(), + level: "level4", + }); + } + }, true); + }); + + // // 全部节点循环创建完成后,统一执行全套重排 + this.rearrangeLevelNodes("level2"); + this.rearrangeLevelNodes("level3"); + this.rearrangeLevelNodes("level4"); + + this.graph.zoomToFit({ + padding: 40, // 四周留白,节点不会贴边框 + maxZoom: 1, // 最大缩放比例,不要放太大;数据多时会缩小 + minZoom: 0.3, }); } @@ -956,7 +1024,7 @@ export class X6Graph { }; } - const syncCount = level3Node.getData().syncCount||""; + const syncCount = level3Node.getData().syncCount || ""; // 2.2.1 校验 level3 是否有子级(必须包含 level4) const level3ChildIds = this.getChildNodeIds(level3Node.id); @@ -986,7 +1054,7 @@ export class X6Graph { targetOwner: name, // 传入的name字段作为targetOwner tableName, columnName, - syncCount + syncCount, }); } } diff --git a/src/pages/sjtb/configTask/dataTable/index.vue b/src/pages/sjtb/configTask/dataTable/index.vue index 468c4f44..4a4db2b3 100644 --- a/src/pages/sjtb/configTask/dataTable/index.vue +++ b/src/pages/sjtb/configTask/dataTable/index.vue @@ -220,45 +220,42 @@ onMounted(() => { }, (res) => { initGraph(); - getModelList((value: any) => { - console.log(value); - if (res.result.userInfos.length) { - graph.value?.importData(res.result.userInfos, formData.sourceId); + if (res.result.userInfos.length) { + graph.value?.importData(res.result.userInfos, formData.sourceId); - res.result.userInfos.forEach((item: any) => { - const graphSl = graph.value?.getGraph(); + res.result.userInfos.forEach((item: any) => { + const graphSl = graph.value?.getGraph(); - // 获取全部节点实例 - const allNodes = graphSl.getNodes(); - console.log(allNodes, "allNodes"); - // 过滤:根据你存名称的字段,这里假设字段是 label;如果你的字段是 name,就改成 item.name - const matchNodesData = allNodes - .map((node) => { - return { - label: node.label, - id: node.id, - }; - }) - .filter((item1) => { - return item1.label == item.userName; - }); - const exists = modeList.value.find( - (item1: any) => item1.label === item.userName - ); - - if (!exists) { - const newItem = { - value: matchNodesData[0].id, - label: item.userName, + // 获取全部节点实例 + const allNodes = graphSl.getNodes(); + console.log(allNodes, "allNodes"); + // 过滤:根据你存名称的字段,这里假设字段是 label;如果你的字段是 name,就改成 item.name + const matchNodesData = allNodes + .map((node) => { + return { + label: node.label, + id: node.id, }; - modeList.value.push(newItem); - formData.mode.push(newItem.value); - } else { - formData.mode.push(exists.value); - } - }); - } - }); + }) + .filter((item1) => { + return item1.label == item.userName; + }); + const exists = modeList.value.find( + (item1: any) => item1.label === item.userName + ); + + if (!exists) { + const newItem = { + value: matchNodesData[0].id, + label: item.userName, + }; + modeList.value.push(newItem); + formData.mode.push(newItem.value); + } else { + formData.mode.push(exists.value); + } + }); + } // mindMap.value = res.result; } @@ -306,7 +303,7 @@ onMounted(() => { const jdItem = graphSl.getCellById(res.id); nowPzTbslId.value = res.id; - const oldSyncCount = jdItem.getData().syncCount; + const oldSyncCount = jdItem?.getData().syncCount; pztbslFlag.value = true; if (oldSyncCount) { pztbslValue.value = oldSyncCount; @@ -332,7 +329,41 @@ function paTbslMethod() { const graphSl = graph.value?.getGraph(); const jdItem = graphSl.getCellById(nowPzTbslId.value); - jdItem.setData({ ...jdItem.getData(), syncCount:pztbslValue.value }) + + if (jdItem.data.syncCount) { + jdItem.setData({ syncCount: pztbslValue.value }); + const outEdges = graphSl.getOutgoingEdges(jdItem); + const children = outEdges + .map((edge) => edge.getTargetNode()) + .filter((n): n is Node => !!n); + + const fhtjNode = children.find((node) => { + const d = node.getData(); + // 这里写你的判断条件 + return d?.tbslFlag; + }); + if (fhtjNode) { + fhtjNode.setLabel(pztbslValue.value); + fhtjNode.setAttrs({ + label: { + text: pztbslValue.value, + }, + }); + } + } else { + jdItem.setData({ ...jdItem.getData(), syncCount: pztbslValue.value }); + let id = uuidv4(); + graph.value?.addChildNode(nowPzTbslId.value, { + id, + label: pztbslValue.value, + level: "level4", // 单选节点层级为level4 + }); + let node = graphSl.getCellById(id); + node.setData({ + ...node.getData(), + tbslFlag: true, + }); + } pztbslFlag.value = false; pztbslValue.value = ""; nowPzTbslId.value = null; @@ -351,18 +382,18 @@ function getTargetUserName(taskId: any) { }); } -// 根据源数据库id获取用户 -function getModelList(callback?: (data: any) => void) { - getUser({ sourceId: formData.sourceId }, (res) => { - modeList.value = res.result.map((item: any) => { - return { - value: uuidv4(), - label: item, - }; - }); - callback?.(res); - }); -} +// // 根据源数据库id获取用户 +// function getModelList(callback?: (data: any) => void) { +// getUser({ sourceId: formData.sourceId }, (res) => { +// modeList.value = res.result.map((item: any) => { +// return { +// value: uuidv4(), +// label: item, +// }; +// }); +// callback?.(res); +// }); +// } // 根据源数据库id以及用户名称获取所有表名称 function getDataListById(name: any) { @@ -581,12 +612,18 @@ function handleCancel() { width="20vw" class="config-dialog" > - +
+ 同步数量: + +
+