接上篇 BootstrapBlazor实战 Chart 图表使用(1)
使用 nuget.org 进行 BootstrapBlazor 组件安装, FreeSql库,Newtonsoft.Json
dotnet add b06chart package Densen.FreeSql.Extensions.BootstrapBlazor dotnet add b06chart package FreeSql.Provider.Sqlite dotnet add b06chart package Newtonsoft.Json
添加FreeSql服务到 Program.cs
到
在 builder.Services.AddBootstrapBlazor();
之前加入
builder.Services.AddFreeSql(option => { //demo演示的是Sqlite驱动,FreeSql支持多种数据库,MySql/SqlServer/PostgreSQL/Oracle/Sqlite/Firebird/达梦/神通/人大金仓/翰高/华为GaussDB/MsAccess option.UseConnectionString(FreeSql.DataType.Sqlite, "Data Source=test.db;") //也可以写到配置文件中 #if DEBUG //开发环境:自动同步实体 .UseAutoSyncStructure(true) .UseNoneCommandParameter(true) //调试sql语句输出 .UseMonitorCommand(cmd => System.Console.WriteLine(cmd.CommandText)) #endif ; });
Model/OrdersEntry.cs
using BootstrapBlazor.Components; using FreeSql.DataAnnotations; using Newtonsoft.Json; using System.ComponentModel; using System.Linq; namespace Blazor100.Data; public partial class Orders { /// <summary> /// 流水号 /// </summary> [AutoGenerateColumn(Editable = false, DefaultSort = true, DefaultSortOrder = SortOrder.Desc, Order = 1)] [JsonProperty, Column(IsIdentity = true)] [DisplayName("流水号")] public int OrderID { get; set; } /// <summary> /// 单据日期 /// </summary> [AutoGenerateColumn(FormatString = "yyyy-MM-dd", ComponentType = typeof(DatePickerBody))] [JsonProperty] [DisplayName("日期")] public DateTime OrderDate { get; set; } /// <summary> /// 合计金额 /// </summary> [AutoGenerateColumn(FormatString = "N2", Align = Alignment.Right)] [JsonProperty, Column(DbType = "decimal(19,4)")] [DisplayName("合计")] public decimal SubTotal { get; set; } [AutoGenerateColumn(Ignore = true)] [Navigate(nameof(OrderID))] public virtual List<OrderDetails>? OrderDetailss { get; set; } } } /// <summary> /// 订单详单 /// </summary> public partial class OrderDetails { [JsonProperty, Column(IsIdentity = true)] public int ID { get; set; } [JsonProperty] public int OrderID { get; set; } [JsonProperty, Column(StringLength = -1)] [DisplayName("条码")] public string? BarCode { get; set; } [AutoGenerateColumn(FormatString = "N0", Align = Alignment.Center)] [JsonProperty, Column(DbType = "numeric(18,3)")] [DisplayName("数量")] public decimal Quantity { get; set; } [AutoGenerateColumn(Ignore = true)] [Navigate(nameof(OrderID))] public virtual Orders Orders { get; set; } }
Shared/_Imports.razor
@using Blazor100.Data
Shared/NavMenu.razor
<div class="nav-item px-3"> <NavLink class="nav-link" href="DayReport"> 月报 </NavLink> </div> <div class="nav-item px-3"> <NavLink class="nav-link" href="TopSales"> 排行榜 </NavLink> </div> <div class="nav-item px-3"> <NavLink class="nav-link" href="YearsCharts"> 年报 </NavLink> </div>
Components/ChartsBase.razor
@namespace b06chart @if (!IsHideSelectores) { <span> @Year 年</span> <span> @Month 月</span> <span> 合计 : @(Total.ToString("N2")) @TotalString2 @TotalString3 </span> } <div class="text-center mt-2 chart"> @if (!IsHideSelectores && UseDateTimeRangeValue) { <DateTimeRange @bind-Value="@DateTimeRangeValue1" OnConfirm="OnConfirm" OnClearValue="OnClear" /> } <div class="btn-group"> @if (!IsHideSelectores) { for (int i = DateTime.Now.Year - 7; i <= DateTime.Now.Year; i++) { var year = i; <Button Color="Color.Primary" IsOutline="@(Year!=year)" Text="@year.ToString()" OnClick="(()=>SetYear(year))" /> } } <Button Color="Color.Primary" IsOutline="true" OnClick="SwitchChart"><i class="fa @(IsLineChart?"fa-bar-chart":"fa-line-chart")"></i><span>切换</span></Button> <Button Color="Color.Primary" IsOutline="true" OnClick="SwitchStacked"><i class="fa @(IsStacked?"fa-toggle-on":"fa-toggle-off")"></i><span>@(IsStacked? "合并" : "不合并")</span></Button> <Button Color="Color.Primary" IsOutline="true" OnClick="e=>ReloadChart(true)"><i class="fa fa-refresh"></i><span>刷新</span></Button> </div> </div> @if (!IsHideSelectores && IsShowMonthSelector) { <div class="text-center mt-2 chart"> <div class="btn-group"> @{ for (int i = 1; i <= 12; i++) { var month = i; <Button Color="Color.Primary" IsOutline="@(Month!=month)" Text="@month.ToString()" OnClick="(()=>SetMonth(month))" /> } } <Button Color="Color.Primary" IsOutline="true" OnClick="PreMonth"><i class="fa fa-calendar-minus-o"></i><span>上月</span></Button> <Button Color="Color.Primary" IsOutline="true" OnClick="NextMonth"><i class="fa fa-calendar-plus-o"></i><span>下月</span></Button> <Button Color="Color.Primary" IsOutline="true" OnClick="SetNow"><i class="fa fa-calendar-check-o"></i><span>本月</span></Button> </div> </div> } <div style="width: calc(80%);display: block;margin: 0 auto;"> @if (Show) { if (!IsLineChart) { <Chart ChartType="ChartType.Bar" OnInitAsync="OnInit" @ref="BarChart" Width="" /> } else { <Chart OnInitAsync="OnInit" @ref="LineChart" /> } } </div>
添加后置代码 Components/ChartsBase.razor.cs
using BootstrapBlazor.Components; using Microsoft.AspNetCore.Components; using System.Diagnostics.CodeAnalysis; namespace b06chart { public partial class ChartsBase { private Chart? LineChart { get; set; } private Chart? BarChart { get; set; } /// <summary> /// 设定当前年份 /// </summary> [Parameter] public int Year { get; set; } = DateTime.Now.Year; /// <summary> /// 设定当前月份 /// </summary> [Parameter] public int Month { get; set; } = DateTime.Now.Month; /// <summary> /// 设定图表抬头 /// </summary> [Parameter] public string TitleCharts { get; set; } = "日报表"; /// <summary> /// 设定X轴文本 /// </summary> [Parameter] public string XAxesText { get; set; } = "天数"; /// <summary> /// 设定Y轴文本 /// </summary> [Parameter] public string YAxesText { get; set; } = "数值"; /// <summary> /// 图表类型:是=LineChart,否=BarChart /// </summary> [Parameter] public bool IsLineChart { get; set; } /// <summary> /// 使用默认数据 /// </summary> [Parameter] public bool IsDemo { get; set; } /// <summary> /// 显示月份选择器 /// </summary> [Parameter] public bool IsShowMonthSelector { get; set; } = true; [Parameter] public EventCallback<ChartDataSource> OnInitCallback { get; set; } [Parameter] public EventCallback<ChartDataSource> 数据生成Callback { get; set; } [Parameter] public decimal Total { get; set; } [Parameter] public string? TotalString2 { get; set; } [Parameter] public string? TotalString3 { get; set; } /// <summary> /// 隐藏选择器 /// </summary> [Parameter] public bool IsHideSelectores { get; set; } /// <summary> /// 使用/初始化日期选择控件日期 /// </summary> [Parameter] public bool UseDateTimeRangeValue { get; set; } /// <summary> /// 是否合并Bar显示 默认false /// </summary> public bool IsStacked { get; set; } /// <summary> /// 强刷显示控件控制,Hack一下 /// </summary> private bool Show { get; set; } = true; public int LastCount { get; set; } public bool FirstLoad { get; set; } = true; public bool ForceRefresh { get; set; } private string? ClickItemID { get; set; } private IEnumerable<string> Colors { get; set; } = new List<string>() { "Blue", "Green", "Red", "Orange", "Yellow", "Tomato", "Pink", "Violet" }; #region 日期选择控件 private DateTimeRangeValue DateTimeRangeValue1 { get; set; } = new DateTimeRangeValue(); DateTime 起始日期 = DateTime.Today.FirstDay(); DateTime 结束日期 = DateTime.Today.LastDay(); private Task OnConfirm(DateTimeRangeValue value) { 起始日期 = value.Start.FirstSecond(); 结束日期 = value.End.Year == 1 ? value.Start.LastSecond() : value.End.LastSecond(); Chart? chart = IsLineChart ? LineChart : BarChart; chart?.Update(ChartAction.Update); //StateHasChanged(); return Task.CompletedTask; } private Task OnClear(DateTimeRangeValue value) { 起始日期 = DateTime.Today.FirstDay(); 结束日期 = DateTime.Today.LastDay(); Chart? chart = IsLineChart ? LineChart : BarChart; chart?.Update(ChartAction.Update); //StateHasChanged(); return Task.CompletedTask; } /// <summary> /// 设置日期选择控件日期 /// </summary> /// <param name="_起始日期"></param> /// <param name="_结束日期"></param> /// <returns></returns> protected Task SetDates(DateTime _起始日期, DateTime _结束日期) { 起始日期 = _起始日期; 结束日期 = _结束日期; DateTimeRangeValue1.Start = 起始日期; DateTimeRangeValue1.End = 结束日期; return Task.CompletedTask; } #endregion protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); if (UseDateTimeRangeValue && firstRender) { DateTimeRangeValue1.Start = 起始日期; DateTimeRangeValue1.End = 结束日期; } } private Task OnAfterInit() { System.Console.WriteLine("Bar 初始化完毕"); return Task.CompletedTask; } /// <summary> /// 初始化 ChartDataSource /// </summary> /// <returns></returns> protected Task<ChartDataSource> OnInit() { var ds = new ChartDataSource(); if (!OnInitCallback.HasDelegate) { ds.Options.Title = TitleCharts; ds.Options.X.Title = XAxesText; ds.Options.X.Stacked = IsStacked; ds.Options.Y.Title = YAxesText; ds.Options.Y.Stacked = IsStacked; } else { OnInitCallback.InvokeAsync(ds); } //设置自定义颜色 ds.Options.Colors = new Dictionary<string, string>() { { "blue:", "rgb(54, 162, 235)" }, { "green:", "rgb(75, 192, 192)" }, { "red:", "rgb(255, 99, 132)" }, { "orange:", "rgb(255, 159, 64)" }, { "yellow:", "rgb(255, 205, 86)" }, { "tomato:", "rgb(255, 99, 71)" }, { "pink:", "rgb(255, 192, 203)" }, { "violet:", "rgb(238, 130, 238)" }, }; if (!数据生成Callback.HasDelegate) 数据生成(ds); else 数据生成Callback.InvokeAsync(ds); if (ds.Labels ==null || ds.Labels!.Count() == 0) { LastCount = 0; Show = false; return Task.FromResult(ds); } Show = true; ForceRefresh = LastCount == 0 || LastCount < ds.Labels!.Count(); LastCount = ds.Labels!.Count(); if (!FirstLoad && ForceRefresh) { ReloadChart(); ForceRefresh = false; } FirstLoad = false; return Task.FromResult(ds); } /// <summary> /// 数据生成,添加Labels和ChartDataset /// </summary> /// <param name="ds"></param> protected virtual void 数据生成(ChartDataSource ds) { } private Task SetYear(int year) { Chart? chart = IsLineChart ? LineChart : BarChart; Year = year; chart?.Update(ChartAction.Update); return Task.CompletedTask; } private Task SetMonth(int month) { Chart? chart = IsLineChart ? LineChart : BarChart; Month = month; chart?.Update(ChartAction.Update); return Task.CompletedTask; } private Task PreMonth() { Chart? chart = IsLineChart ? LineChart : BarChart; Year = Month - 1 >= 1 ? Year : Year - 1; Month = Month - 1 >= 1 ? Month - 1 : 12; chart?.Update(ChartAction.Update); return Task.CompletedTask; } private Task NextMonth() { Chart? chart = IsLineChart ? LineChart : BarChart; Year = Month + 1 <= 12 ? Year : Year + 1; Month = Month + 1 <= 12 ? Month + 1 : 1; chart?.Update(ChartAction.Update); return Task.CompletedTask; } private Task SetNow() { Chart? chart = IsLineChart ? LineChart : BarChart; Year = DateTime.Now.Year; Month = DateTime.Now.Month; chart?.Update(ChartAction.Update); return Task.CompletedTask; } private Task RandomData() { Chart? chart = IsLineChart ? LineChart : BarChart; chart?.Update(ChartAction.Update); return Task.CompletedTask; } private Task SwitchChart() { IsLineChart = !IsLineChart; return Task.CompletedTask; } /// <summary> /// 切换合并显示 /// </summary> private void SwitchStacked() { IsStacked = !IsStacked; ReloadChart(); } /// <summary> /// 强刷控件,重新初始化控件外观 /// </summary> private async void ReloadChart(bool reloadData=false) { Chart? chart = IsLineChart ? LineChart : BarChart; if (reloadData) chart?.Update(ChartAction.Update); Show = false; await InvokeAsync(StateHasChanged); await Task.Delay(1); Show = true; await InvokeAsync(StateHasChanged); } } public static class DateTimeExtensions { public static DateTime FirstDay(this DateTime obj) => new DateTime(obj.Year, obj.Month, 1, 0, 0, 0); public static DateTime LastDay(this DateTime obj) => obj.FirstDay().AddMonths(1).AddDays(-1).LastSecond(); public static DateTime FirstSecond(this DateTime obj) => new DateTime(obj.Year, obj.Month, obj.Day, 0, 0, 0); public static DateTime LastSecond(this DateTime obj) => new DateTime(obj.Year, obj.Month, obj.Day, 23, 59, 59); } }
@page "/DayReport" @namespace b06chart <Tab> <TabItem Text="日报表"> <ChartsBase @ref="charts" TitleCharts="日报表" 数据生成Callback="@((ds)=>数据生成(ds))" Total="@Total" TotalString2="@TotalString2" /> </TabItem> <TabItem Text="数据"> <Table TItem="Orders" IsPagination="true" IsStriped="true" IsBordered="true" AutoGenerateColumns="true" ShowSearch="true" ShowToolbar="true" ShowExtendButtons="true" DoubleClickToEdit=true ShowColumnList=true ShowCardView=true> </Table> </TabItem> </Tab>
添加后置代码 Pages/DayReport.razor.cs
using Blazor100.Data; using BootstrapBlazor.Components; using Microsoft.AspNetCore.Components; using System.Diagnostics.CodeAnalysis; namespace b06chart { public partial class DayReport { [Inject] [NotNull] IFreeSql? fsql { get; set; } [Inject] ToastService? toastService { get; set; } List<Orders> orders { get; set; } = new List<Orders>(); ChartsBase? charts; decimal Total { get; set; } string? TotalString2 { get; set; } private Task 数据生成(ChartDataSource ds) { var orders = fsql.Select<Orders>() .Where(a => a.OrderDate.Month == charts!. Month && a.OrderDate.Year == charts.Year) .GroupBy(a => new { a.OrderDate.Day }) .ToList(a => new { cou1 = a.Count(), OrderDate = a.Key.Day, Total = a.Sum(a.Value.SubTotal) }); orders = orders.OrderBy(a => a.OrderDate).ToList(); ds.Labels = orders.Select(a => a.OrderDate.ToString()); ds.Data.Add(new ChartDataset() { Label = $"单据数", Data = orders.Select(a => a.cou1).Cast<object>() }); ds.Data.Add(new ChartDataset() { Label = $"金额", Data = orders.Select(a => a.Total).Cast<object>() }); Total = orders.Select(a => a.Total).Sum(); return Task.CompletedTask; } protected override void OnAfterRender(bool firstRender) { if (firstRender) { Orders.DemoDatas(fsql!); } } } }
@page "/YearsCharts" @namespace b06chart <ChartsBase @ref="charts" TitleCharts="年报表" XAxesText="月" IsShowMonthSelector="false" 数据生成Callback="@((ds)=>数据生成(ds))" Total="@Total" />
添加后置代码 Pages/YearsCharts.razor.cs
using Blazor100.Data; using BootstrapBlazor.Components; using Microsoft.AspNetCore.Components; using System.Diagnostics.CodeAnalysis; namespace b06chart { public partial class YearsCharts { [Inject] [NotNull] IFreeSql? fsql { get; set; } [Inject] ToastService? toastService { get; set; } List<Orders> orders { get; set; } = new List<Orders>(); ChartsBase? charts; decimal Total { get; set; } string? TotalString2 { get; set; } private Task 数据生成(ChartDataSource ds) { var orders = fsql.Select<Orders>() .Where(a => a.OrderDate.Year == charts!.Year) .GroupBy(a => new { a.OrderDate.Month }) .ToList(a => new { cou1 = a.Count(), OrderDate = a.Key.Month, Total = a.Sum(a.Value.SubTotal) }); orders = orders.OrderBy(a => a.OrderDate).ToList(); ds.Labels = orders.Select(a => a.OrderDate.ToString()); ds.Data.Add(new ChartDataset() { Label = $"单据数", Data = orders.Select(a => a.cou1).Cast<object>() }); ds.Data.Add(new ChartDataset() { Label = $"金额", Data = orders.Select(a => a.Total).Cast<object>() }); Total = orders.Select(a => a.Total).Sum(); return Task.CompletedTask; } protected override void OnAfterRender(bool firstRender) { if (firstRender) { Orders.DemoDatas(fsql!); } } } }
Pages/OrdersTopSalesCharts.razor
@page "/TopSales" @namespace b06chart <Tab> <TabItem Text="销售排行榜"> <ChartsBase @ref="charts" TitleCharts="销售排行榜" 数据生成Callback="@((ds)=>数据生成(ds))" Total="@Total" /> </TabItem> <TabItem Text="数据"> <Table TItem="OrderDetails" IsPagination="true" IsStriped="true" IsBordered="true" AutoGenerateColumns="true" ShowSearch="true" ShowToolbar="true" ShowExtendButtons="true" DoubleClickToEdit=true ShowColumnList=true ShowCardView=true> </Table> </TabItem> </Tab>
添加后置代码 Pages/OrdersTopSalesCharts.razor.cs
using Blazor100.Data; using BootstrapBlazor.Components; using Microsoft.AspNetCore.Components; namespace b06chart { public partial class OrdersTopSalesCharts { [Inject] IFreeSql? fsql { get; set; } [Inject] ToastService? toastService { get; set; } List<OrderDetails> orders { get; set; } = new List<OrderDetails>(); ChartsBase? charts; decimal Total { get; set; } private Task 数据生成(ChartDataSource ds) { var 起始日期 = (new DateTime(charts!.Year, charts.Month, 1)).FirstDay(); var 结束日期 = 起始日期.LastDay(); orders = fsql!.Select<OrderDetails>() .Where( a => a.Orders.OrderDate.Between(起始日期, 结束日期)) .GroupBy(a => a.BarCode ) .OrderByDescending(a => a.Sum(a.Value.Quantity)) .ToList(a => new OrderDetails { BarCode = a.Key, Quantity = a.Sum(a.Value.Quantity) } ); ds.Labels = orders.Select(a => $"{a.BarCode}"); ds.Data.Add(new ChartDataset() { Label = $"销售量", Data = orders.Select(a => a.Quantity).Cast<object>() }); Total = orders.Sum(a => a.Quantity); return Task.CompletedTask; } protected override void OnAfterRender(bool firstRender) { if (firstRender) { Orders.DemoDatas(fsql!); } } } }
Github | Gitee
FreeSql QQ群:4336577(已满)、8578575(已满)、52508226(在线)
BA & Blazor QQ群:795206915、675147445
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow(包含链接: https://github.com/densen2014 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系 。
今日头条 | 博客园 | 知乎 | Gitee | GitHub