在 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

如果直接將 seriesname 設為“1”“1”“0”,會出現兩個問題:

  1. legend 只顯示 2 項(“0”和“1”),丟失一個“1”的圖例;
  2. 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(給重複項加標識)

給每個 seriesnamelegend.data 的每一項,添加“唯一標識 + 分隔符 + 原始 Name”的結構,確保無重複。
常用的標識規則:

  • 靜態數據:用“索引 + 分隔符(如 -)+ 原始 Name”,如“0-0”“1-1”“2-1”;
  • 動態數據:用“分類 ID + 分隔符 + 原始 Name”,如“deviceA-1”“deviceB-1”“deviceA-0”。

以靜態數據為例,改造後的 series.namelegend.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.formattertooltip.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 必須遵循同一標識規則,否則聯動時會出現控制失效的問題。