Skip to content

Commit

Permalink
feat(Table): order of edit pop-up consisitent with table column (#2223)
Browse files Browse the repository at this point in the history
* test: 增加单元测试

* test: 更新单元测试

* test: 更新单元测试

* test: add test

* test: 更新单元测试

* test: 更新单元测试

* refactor: 优化代码

* chore:  更新依赖包

* doc: 增加文档链接

* feat: 增加默认排序回调参数

* doc: 增加忽略章节配置

* test: 增加单元测试

* refactor: 增加 OrderFunc 扩展方法

* feat: 增加 ColumnOrderCallback 回调方法

* doc: 增加 ColumnOrderCallback 回调方法文档说明

* test: 增加 ColumnOrderCallback 单元测试

* refactor: PlaceHolderText 更改为参数

* test: 增加单元测试
  • Loading branch information
ArgoZhang authored Oct 6, 2023
1 parent 9869ee2 commit fbdc2e5
Show file tree
Hide file tree
Showing 14 changed files with 211 additions and 56 deletions.
2 changes: 1 addition & 1 deletion src/BootstrapBlazor.Shared/BootstrapBlazor.Shared.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<PackageReference Include="BootstrapBlazor.Bluetooth" Version="7.0.1" />
<PackageReference Include="BootstrapBlazor.Chart" Version="7.6.0" />
<PackageReference Include="BootstrapBlazor.CherryMarkdown" Version="7.2.1" />
<PackageReference Include="BootstrapBlazor.Dock" Version="7.0.11" />
<PackageReference Include="BootstrapBlazor.Dock" Version="7.0.12" />
<PackageReference Include="BootstrapBlazor.FileViewer" Version="7.0.3" />
<PackageReference Include="BootstrapBlazor.FontAwesome" Version="7.5.0" />
<PackageReference Include="BootstrapBlazor.Html2Pdf" Version="7.2.0" />
Expand Down
1 change: 1 addition & 0 deletions src/BootstrapBlazor.Shared/Locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4665,6 +4665,7 @@
"OnQueryAsyncAttr": "Asynchronous query callback method",
"OnAddAsyncAttr": "New Button Callback Method",
"OnColumnCreatingAttr": "Callback delegate method on column creation",
"ColumnOrderCallbackAttr": "Callback delegate method on column order",
"OnDoubleClickCellCallbackAttr": "Set cell double-click event",
"OnDeleteAsyncAttr": "Delete Button Asynchronous Callback Method",
"OnEditAsyncAttr": "Edit button asynchronous callback method",
Expand Down
1 change: 1 addition & 0 deletions src/BootstrapBlazor.Shared/Locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -4665,6 +4665,7 @@
"OnQueryAsyncAttr": "异步查询回调方法",
"OnAddAsyncAttr": "新建按钮回调方法",
"OnColumnCreatingAttr": "列创建时回调委托方法",
"ColumnOrderCallbackAttr": "列排序回调委托方法",
"OnDoubleClickCellCallbackAttr": "设置单元格双击事件",
"OnDeleteAsyncAttr": "删除按钮异步回调方法",
"OnEditAsyncAttr": "编辑按钮异步回调方法",
Expand Down
8 changes: 8 additions & 0 deletions src/BootstrapBlazor.Shared/Samples/Table/Tables.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,14 @@ private void OnClick()
DefaultValue = " — "
},
new()
{
Name = nameof(Table<Foo>.ColumnOrderCallback),
Description = Localizer["ColumnOrderCallbackAttr"],
Type = "Func<List<ITableColumn>, IEnumerable<ITableColumn>>",
ValueList = " — ",
DefaultValue = " — "
},
new()
{
Name = nameof(Table<Foo>.OnDoubleClickCellCallback),
Description = Localizer["OnDoubleClickCellCallbackAttr"],
Expand Down
54 changes: 31 additions & 23 deletions src/BootstrapBlazor.Shared/Samples/Table/TablesEdit.razor
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<DemoBlock Title="@Localizer["TablesEditItemsTitle"]"
Introduction="@Localizer["TablesEditItemsIntro"]"
Name="EditItems">
<p class="mb-3">@((MarkupString)Localizer["TablesEditItemsDescription"].Value)</p>
<section ignore class="mb-3">@((MarkupString)Localizer["TablesEditItemsDescription"].Value)</section>
<Table TItem="Foo" @bind-Items="EditItems"
IsStriped="true" IsBordered="true" IsMultipleSelect="true"
ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true"
Expand Down Expand Up @@ -54,13 +54,15 @@
<DemoBlock Title="@Localizer["TablesEditOnAddAsyncTitle"]"
Introduction="@Localizer["TablesEditOnAddAsyncIntro"]"
Name="OnAddAsync">
<p id="anchor2">@((MarkupString)Localizer["TablesEditOnAddAsyncDescription"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditOnAddAsyncTips1"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditOnAddAsyncTips2"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditOnAddAsyncTips3"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditOnAddAsyncTips4"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditOnAddAsyncTips5"].Value)</p>
<p class="mb-3">@((MarkupString)Localizer["TablesEditOnAddAsyncTips6"].Value)</p>
<section ignore>
<p>@((MarkupString)Localizer["TablesEditOnAddAsyncDescription"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditOnAddAsyncTips1"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditOnAddAsyncTips2"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditOnAddAsyncTips3"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditOnAddAsyncTips4"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditOnAddAsyncTips5"].Value)</p>
<p class="mb-3">@((MarkupString)Localizer["TablesEditOnAddAsyncTips6"].Value)</p>
</section>
<Table TItem="Foo"
IsPagination="true" PageItemsSource="@PageItemsSource"
IsStriped="true" IsBordered="true" IsMultipleSelect="true" EditDialogIsDraggable="true"
Expand All @@ -81,10 +83,10 @@
<DemoBlock Title="@Localizer["TablesColumnEditTemplateTitle"]"
Introduction="@Localizer["TablesColumnEditTemplateIntro"]"
Name="EditTemplate">
<p class="mb-3">
<section ignore class="mb-3">
<div>@((MarkupString)Localizer["TablesColumnEditTemplateDescription1"].Value)</div>
<div>@((MarkupString)Localizer["TablesColumnEditTemplateTips"].Value)</div>
</p>
</section>
<Table TItem="Foo"
IsPagination="true" PageItemsSource="@PageItemsSource"
IsStriped="true" IsBordered="true" IsMultipleSelect="true" IsExtendButtonsInRowHeader="true"
Expand All @@ -110,9 +112,11 @@
<DemoBlock Title="@Localizer["TablesEditModeTitle"]"
Introduction="@Localizer["TablesEditModeIntro"]"
Name="EditMode">
<p id="anchor4">@((MarkupString)Localizer["TablesEditModeDescription"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditModeTips1"].Value)</p>
<p class="mb-3">@((MarkupString)Localizer["TablesEditModeTips2"].Value)</p>
<section ignore class="mb-3">
<p>@((MarkupString)Localizer["TablesEditModeDescription"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditModeTips1"].Value)</p>
<p>@((MarkupString)Localizer["TablesEditModeTips2"].Value)</p>
</section>
<Table TItem="Foo"
IsPagination="true" PageItemsSource="@PageItemsSource"
IsStriped="true" IsBordered="true" IsMultipleSelect="true"
Expand All @@ -130,8 +134,10 @@
</TableColumns>
</Table>

<p id="anchor5" class="mt-3">@((MarkupString)Localizer["TablesEditModeInCell"].Value)</p>
<RadioList @bind-Value="InsertMode" class="mb-3" />
<section ignore class="my-3">
<p>@((MarkupString)Localizer["TablesEditModeInCell"].Value)</p>
<RadioList @bind-Value="InsertMode" />
</section>
<Table TItem="Foo"
IsPagination="true" PageItemsSource="@PageItemsSource"
IsStriped="true" IsBordered="true" IsMultipleSelect="true"
Expand All @@ -152,7 +158,7 @@
<DemoBlock Title="@Localizer["TablesEditInjectDataServiceTitle"]"
Introduction="@Localizer["TablesEditInjectDataServiceIntro"]"
Name="InjectDataService">
<p id="anchor6">
<section ignore>
@((MarkupString)Localizer["TablesEditInjectDataServiceDescription"].Value)
<ul class="ul-demo mb-3">
<li><code>OnAddAsync</code></li>
Expand All @@ -163,8 +169,8 @@
<div class="mb-3">@Localizer["TablesEditInjectDataServiceTips1"] <a href="@DataServiceUrl" target="_blank">@Localizer["TablesEditInjectDataServiceTips2"]</a></div>
<b>@Localizer["TablesEditInjectDataServiceTips3"]</b>
<div class="mt-1">@Localizer["TablesEditInjectDataServiceTips4"]</div>
</p>
<Pre class="no-highlight my-3">services.AddTableDemoDataService();</Pre>
<Pre class="no-highlight my-3">services.AddTableDemoDataService();</Pre>
</section>
<Table TItem="Foo"
IsPagination="true" PageItemsSource="@PageItemsSource"
IsStriped="true" IsBordered="true" IsMultipleSelect="true" AutoGenerateColumns="true"
Expand All @@ -178,12 +184,12 @@
<DemoBlock Title="@Localizer["TablesEditDataServiceTitle"]"
Introduction="@Localizer["TablesEditDataServiceIntro"]"
Name="DataService">
<p id="anchor7">
<section ignore>
<b>@Localizer["TablesEditDataServiceDescription"]</b>
<div class="mt-1">@((MarkupString)Localizer["TablesEditDataServiceTips1"].Value)</div>
<div class="mt-1">@((MarkupString)Localizer["TablesEditDataServiceTips2"].Value)</div>
</p>
<Pre class="no-highlight my-3 mb-3">services.AddTableDemoDataService();</Pre>
<Pre class="no-highlight my-3 mb-3">services.AddTableDemoDataService();</Pre>
</section>
<Table TItem="Foo" EditDialogShowMaximizeButton="true"
IsPagination="true" PageItemsSource="@PageItemsSource" DataService="@CustomerDataService"
IsStriped="true" IsBordered="true" IsMultipleSelect="true" AutoGenerateColumns="true"
Expand All @@ -197,12 +203,14 @@
<DemoBlock Title="@Localizer["TablesEditFooterTemplateTitle"]"
Introduction="@Localizer["TablesEditFooterTemplateIntro"]"
Name="EditFooterTemplate">
<p>@((MarkupString)Localizer["TablesEditFooterTemplateDescription"].Value)</p>
<Pre class="mb-3">&lt;EditFooterTemplate Context="model"&gt;
<section ignore>
<p>@((MarkupString)Localizer["TablesEditFooterTemplateDescription"].Value)</p>
<Pre class="mb-3">&lt;EditFooterTemplate Context="model"&gt;
&lt;Button Text="Popup" Color="Color.Danger" Icon="fa-regular fa-comment-dots" OnClick="() =&gt; OnClick(model)"&gt;&lt;/Button&gt;
&lt;DialogCloseButton /&gt;
&lt;DialogSaveButton Color="Color.Primary" Icon="fa-solid fa-floppy-disk" Text="Save" /&gt;
&lt;/EditFooterTemplate&gt;</Pre>
</section>
<Table TItem="Foo" IsPagination="true" PageItemsSource="@PageItemsSource"
IsStriped="true" IsBordered="true" IsMultipleSelect="true"
ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true" IsExtendButtonsInRowHeader="true"
Expand Down
1 change: 1 addition & 0 deletions src/BootstrapBlazor.Shared/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"dock-view/stack": "DockViews\\DockViewStack",
"dock-view/lock": "DockViews\\DockViewLock",
"dock-view/visible": "DockViews\\DockViewVisible",
"dock-view/layout": "DockViews\\DockViewLayout",
"download": "Downloads",
"drag-drop": "DragDrops",
"drawer": "Drawers",
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/Components/EditorForm/EditorForm.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@typeparam TModel
@inherits BootstrapComponentBase

<CascadingValue Value="@EditorItems" IsFixed="true">
<CascadingValue Value="@_editorItems" IsFixed="true">
@FieldItems?.Invoke(Model)
</CascadingValue>

Expand Down
34 changes: 22 additions & 12 deletions src/BootstrapBlazor/Components/EditorForm/EditorForm.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,25 @@ public partial class EditorForm<TModel> : IShowLabel
[Parameter]
public IEnumerable<IEditorItem>? Items { get; set; }

/// <summary>
/// 获得/设置 自定义列排序规则 默认 null 未设置 使用内部排序机制 1 2 3 0 -3 -2 -1 顺序
/// </summary>
[Parameter]
public Func<IEnumerable<ITableColumn>, IEnumerable<ITableColumn>>? ColumnOrderCallback { get; set; }

/// <summary>
/// 获得/设置 未设置 GroupName 编辑项是否放置在顶部 默认 false
/// </summary>
[Parameter]
public bool ShowUnsetGroupItemsOnTop { get; set; }

/// <summary>
/// 获得/设置 默认占位符文本 默认 null
/// </summary>
[Parameter]
[NotNull]
public string? PlaceHolderText { get; set; }

/// <summary>
/// 获得/设置 级联上下文 EditContext 实例 内置于 EditForm 或者 ValidateForm 时有值
/// </summary>
Expand All @@ -158,23 +171,20 @@ public partial class EditorForm<TModel> : IShowLabel
/// <summary>
/// 获得/设置 配置编辑项目集合
/// </summary>
private List<IEditorItem> EditorItems { get; } = new();
private readonly List<IEditorItem> _editorItems = new();

/// <summary>
/// 获得/设置 渲染的编辑项集合
/// </summary>
private List<IEditorItem> FormItems { get; } = new();
private readonly List<IEditorItem> _formItems = new();

private IEnumerable<IEditorItem> UnsetGroupItems => FormItems.Where(i => string.IsNullOrEmpty(i.GroupName)).OrderBy(i => i.Order);
private IEnumerable<IEditorItem> UnsetGroupItems => _formItems.Where(i => string.IsNullOrEmpty(i.GroupName));

private IEnumerable<KeyValuePair<string, IOrderedEnumerable<IEditorItem>>> GroupItems => FormItems
private IEnumerable<KeyValuePair<string, IOrderedEnumerable<IEditorItem>>> GroupItems => _formItems
.Where(i => !string.IsNullOrEmpty(i.GroupName))
.GroupBy(i => i.GroupOrder).OrderBy(i => i.Key)
.Select(i => new KeyValuePair<string, IOrderedEnumerable<IEditorItem>>(i.First().GroupName!, i.OrderBy(x => x.Order)));

[NotNull]
private string? PlaceHolderText { get; set; }

/// <summary>
/// OnInitialized 方法
/// </summary>
Expand Down Expand Up @@ -231,18 +241,18 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
if (Items != null)
{
// 通过级联参数渲染组件
FormItems.AddRange(Items);
_formItems.AddRange(Items);
}
else
{
// 如果 EditorItems 有值表示 用户自定义列
if (AutoGenerateAllItem)
{
// 获取绑定模型所有属性
var items = Utility.GetTableColumns<TModel>().ToList();
var items = Utility.GetTableColumns<TModel>(defaultOrderCallback: ColumnOrderCallback).ToList();

// 通过设定的 FieldItems 模板获取项进行渲染
foreach (var el in EditorItems)
foreach (var el in _editorItems)
{
var item = items.FirstOrDefault(i => i.GetFieldName() == el.GetFieldName());
if (item != null)
Expand All @@ -260,11 +270,11 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
}
}
}
FormItems.AddRange(items.Where(i => i.Editable));
_formItems.AddRange(items.Where(i => i.Editable));
}
else
{
FormItems.AddRange(EditorItems.Where(i => i.Editable));
_formItems.AddRange(_editorItems.Where(i => i.Editable));
}
}
StateHasChanged();
Expand Down
8 changes: 7 additions & 1 deletion src/BootstrapBlazor/Components/Table/Table.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,12 @@ public void ExpandDetailRow(TItem item)
[Parameter]
public Func<List<ITableColumn>, Task>? OnColumnCreating { get; set; }

/// <summary>
/// 获得/设置 自定义列排序规则 默认 null 未设置 使用内部排序机制 1 2 3 0 -3 -2 -1 顺序
/// </summary>
[Parameter]
public Func<IEnumerable<ITableColumn>, IEnumerable<ITableColumn>>? ColumnOrderCallback { get; set; }

/// <summary>
/// 获得/设置 OnAfterRenderCallback 是否已经触发 默认 false
/// </summary>
Expand Down Expand Up @@ -822,7 +828,7 @@ private async Task ProcessFirstRender()
// 初始化列
if (AutoGenerateColumns)
{
var cols = Utility.GetTableColumns<TItem>(Columns);
var cols = Utility.GetTableColumns<TItem>(Columns, ColumnOrderCallback);
Columns.Clear();
Columns.AddRange(cols);
}
Expand Down
14 changes: 9 additions & 5 deletions src/BootstrapBlazor/Utils/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,16 +263,18 @@ public static void Copy<TModel>(TModel source, TModel destination) where TModel
/// </summary>
/// <typeparam name="TModel"></typeparam>
/// <param name="source"></param>
/// <param name="defaultOrderCallback">默认排序回调方法</param>
/// <returns></returns>
public static IEnumerable<ITableColumn> GetTableColumns<TModel>(IEnumerable<ITableColumn>? source = null) => GetTableColumns(typeof(TModel), source);
public static IEnumerable<ITableColumn> GetTableColumns<TModel>(IEnumerable<ITableColumn>? source = null, Func<IEnumerable<ITableColumn>, IEnumerable<ITableColumn>>? defaultOrderCallback = null) => GetTableColumns(typeof(TModel), source, defaultOrderCallback);

/// <summary>
/// 通过特定类型模型获取模型属性集合
/// </summary>
/// <param name="type">绑定模型类型</param>
/// <param name="source">Razor 文件中列集合</param>
/// <param name="defaultOrderCallback">默认排序回调方法</param>
/// <returns></returns>
public static IEnumerable<ITableColumn> GetTableColumns(Type type, IEnumerable<ITableColumn>? source = null)
public static IEnumerable<ITableColumn> GetTableColumns(Type type, IEnumerable<ITableColumn>? source = null, Func<IEnumerable<ITableColumn>, IEnumerable<ITableColumn>>? defaultOrderCallback = null)
{
var cols = new List<ITableColumn>(50);
var classAttribute = type.GetCustomAttribute<AutoGenerateClassAttribute>(true);
Expand Down Expand Up @@ -320,11 +322,13 @@ public static IEnumerable<ITableColumn> GetTableColumns(Type type, IEnumerable<I
cols.Add(tc);
}

return cols.Where(a => a.Order > 0).OrderBy(a => a.Order)
.Concat(cols.Where(a => a.Order == 0))
.Concat(cols.Where(a => a.Order < 0).OrderBy(a => a.Order));
return defaultOrderCallback?.Invoke(cols) ?? cols.OrderFunc();
}

private static IEnumerable<ITableColumn> OrderFunc(this List<ITableColumn> cols) => cols.Where(a => a.Order > 0).OrderBy(a => a.Order)
.Concat(cols.Where(a => a.Order == 0))
.Concat(cols.Where(a => a.Order < 0).OrderBy(a => a.Order));

/// <summary>
/// 通过指定 Model 获得 IEditorItem 集合方法
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ const unLockStack = (stack, dock) => {
const resetDockLock = dock => {
const unlocks = dock.layout.getAllContentItems().filter(com => com.isComponent && !com.container.initialState.lock)
const lock = unlocks.length === 0
if (dock.lock != lock) {
if (dock.lock !== lock) {
dock.lock = lock
dock.invokeLockAsync(lock)
}
Expand Down
22 changes: 22 additions & 0 deletions test/UnitTest/Components/EditorFormTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,28 @@ public void Order_Ok()
Assert.Equal(1, item.Instance.Order);
}

[Fact]
public void ColumnOrderCallback_Ok()
{
var foo = new Foo();
var cut = Context.RenderComponent<EditorForm<Foo>>(pb =>
{
pb.Add(a => a.Model, foo);
pb.Add(a => a.AutoGenerateAllItem, true);
pb.Add(a => a.ColumnOrderCallback, cols =>
{
return cols.OrderByDescending(i => i.Order);
});
});
var editor = cut.Instance;
var itemsField = editor.GetType().GetField("_formItems", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetField);
Assert.NotNull(itemsField);

var v = itemsField.GetValue(editor) as List<IEditorItem>;
Assert.NotNull(v);
Assert.Equal(new List<int>() { 60, 50, 40, 20, 10, 1 }, v.Select(i => i.Order));
}

[Fact]
public void LookupServiceKey_Ok()
{
Expand Down
Loading

0 comments on commit fbdc2e5

Please sign in to comment.