在 ECharts 數據可視化開發中,你是否遇到過這樣的場景:明明在 series 中配置了多個數據系列,且部分系列的 name 重複(比如兩個“1”類別的月度數據),但圖例(legend)只顯示一個重複項,而tooltip 提示卻能正常展示所有重複數據?這種“圖例缺失、tooltip 正常”的矛盾,本質是 ECharts 對 legend 的默認去重機制導致的。本文將從問題場景、根源分析到分步解決,帶你徹底搞定重複 Name 的渲染問題。
一、問題場景:什麼時候會遇到重複 Name?
最典型的場景是 多維度重複分類數據,比如:
- 月度電量統計:不同設備的“故障類型”可能有重複編碼(如設備 A 和設備 B 都有“1 類故障”);
- 多區域銷售數據:不同區域的“產品類別”名稱重複(如北京和上海都有“基礎款”產品);
- 多時段對比數據:不同年份的“季度標識”重複(如 2024 和 2025 年都有“Q1”)。
以“雙設備月度故障統計”為例,假設數據如下:
| 月份 | 設備 A-故障1 | 設備 B-故障1 | 設備 A-故障0 |
|---|---|---|---|
| 1月 | 1.31 | 0.56 | 0.82 |
| 2月 | 1.33 | 0.61 | 0.95 |
如果直接將 series 的 name 設為“1”“1”“0”,會出現兩個問題:
- legend 只顯示 2 項(“0”和“1”),丟失一個“1”的圖例;
- tooltip 正常顯示 3 項數據(兩個“1”和一個“0”),但圖例無法對應,用户不知道哪個“1”對應哪個設備。
二、問題根源:ECharts 的 legend 去重機制
ECharts 對 legend 的渲染邏輯是 **“以 name 為唯一標識”**:
- 當
legend.data中存在重複的name時,ECharts 會自動合併重複項,只保留第一個; - 即使
series中存在多個相同name的系列,legend也只會為該name生成一個圖例項,點擊該圖例時,會同時控制所有同名series的顯示/隱藏。
比如 legend.data: ['0', '1', '1'],ECharts 會自動去重為 ['0', '1'],formatter 函數也只會執行 2 次(對應“0”和“1”),導致第二個“1”的圖例丟失。
三、解決方案:“唯一標識 + 格式化”兩步走
核心思路是 “給重複 Name 加唯一前綴,再通過 formatter 隱藏前綴”,既保證 ECharts 能識別唯一系列,又不影響用户看到的顯示效果。具體分兩步:
步驟 1:構建唯一的 Name(給重複項加標識)
給每個 series 的 name 和 legend.data 的每一項,添加“唯一標識 + 分隔符 + 原始 Name”的結構,確保無重複。
常用的標識規則:
- 靜態數據:用“索引 + 分隔符(如
-)+ 原始 Name”,如“0-0”“1-1”“2-1”; - 動態數據:用“分類 ID + 分隔符 + 原始 Name”,如“deviceA-1”“deviceB-1”“deviceA-0”。
以靜態數據為例,改造後的 series.name 和 legend.data 如下:
| 原始 Name | 唯一 Name(索引+分隔符+原始) | 對應含義 |
|---|---|---|
| 0-0 | 設備 A-故障0 | |
| 1 | 1-1 | 設備 A-故障1 |
| 1 | 2-1 | 設備 B-故障1 |
此時 legend.data: ['0-0', '1-1', '2-1'],series 的每個 name 對應上述唯一標識,ECharts 不會去重(因為每個 Name 唯一)。
步驟 2:用 formatter 格式化顯示(隱藏唯一標識)
通過 legend.formatter 和 tooltip.formatter 切割唯一標識,只顯示原始 Name,讓用户看不到前綴,保持界面簡潔。
(1)legend 格式化:隱藏前綴,顯示原始 Name
在 legend 配置中,用 formatter 函數將唯一標識按分隔符切割,取後半部分(原始 Name):
legend: {
data: ['0-0', '1-1', '2-1'], // 唯一標識的 Name
formatter: (label) => {
// 按“-”切割,取第2部分(索引1)作為顯示文本
return label.split('-')[1];
},
top: '5%',
left: 'center',
itemWidth: 12, // 圖例圖標寬度
itemHeight: 12, // 圖例圖標高度
textStyle: {
color: '#333',
fontSize: 12
}
}
此時圖例顯示為“0”“1”“1”,與原始需求一致,且不會被 ECharts 去重。
(2)tooltip 格式化:處理系列名,顯示清晰關聯
tooltip 默認會顯示完整的唯一標識(如“1-1”),需要用 formatter 切割,同時可添加額外信息(如設備名稱),讓用户知道每個“1”對應的維度:
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow' // 陰影指示器,適合柱狀圖
},
formatter: function (params) {
// params 是所有系列的 tooltip 數據數組
let result = `${params[0].name}(月份)<br/>`; // 第一行顯示月份
// 遍歷每個系列,格式化顯示
params.forEach((item) => {
// 切割唯一標識,獲取索引和原始 Name
const [index, originalName] = item.seriesName.split('-');
// 映射索引到具體維度(如設備名稱)
const deviceMap = {
'0': '設備A',
'1': '設備A',
'2': '設備B'
};
const deviceName = deviceMap[index];
// 拼接內容:圖例圖標 + 設備名 + 原始Name + 數值
result += `${item.marker} ${deviceName}-故障${originalName}
<span style="margin-left:20px; font-weight:700;">${item.value}</span><br/>`;
});
return result;
}
}
此時 tooltip 會顯示“設備A-故障0”“設備A-故障1”“設備B-故障1”,清晰區分重複的“1”對應的設備。
四、完整實戰示例:月度故障數據可視化
下面以“雙設備月度故障統計”為例,給出完整的 ECharts 配置代碼,你可直接複製使用並修改適配自己的數據。
1. HTML 結構(容器)
<!-- 為 ECharts 準備一個具備寬高的 DOM 容器 -->
<div id="faultChart" style="width: 800px; height: 400px;"></div>
2. ECharts 初始化與配置
// 引入 ECharts(如果是 CDN 引入,無需這步)
import * as echarts from 'echarts';
// 初始化圖表實例
const chart = echarts.init(document.getElementById('faultChart'));
// 1. 準備數據(月度 + 系列數據)
const months = ['1月', '2月', '3月', '4月', '5月', '6月'];
const seriesData = [
// 系列1:設備A-故障0(唯一Name:0-0)
{
name: '0-0',
type: 'bar',
data: [0.82, 0.95, 0.78, 1.02, 0.91, 0.85],
barWidth: '20%', // 柱子寬度
itemStyle: {
color: '#4895ef' // 藍色:設備A-故障0
}
},
// 系列2:設備A-故障1(唯一Name:1-1)
{
name: '1-1',
type: 'bar',
data: [1.31, 1.33, 1.25, 1.41, 1.38, 1.29],
barWidth: '20%',
itemStyle: {
color: '#f9c74f' // 黃色:設備A-故障1
}
},
// 系列3:設備B-故障1(唯一Name:2-1)
{
name: '2-1',
type: 'bar',
data: [0.56, 0.61, 0.52, 0.68, 0.63, 0.59],
barWidth: '20%',
itemStyle: {
color: '#f8961e' // 橙色:設備B-故障1
}
}
];
// 2. ECharts 配置項
const option = {
title: {
text: '雙設備月度故障次數統計',
left: 'center',
textStyle: {
fontSize: 16,
color: '#333'
}
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function (params) {
let result = `${params[0].name}(月份)<br/>`;
const deviceMap = { '0': '設備A', '1': '設備A', '2': '設備B' };
params.forEach(item => {
const [index, originalName] = item.seriesName.split('-');
const deviceName = deviceMap[index];
result += `${item.marker} ${deviceName}-故障${originalName}
<span style="margin-left:20px; font-weight:700;">${item.value}</span><br/>`;
});
return result;
}
},
legend: {
data: seriesData.map(item => item.name), // 從series中提取唯一Name
formatter: (label) => label.split('-')[1], // 隱藏前綴
top: '15%',
left: 'center',
itemWidth: 12,
itemHeight: 12,
textStyle: { color: '#666', fontSize: 12 }
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true // 防止標籤溢出
},
xAxis: {
type: 'category',
data: months,
axisLabel: {
color: '#666',
fontSize: 12
}
},
yAxis: {
type: 'value',
name: '故障次數',
nameTextStyle: { color: '#666', fontSize: 12 },
axisLabel: {
color: '#666',
fontSize: 12,
formatter: '{value}'
}
},
series: seriesData
};
// 3. 渲染圖表
chart.setOption(option);
// 4. 監聽窗口 resize,自適應圖表
window.addEventListener('resize', () => {
chart.resize();
});
五、關鍵注意事項:避免踩坑
1. 分隔符選擇:避免與原始 Name 衝突
如果原始 Name 中包含“-”(如“1-級故障”),則不能用“-”作為分隔符,可改用“|”“_”等不常用的符號,比如“0|1-級故障”,確保切割時能準確分離唯一標識和原始 Name。
2. 動態數據處理:批量生成唯一 Name
如果數據是從接口獲取的(動態數據),可通過循環給重複 Name 加唯一標識:
// 假設接口返回的原始數據
const rawSeries = [
{ name: '1', data: [1.31, 1.33], device: 'A' },
{ name: '1', data: [0.56, 0.61], device: 'B' },
{ name: '0', data: [0.82, 0.95], device: 'A' }
];
// 批量生成唯一 Name(用設備名+原始Name)
const seriesData = rawSeries.map((item, index) => {
return {
name: `${item.device}-${item.name}`, // 唯一標識:A-1、B-1、A-0
type: 'bar',
data: item.data,
// 其他配置...
};
});
// legend.data 自動生成
const legendData = seriesData.map(item => item.name);
3. 多圖表聯動:保持標識規則一致
如果多個圖表共用一個 legend(如聯動圖表),所有圖表的 series.name 必須遵循同一標識規則,否則聯動時會出現控制失效的問題。