From 1688d8b200ca9e8eb909ddfe1ce06bb738415381 Mon Sep 17 00:00:00 2001 From: chatgpt-yunju <38625060+chatgpt-yunju@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:26:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9C=86=E5=9C=88=E4=B8=8D=E9=87=8D=E5=8F=A0?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/anchorGraph.vue | 106 +++++++++++++++--- 1 file changed, 92 insertions(+), 14 deletions(-) diff --git a/src/views/keyNodeRecognition3/components/anchorGraph.vue b/src/views/keyNodeRecognition3/components/anchorGraph.vue index 0e041ac..9001568 100644 --- a/src/views/keyNodeRecognition3/components/anchorGraph.vue +++ b/src/views/keyNodeRecognition3/components/anchorGraph.vue @@ -8,6 +8,38 @@ import { ref, onMounted, onBeforeUnmount } from "vue"; import G6 from "@antv/g6"; +// 注册自定义边 +G6.registerEdge('combo-border-edge', { + draw(cfg, group) { + const { sourceCombo, targetCombo } = cfg; + if (!sourceCombo || !targetCombo) return; + // 获取圆心和半径 + const { x: x1, y: y1, r: r1 } = sourceCombo; + const { x: x2, y: y2, r: r2 } = targetCombo; + // 计算两个圆心连线的单位向量 + const dx = x2 - x1, dy = y2 - y1; + const len = Math.sqrt(dx * dx + dy * dy); + const ux = dx / len, uy = dy / len; + // 计算切点 + const sx = x1 + ux * r1; + const sy = y1 + uy * r1; + const tx = x2 - ux * r2; + const ty = y2 - uy * r2; + // 绘制曲线或直线 + return group.addShape('path', { + attrs: { + stroke: '#fff', + lineWidth: 2, + path: [ + ['M', sx, sy], + // 可加弯曲 ['Q', (sx+tx)/2+20, (sy+ty)/2-20, tx, ty] + ['L', tx, ty] + ] + } + }); + } +}, 'line'); + let graph = null; const containerRef = ref(null); @@ -87,28 +119,74 @@ onMounted(() => { // --- 2. G6 Graph Initialization --- const container = containerRef.value; + // 1. 统计每个combo的节点数量 + const comboNodeCount = {}; + nodes.forEach(node => { + comboNodeCount[node.comboId] = (comboNodeCount[node.comboId] || 0) + 1; + }); + + // 先排序 + const sortedCombos = combos + .map(combo => ({ ...combo, count: comboNodeCount[combo.id] || 0 })) + .sort((a, b) => b.count - a.count); + + // 计算每排最大半径 + const topCombos = sortedCombos.slice(0, 3); + const bottomCombos = sortedCombos.slice(3, 6); + const topMaxR = Math.max(...topCombos.map(c => c.r)); + const bottomMaxR = Math.max(...bottomCombos.map(c => c.r)); + + // 横向间距 = 前后两个combo半径之和 + 额外间隔 + function getX(i, combosArr, width) { + let x = combosArr[0].r + 60; // 左边留空 + for (let j = 0; j < i; j++) { + x += combosArr[j].r + combosArr[j + 1].r + 80; // 80为额外间隔 + } + return x; + } + const width = container.scrollWidth || 1200; const height = container.scrollHeight || 800; + const topY = height * 0.25; + const bottomY = height * 0.75; + // 上排 + topCombos.forEach((combo, idx) => { + combo.x = getX(idx, topCombos, width); + combo.y = topY; + }); + // 下排 + bottomCombos.forEach((combo, idx) => { + combo.x = getX(idx, bottomCombos, width); + combo.y = bottomY; + }); + + // 更新 combos + combos.forEach(c => { + const found = [...topCombos, ...bottomCombos].find(sc => sc.id === c.id); + c.x = found.x; + c.y = found.y; + }); + + // 5. 节点分布在各自combo圆内 + nodes.forEach(node => { + const combo = combos.find(c => c.id === node.comboId); + // 随机分布在combo圆内 + const angle = Math.random() * 2 * Math.PI; + const radius = Math.random() * (combo.r - nodeSize / 2); + node.x = combo.x + Math.cos(angle) * radius; + node.y = combo.y + Math.sin(angle) * radius; + }); + + // 6. 用 layout: null,G6 不再自动布局 graph = new G6.Graph({ container, width, height, - layout: null, // 或你自定义的布局 - groupByTypes: false, // 允许 combo-to-combo 边 - // --- MODIFICATION: Set preventComboOverlap to true to avoid circle overlaps --- - layout: { - type: 'comboForce', - preventComboOverlap: true, // 让combo不重叠 - preventNodeOverlap: true, // 节点不重叠 - linkDistance: 250, // 连接距离 - nodeSpacing: 50, // 节点间距 - comboSpacing: 150, // combo间距(可根据实际效果调整,数值越大圆圈越分开) - gravity: 40, - comboGravity: 30, - }, + groupByTypes: false, + layout: null, modes: { - default: ['drag-canvas', 'zoom-canvas', 'drag-combo', 'drag-node'], + default: ['drag-canvas', 'zoom-canvas', 'drag-combo', 'drag-node'] }, defaultNode: { size: nodeSize,