B06. BootstrapBlazor FreeSql实战 Chart 图表使用(2) - densen2014/Blazor100 GitHub Wiki
接上篇 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
;
});
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; }
}
@using Blazor100.Data
<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>
@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!);
}
}
}
}
@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!);
}
}
}
}