diff --git a/src/BootstrapBlazor.Shared/Samples/Table/TablesSelection.razor b/src/BootstrapBlazor.Shared/Samples/Table/TablesSelection.razor
index 680652ec0089a623763eb91e44a2f7fa58616dc0..b9e671d43a54ed668932d7792dd0b7272d92c6a5 100644
--- a/src/BootstrapBlazor.Shared/Samples/Table/TablesSelection.razor
+++ b/src/BootstrapBlazor.Shared/Samples/Table/TablesSelection.razor
@@ -26,4 +26,4 @@
}
-
+
diff --git a/src/BootstrapBlazor/Components/Table/Table.razor b/src/BootstrapBlazor/Components/Table/Table.razor
index 228baa0b63e5e29ac2e80c42304558518d030bb2..6434fcfe0c1344960752cfa47bc4f24e19a8c74e 100644
--- a/src/BootstrapBlazor/Components/Table/Table.razor
+++ b/src/BootstrapBlazor/Components/Table/Table.razor
@@ -91,18 +91,10 @@
-
-
- CSV (未实现)
-
MS-Excel
-
-
- PDF(未实现)
-
}
diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.Edit.cs b/src/BootstrapBlazor/Components/Table/Table.razor.Edit.cs
index d95af7842fb46403c774d895577c38f486c51fdd..5a0a503e61c7ab28e7d58b4e117339997d54e743 100644
--- a/src/BootstrapBlazor/Components/Table/Table.razor.Edit.cs
+++ b/src/BootstrapBlazor/Components/Table/Table.razor.Edit.cs
@@ -4,6 +4,8 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
+using System.ComponentModel.DataAnnotations;
+using System.Reflection;
namespace BootstrapBlazor.Components;
@@ -414,15 +416,18 @@ public partial class Table
///
protected async Task QueryData()
{
+ // 目前设计使用 Items 参数后不回调 OnQueryAsync 方法
if (Items == null)
{
if (OnQueryAsync == null && DynamicContext != null && typeof(TItem).IsAssignableTo(typeof(IDynamicObject)))
{
+ // 动态数据
QueryItems = DynamicContext.GetItems().Cast();
TotalCount = QueryItems.Count();
}
else
{
+ // 数据集合
await OnQuery();
}
}
@@ -462,6 +467,9 @@ public partial class Table
TotalCount = queryData.TotalCount;
IsAdvanceSearch = queryData.IsAdvanceSearch;
+ // 处理选中行逻辑
+ ProcessSelectedRows();
+
// 分页情况下内部不做处理防止页码错乱
if (!queryOption.IsPage)
{
@@ -473,83 +481,109 @@ public partial class Table
await ProcessTreeData();
}
}
- }
- void ProcessPageData(QueryData queryData, QueryPageOptions queryOption)
- {
- var filtered = queryData.IsFiltered;
- var sorted = queryData.IsSorted;
- var searched = queryData.IsSearch;
-
- // 外部未处理 SearchText 模糊查询
- if (!searched && queryOption.Searchs.Any())
+ void ProcessSelectedRows()
{
- QueryItems = QueryItems.Where(queryOption.Searchs.GetFilterFunc(FilterLogic.Or));
- TotalCount = QueryItems.Count();
+ // 判断模型是否有 [Key] Id 等可识别字段尝试重构
+ if (typeof(TItem).GetRuntimeProperties().Any(p => p.IsDefined(typeof(KeyAttribute))))
+ {
+ var rows = new List();
+ // 更新选中行逻辑
+ foreach (var item in SelectedRows)
+ {
+ var key = Utility.GetKeyValue(item);
+ if (key != null)
+ {
+ if (QueryItems.Any(i => Utility.GetKeyValue(i)?.ToString() == key.ToString()))
+ {
+ rows.Add(item);
+ }
+ }
+ }
+ SelectedRows = rows;
+ }
+ else
+ {
+ SelectedRows.Clear();
+ }
}
- // 外部未处理自定义高级搜索 内部进行高级自定义搜索过滤
- if (!IsAdvanceSearch && queryOption.CustomerSearchs.Any())
+ void ProcessPageData(QueryData queryData, QueryPageOptions queryOption)
{
- QueryItems = QueryItems.Where(queryOption.CustomerSearchs.GetFilterFunc());
- TotalCount = QueryItems.Count();
- IsAdvanceSearch = true;
- }
+ var filtered = queryData.IsFiltered;
+ var sorted = queryData.IsSorted;
+ var searched = queryData.IsSearch;
- // 外部未过滤,内部自行过滤
- if (!filtered && queryOption.Filters.Any())
- {
- QueryItems = QueryItems.Where(queryOption.Filters.GetFilterFunc());
- TotalCount = QueryItems.Count();
- }
+ // 外部未处理 SearchText 模糊查询
+ if (!searched && queryOption.Searchs.Any())
+ {
+ QueryItems = QueryItems.Where(queryOption.Searchs.GetFilterFunc(FilterLogic.Or));
+ TotalCount = QueryItems.Count();
+ }
- // 外部未处理排序,内部自行排序
- // 先处理列头排序 再处理默认多列排序
- if (!sorted)
- {
- if (queryOption.SortOrder != SortOrder.Unset && !string.IsNullOrEmpty(queryOption.SortName))
+ // 外部未处理自定义高级搜索 内部进行高级自定义搜索过滤
+ if (!IsAdvanceSearch && queryOption.CustomerSearchs.Any())
{
- var invoker = Utility.GetSortFunc();
- QueryItems = invoker(QueryItems, queryOption.SortName, queryOption.SortOrder);
+ QueryItems = QueryItems.Where(queryOption.CustomerSearchs.GetFilterFunc());
+ TotalCount = QueryItems.Count();
+ IsAdvanceSearch = true;
}
- else if (queryOption.SortList != null && queryOption.SortList.Any())
+
+ // 外部未过滤,内部自行过滤
+ if (!filtered && queryOption.Filters.Any())
{
- var invoker = Utility.GetSortListFunc();
- QueryItems = invoker(QueryItems, queryOption.SortList);
+ QueryItems = QueryItems.Where(queryOption.Filters.GetFilterFunc());
+ TotalCount = QueryItems.Count();
}
- }
- }
- async Task ProcessTreeData()
- {
- KeySet.Clear();
- if (TableTreeNode.HasKey)
- {
- CheckExpandKeys(TreeRows);
- }
- if (KeySet.Count > 0)
- {
- TreeRows = new List>();
- foreach (var item in QueryItems)
+ // 外部未处理排序,内部自行排序
+ // 先处理列头排序 再处理默认多列排序
+ if (!sorted)
{
- var node = new TableTreeNode(item)
+ if (queryOption.SortOrder != SortOrder.Unset && !string.IsNullOrEmpty(queryOption.SortName))
{
- HasChildren = CheckTreeChildren(item),
- };
- node.IsExpand = node.HasChildren && node.Key != null && KeySet.Contains(node.Key);
- if (node.IsExpand)
+ var invoker = Utility.GetSortFunc();
+ QueryItems = invoker(QueryItems, queryOption.SortName, queryOption.SortOrder);
+ }
+ else if (queryOption.SortList != null && queryOption.SortList.Any())
{
- await RestoreIsExpand(node);
+ var invoker = Utility.GetSortListFunc();
+ QueryItems = invoker(QueryItems, queryOption.SortList);
}
- TreeRows.Add(node);
}
}
- else
+
+ async Task ProcessTreeData()
{
- TreeRows = QueryItems.Select(item => new TableTreeNode(item)
+ KeySet.Clear();
+ if (TableTreeNode.HasKey)
+ {
+ CheckExpandKeys(TreeRows);
+ }
+ if (KeySet.Count > 0)
{
- HasChildren = CheckTreeChildren(item)
- }).ToList();
+ TreeRows = new List>();
+ foreach (var item in QueryItems)
+ {
+ var node = new TableTreeNode(item)
+ {
+ HasChildren = CheckTreeChildren(item),
+ };
+ node.IsExpand = node.HasChildren && node.Key != null && KeySet.Contains(node.Key);
+ if (node.IsExpand)
+ {
+ await RestoreIsExpand(node);
+ }
+ TreeRows.Add(node);
+ }
+ }
+ else
+ {
+ TreeRows = QueryItems.Select(item => new TableTreeNode(item)
+ {
+ HasChildren = CheckTreeChildren(item)
+ }).ToList();
+ }
}
}
}
diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.Pagination.cs b/src/BootstrapBlazor/Components/Table/Table.razor.Pagination.cs
index 90ba2e555f9edcd12540abfac0a3bd9d550596d3..4108341b72e1d8c7e14e3c7d30f35500219f04ef 100644
--- a/src/BootstrapBlazor/Components/Table/Table.razor.Pagination.cs
+++ b/src/BootstrapBlazor/Components/Table/Table.razor.Pagination.cs
@@ -54,7 +54,7 @@ public partial class Table
}
///
- /// 异步查询回调方法
+ /// 异步查询回调方法,设置 后无法触发此回调方法
///
[Parameter]
public Func>>? OnQueryAsync { get; set; }
diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.cs b/src/BootstrapBlazor/Components/Table/Table.razor.cs
index cc883b6bc68cc3d75d2b93e27ec326a965101661..a4b2bab9d1b4d4c3c2b10a565008210bc2977de3 100644
--- a/src/BootstrapBlazor/Components/Table/Table.razor.cs
+++ b/src/BootstrapBlazor/Components/Table/Table.razor.cs
@@ -477,7 +477,7 @@ public partial class Table : BootstrapComponentBase, IDisposable, ITable
public RenderFragment>? FooterTemplate { get; set; }
///
- /// 获得/设置 数据集合,适用于无功能时仅做数据展示使用,高级功能时请使用 回调委托
+ /// 获得/设置 数据集合,适用于无功能仅做数据展示使用,高级功能时请使用 回调委托
///
[Parameter]
public IEnumerable? Items { get; set; }
diff --git a/src/BootstrapBlazor/Extensions/LambdaExtensions.cs b/src/BootstrapBlazor/Extensions/LambdaExtensions.cs
index 4b96bdd275fb671fbbad26d50ad4c49132d85db7..c54c622db3b2bdf07700f46e7ef362543fc59989 100644
--- a/src/BootstrapBlazor/Extensions/LambdaExtensions.cs
+++ b/src/BootstrapBlazor/Extensions/LambdaExtensions.cs
@@ -3,6 +3,7 @@
// Website: https://www.blazor.zone or https://argozhang.github.io/
using BootstrapBlazor.Components;
+using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
using System.Reflection;
@@ -600,4 +601,24 @@ public static class LambdaExtensions
return false;
}
#endregion
+
+ ///
+ /// 获得 指定模型标记 的属性值
+ ///
+ ///
+ ///
+ ///
+ public static Expression> GetKeyValue(TModel model)
+ {
+ var type = model is not null ? model.GetType() : typeof(TModel);
+ Expression> ret = _ => default!;
+ var property = type.GetRuntimeProperties().FirstOrDefault(p => p.IsDefined(typeof(KeyAttribute)));
+ if (property != null)
+ {
+ var param = Expression.Parameter(typeof(TModel));
+ var body = Expression.Property(Expression.Convert(param, type), property);
+ ret = Expression.Lambda>(Expression.Convert(body, typeof(TValue)), param);
+ }
+ return ret;
+ }
}
diff --git a/src/BootstrapBlazor/Services/CacheManager.cs b/src/BootstrapBlazor/Services/CacheManager.cs
index dce57592f697048aecbf297d6d590663e88d693b..186edade26327da361a811355277f5057f646bac 100644
--- a/src/BootstrapBlazor/Services/CacheManager.cs
+++ b/src/BootstrapBlazor/Services/CacheManager.cs
@@ -301,6 +301,25 @@ internal class CacheManager : ICacheManager
});
invoker(model, value);
}
+
+ ///
+ /// 获得 指定模型标记 的属性值
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TValue GetKeyValue(TModel model)
+ {
+ var type = model is object o ? o.GetType() : typeof(TModel);
+ var cacheKey = ($"Lambda-GetKeyValue-{type.FullName}", typeof(TModel));
+ var invoker = Instance.GetOrCreate(cacheKey, entry =>
+ {
+ entry.SetDynamicAssemblyPolicy(type);
+ return LambdaExtensions.GetKeyValue(model).Compile();
+ });
+ return invoker(model);
+ }
#endregion
#region Lambda Sort
diff --git a/src/BootstrapBlazor/Utils/Utility.cs b/src/BootstrapBlazor/Utils/Utility.cs
index e6586f194b4357425525e2f8be63523a6b6705d9..e9944f00ca81fededf60a1dc8da6f6f50a1f6a41 100644
--- a/src/BootstrapBlazor/Utils/Utility.cs
+++ b/src/BootstrapBlazor/Utils/Utility.cs
@@ -4,6 +4,7 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
+using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq.Expressions;
using System.Reflection;
@@ -31,6 +32,15 @@ public static class Utility
///
public static string GetDisplayName(Type modelType, string fieldName) => CacheManager.GetDisplayName(modelType, fieldName);
+ ///
+ /// 获得 指定模型标记 的属性值
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TValue GetKeyValue(TModel model) => CacheManager.GetKeyValue(model);
+
///
///
///
diff --git a/test/UnitTest/Extensions/UtilityTest.cs b/test/UnitTest/Extensions/UtilityTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..907e222674316dee43908e8d72fb69ce2ce96f8b
--- /dev/null
+++ b/test/UnitTest/Extensions/UtilityTest.cs
@@ -0,0 +1,22 @@
+// 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/
+
+using BootstrapBlazor.Shared;
+
+namespace UnitTest.Extensions;
+
+public class UtilityTest : BootstrapBlazorTestBase
+{
+ [Fact]
+ public void GetKeyValue_Ok()
+ {
+ var foo = new Foo() { Id = 1 };
+ var v = Utility.GetKeyValue(foo);
+ Assert.Equal(1, v);
+
+ object foo1 = new Foo() { Id = 2 };
+ v = Utility.GetKeyValue