diff --git a/src/BootstrapBlazor.Server/Components/Samples/DockViews/BaseDockView.cs b/src/BootstrapBlazor.Server/Components/Samples/DockViews/BaseDockView.cs index f8960886800..f7d6528e4f5 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/DockViews/BaseDockView.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/DockViews/BaseDockView.cs @@ -147,10 +147,10 @@ public class TreeFoo : Foo ParentId = parentId, Name = localizer["Foo.Name", $"{id + i:d4}"], DateTime = System.DateTime.Now.AddDays(i - 1), - Address = localizer["Foo.Address", $"{Random.Next(1000, 2000)}"], - Count = Random.Next(1, 100), - Complete = Random.Next(1, 100) > 50, - Education = Random.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle + Address = localizer["Foo.Address", $"{Random.Shared.Next(1000, 2000)}"], + Count = Random.Shared.Next(1, 100), + Complete = Random.Shared.Next(1, 100) > 50, + Education = Random.Shared.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle }).ToList(); } } diff --git a/src/BootstrapBlazor.Server/Components/Samples/DockViews2/BaseDockView.cs b/src/BootstrapBlazor.Server/Components/Samples/DockViews2/BaseDockView.cs index c7bb00fc102..b43fb66ad7d 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/DockViews2/BaseDockView.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/DockViews2/BaseDockView.cs @@ -141,10 +141,10 @@ public class TreeFoo : Foo ParentId = parentId, Name = localizer["Foo.Name", $"{id + i:d4}"], DateTime = System.DateTime.Now.AddDays(i - 1), - Address = localizer["Foo.Address", $"{Random.Next(1000, 2000)}"], - Count = Random.Next(1, 100), - Complete = Random.Next(1, 100) > 50, - Education = Random.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle + Address = localizer["Foo.Address", $"{Random.Shared.Next(1000, 2000)}"], + Count = Random.Shared.Next(1, 100), + Complete = Random.Shared.Next(1, 100) > 50, + Education = Random.Shared.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle }).ToList(); } } diff --git a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumn.razor b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumn.razor index b68860edc2c..ba56e6b93ef 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumn.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumn.razor @@ -146,7 +146,7 @@ - + diff --git a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumn.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumn.razor.cs index 62c7e30e2a1..a529a172e9b 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumn.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumn.razor.cs @@ -41,13 +41,13 @@ protected override void OnInitialized() /// /// /// - private static Task IntFormatter(object? d) + private static Task IntFormatter(object d) { - var ret = ""; + string? ret = null; if (d is TableColumnContext data && data.Value != null) { var val = (int)data.Value; - ret = val.ToString("0.00"); + ret = $"Sales: {val:0.00}"; } return Task.FromResult(ret); } @@ -58,7 +58,7 @@ private Task> OnQueryAsync(QueryPageOptions options) // 先处理过滤再处理排序 提高性能 var isFiltered = false; - if (options.Filters.Any()) + if (options.Filters.Count != 0) { items = items.Where(options.Filters.GetFilterFunc()); isFiltered = true; diff --git a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnList.razor b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnList.razor index 3e5c1f9ade8..6a0c811af11 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnList.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnList.razor @@ -62,7 +62,7 @@ - + diff --git a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnList.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnList.razor.cs index 49e07411ac6..ffe6e3be8ed 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnList.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnList.razor.cs @@ -27,23 +27,6 @@ public partial class TablesColumnList [NotNull] private Table? TableColumnVisible { get; set; } - /// - /// IntFormatter - /// - /// - /// - private static Task IntFormatter(object? d) - { - var ret = ""; - if (d is TableColumnContext data && data.Value != null) - { - var val = (int)data.Value; - ret = val.ToString("0.00"); - } - - return Task.FromResult(ret); - } - /// /// OnInitialized 方法 /// diff --git a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesTree.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesTree.razor.cs index f759cae03d8..8120211a37d 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesTree.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesTree.razor.cs @@ -125,10 +125,10 @@ private class TreeFoo : Foo ParentId = parentId, Name = localizer["Foo.Name", $"{id + i:d4}"], DateTime = System.DateTime.Now.AddDays(i - 1), - Address = localizer["Foo.Address", $"{Random.Next(1000, 2000)}"], - Count = Random.Next(1, 100), - Complete = Random.Next(1, 100) > 50, - Education = Random.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle + Address = localizer["Foo.Address", $"{Random.Shared.Next(1000, 2000)}"], + Count = Random.Shared.Next(1, 100), + Complete = Random.Shared.Next(1, 100) > 50, + Education = Random.Shared.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle }).ToList(); } } diff --git a/src/BootstrapBlazor.Server/Data/Foo.cs b/src/BootstrapBlazor.Server/Data/Foo.cs index 1a6cdfb01ad..8b0988cc851 100644 --- a/src/BootstrapBlazor.Server/Data/Foo.cs +++ b/src/BootstrapBlazor.Server/Data/Foo.cs @@ -87,11 +87,6 @@ public class Foo public int ReadonlyColumn { get; init; } #region Static methods - /// - /// 随机数 Random 实例 - /// - protected static readonly Random Random = new(); - /// /// 生成Foo类,随机数据 /// Generate Foo class, random data @@ -103,10 +98,10 @@ public class Foo Id = 1, Name = localizer["Foo.Name", "1000"], DateTime = System.DateTime.Now, - Address = localizer["Foo.Address", $"{Random.Next(1000, 2000)}"], - Count = Random.Next(1, 100), - Complete = Random.Next(1, 100) > 50, - Education = Random.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle + Address = localizer["Foo.Address", $"{Random.Shared.Next(1000, 2000)}"], + Count = Random.Shared.Next(1, 100), + Complete = Random.Shared.Next(1, 100) > 50, + Education = Random.Shared.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle }; /// @@ -119,11 +114,11 @@ public class Foo Id = i, Name = localizer["Foo.Name", $"{i:d4}"], DateTime = System.DateTime.Now.AddDays(i - 1), - Address = localizer["Foo.Address", $"{Random.Next(1000, 2000)}"], - Count = Random.Next(1, 100), - Complete = Random.Next(1, 100) > 50, - Education = Random.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle, - ReadonlyColumn = Random.Next(10, 50) + Address = localizer["Foo.Address", $"{Random.Shared.Next(1000, 2000)}"], + Count = Random.Shared.Next(1, 100), + Complete = Random.Shared.Next(1, 100) > 50, + Education = Random.Shared.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle, + ReadonlyColumn = Random.Shared.Next(10, 50) }).ToList(); /// @@ -162,7 +157,7 @@ public static List GetCompleteItems(IStringLocalizer localize /// 通过 Id 获取 Title /// /// - private static string GetTitle() => Random.Next(1, 80) switch + private static string GetTitle() => Random.Shared.Next(1, 80) switch { >= 1 and < 10 => "Clerk", >= 10 and < 50 => "Engineer", diff --git a/src/BootstrapBlazor/Attributes/AutoGenerateColumnAttribute.cs b/src/BootstrapBlazor/Attributes/AutoGenerateColumnAttribute.cs index 4f4d105913a..9560b190413 100644 --- a/src/BootstrapBlazor/Attributes/AutoGenerateColumnAttribute.cs +++ b/src/BootstrapBlazor/Attributes/AutoGenerateColumnAttribute.cs @@ -158,7 +158,7 @@ public class AutoGenerateColumnAttribute : AutoGenerateBaseAttribute, ITableColu /// /// /// - public Func>? Formatter { get; set; } + public Func>? Formatter { get; set; } /// /// 获得/设置 编辑模板 diff --git a/src/BootstrapBlazor/Components/Table/ITableColumn.cs b/src/BootstrapBlazor/Components/Table/ITableColumn.cs index ab4eb4561eb..fe0cd5224d3 100644 --- a/src/BootstrapBlazor/Components/Table/ITableColumn.cs +++ b/src/BootstrapBlazor/Components/Table/ITableColumn.cs @@ -128,7 +128,7 @@ public interface ITableColumn : IEditorItem /// /// 获得/设置 列格式化回调委托 /// - Func>? Formatter { get; set; } + Func>? Formatter { get; set; } /// /// 获得/设置 文字对齐方式 默认为 null 使用 Alignment.None diff --git a/src/BootstrapBlazor/Components/Table/InternalTableColumn.cs b/src/BootstrapBlazor/Components/Table/InternalTableColumn.cs index e5e574d86fe..a7467d340ed 100644 --- a/src/BootstrapBlazor/Components/Table/InternalTableColumn.cs +++ b/src/BootstrapBlazor/Components/Table/InternalTableColumn.cs @@ -127,7 +127,7 @@ class InternalTableColumn(string fieldName, Type fieldType, string? fieldText = /// public string? PlaceHolder { get; set; } - public Func>? Formatter { get; set; } + public Func>? Formatter { get; set; } public Alignment? Align { get; set; } diff --git a/src/BootstrapBlazor/Components/Table/TableColumn.cs b/src/BootstrapBlazor/Components/Table/TableColumn.cs index 217091be838..f80610fdde5 100644 --- a/src/BootstrapBlazor/Components/Table/TableColumn.cs +++ b/src/BootstrapBlazor/Components/Table/TableColumn.cs @@ -8,20 +8,12 @@ namespace BootstrapBlazor.Components; -#if NET5_0 -/// -/// 表头组件 -/// -/// 绑定字段值类型 -public class TableColumn : BootstrapComponentBase, ITableColumn -#elif NET6_0_OR_GREATER /// /// 表头组件 /// /// 模型泛型 /// 绑定字段值类型 public class TableColumn : BootstrapComponentBase, ITableColumn -#endif { /// /// 获得/设置 相关过滤器 @@ -293,7 +285,7 @@ public class TableColumn : BootstrapComponentBase, ITableColumn /// /// [Parameter] - public Func>? Formatter { get; set; } + public Func>? Formatter { get; set; } /// /// 获得/设置 显示模板 diff --git a/src/BootstrapBlazor/Components/Table/TableFormatContent.cs b/src/BootstrapBlazor/Components/Table/TableFormatContent.cs new file mode 100644 index 00000000000..ad0d1cb9a08 --- /dev/null +++ b/src/BootstrapBlazor/Components/Table/TableFormatContent.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +using Microsoft.AspNetCore.Components.Rendering; + +namespace BootstrapBlazor.Components; + +internal class TableFormatContent : ComponentBase +{ + /// + /// 获得/设置 格式化方法 + /// + [Parameter] + [NotNull] + [EditorRequired] + public Func>? Formatter { get; set; } + + /// + /// 获得/设置 当前显示数据项 + /// + [Parameter] + [NotNull] + [EditorRequired] + public object? Item { get; set; } + + private string? _content; + + protected override async Task OnParametersSetAsync() + { + await base.OnParametersSetAsync(); + + _content = await Formatter(Item); + } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + if (!string.IsNullOrEmpty(_content)) + { + builder.AddContent(0, _content); + } + } +} diff --git a/src/BootstrapBlazor/Extensions/ITableColumnExtensions.cs b/src/BootstrapBlazor/Extensions/ITableColumnExtensions.cs index 01f0d7646b1..9c1c0687134 100644 --- a/src/BootstrapBlazor/Extensions/ITableColumnExtensions.cs +++ b/src/BootstrapBlazor/Extensions/ITableColumnExtensions.cs @@ -172,7 +172,7 @@ public static List ToSearches(this IEnumerable colu return searches; } - internal static RenderFragment RenderValue(this ITableColumn col, TItem item) => async builder => + internal static RenderFragment RenderValue(this ITableColumn col, TItem item) => builder => { var val = col.GetItemValue(item); if (col.Lookup != null && val != null) @@ -194,27 +194,32 @@ internal static RenderFragment RenderValue(this ITableColumn col, TItem i if (col.Formatter != null) { // 格式化回调委托 - content = await col.Formatter(new TableColumnContext(item, val)); - } - else if (!string.IsNullOrEmpty(col.FormatString)) - { - // 格式化字符串 - content = Utility.Format(val, col.FormatString); - } - else if (col.PropertyType.IsDateTime()) - { - content = Utility.Format(val, CultureInfo.CurrentUICulture.DateTimeFormat); - } - else if (val is IEnumerable v) - { - content = string.Join(",", v); + builder.OpenComponent(40); + builder.AddAttribute(45, nameof(TableFormatContent.Formatter), col.Formatter); + builder.AddAttribute(46, nameof(TableFormatContent.Item), new TableColumnContext(item, val)); + builder.CloseComponent(); } else { - content = val?.ToString(); + if (!string.IsNullOrEmpty(col.FormatString)) + { + // 格式化字符串 + content = Utility.Format(val, col.FormatString); + } + else if (col.PropertyType.IsDateTime()) + { + content = Utility.Format(val, CultureInfo.CurrentUICulture.DateTimeFormat); + } + else if (val is IEnumerable v) + { + content = string.Join(",", v); + } + else + { + content = val?.ToString(); + } + builder.AddContent(30, col.RenderTooltip(content, item)); } - - builder.AddContent(30, col.RenderTooltip(content, item)); } }; diff --git a/src/BootstrapBlazor/Services/CacheManager.cs b/src/BootstrapBlazor/Services/CacheManager.cs index 12866ebaedd..c7e626fc05a 100644 --- a/src/BootstrapBlazor/Services/CacheManager.cs +++ b/src/BootstrapBlazor/Services/CacheManager.cs @@ -697,7 +697,7 @@ public static Func, List, IEnumerable> GetSortListFunc } } - public static object GetFormatterInvoker(Type type, Func> formatter) + public static object GetFormatterInvoker(Type type, Func> formatter) { var cacheKey = $"{nameof(GetFormatterInvoker)}-{type.GetUniqueTypeName()}"; var invoker = Instance.GetOrCreate(cacheKey, entry => @@ -707,12 +707,12 @@ public static object GetFormatterInvoker(Type type, Func> }); return invoker(formatter); - static Expression>, object>> GetFormatterInvokerLambda(Type type) + static Expression>, object>> GetFormatterInvokerLambda(Type type) { var method = typeof(CacheManager).GetMethod(nameof(InvokeFormatterAsync), BindingFlags.Static | BindingFlags.NonPublic)!.MakeGenericMethod(type); var exp_p1 = Expression.Parameter(typeof(Func>)); var body = Expression.Call(null, method, exp_p1); - return Expression.Lambda>, object>>(body, exp_p1); + return Expression.Lambda>, object>>(body, exp_p1); } } diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index 76503ae1d35..bece13e9b74 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -5161,57 +5161,22 @@ public void TableColumn_Align() Assert.Equal("table-cell center", css); } - [Fact] - public void TableColumn_Ignore() - { - var items = new List([ - new() { Name = "Test1", Foo = new() { Id = 1, Name = "Test_1" } }, - new() { Name = "Test2", Foo = new() { Id = 2, Name = "Test_2" } }, - new() { Name = "Test3", Foo = new() { Id = 3, Name = "Test_3" } }, - ]); - var cut = Context.RenderComponent(pb => - { - pb.AddChildContent>(pb => - { - pb.Add(a => a.Items, items); - pb.Add(a => a.AutoGenerateColumns, true); - pb.Add(a => a.RenderMode, TableRenderMode.Table); - - pb.Add(a => a.TableColumns, foo => builder => - { - builder.OpenComponent>(0); - builder.AddAttribute(1, "Field", 0); - builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Foo.Id", typeof(int))); - builder.CloseComponent(); - - builder.OpenComponent>(0); - builder.AddAttribute(1, "Field", "Test"); - builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Foo.Name", typeof(string))); - builder.CloseComponent(); - - builder.OpenComponent>(0); - builder.AddAttribute(1, "Field", "Test"); - builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Foo.Address", typeof(string))); - builder.CloseComponent(); - }); - }); - }); - - // 自动生成 2 列 手动 Id 列忽略 Name, Address 列追加 - var table = cut.FindComponent>(); - Assert.Equal(4, table.Instance.Columns.Count); - } - [Fact] public void TableColumn_ComplexObject() { var cut = Context.RenderComponent>(pb => { - pb.Add(a => a.Field, "Foo.Name"); - pb.Add(a => a.FieldExpression, Utility.GenerateValueExpression(new MockComplexFoo(), "Foo.Name", typeof(string))); + pb.Add(a => a.Field, ""); }); var col = cut.Instance; var v = col.GetFieldName(); + Assert.Equal("", v); + + cut.SetParametersAndRender(pb => + { + pb.Add(a => a.FieldExpression, Utility.GenerateValueExpression(new MockComplexFoo(), "Foo.Name", typeof(string))); + }); + v = col.GetFieldName(); Assert.Equal("Name", v); } @@ -7703,7 +7668,7 @@ public async Task GetValue_LookupServiceKey() } [Fact] - public void Value_Formatter() + public async Task Value_Formatter() { var localizer = Context.Services.GetRequiredService>(); var cut = Context.RenderComponent(pb => @@ -7724,16 +7689,20 @@ public void Value_Formatter() cut.Contains("010"); var col = cut.FindComponent>(); + var formatted = false; col.SetParametersAndRender(pb => { pb.Add(a => a.Formatter, new Func>(obj => { + formatted = true; return Task.FromResult("test-formatter"); })); }); var table = cut.FindComponent(); - cut.InvokeAsync(() => table.Instance.QueryAsync()); - cut.WaitForAssertion(() => cut.Contains("test-formatter")); + await cut.InvokeAsync(() => table.Instance.QueryAsync()); + + // Formatter 方法不被调用 + Assert.True(formatted); } [Fact] @@ -8660,10 +8629,10 @@ private class FooTree : Foo ParentId = parentId, Name = localizer["Foo.Name", $"{seed:d2}{(i + seed):d2}"], DateTime = System.DateTime.Now.AddDays(i - 1), - Address = localizer["Foo.Address", $"{Random.Next(1000, 2000)}"], - Count = Random.Next(1, 100), - Complete = Random.Next(1, 100) > 50, - Education = Random.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle + Address = localizer["Foo.Address", $"{Random.Shared.Next(1000, 2000)}"], + Count = Random.Shared.Next(1, 100), + Complete = Random.Shared.Next(1, 100) > 50, + Education = Random.Shared.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle }).ToList(); } @@ -8678,10 +8647,10 @@ private class FooNoKeyTree : FooTree Id = i + seed, Name = localizer["Foo.Name", $"{seed:d2}{(i + seed):d2}"], DateTime = System.DateTime.Now.AddDays(i - 1), - Address = localizer["Foo.Address", $"{Random.Next(1000, 2000)}"], - Count = Random.Next(1, 100), - Complete = Random.Next(1, 100) > 50, - Education = Random.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle + Address = localizer["Foo.Address", $"{Random.Shared.Next(1000, 2000)}"], + Count = Random.Shared.Next(1, 100), + Complete = Random.Shared.Next(1, 100) > 50, + Education = Random.Shared.Next(1, 100) > 50 ? EnumEducation.Primary : EnumEducation.Middle }).ToList(); } diff --git a/test/UnitTest/Utils/UtilityTest.cs b/test/UnitTest/Utils/UtilityTest.cs index c8de533fd4c..d3d8759fd79 100644 --- a/test/UnitTest/Utils/UtilityTest.cs +++ b/test/UnitTest/Utils/UtilityTest.cs @@ -696,8 +696,12 @@ public void Format_Format() [Fact] public void GetTableColumns_Ok() { - var cols = Utility.GetTableColumns().ToList(); - Assert.Equal(2, cols.Count); + var columns = new InternalTableColumn[] + { + new("Name3", typeof(string)), + }; + var cols = Utility.GetTableColumns(columns).ToList(); + Assert.Equal(3, cols.Count); } [Fact]