You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AngularJS中Highcharts分组堆叠柱状图实现问题求助

解决方案

一、先调整数据结构

原数据格式不适合图表库直接处理,先转成结构化的键值对,方便后续生成图表系列:

// 转换后的结构化数据
const rawData = {
  'JAN': {
    '2023': {'Asset 1':5, 'Asset 2':4, 'Asset 3':5},
    '2024': {'Asset 1':6, 'Asset 2':7, 'Asset 3':6}
  },
  'FEB': {
    '2023': {'Asset 1':3, 'Asset 2':6, 'Asset 3':7},
    '2024': {'Asset 1':1, 'Asset 2':2, 'Asset 3':5}
  }
  // 其他月份数据...
};

二、基于Chart.js+AngularJS实现需求

这里用angular-chart.js插件(适配AngularJS的Chart.js封装),核心思路是:

  1. 给每个资产生成两个系列(2023/2024),通过stack属性区分年份堆叠组
  2. 自定义图例逻辑,把同资产的两个系列合并成一个图例条目
  3. 自定义图例点击事件,实现点击单个资产时同时切换两年数据的显示/隐藏

1. 控制器代码(AngularJS)

angular.module('myAssetApp', ['chart.js'])
.controller('AssetChartCtrl', function($scope) {
  // 提取基础数据
  $scope.months = Object.keys(rawData);
  $scope.assets = Object.keys(rawData['JAN']['2023']);
  $scope.datasets = [];

  // 生成Chart.js所需的系列数据
  $scope.assets.forEach(asset => {
    // 生成该资产的2023年数据数组
    const data2023 = $scope.months.map(month => rawData[month]['2023'][asset]);
    // 生成该资产的2024年数据数组
    const data2024 = $scope.months.map(month => rawData[month]['2024'][asset]);
    // 给同资产的两年数据用同一种颜色(也可固定配色)
    const assetColor = `rgba(${Math.floor(Math.random()*200)}, ${Math.floor(Math.random()*200)}, ${Math.floor(Math.random()*200)}, 0.7)`;

    // 2023年系列
    $scope.datasets.push({
      label: `${asset} (2023)`,
      data: data2023,
      backgroundColor: assetColor,
      stack: '2023', // 标记为2023堆叠组
      legendGroup: asset, // 自定义分组,用于图例合并
      hidden: false
    });

    // 2024年系列
    $scope.datasets.push({
      label: `${asset} (2024)`,
      data: data2024,
      backgroundColor: assetColor,
      stack: '2024', // 标记为2024堆叠组
      legendGroup: asset,
      hidden: false
    });
  });

  // 图表基础数据
  $scope.chartData = {
    labels: $scope.months,
    datasets: $scope.datasets
  };

  // 图表配置(核心是自定义图例)
  $scope.chartOptions = {
    responsive: true,
    scales: {
      x: {
        stacked: false, // X轴不堆叠,让同月份的2023/2024柱形并列
        title: { display: true, text: '月份' }
      },
      y: {
        stacked: true, // Y轴堆叠,同年份下的资产数值叠加
        title: { display: true, text: '数值' }
      }
    },
    plugins: {
      legend: {
        // 自定义图例生成逻辑:合并同资产的两个系列
        labels: {
          generateLabels: function(chart) {
            const groupedLegends = {};
            chart.data.datasets.forEach((dataset, idx) => {
              const assetGroup = dataset.legendGroup;
              if (!groupedLegends[assetGroup]) {
                groupedLegends[assetGroup] = {
                  text: assetGroup,
                  fillStyle: dataset.backgroundColor,
                  datasetIndices: [idx],
                  hidden: dataset.hidden
                };
              } else {
                groupedLegends[assetGroup].datasetIndices.push(idx);
                // 只要有一个系列隐藏,就标记为隐藏状态
                groupedLegends[assetGroup].hidden = groupedLegends[assetGroup].hidden || dataset.hidden;
              }
            });
            return Object.values(groupedLegends);
          }
        },
        // 自定义图例点击事件:切换同资产下所有系列的显示状态
        onClick: function(event, legendItem, legend) {
          legendItem.datasetIndices.forEach(idx => {
            const dataset = legend.chart.data.datasets[idx];
            dataset.hidden = !dataset.hidden;
          });
          legend.chart.update();
        }
      }
    }
  };
});

2. HTML视图代码

<div ng-controller="AssetChartCtrl">
  <canvas id="assetDebugChart" 
          class="chart chart-bar"
          chart-data="chartData.datasets"
          chart-labels="chartData.labels"
          chart-options="chartOptions">
  </canvas>
</div>

三、核心逻辑说明

  • 堆叠分组:通过stack属性将2023和2024的系列分别归入不同堆叠组,实现同月份两年柱形并列、同年份资产数值堆叠的效果。
  • 图例合并:利用自定义legendGroup标记同资产的系列,在图例生成时合并成单个条目,避免重复显示。
  • 交互控制:自定义图例点击事件,点击单个资产条目时,同步切换该资产下2023/2024两个系列的显示状态,满足单独查看某资产的需求。

内容的提问来源于stack exchange,提问作者Dotan Beck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 00:47:35