[硬核] Bootstrap Blazor Table 综合演示例子
知识点:
1.导入导出
2.分页功能
3.增删改查
4.批量删除
5.批量编辑(审核)
6.列排序与列搜索
7.顶部搜索实现所有列搜索
8.高级搜索实现多条件搜索
9.顶部与刷新与视图列
10.实现文本类型明细行
11.列的统计
12.隐藏列,时间日期列格式化
13.新窗口打开
14.随机数据
15.自由编辑
16.清空数据
17.模板下载
截图
基础工程
表格组件导出 Excel Word Html Pdf
注入FreeSqlDataService
服务,支持全数据导出
更新包
Program.cs
添加代码
using Densen.DataAcces.FreeSql;
builder.Services.AddSingleton(typeof(FreeSqlDataService<>));
builder.Services.ConfigureJsonLocalizationOptions(op =>
{
// 忽略文化信息丢失日志
op.IgnoreLocalizerMissing = true;
});
Index.razor
添加一个 TabItem
Tab 顺便改为懒加载
...
添加打印预览 Pages\_Host.cshtml
< / body > 前加一句
数据实体类 Data\SalesChannels.cs
查看代码
using BootstrapBlazor.Components;
using DocumentFormat.OpenXml.Wordprocessing;
using FreeSql.DataAnnotations;
using Magicodes.ExporterAndImporter.Excel;
using OfficeOpenXml.Table;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace b14table.Data;
[ExcelImporter(IsLabelingError = true)]
[ExcelExporter(Name = "导入商品中间表", TableStyle = TableStyles.Light10, AutoFitAllColumn = true)]
[AutoGenerateClass(Searchable = true, Filterable = true, Sortable = true, ShowTips = true)]
public class SalesChannels
{
[AutoGenerateColumn(Ignore = true)]
[Column(IsIdentity = true)]
[DisplayName("序号")]
public int ID { get; set; }
[AutoGenerateColumn(ComponentType = typeof(ColorPicker), Width = 30)]
[DisplayName("级别")]
public string? Background { get; set; }
[AutoGenerateColumn(FormatString = "yyyy-MM-dd")]
[DisplayName("日期")]
public DateTime Date { get; set; }
[Required(ErrorMessage = "{0}不能为空")]
[DisplayName("名称")]
public string? Name { get; set; }
[DisplayName("项目数量")]
public int Projects { get; set; }
[DisplayName("交单数量")]
public int Orders { get; set; }
[DisplayName("结单数量")]
public int Checkouts { get; set; }
// 编辑界面无法显示小数, 以后再思考
[DisplayName("结单率")]
[AutoGenerateColumn(Readonly = true)]
public string? CheckoutRates { get => GetCheckoutRates(Checkouts, Orders); set => checkoutRates = value; }
string? checkoutRates;
[DisplayName("合格数量")]
public int Qualifieds { get; set; }
[DisplayName("合格率")]
[AutoGenerateColumn(Readonly = true)]
public string? QualifiedRates { get => GetQualifiedRates(Qualifieds, Checkouts); set => qualifiedRates = value; }
string? qualifiedRates;
[DisplayName("总价值")]
public int Total { get; set; }
[DisplayName("应收款")]
public int Receivables { get; set; }
[DisplayName("已收款")]
public int Received { get; set; }
[AutoGenerateColumn(FormatString = "HH:mm:ss")]
[DisplayName("修改日期")]
public DateTime ModifiedDate { get; set; } = DateTime.Now;
[AutoGenerateColumn(TextEllipsis = true, Visible = false, ShowTips = true, ComponentType = typeof(Textarea))]
[DisplayName("备注")]
public string? Remark { get; set; }
[AutoGenerateColumn(Visible = false, ComponentType = typeof(BootstrapInput), Width = 80)]
[DisplayName("Test1")]
public decimal Test1 { get; set; }
private string GetCheckoutRates(int checkouts, int orders) => orders > 0 ? (checkouts /(double) orders).ToString("P2") : "0%";
private string GetQualifiedRates(int qualifieds, int checkouts) => checkouts > 0 ? (qualifieds / (double)checkouts).ToString("P2") : "0%";
}
页面 Pages\ImpExpIII.razor
查看代码
@page "/impexpiii"
@using b14table.Data
@using static Blazor100.Service.ImportExportsService
综合演示
备注: @context.Remark
当前页数据
ExportExcelAsync(list1.Rows)">
Excel
ExportWordAsync(list1.Rows)">
Word
ExportHtmlAsync(list1.Rows)">
Html
ExportPDFAsync(list1.Rows)">
PDF
全部数据
ExportExcelAsync(DataService.GetAllItems())">
Excel
ExportWordAsync(DataService.GetAllItems())">
Word
ExportHtmlAsync(DataService.GetAllItems())">
Html
ExportPDFAsync(DataService.GetAllItems())">
PDF
页面代码 Pages\ImpExpIII.razor
查看代码
using AmeBlazor.Components;
using b14table.Data;
using Blazor100.Service;
using BootstrapBlazor.Components;
using Densen.DataAcces.FreeSql;
using DocumentFormat.OpenXml.Spreadsheet;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.JSInterop;
using System.Diagnostics.CodeAnalysis;
using static Blazor100.Service.ImportExportsService;
namespace b14table.Pages
{
public partial class ImpExpIII
{
[Inject]
IWebHostEnvironment? HostEnvironment { get; set; }
[Inject]
[NotNull]
NavigationManager? NavigationManager { get; set; }
[Inject]
[NotNull]
ImportExportsService? ImportExportsService { get; set; }
[Inject]
[NotNull]
ToastService? ToastService { get; set; }
[Inject]
[NotNull]
FreeSqlDataService? DataService { get; set; }
[NotNull]
Table? list1 { get; set; }
[Parameter] public int Footercolspan1 { get; set; } = 3;
[Parameter] public int Footercolspan2 { get; set; } = 2;
[Parameter] public int Footercolspan3 { get; set; }
[Parameter] public int FootercolspanTotal { get; set; } = 2;
[Parameter] public string? FooterText { get; set; } = "合计:";
[Parameter] public string? FooterText2 { get; set; }
[Parameter] public string? FooterText3 { get; set; }
[Parameter] public string? FooterTotal { get; set; }
///
/// 获得/设置 IJSRuntime 实例
///
[Inject]
[NotNull]
protected IJSRuntime? JsRuntime { get; set; }
[Parameter] public string? 新窗口打开Url { get; set; } = "https://localhost:7292/";
// 由于使用了FreeSql ORM 数据服务,可以直接取对象
[Inject]
[NotNull]
IFreeSql? fsql { get; set; }
[Inject] ToastService? toastService { get; set; }
[Inject] SwalService? SwalService { get; set; }
public bool IsExcel { get; set; }
public bool DoubleClickToEdit { get; set; } = true;
protected string UploadPath = "";
protected string? uploadstatus;
long maxFileSize = 1024 * 1024 * 15;
string? tempfilename;
private AggregateType Aggregate { get; set; }
protected async Task GetDatasAsync()
{
var datas = GetDemoDatas();
await fsql.Insert().AppendData(datas).ExecuteAffrowsAsync();
await list1!.QueryAsync();
}
protected override async void OnAfterRender(bool firstRender)
{
if (firstRender)
{
UploadPath = Path.Combine(HostEnvironment!.WebRootPath, "uploads");
if (!Directory.Exists(UploadPath)) Directory.CreateDirectory(UploadPath);
await list1!.QueryAsync();
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
//懒的人,直接初始化一些数据用用
var res = fsql.Select().Count();
if (res == 0)
{
var datas = GetDemoDatas();
await fsql.Insert().AppendData(datas).ExecuteAffrowsAsync();
await list1!.QueryAsync();
}
}
}
public List GetDemoDatas()
{
var list = new List();
for (int i = 0; i < 100; i++)
{
try
{
var total = Random.Shared.Next(100, 3000);
list.Add(new SalesChannels()
{
ID = i,
Name = "渠道" + i,
Date = DateTime.Now,
Projects = Random.Shared.Next(10, 55),
Orders = Random.Shared.Next(3, 10),
Qualifieds = i,
Total = total,
Receivables = total - i,
Received = i,
Remark= $"{i} 明细行内嵌套另外一个 Table 组件,由于每行都要关联子表数据,出于性能的考虑,此功能采用 懒加载 模式,即点击展开按钮后,再对嵌套 Table 进行数据填充,通过 ShowDetailRow 回调委托可以控制每一行是否显示明细行,本例中通过 Complete 属性来控制是否显示明细行,可通过翻页来测试本功能"
});
}
catch (Exception e)
{
System.Console.WriteLine(e.Message);
}
}
return list;
}
private Task IsExcelToggle()
{
IsExcel = !IsExcel;
DoubleClickToEdit = !IsExcel;
StateHasChanged();
return Task.CompletedTask;
}
public async Task Export模板Async()
{
await Export();
return true;
}
private async Task ExportExcelAsync(IEnumerable items) => await ExportAutoAsync(items, ExportType.Excel);
private async Task ExportPDFAsync(IEnumerable items) => await ExportAutoAsync(items, ExportType.Pdf);
private async Task ExportWordAsync(IEnumerable items) => await ExportAutoAsync(items, ExportType.Word);
private async Task ExportHtmlAsync(IEnumerable items) => await ExportAutoAsync(items, ExportType.Html);
private async Task ExportAutoAsync(IEnumerable items, ExportType exportType = ExportType.Excel)
{
if (items == null || !items.Any())
{
await ToastService.Error("提示", "无数据可导出");
return false;
}
var option = new ToastOption()
{
Category = ToastCategory.Information,
Title = "提示",
Content = $"导出正在执行,请稍等片刻...",
IsAutoHide = false
};
// 弹出 Toast
await ToastService.Show(option);
await Task.Delay(100);
// 开启后台进程进行数据处理
await Export(items?.ToList(), exportType);
// 关闭 option 相关联的弹窗
option.Close();
// 弹窗告知下载完毕
await ToastService.Show(new ToastOption()
{
Category = ToastCategory.Success,
Title = "提示",
Content = $"导出成功,请检查数据",
IsAutoHide = false
});
return true;
}
private async Task Export(List? items = null, ExportType exportType = ExportType.Excel)
{
try
{
if (items == null || !items.Any())
{
ToastService?.Error($"导出", $"{exportType}出错,无数据可导出");
return;
}
var fileName = items == null ? "模板" : typeof(SalesChannels).Name;
var fullName = Path.Combine(UploadPath, fileName);
fullName = await ImportExportsService.Export(fullName, items, exportType);
fileName = (new System.IO.FileInfo(fullName)).Name;
ToastService?.Success("提示", fileName + "已生成");
//下载后清除文件
NavigationManager.NavigateTo($"uploads/{fileName}", true);
_ = Task.Run(() =>
{
Thread.Sleep(50000);
System.IO.File.Delete(fullName);
});
}
catch (Exception e)
{
ToastService?.Error($"导出", $"{exportType}出错,请检查. {e.Message}");
}
}
public async Task EmptyAll()
{
fsql.Delete().Where(a => 1 == 1).ExecuteAffrows();
await ToastService!.Show(new ToastOption()
{
Category = ToastCategory.Success,
Title = "提示",
Content = "已清空数据",
});
await list1!.QueryAsync();
return true;
}
private async Task ImportExcel()
{
if (string.IsNullOrEmpty(tempfilename))
{
ToastService?.Error("提示", "请正确选择文件上传");
return;
}
var option = new ToastOption()
{
Category = ToastCategory.Information,
Title = "提示",
Content = "导入文件中,请稍等片刻...",
IsAutoHide = false
};
// 弹出 Toast
await ToastService!.Show(option);
await Task.Delay(100);
// 开启后台进程进行数据处理
var isSuccess = await MockImportExcel();
// 关闭 option 相关联的弹窗
option.Close();
// 弹窗告知下载完毕
await ToastService.Show(new ToastOption()
{
Category = isSuccess ? ToastCategory.Success : ToastCategory.Error,
Title = "提示",
Content = isSuccess ? "操作成功,请检查数据" : "出现错误,请重试导入或者上传",
IsAutoHide = false
});
await list1!.QueryAsync();
}
private async Task MockImportExcel()
{
var items_temp = await ImportExportsService!.ImportFormExcel(tempfilename!);
if (items_temp.items == null)
{
ToastService?.Error("提示", "文件导入失败: " + items_temp.error);
return false;
}
//items = SmartCombine(items_temp, items).ToList(); 新数据和老数据合并处理,略100字
await fsql.Insert().AppendData(items_temp!.items.ToList()).ExecuteAffrowsAsync();
return true;
}
protected async Task OnChange(InputFileChangeEventArgs e)
{
if (e.File == null) return;
tempfilename = Path.Combine(UploadPath, e.File.Name);
await using FileStream fs = new(tempfilename, FileMode.Create);
using var stream = e.File.OpenReadStream(maxFileSize);
await stream.CopyToAsync(fs);
//正式工程此处是回调,简化版必须InvokeAsync一下,自由发挥
_ = Task.Run(async () => await InvokeAsync(async () => await ImportExcel()));
}
///
/// 导出数据方法
///
///
///
///
protected async Task ExportAsync(IEnumerable Items, QueryPageOptions opt)
{
var ret = await ExportExcelAsync(Items);
return ret;
}
public Task PrintPreview(IEnumerable item)
{
//实际工程自己完善js打印
JsRuntime.InvokeVoidAsync("printDiv");
return Task.CompletedTask;
}
private Task 新窗口打开()
{
if (string.IsNullOrEmpty(新窗口打开Url))
{
ToastService?.Error("提示", "Url为空!");
return Task.CompletedTask;
}
JsRuntime.NavigateToNewTab(新窗口打开Url);
return Task.CompletedTask;
}
public async Task 批量审批(IEnumerable items)
{
items.ToList().ForEach(a =>
{
a.Checkouts = a.Orders;
a.Receivables = 0;
a.Received = a.Total;
a.ModifiedDate = DateTime.Now;
});
var res = await fsql.Update().SetSource(items).ExecuteAffrowsAsync();
await SwalService!.Show(new SwalOption()
{
Title = res == 0 ? "提示: 操作失败" : "提示: 操作成功"
});
if (res != 0) await list1!.QueryAsync();
}
}
}
预览
源代码
https://gitee.com/densen2014/Blazor100/tree/master/b04table
https://github.com/densen2014/Blazor100/tree/master/b04table
项目地址
https://gitee.com/LongbowEnterprise/BootstrapBlazor
项目Wiki
https://gitee.com/LongbowEnterprise/BootstrapBlazor/wikis/QuickStart/[硬核] Table 综合演示例子?sort_id=7452536