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

如何用C#和Blazor ApexCharts分组数据并构建堆叠柱状图?

Blazor Apex Charts 堆叠柱状图数据展示错误问题

我用Blazor + Blazor Apex Charts开发仪表盘图表,当前遇到以下问题:

  • 应用按周期运行,仅展示选中周期回溯的数据,周期为月份的整数格式(如202302、202401)
  • 需要实现堆叠柱状图:每个柱子按项目类型堆叠展示收入,数据范围覆盖选中周期至当年首个月份,后续需支持与上一年同期数据对比,且各月项目类型无需一致
  • 目前testStack列表已包含每个周期各项目类型的汇总收入数据,但代码生成的堆叠柱状图数据显示错误,先尝试实现当年数据的堆叠展示

后端代码

[Inject]
AppData appData { get; set; }
[Parameter]
public List<ProjectReport> chartDataForStack { get; set; }

private List<RevenueProjectType> testStack = new List<RevenueProjectType>();
private ApexChartOptions<RevenueProjectType> options;

protected override Task OnInitializedAsync()
{
    options = new ApexChartOptions<RevenueProjectType>
    {
        Chart = new Chart
        {
            Stacked = true,
        },
        PlotOptions = new PlotOptions
        {
            Bar = new PlotOptionsBar
            {
                Horizontal = false
            }
        },
    };

    SortData();
    return base.OnInitializedAsync();
}
public void SortData()
{
    // chartDataForStack在传入本组件前已按部分参数排序,最早月份为selectedYear
    var selectedYear = appData.SelectedPeriod / 100;

    var testData = chartDataForStack.Where(x => x.type == "revenue")
        .GroupBy(g => g.period)
        .OrderBy(o => o.Key)
        .ToList();

    var thisYear = new List<IGrouping<int, ProjectReport>>();
    var lastYear = new List<IGrouping<int, ProjectReport>>();

    testGroup2 = new List<RevenueProjectType>();

    foreach (var group in testData)
    {
        if (group.Key / 100 == selectedYear)
        {
            thisYear.Add(group);
        }
        else if (group.Key % 100 <= appData.SelectedPeriod % 100)
        {
            lastYear.Add(group);
        }
    }

    foreach (var group in thisYear)
    {
        var temp = group.GroupBy(x => x.project_type).ToList();
        testGroup.AddRange(temp);
    }

    foreach (var item in testGroup)
    {
        var revProjType = new RevenueProjectType();
        revProjType.revenue = item.Sum(x => x.acc);
        revProjType.projectType = item.Key;
        revProjType.period = item.FirstOrDefault(x => x.period != 0).period.ConvertRawPeriod();
        testStack.Add(revProjType);
    }
}

cshtml代码

<ApexChart TItem="RevenueProjectType"
           Title="Test"
           Options=options>
    @foreach(var item in testStack)
    {
        <ApexPointSeries TItem="RevenueProjectType"
                         Items="@testStack"
                         Name="@item.projectType"
                         SeriesType="SeriesType.Bar"
                         XValue="@(e => e.period)"
                         YAggregate="@(e => e.Sum(e => e.revenue))"
                         ShowDataLabels />
    }
    
 </ApexChart>

RevenueProjectType类

public class RevenueProjectType
{
    public string projectType { get; set; }
    public string period { get; set; }
    public decimal? revenue {  get; set; }
    public string projectTypeName { get; set; }
}

问题分析与修复

当前代码核心问题是重复创建了过多的ApexPointSeries:遍历testStack每条数据都生成一个系列,而正确逻辑是按项目类型创建系列,每个项目类型对应一个跨月份的数据集。

修复步骤:

  1. 重构数据分组逻辑
    修改SortData方法,先收集所有唯一项目类型,再为每个类型整理对应各月份的收入:

    // 替换原SortData中最后部分代码
    var allProjectTypes = testGroup.Select(g => g.Key).Distinct().ToList();
    testStack = new List<RevenueProjectType>();
    
    foreach (var type in allProjectTypes)
    {
        foreach (var monthGroup in thisYear)
        {
            var periodStr = monthGroup.Key.ConvertRawPeriod();
            var revenue = monthGroup.Where(x => x.project_type == type).Sum(x => x.acc);
            testStack.Add(new RevenueProjectType
            {
                projectType = type,
                period = periodStr,
                revenue = revenue
            });
        }
    }
    
  2. 调整前端系列渲染逻辑
    遍历唯一项目类型创建系列,每个系列仅绑定对应类型的数据:

    @{
        var uniqueProjectTypes = testStack.Select(x => x.projectType).Distinct().ToList();
    }
    
    <ApexChart TItem="RevenueProjectType"
               Title="Test"
               Options=options>
        @foreach(var type in uniqueProjectTypes)
        {
            var typeData = testStack.Where(x => x.projectType == type).ToList();
            <ApexPointSeries TItem="RevenueProjectType"
                             Items="@typeData"
                             Name="@type"
                             SeriesType="SeriesType.Bar"
                             XValue="@(e => e.period)"
                             YValue="@(e => e.revenue)"
                             ShowDataLabels />
        }
    </ApexChart>
    
  3. 后续扩展(上年数据对比)
    若要添加上年数据对比,可为RevenueProjectType增加IsLastYear标识字段,渲染时通过StackGroup属性区分当年/上年系列:

    <ApexPointSeries ... StackGroup="@(item.IsLastYear ? "lastYear" : "thisYear")" />
    

关键说明:

  • Blazor Apex Charts的堆叠逻辑:相同StackGroup的系列会堆叠在一起,默认所有系列归为同一组
  • 每个系列对应一个数据维度(项目类型),需包含该维度下所有X轴(月份)的数据
  • 避免在前端用YAggregate求和,应提前在后端计算好每个项目类型、每个月份的收入值,前端直接绑定YValue

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 00:05:00