SocialNetworks_duan/src/views/KeyNodeRecognition2/components/graph/bridgeCommunityGraph.vue
2025-07-23 16:11:53 +08:00

255 lines
6.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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'
let chartInstance = null;
// 处理数据生成ECharts所需格式
const processData = () => {
const nodes = [];
const links = [];
const addedCommunities = new Set();
// 找出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;
// 添加桥梁节点 - 可以调整这个值来改变桥梁节点大小
nodes.push({
id: `bridge_${bridgeId}`,
name: `桥梁节点 ${bridgeId.substring(0, 5)}...`,
category: 0,
value: 20 // 桥梁节点大小(可修改)
});
// 添加社团节点和边
item.bridgeCommunities.forEach(community => {
const communityId = community.communityId;
const communityKey = `community_${communityId}`;
const usersNum = community.usersNum;
// 归一化usersNum到节点大小范围
const size = minNodeSize +
((usersNum - minUsersNum) / (maxUsersNum - minUsersNum)) *
(maxNodeSize - minNodeSize);
if (!addedCommunities.has(communityKey)) {
nodes.push({
id: communityKey,
name: `社团 ${communityId} (${usersNum})`,
category: 1,
value: size // 根据usersNum动态设置大小
});
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) {
// 社团节点
const usersNum = params.data.name.match(/\((\d+)\)/)?.[1] || 0;
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;">
社团用户数:${usersNum}
</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,
// 锚点账号用圆形头像,普通账号保持默认
// 添加自定义图标
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)}`
})),
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: {
color: '#ff7300'
}
},
{
name: '社团节点',
itemStyle: {
color: '#1890ff'
}
}
],
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>