!2682 feat(#I5338U): add GroupName on IEditItem interface

* chore: bump version 6.5.9-beta01
* Merge branch 'main' into Group
* test: 增加 EditorForm 单元测试
* test: 增加 LookupStringComparison 单元测试
* style: 增加样式兼容 row 排前面情况
* revert: 撤销分组排序代码
* feat: 增加分组排序渲染逻辑
* test: 增加 Group 单元测试
* refactor: 格式化代码
* feat: 增加 GroupName 与 GroupOrder
* feat: 增加 CategoryOrder 参数
* Update BootstrapBlazor.sln
* 移除分组测试工程
* refactor: 更改 GroupBox 内部使用 row
* doc: 移除背景色
* refactor: 更改 Group 为 Category
* Merge branch 'main' into Group
* SearchDialog 高级搜索
* 测试 ValidateForm 表单组件
* Table 的 ColumnAttribute 添加属性分组 Group 特性 https://gitee.com/LongbowEnter
This commit is contained in:
alex_zou 2022-04-19 13:35:40 +00:00 committed by Argo
parent 6b1b7aad27
commit 2720c8746e
14 changed files with 202 additions and 25 deletions

View File

@ -215,4 +215,14 @@ public class AutoGenerateColumnAttribute : AutoGenerateBaseAttribute, ITableColu
/// </summary>
/// <returns></returns>
public string GetFieldName() => FieldName;
/// <summary>
/// 获得/设置 当前属性分组
/// </summary>
public string? GroupName { get; set; }
/// <summary>
/// 获得/设置 当前属性分组排序 默认 0
/// </summary>
public int GroupOrder { get; set; }
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<Version>6.5.8</Version>
<Version>6.5.9-beta01</Version>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">

View File

@ -15,9 +15,13 @@
else
{
<div class="form-body">
<div class="@FormClassString">
<CascadingValue Value="this" Name="EidtorForm">
@foreach (var item in FormItems)
@foreach (var g in FormItems.GroupBy(i => i.GroupOrder).OrderBy(i => i.Key).Select(i => new { i.First().GroupName, Items = i.OrderBy(x => x.Order) }))
{
if (string.IsNullOrEmpty(g.GroupName))
{
<div class="@FormClassString">
@foreach (var item in g.Items)
{
var render = GetRenderTemplate(item);
@if (render != null)
@ -31,8 +35,32 @@ else
</div>
}
}
</CascadingValue>
</div>
}
else
{
<GroupBox Title="@g.GroupName">
<div class="@FormClassString">
@foreach (var item in g.Items)
{
var render = GetRenderTemplate(item);
@if (render != null)
{
@render(Model)
}
else
{
<div class="@GetCssString(item)">
@AutoGenerateTemplate(item)
</div>
}
}
</div>
</GroupBox>
}
}
</CascadingValue>
</div>
@if (Buttons != null)
{

View File

@ -134,12 +134,12 @@ public sealed partial class EditorForm<TModel> : IShowLabel
/// <summary>
/// 获得/设置 配置编辑项目集合
/// </summary>
private List<IEditorItem> EditorItems { get; } = new List<IEditorItem>();
private List<IEditorItem> EditorItems { get; } = new();
/// <summary>
/// 获得/设置 渲染的编辑项集合
/// </summary>
private List<IEditorItem> FormItems { get; } = new List<IEditorItem>();
private List<IEditorItem> FormItems { get; } = new();
[NotNull]
private string? PlaceHolderText { get; set; }

View File

@ -180,6 +180,18 @@ public class EditorItem<TValue> : ComponentBase, IEditorItem
[CascadingParameter]
private List<IEditorItem>? EditorItems { get; set; }
/// <summary>
/// 获得/设置 当前属性分组
/// </summary>
[Parameter]
public string? GroupName { get; set; }
/// <summary>
/// 获得/设置 当前属性分组排序 默认 0
/// </summary>
[Parameter]
public int GroupOrder { get; set; }
/// <summary>
/// OnInitialized 方法
/// </summary>

View File

@ -116,4 +116,19 @@ public interface IEditorItem
/// 获取绑定字段信息方法
/// </summary>
string GetFieldName();
/// <summary>
/// 获得/设置 顺序号
/// </summary>
int Order { get; set; }
/// <summary>
/// 获得/设置 当前属性分组
/// </summary>
string? GroupName { get; set; }
/// <summary>
/// 获得/设置 当前属性分组排序 默认 0
/// </summary>
int GroupOrder { get; set; }
}

View File

@ -13,3 +13,12 @@
background-color: #fff;
padding: 0 0.5rem;
}
.form-body > .groupbox:not(:last-child) {
margin-bottom: 1rem;
margin-top: 0;
}
.form-body > .row + .groupbox {
margin-top: 1rem;
}

View File

@ -116,11 +116,6 @@ public interface ITableColumn : IEditorItem
/// </summary>
bool ShowTips { get; set; }
/// <summary>
/// 获得/设置 顺序号
/// </summary>
int Order { get; set; }
/// <summary>
/// 获得/设置 单元格回调方法
/// </summary>

View File

