SocialNetworks_duan/src/views/KeyNodeRecognition2/components/graph/bridgeCommunityGraph.vue

279 lines
8.0 KiB
Vue
Raw Normal View History

2025-07-23 15:50:49 +08:00
<template>
<div id="bridgeCommunityChart" style="width: 100%; height: 100%;"></div>
</template>
<script setup>
import { onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
import bridgeData from '@/assets/json/bridge_neighbors_communities.json';
import bridgeNodeHoverBgImg from '@/assets/images/bridge_node_hoverbgimg.png'
2025-07-23 19:40:41 +08:00
import { useKeyNodeStore2 } from '@/store/keyNodeStore2'; // 引入store
2025-07-23 15:50:49 +08:00
let chartInstance = null;
// 处理数据生成ECharts所需格式
const processData = () => {
const nodes = [];
const links = [];
const addedCommunities = new Set();
2025-07-23 19:40:41 +08:00
const keyNodeStore2 = useKeyNodeStore2(); // 获取store实例
2025-07-23 15:50:49 +08:00
// 找出usersNum的最小值和最大值用于归一化
let minUsersNum = Infinity;
let maxUsersNum = -Infinity;
bridgeData.forEach(item => {
item.bridgeCommunities.forEach(community => {
minUsersNum = Math.min(minUsersNum, community.usersNum);
maxUsersNum = Math.max(maxUsersNum, community.usersNum);
});
});
// 节点大小的范围
const minNodeSize = 30;
const maxNodeSize = 45;
// 添加桥梁节点和社团节点
bridgeData.forEach(item => {
const bridgeId = item.bridgeId;
2025-07-23 19:40:41 +08:00
// 查找对应的桥梁节点图片
const bridgeNodeInfo = keyNodeStore2.bridgeNodes.find(node => node.Node === bridgeId);
const bridgeNodeImg = bridgeNodeInfo ? bridgeNodeInfo.defImg : '';
2025-07-23 15:50:49 +08:00
// 添加桥梁节点 - 可以调整这个值来改变桥梁节点大小
nodes.push({
id: `bridge_${bridgeId}`,
2025-07-23 19:40:41 +08:00
// name: `桥梁节点 ${bridgeId.substring(0, 5)}...`,
2025-07-23 15:50:49 +08:00
category: 0,
2025-07-23 19:40:41 +08:00
value: 20, // 桥梁节点大小(可修改)
symbol: bridgeNodeImg ? `image://${bridgeNodeImg}` : 'circle', // 使用图片或默认圆形
symbolSize: 40 // 调整图片大小
2025-07-23 15:50:49 +08:00
});
// 添加社团节点和边
item.bridgeCommunities.forEach(community => {
const communityId = community.communityId;
2025-07-23 19:40:41 +08:00
const userNumofCommunity = community.usersNum
const communityKey = `community_${communityId}_${userNumofCommunity}`;
2025-07-23 15:50:49 +08:00
const usersNum = community.usersNum;
// 归一化usersNum到节点大小范围
const size = minNodeSize +
((usersNum - minUsersNum) / (maxUsersNum - minUsersNum)) *
(maxNodeSize - minNodeSize);
if (!addedCommunities.has(communityKey)) {
nodes.push({
id: communityKey,
2025-07-23 19:40:41 +08:00
// name: `社团 ${communityId} (${usersNum})`,
2025-07-23 15:50:49 +08:00
category: 1,
2025-07-23 19:40:41 +08:00
value: size, // 根据usersNum动态设置大小
symbolSize: size
2025-07-23 15:50:49 +08:00
});
addedCommunities.add(communityKey);
}
links.push({
source: `bridge_${bridgeId}`,
target: communityKey,
value: 1
});
});
});
return {
nodes,
links
};
};
// 初始化图表
const initChart = () => {
if (chartInstance) {
chartInstance.dispose();
}
const chartDom = document.getElementById('bridgeCommunityChart');
if (!chartDom) return;
chartInstance = echarts.init(chartDom);
const { nodes, links } = processData();
const categories = [
{name: "桥梁节点", category: 0},
{name: "社团节点", category: 1}
]
const option = {
//hover上去的窗口
tooltip: {
trigger: "item",
trigger: "item",
backgroundColor: "rgba(0,0,0,0)", // 透明背景
borderColor: "rgba(0,0,0,0)", // 透明边框
borderWidth: 0,
extraCssText: "box-shadow:none;padding:0;",
textStyle: {
color: '#fff', // 设置字体颜色为白色
fontSize: 14,
},
formatter: function(params) {
// 判断节点类型
if(params.data.category === 1) {
// 社团节点
2025-07-23 19:40:41 +08:00
// const usersNum = params.data.name.match(/\((\d+)\)/)?.[1] || 0;
const parts = params.data.id.split('_');
const extractedUserNum = parseInt(parts[parts.length - 1], 10);
2025-07-23 15:50:49 +08:00
return `<div
style = "
width: 126px;
height: 44px;
border-radius: 4px;
background: url('${bridgeNodeHoverBgImg}');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
">
<div style="color:#fff;letter-spacing: 0.14px;">
2025-07-23 19:40:41 +08:00
社团用户数${extractedUserNum}
2025-07-23 15:50:49 +08:00
</div>
</div>`;
}else {
// 桥梁节点: 查找对应的bridgeCommunities长度
const bridgeItem = bridgeData.find(item => item.bridgeId === params.data.id.replace('bridge_', ''));
const communityCount = bridgeItem ? bridgeItem.bridgeCommunities.length : 0;
return `<div
style = "
width: 126px;
height: 44px;
border-radius: 4px;
background: url('${bridgeNodeHoverBgImg}');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding:20px 20px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
">
链接社团数${communityCount}
</div>`;
}
},
},
//图例配置
legend: [
{
data: categories.map((c) => ({
name: c.name,
// 锚点账号用圆形头像,普通账号保持默认
// 添加自定义图标
2025-07-23 16:01:19 +08:00
icon: c.category === 0
? `image://${new URL('@/assets/images/icon/bridge_node_legend.png', import.meta.url)}`
: `image://${new URL('@/assets/images/icon/community_node_legend.png', import.meta.url)}`
2025-07-23 15:50:49 +08:00
})),
right: 15,
bottom: 13,
icon: "circle",
orient: "vertical",
itemWidth: 16,
itemHeight: 16,
itemGap: 12,
backgroundColor: "rgba(0,67,125,0.56)", // 半透明深蓝
borderRadius: 8, // 圆角
borderColor: "#c2f2ff", // 淡蓝色边框
borderWidth: 0.3,
padding: [12, 20, 12, 20], // 上右下左
textStyle: {
color: "#fff",
fontSize: 16,
fontWeight: "normal"
}
}
],
series: [
{
type: 'graph',
zoom: 0.8,
layout: 'force',
animation: false,
draggable: true,
data: nodes,
links: links,
categories: [
{
name: '桥梁节点',
itemStyle: {
2025-07-23 19:40:41 +08:00
// 节点颜色
color: '#ff7300',
2025-07-23 15:50:49 +08:00
}
},
{
name: '社团节点',
itemStyle: {
2025-07-23 19:40:41 +08:00
// 节点颜色
color: new echarts.graphic.RadialGradient(0.97, 0.38, 0.8, [
{ offset: 0, color: "#49c3ed" }, // 最右侧
{ offset: 0.5, color: "#5fa3e0" }, // 中间
{ offset: 1, color: "#7286d4" } // 最左侧
]),
// 边框样式
borderColor: '#2AB9FE',
borderWidth: 1,
borderType: 'dashed',
borderImageSource: 'linear-gradient(90deg, #2AB9FE 12.25%, #52FFF3 100.6%)',
borderImageSlice: 1,
},
symbolSize: 18
2025-07-23 15:50:49 +08:00
}
],
roam: true,
lineStyle: {
color: 'source',
curveness: 0.3
},
force: {
repulsion: 1000,
edgeLength: 100
},
animationDurationUpdate: 3500, // 节点移动更平滑
}
]
};
chartInstance.setOption(option);
};
onMounted(() => {
setTimeout(() => {
initChart();
}, 100);
window.addEventListener('resize', () => {
if (chartInstance) {
chartInstance.resize();
}
});
});
onUnmounted(() => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener('resize', () => {});
});
</script>
<style scoped>
#bridgeCommunityChart {
width: 100%;
height: 600px;
}
</style>