You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

451 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 3 + ECharts 饼图点击交互</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
width: 100%;
max-width: 1200px;
background: rgba(255, 255, 255, 0.92);
border-radius: 20px;
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.3);
overflow: hidden;
padding: 30px;
}
header {
text-align: center;
margin-bottom: 30px;
}
h1 {
color: #2c3e50;
font-size: 2.8rem;
margin-bottom: 10px;
background: linear-gradient(45deg, #3498db, #8e44ad);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle {
color: #7f8c8d;
font-size: 1.2rem;
max-width: 800px;
margin: 0 auto;
line-height: 1.6;
}
.content {
display: flex;
flex-wrap: wrap;
gap: 30px;
}
.chart-container {
flex: 1;
min-width: 350px;
height: 450px;
background: white;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 15px;
transition: transform 0.3s ease;
}
.chart-container:hover {
transform: translateY(-5px);
}
.info-panel {
flex: 1;
min-width: 350px;
background: white;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 25px;
display: flex;
flex-direction: column;
}
.info-header {
text-align: center;
margin-bottom: 25px;
padding-bottom: 15px;
border-bottom: 2px solid #f1f2f6;
}
.info-header h2 {
color: #2c3e50;
font-size: 1.8rem;
}
.default-info {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
color: #95a5a6;
}
.default-info i {
font-size: 5rem;
margin-bottom: 20px;
color: #ecf0f1;
}
.info-content {
display: flex;
flex-direction: column;
gap: 20px;
}
.info-card {
background: #f8f9fa;
border-radius: 12px;
padding: 20px;
transition: all 0.3s ease;
}
.info-card:hover {
transform: translateX(5px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
}
.info-title {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.info-icon {
width: 40px;
height: 40px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
font-size: 1.2rem;
color: white;
}
.info-name {
font-size: 1.5rem;
font-weight: bold;
color: #2c3e50;
}
.info-item {
display: flex;
margin-top: 10px;
color: #7f8c8d;
}
.info-label {
font-weight: 600;
width: 120px;
color: #34495e;
}
.instructions {
margin-top: 30px;
background: #e8f4fc;
border-radius: 15px;
padding: 20px;
color: #2c3e50;
}
.instructions h3 {
margin-bottom: 15px;
color: #2980b9;
display: flex;
align-items: center;
gap: 10px;
}
.instructions ul {
padding-left: 25px;
}
.instructions li {
margin: 10px 0;
line-height: 1.6;
}
.highlight {
background-color: #f1c40f;
color: #2c3e50;
padding: 0 5px;
border-radius: 4px;
font-weight: 600;
}
footer {
text-align: center;
margin-top: 30px;
color: #7f8c8d;
font-size: 0.9rem;
}
@media (max-width: 768px) {
.content {
flex-direction: column;
}
h1 {
font-size: 2.2rem;
}
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<header>
<h1>ECharts 饼图点击交互</h1>
<p class="subtitle">本示例展示如何在 Vue 3 中为 ECharts 饼图添加点击事件,点击不同扇区查看详情信息</p>
</header>
<div class="content">
<div class="chart-container">
<div ref="chartDom" style="width: 100%; height: 100%;"></div>
</div>
<div class="info-panel">
<div class="info-header">
<h2>点击详情</h2>
</div>
<div v-if="!clickData" class="default-info">
<i class="fas fa-mouse-pointer"></i>
<h3>点击饼图扇区查看详情</h3>
<p>点击左侧饼图的任何部分将会在此显示详细信息</p>
</div>
<div v-else class="info-content">
<div class="info-card">
<div class="info-title">
<div class="info-icon" :style="{ background: clickData.color }">
<i :class="getIcon(clickData.name)"></i>
</div>
<div class="info-name">{{ clickData.name }}</div>
</div>
<div class="info-item">
<span class="info-label">数值:</span>
<span>{{ clickData.value }}</span>
</div>
<div class="info-item">
<span class="info-label">百分比:</span>
<span>{{ clickData.percent }}%</span>
</div>
<div class="info-item">
<span class="info-label">描述:</span>
<span>{{ getDescription(clickData.name) }}</span>
</div>
</div>
<div class="info-card">
<div class="info-item">
<span class="info-label">所属系列:</span>
<span>{{ clickData.seriesName }}</span>
</div>
<div class="info-item">
<span class="info-label">数据索引:</span>
<span>{{ clickData.dataIndex }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="instructions">
<h3><i class="fas fa-lightbulb"></i> 实现说明</h3>
<ul>
<li>使用 ECharts 的 <span class="highlight">chart.on('click')</span> 方法监听饼图点击事件</li>
<li>点击事件参数包含名称(name)、值(value)、百分比(percent)等关键数据</li>
<li>通过 Vue 的响应式特性更新点击信息展示区域</li>
<li>添加了窗口大小变化监听器,确保图表响应式调整</li>
<li>在组件卸载时正确销毁 ECharts 实例和事件监听器</li>
</ul>
</div>
<footer>
<p>Vue 3 + ECharts 饼图交互示例 | 点击饼图扇区体验交互效果</p>
</footer>
</div>
</div>
<script>
const { createApp, ref, onMounted, onBeforeUnmount } = Vue;
createApp({
setup() {
const chartDom = ref(null);
let chartInstance = null;
const clickData = ref(null);
// 饼图数据
const chartData = [
{ value: 1048, name: '搜索引擎' },
{ value: 735, name: '直接访问' },
{ value: 580, name: '邮件营销' },
{ value: 484, name: '联盟广告' },
{ value: 300, name: '视频广告' }
];
// 颜色列表
const colors = ['#5470C6', '#91CC75', '#FAC858', '#EE6666', '#73C0DE'];
// 根据名称获取图标
const getIcon = (name) => {
const icons = {
'搜索引擎': 'fas fa-search',
'直接访问': 'fas fa-globe',
'邮件营销': 'fas fa-envelope',
'联盟广告': 'fas fa-handshake',
'视频广告': 'fas fa-video'
};
return icons[name] || 'fas fa-chart-pie';
};
// 获取描述信息
const getDescription = (name) => {
const descriptions = {
'搜索引擎': '通过Google、百度等搜索引擎带来的流量',
'直接访问': '用户直接输入网址或书签访问',
'邮件营销': '通过电子邮件营销活动带来的访问',
'联盟广告': '通过联盟广告网络获得的流量',
'视频广告': '来自YouTube、TikTok等平台的视频广告'
};
return descriptions[name] || '暂无描述信息';
};
// 初始化图表
const initChart = () => {
if (!chartDom.value) return;
chartInstance = echarts.init(chartDom.value);
const option = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
right: 10,
top: 'center',
formatter: (name) => {
const data = chartData.find(item => item.name === name);
return `${name}: ${data.value}`;
}
},
color: colors,
series: [
{
name: '访问来源',
type: 'pie',
radius: ['40%', '70%'],
center: ['40%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
formatter: '{b}: {c}',
fontWeight: 'bold'
},
emphasis: {
label: {
show: true,
fontSize: '18',
fontWeight: 'bold'
},
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
labelLine: {
show: true
},
data: chartData
}
]
};
chartInstance.setOption(option);
// 添加点击事件监听
chartInstance.on('click', (params) => {
clickData.value = {
name: params.name,
value: params.value,
percent: params.percent,
color: params.color,
seriesName: params.seriesName,
dataIndex: params.dataIndex
};
});
};
// 处理窗口大小变化
const handleResize = () => {
chartInstance && chartInstance.resize();
};
onMounted(() => {
initChart();
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
if (chartInstance) {
chartInstance.off('click');
chartInstance.dispose();
}
window.removeEventListener('resize', handleResize);
});
return {
chartDom,
clickData,
getIcon,
getDescription
};
}
}).mount('#app');
</script>
</body>
</html>