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
451 lines
12 KiB
HTML
1 day ago
|
<!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>
|