From b00b1293e47902d0981460a5dc04f76168274e89 Mon Sep 17 00:00:00 2001 From: chatgpt-yunju <38625060+chatgpt-yunju@users.noreply.github.com> Date: Mon, 7 Jul 2025 18:56:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B06=E4=B8=AA?= =?UTF-8?q?=E5=9C=86=E5=9C=88(=E4=BD=86=E8=BF=9E=E7=BA=BF=E7=A9=BF?= =?UTF-8?q?=E8=BF=87=E5=9C=86=E5=9C=88=E4=BA=86=E9=9C=80=E8=A6=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/anchorGraph.vue | 87 +++++++++---------- vite.config.js | 7 +- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/views/keyNodeRecognition3/components/anchorGraph.vue b/src/views/keyNodeRecognition3/components/anchorGraph.vue index 7fea085..0e041ac 100644 --- a/src/views/keyNodeRecognition3/components/anchorGraph.vue +++ b/src/views/keyNodeRecognition3/components/anchorGraph.vue @@ -14,7 +14,6 @@ const containerRef = ref(null); onMounted(() => { // --- 1. Data Preparation --- - // Initial node data (no changes needed here) const initialNodes = [ { id: '6010609377', name: '外贸发布BBS', default_avatar: '外贸发布BBS_waimaofabuBBS.png' }, { id: '6797803070', name: '爱锤盾海桃-霆恩启副', default_avatar: '爱锤盾海桃-霆恩启副_aichuidunhaitao-tingenqifu.png' }, @@ -37,8 +36,8 @@ onMounted(() => { { id: '2548116484', name: '肥_谍_gg', default_avatar: '肥_谍_gg_fei_die_gg.png' }, { id: '1854070075', name: '深海一万米', default_avatar: '深海一万米_shenhaiyiwanmi.png' }, ]; - - // Map node names to their cluster ID + + // The mapping of nodes to their clusters remains the same const clusterMapping = { '中国海警': 'cluster-0', '十八子91221': 'cluster-0', '大侠啊啊啊啊': 'cluster-0', '苍龙飞天79': 'cluster-1', '平安泸县': 'cluster-1', '新浪军事': 'cluster-1', '环球时报': 'cluster-1', '乐之567': 'cluster-1', 'CGTN记者团': 'cluster-1', '深海一万米': 'cluster-1', @@ -48,36 +47,29 @@ onMounted(() => { '肥_谍_gg': 'cluster-5', }; + const nodeSize = 50; + const nodes = initialNodes.map(node => ({ ...node, comboId: clusterMapping[node.name], - label: '', // Labels are disabled for this design + label: '', type: 'image', img: new URL(`/src/assets/user3/${node.default_avatar}`, import.meta.url).href, - clipCfg: { show: true, type: 'circle', r: 25 }, - })); - - // --- Fixed Combo Layout, Styles, and Connections --- - - // Define fixed positions, sizes, and styles for each combo based on SwiftUI code and screenshot - const comboLayoutData = { - 'cluster-0': { x: 250, y: 220, r: 120, style: { fill: '#FF5E0014', stroke: '#FF5E007A' } }, // Orange - 'cluster-1': { x: 500, y: 180, r: 120, style: { fill: '#BA21EB14', stroke: '#BA21EB7A' } }, // Purple - 'cluster-2': { x: 700, y: 500, r: 80, style: { fill: '#2191EB29', stroke: '#2191EB' } }, // Blue - 'cluster-3': { x: 800, y: 250, r: 137, style: { fill: '#FFD90A14', stroke: '#FFD90A7A' } }, // Yellow - 'cluster-4': { x: 180, y: 480, r: 80, style: { fill: '#3DD99414', stroke: '#3DD9947A' } }, // Green - 'cluster-5': { x: 450, y: 450, r: 110, style: { fill: '#D93D6B14', stroke: '#D93D6B7A' } }, // Pink/Red - }; - - const combos = Object.entries(comboLayoutData).map(([id, { x, y, r, style }]) => ({ - id, - label: '', - x, y, fx: x, fy: y, // Fix combo positions - r, // Set radius directly - style + clipCfg: { show: true, type: 'circle', r: nodeSize / 2 }, })); - // Define connections between combos based on the target image + // Define Combos with styles and relative sizes. Positions will be calculated by the layout engine. + const combos = [ + { id: 'cluster-0', label: '', r: 90, style: { fill: '#FF5E0014', stroke: '#FF5E007A' } }, + { id: 'cluster-1', label: '', r: 110, style: { fill: '#BA21EB14', stroke: '#BA21EB7A' } }, + { id: 'cluster-2', label: '', r: 80, style: { fill: '#2191EB29', stroke: '#2191EB' } }, + { id: 'cluster-3', label: '', r: 130, style: { fill: '#FFD90A14', stroke: '#FFD90A7A' } }, + { id: 'cluster-4', label: '', r: 75, style: { fill: '#3DD99414', stroke: '#3DD9947A' } }, + { id: 'cluster-5', label: '', r: 100, style: { fill: '#D93D6B14', stroke: '#D93D6B7A' } }, + ]; + + // Re-introduce edges to define relationships for the force layout. + // These connections are based on the visual overlaps in the original image. const comboConnections = [ ['cluster-0', 'cluster-1'], ['cluster-0', 'cluster-4'], ['cluster-1', 'cluster-3'], ['cluster-1', 'cluster-5'], @@ -86,39 +78,40 @@ onMounted(() => { ['cluster-5', 'cluster-2'], ]; + // G6 needs edges between nodes, so we find one node from each combo to connect. const edges = comboConnections.map(([sourceComboId, targetComboId], i) => { - const sourceNode = nodes.find(n => n.comboId === sourceComboId); - const targetNode = nodes.find(n => n.comboId === targetComboId); - return { - id: `edge-${i}`, - source: sourceNode.id, - target: targetNode.id, - }; + return { id: `edge-${i}`, source: sourceComboId, target: targetComboId }; }); const data = { nodes, combos, edges }; // --- 2. G6 Graph Initialization --- const container = containerRef.value; - const width = container.scrollWidth; - const height = container.scrollHeight || 700; + const width = container.scrollWidth || 1200; + const height = container.scrollHeight || 800; 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', - gravity: 80, // Increase gravity to pull nodes towards combo centers - nodeSpacing: 25, - preventOverlap: true, - preventComboOverlap: true, // Should be redundant with fixed combos, but safe to keep + preventComboOverlap: true, // 让combo不重叠 + preventNodeOverlap: true, // 节点不重叠 + linkDistance: 250, // 连接距离 + nodeSpacing: 50, // 节点间距 + comboSpacing: 150, // combo间距(可根据实际效果调整,数值越大圆圈越分开) + gravity: 40, + comboGravity: 30, }, modes: { - default: ['drag-canvas', 'zoom-canvas'], // Disable node and combo dragging + default: ['drag-canvas', 'zoom-canvas', 'drag-combo', 'drag-node'], }, defaultNode: { - size: 50, + size: nodeSize, style: { lineWidth: 2, stroke: '#37ACD7', @@ -128,15 +121,15 @@ onMounted(() => { }, defaultEdge: { style: { - stroke: '#37ACD7', - lineWidth: 1, - opacity: 0.5, + stroke: '#FFFFFF', + lineWidth: 2, + opacity: 0.3, }, }, - defaultCombo: { // This is now a base style, individual styles will override + defaultCombo: { type: 'circle', style: { - lineWidth: 1, + lineWidth: 2, }, }, }); @@ -168,7 +161,7 @@ onMounted(() => { .container { width: 100%; height: 100%; - background: #0b1120; // Match the dark background from the screenshot + background: #0b1120; // Set a background similar to the image } } \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 0f2c43c..b65ed1e 100644 --- a/vite.config.js +++ b/vite.config.js @@ -10,5 +10,10 @@ export default defineConfig({ "@": path.resolve(__dirname, "src"), "@assets": path.resolve(__dirname, "src/assets") } - } + }, + server: { + watch: { + usePolling: true, // 解决部分环境下热更新失效 + } + }, });