<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ECharts 关系图示例</title>
<!-- 引入 Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- 引入 Font Awesome -->
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- 引入 ECharts -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<script>
// 配置 Tailwind
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#3B82F6',
secondary: '#10B981',
accent: '#8B5CF6',
dark: '#1E293B',
light: '#F8FAFC'
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.graph-container {
height: calc(100vh - 12rem);
}
.card-shadow {
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
}
</style>
</head>
<body class="bg-gray-50 text-gray-800 font-sans">
<!-- 页面头部 -->
<header class="bg-white shadow-md">
<div class="container mx-auto px-4 py-6">
<h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-dark flex items-center">
<i class="fa fa-project-diagram text-primary mr-3"></i>
ECharts 关系图可视化
</h1>
<p class="text-gray-600 mt-2">展示节点与节点之间的关联关系</p>
</div>
</header>
<!-- 主内容区 -->
<main class="container mx-auto px-4 py-8">
<!-- 控制面板 -->
<div class="bg-white rounded-lg p-6 mb-8 card-shadow">
<div class="flex flex-wrap gap-4 items-center">
<div class="flex-1 min-w-[200px]">
<label class="block text-sm font-medium text-gray-700 mb-1">布局类型</label>
<select id="layoutType" class="w-full p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-primary focus:border-primary">
<option value="force">力导向布局</option>
<option value="circular">环形布局</option>
<option value="radial">辐射状布局</option>
</select>
</div>
<div class="flex-1 min-w-[200px]">
<label class="block text-sm font-medium text-gray-700 mb-1">节点大小</label>
<input type="range" id="nodeSize" min="10" max="50" value="20"
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary">
</div>
<div class="flex-1 min-w-[200px]">
<label class="block text-sm font-medium text-gray-700 mb-1">关系线粗细</label>
<input type="range" id="lineWidth" min="1" max="5" value="2"
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary">
</div>
<div class="flex items-end">
<button id="refreshGraph" class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-md transition duration-200 flex items-center">
<i class="fa fa-refresh mr-2"></i>刷新图表
</button>
</div>
</div>
</div>
<!-- 图表容器 -->
<div class="bg-white rounded-lg p-4 card-shadow">
<div id="graphContainer" class="graph-container w-full rounded-md"></div>
</div>
<!-- 说明面板 -->
<div class="bg-white rounded-lg p-6 mt-8 card-shadow">
<h2 class="text-xl font-semibold mb-4 text-dark">关系图说明</h2>
<div class="grid md:grid-cols-2 gap-6">
<div>
<h3 class="text-lg font-medium mb-2 text-primary">交互操作</h3>
<ul class="list-disc pl-5 space-y-2 text-gray-700">
<li>拖动节点可以调整位置</li>
<li>鼠标滚轮可以缩放视图</li>
<li>点击节点可以查看详细信息</li>
<li>按住鼠标左键拖动可以平移视图</li>
</ul>
</div>
<div>
<h3 class="text-lg font-medium mb-2 text-primary">节点含义</h3>
<ul class="list-disc pl-5 space-y-2 text-gray-700">
<li><span class="inline-block w-3 h-3 rounded-full bg-blue-500 mr-2"></span>蓝色:个人</li>
<li><span class="inline-block w-3 h-3 rounded-full bg-green-500 mr-2"></span>绿色:组织</li>
<li><span class="inline-block w-3 h-3 rounded-full bg-purple-500 mr-2"></span>紫色:项目</li>
<li><span class="inline-block w-3 h-3 rounded-full bg-yellow-500 mr-2"></span>黄色:文档</li>
</ul>
</div>
</div>
</div>
</main>
<!-- 页脚 -->
<footer class="bg-dark text-white py-6 mt-12">
<div class="container mx-auto px-4 text-center">
<p>© 2023 ECharts 关系图可视化示例 | 使用 ECharts、Tailwind CSS 构建</p>
</div>
</footer>
<script>
// 初始化图表
const chartDom = document.getElementById('graphContainer');
const myChart = echarts.init(chartDom);
let option;
// 生成随机数据
function generateData() {
// 节点数据
const nodes = [
{ id: 1, name: '张三', category: 0, value: 30, symbolSize: 30 },
{ id: 2, name: '李四', category: 0, value: 25, symbolSize: 25 },
{ id: 3, name: '王五', category: 0, value: 28, symbolSize: 28 },
{ id: 4, name: '赵六', category: 0, value: 22, symbolSize: 22 },
{ id: 5, name: '技术部', category: 1, value: 40, symbolSize: 40 },
{ id: 6, name: '市场部', category: 1, value: 35, symbolSize: 35 },
{ id: 7, name: '产品A', category: 2, value: 32, symbolSize: 32 },
{ id: 8, name: '产品B', category: 2, value: 29, symbolSize: 29 },
{ id: 9, name: '需求文档', category: 3, value: 20, symbolSize: 20 },
{ id: 10, name: '设计方案', category: 3, value: 22, symbolSize: 22 },
{ id: 11, name: '测试报告', category: 3, value: 21, symbolSize: 21 },
{ id: 12, name: '钱七', category: 0, value: 26, symbolSize: 26 },
{ id: 13, name: '研发中心', category: 1, value: 45, symbolSize: 45 },
{ id: 14, name: '产品C', category: 2, value: 31, symbolSize: 31 },
{ id: 15, name: '用户手册', category: 3, value: 19, symbolSize: 19 }
];
// 关系数据
const links = [
{ source: 1, target: 5, value: 5, label: { show: true, formatter: '隶属' } },
{ source: 2, target: 5, value: 5, label: { show: true, formatter: '隶属' } },
{ source: 3, target: 6, value: 5, label: { show: true, formatter: '隶属' } },
{ source: 4, target: 6, value: 5, label: { show: true, formatter: '隶属' } },
{ source: 12, target: 13, value: 5, label: { show: true, formatter: '隶属' } },
{ source: 1, target: 7, value: 3, label: { show: true, formatter: '负责' } },
{ source: 2, target: 8, value: 3, label: { show: true, formatter: '负责' } },
{ source: 12, target: 14, value: 3, label: { show: true, formatter: '负责' } },
{ source: 3, target: 7, value: 2, label: { show: true, formatter: '参与' } },
{ source: 4, target: 8, value: 2, label: { show: true, formatter: '参与' } },
{ source: 7, target: 9, value: 4, label: { show: true, formatter: '关联' } },
{ source: 7, target: 10, value: 4, label: { show: true, formatter: '关联' } },
{ source: 8, target: 11, value: 4, label: { show: true, formatter: '关联' } },
{ source: 14, target: 15, value: 4, label: { show: true, formatter: '关联' } },
{ source: 1, target: 2, value: 1, label: { show: false } },
{ source: 3, target: 4, value: 1, label: { show: false } },
{ source: 5, target: 6, value: 2, label: { show: true, formatter: '协作' } },
{ source: 5, target: 13, value: 2, label: { show: true, formatter: '协作' } },
{ source: 7, target: 8, value: 1, label: { show: false } },
{ source: 1, target: 12, value: 1, label: { show: false } }
];
// 类别数据
const categories = [
{ name: '个人', itemStyle: { color: '#3B82F6' } },
{ name: '组织', itemStyle: { color: '#10B981' } },
{ name: '项目', itemStyle: { color: '#8B5CF6' } },
{ name: '文档', itemStyle: { color: '#F59E0B' } }
];
return { nodes, links, categories };
}
// 更新图表配置
function updateChart() {
const layoutType = document.getElementById('layoutType').value;
const nodeSize = parseInt(document.getElementById('nodeSize').value);
const lineWidth = parseInt(document.getElementById('lineWidth').value);
const data = generateData();
// 根据选择的布局类型设置布局配置
let layoutConfig;
switch (layoutType) {
case 'force':
layoutConfig = {
type: 'force',
repulsion: 200,
edgeLength: 100,
layoutAnimation: true
};
break;
case 'circular':
layoutConfig = {
type: 'circular',
circular: {
rotateLabel: true
}
};
break;
case 'radial':
layoutConfig = {
type: 'radial',
center: ['50%', '50%'],
radius: ['20%', '80%'],
sortBy: 'value'
};
break;
}
// 更新节点大小
data.nodes.forEach(node => {
node.symbolSize = node.symbolSize * (nodeSize / 20);
});
option = {
tooltip: {
formatter: function(params) {
return `<div class="font-semibold">${params.name}</div>
<div>类型: ${data.categories[params.data.category].name}</div>
<div>值: ${params.data.value}</div>`;
}
},
legend: {
data: data.categories.map(cat => cat.name),
top: 10,
textStyle: {
color: '#666'
}
},
series: [
{
name: '关系图',
type: 'graph',
layout: layoutType,
force: layoutType === 'force' ? layoutConfig : null,
circular: layoutType === 'circular' ? layoutConfig.circular : null,
radial: layoutType === 'radial' ? layoutConfig : null,
data: data.nodes,
links: data.links.map(link => ({
...link,
lineStyle: {
width: link.value * (lineWidth / 2),
curveness: 0.2
}
})),
categories: data.categories,
roam: true, // 允许缩放和平移
label: {
show: true,
position: 'right',
formatter: '{b}',
fontSize: 12
},
lineStyle: {
opacity: 0.9,
width: 2,
curveness: 0
},
emphasis: {
focus: 'adjacency',
lineStyle: {
width: 5
}
},
// 动画配置
animationDuration: 1500,
animationEasingUpdate: 'quinticInOut'
}
]
};
myChart.setOption(option);
}
// 初始化图表
updateChart();
// 监听窗口大小变化,调整图表尺寸
window.addEventListener('resize', () => {
myChart.resize();
});
// 事件监听
document.getElementById('layoutType').addEventListener('change', updateChart);
document.getElementById('nodeSize').addEventListener('input', updateChart);
document.getElementById('lineWidth').addEventListener('input', updateChart);
document.getElementById('refreshGraph').addEventListener('click', updateChart);
// 节点点击事件
myChart.on('click', function(params) {
if (params.dataType === 'node') {
alert(`你点击了: ${params.name}\n类型: ${data.categories[params.data.category].name}`);
}
});
</script>
</body>
</html>
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。