@ -141,6 +141,16 @@ internal class InternalTableColumn : ITableColumn
/// </summary>
public List<IValidator>? ValidateRules { get; set; }
/// <summary>
/// 获得/设置 当前属性分组
/// </summary>
public string? GroupName { get; set; }
/// <summary>
/// 获得/设置 当前属性分组排序 默认 0
/// </summary>
public int GroupOrder { get; set; }
/// <summary>
/// 构造函数
/// </summary>
@ -286,5 +296,7 @@ internal class InternalTableColumn : ITableColumn
if (source.Width != null) dest.Width = source.Width;
if (source.ValidateRules != null) dest.ValidateRules = source.ValidateRules;
if (source.ShowLabelTooltip != null) dest.ShowLabelTooltip = source.ShowLabelTooltip;
if (!string.IsNullOrEmpty(source.GroupName)) dest.GroupName = source.GroupName;
if (source.GroupOrder != 0) dest.GroupOrder = source.GroupOrder;
}
}

View File

@ -397,6 +397,17 @@ public class TableColumn<TItem, TType> : BootstrapComponentBase, ITableColumn
[Parameter]
public string? FieldName { get; set; }
/// <summary>
/// 获得/设置 当前属性分组 默认 null
/// </summary>
public string? GroupName { get; set; }
/// <summary>
/// 获得/设置 当前属性分组排序 默认 0
/// </summary>
[Parameter]
public int GroupOrder { get; set; }
/// <summary>
/// 获取绑定字段信息方法
/// </summary>

File diff suppressed because one or more lines are too long

View File

@ -344,6 +344,7 @@ public class EditorFormTest : BootstrapBlazorTestBase
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.FieldExpression), Utility.GenerateValueExpression(foo, nameof(Foo.Name), typeof(string)));
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.Text), "Test-Text");
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.LookUpServiceKey), "FooLookup");
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.LookupStringComparison), StringComparison.OrdinalIgnoreCase);
builder.CloseComponent();
});
});
@ -353,6 +354,47 @@ public class EditorFormTest : BootstrapBlazorTestBase
Assert.Equal(lookup!.Count(), select.Instance.Items.Count());
}
[Fact]
public void GroupName_Order_Ok()
{
var foo = new Foo();
var cut = Context.RenderComponent<EditorForm<Foo>>(pb =>
{
pb.AddCascadingValue("IsSearch", true);
pb.Add(a => a.Model, foo);
pb.Add(a => a.AutoGenerateAllItem, false);
pb.Add(a => a.FieldItems, f => builder =>
{
var index = 0;
builder.OpenComponent<EditorItem<Foo, string>>(index++);
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.Field), f.Name);
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.FieldExpression), Utility.GenerateValueExpression(foo, nameof(Foo.Name), typeof(string)));
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.Text), "Test-Text");
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.Order), 1);
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.GroupName), "Test-Group-1");
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.GroupOrder), 1);
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.EditTemplate), new RenderFragment<Foo>(foo => builder => builder.AddContent(0, "Test")));
builder.CloseComponent();
builder.OpenComponent<EditorItem<Foo, string>>(index++);
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.Field), f.Address);
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.FieldExpression), Utility.GenerateValueExpression(foo, nameof(Foo.Address), typeof(string)));
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.Text), "Test-Address");
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.Order), 1);
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.GroupName), "Test-Group-2");
builder.AddAttribute(index++, nameof(EditorItem<Foo, string>.GroupOrder), 2);
builder.CloseComponent();
builder.OpenComponent<EditorItem<Foo, bool>>(index++);
builder.AddAttribute(index++, nameof(EditorItem<Foo, bool>.Field), f.Complete);
builder.AddAttribute(index++, nameof(EditorItem<Foo, bool>.FieldExpression), Utility.GenerateValueExpression(foo, nameof(Foo.Complete), typeof(bool)));
builder.AddAttribute(index++, nameof(EditorItem<Foo, bool>.Text), "Test-Complete");
builder.AddAttribute(index++, nameof(EditorItem<Foo, bool>.Order), 1);
builder.CloseComponent();
});
});
}
private static RenderFragment<Foo> GenerateEditorItems(Foo foo) => f => builder =>
{
builder.OpenComponent<EditorItem<Foo, string>>(0);

View File

@ -106,4 +106,8 @@ internal class MockTableColumn : ITableColumn
public string GetDisplayName() => Text ?? FieldName;
public string GetFieldName() => FieldName;
public string? GroupName { get; set; }
public int GroupOrder { get; set; }
}

View File

@ -0,0 +1,39 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace UnitTest.Utils;
public class GroupTest
{
ITestOutputHelper Logger { get; set; }
public GroupTest(ITestOutputHelper logger) => Logger = logger;
[Fact]
public void Group_Order_Ok()
{
var results = new List<string>();
var items = new List<MockTableColumn>(50)
{
new MockTableColumn("Test1", typeof(string)) { GroupName = "Test1", GroupOrder = 2, Order = 2 },
new MockTableColumn("Test2", typeof(string)) { GroupName = "Test1", GroupOrder = 2, Order = 1 },
new MockTableColumn("Test3", typeof(string)) { GroupName = "Test2", GroupOrder = 1, Order = 2 },
new MockTableColumn("Test4", typeof(string)) { GroupName = "Test2", GroupOrder = 1, Order = 1 }
};
var groups = items.GroupBy(i => i.GroupOrder).OrderBy(i => i.Key).Select(i => new { i.Key, Items = i.OrderBy(x => x.Order) });
foreach (var g in groups)
{
foreach (var item in g.Items)
{
results.Add(item.FieldName);
Logger.WriteLine($"{item.FieldName} - {item.GroupName} - {item.GroupOrder}");
}
}
var expected = string.Join(",", new List<string>() { "Test4", "Test3", "Test2", "Test1" });
var actual = string.Join(",", results);
Assert.Equal(expected, actual);
}
}