From d0037eb1eaafa786983acd0860603e545d17507a Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 6 May 2026 17:27:31 +0800 Subject: [PATCH 01/26] =?UTF-8?q?refactor:=20=E9=87=8D=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Table/Table.razor.cs | 50 +++++++++++-------- .../Components/Table/Table.razor.js | 4 +- ...geStatus.cs => TableColumnClientStatus.cs} | 14 +++++- 3 files changed, 46 insertions(+), 22 deletions(-) rename src/BootstrapBlazor/Components/Table/{TableColumnLocalstorageStatus.cs => TableColumnClientStatus.cs} (55%) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.cs b/src/BootstrapBlazor/Components/Table/Table.razor.cs index 2ffd6466fcf..9fc84c54890 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.cs @@ -6,7 +6,6 @@ using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web.Virtualization; using System.Reflection; -using System.Text.Json; namespace BootstrapBlazor.Components; @@ -516,7 +515,7 @@ private string GetSortTooltip(ITableColumn col) => SortName != col.GetFieldName( [NotNull] private ILookupService? InjectLookupService { get; set; } - private TableColumnLocalstorageStatus _tableColumnStateCache = new(); + private TableColumnClientStatus _tableColumnStateCache = new(); private BreakPoint _screenSize; private string? _clientTableName; private bool _lastIsPopoverToolbarDropdownButtonValue; @@ -1293,7 +1292,7 @@ private async Task ReloadColumnStatesFromBrowserAsync() return; } - var state = await InvokeAsync("getColumnStates", ClientTableName); + var state = await InvokeAsync("getColumnStates", ClientTableName); if (state == null) { state = new(); @@ -1391,6 +1390,7 @@ private async Task OnTableRenderAsync(bool firstRender) TableName = ClientTableName, DragColumnCallback = nameof(DragColumnCallback), FitColumnWidthIncludeHeader, + AutoFitColumnWidthCallback = nameof(AutoFitColumnWidthCallback), ResizeColumnCallback = nameof(ResizeColumnCallback), ColumnMinWidth = ColumnMinWidth ?? Options.CurrentValue.TableSettings.ColumnMinWidth, VisibleColumns = string.IsNullOrEmpty(ClientTableName) ? null : _columnVisibleItems, @@ -1823,14 +1823,15 @@ private async Task OnContextMenu(MouseEventArgs e, TItem item) /// Gets or sets Resize Column Callback /// [Parameter] - public Func? OnResizeColumnAsync { get; set; } + public Func? OnResizeColumnAsync { get; set; } /// /// 获得/设置 自动调整列宽回调方法 /// Gets or sets Auto Fit Column Width Callback + /// /// [Parameter] - public Func>? OnAutoFitColumnWidthCallback { get; set; } + public Func? OnAutoFitColumnWidthCallback { get; set; } /// /// 获得/设置 列宽自适应时是否包含表头 默认 false @@ -1892,15 +1893,34 @@ public async Task DragColumnCallback(int originIndex, int currentIndex) /// Resize Column Method called by JavaScript /// [JSInvokable] - public async Task ResizeColumnCallback(string name, JsonElement state) + public async Task ResizeColumnCallback(string name, TableColumnClientStatus columnState) { - // 调整列宽时返回所有列状态,更新缓存数据中列宽度与可见性 - var columnState = state.Parse(); - if (columnState == null) + UpdateTableColumnState(columnState); + + // 触发回调 + if (OnResizeColumnAsync != null) { - return; + await OnResizeColumnAsync(name, columnState); + } + } + + /// + /// 列宽自适应回调方法 由 JavaScript 脚本调用 + /// Auto Fit Column Width Callback called by JavaScript + /// + [JSInvokable] + public async Task AutoFitColumnWidthCallback(string fieldName, TableColumnClientStatus columnState) + { + UpdateTableColumnState(columnState); + + if (OnAutoFitColumnWidthCallback != null) + { + await OnAutoFitColumnWidthCallback(fieldName, columnState); } + } + private void UpdateTableColumnState(TableColumnClientStatus columnState) + { if (!string.IsNullOrEmpty(ClientTableName)) { // 更新缓存数据中列宽度 @@ -1914,16 +1934,6 @@ public async Task ResizeColumnCallback(string name, JsonElement state) } _tableColumnStateCache.TableWidth = columnState.TableWidth; } - - // 触发回调 - if (OnResizeColumnAsync != null) - { - var column = columnState.Columns.Find(i => i.Name == name); - if (column != null) - { - await OnResizeColumnAsync(name, column.Width); - } - } } /// diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.js b/src/BootstrapBlazor/Components/Table/Table.razor.js index 25b6094d984..afde784cf1d 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.js +++ b/src/BootstrapBlazor/Components/Table/Table.razor.js @@ -651,9 +651,11 @@ const autoFitColumnWidth = async (table, col) => { }); resetColumnWidthTips(table, col); + const state = getColumnStateObject(table); saveColumnStateToLocalstorage(table, state); - await table.invoke.invokeMethodAsync(table.options.resizeColumnCallback, field, state) + + await table.invoke.invokeMethodAsync(table.options.autoFitColumnWidthCallback, field, state); } } diff --git a/src/BootstrapBlazor/Components/Table/TableColumnLocalstorageStatus.cs b/src/BootstrapBlazor/Components/Table/TableColumnClientStatus.cs similarity index 55% rename from src/BootstrapBlazor/Components/Table/TableColumnLocalstorageStatus.cs rename to src/BootstrapBlazor/Components/Table/TableColumnClientStatus.cs index fa37373e59d..a8099be4cc0 100644 --- a/src/BootstrapBlazor/Components/Table/TableColumnLocalstorageStatus.cs +++ b/src/BootstrapBlazor/Components/Table/TableColumnClientStatus.cs @@ -7,11 +7,23 @@ namespace BootstrapBlazor.Components; -class TableColumnLocalstorageStatus +/// +/// 表格列状态类 +/// Table Column Status Class +/// +public class TableColumnClientStatus { + /// + /// 列状态集合 + /// Column State Collection + /// [JsonPropertyName("cols")] public List Columns { get; set; } = []; + /// + /// 表格宽度 + /// Table Width + /// [JsonPropertyName("table")] public int TableWidth { get; set; } } From 4327521ffec403b087cbb9533b29939adb6cdfa9 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 6 May 2026 17:27:41 +0800 Subject: [PATCH 02/26] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/TableTest.cs | 4 ++-- .../ColumnVisibleItemConverterTest.cs | 21 ------------------- .../JsonDescriptionEnumConverterTest.cs | 14 ------------- 3 files changed, 2 insertions(+), 37 deletions(-) delete mode 100644 test/UnitTest/Converters/ColumnVisibleItemConverterTest.cs diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index e25a595a07c..4a1ceef4518 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -8771,7 +8771,7 @@ public async Task OnAutoFitColumnWidthCallback_Ok() }); var table = cut.FindComponent>(); - float v = 0f; + int? v = 0f; await cut.InvokeAsync(async () => v = await table.Instance.AutoFitColumnWidthCallback("DateTime", 90)); Assert.Equal(100.65f, v); } @@ -8851,7 +8851,7 @@ await cut.InvokeAsync(async () => public async Task OnResizeColumnCallback_Ok() { var name = ""; - var width = 0f; + int? width = null; var localizer = Context.Services.GetRequiredService>(); var cut = Context.Render(pb => { diff --git a/test/UnitTest/Converters/ColumnVisibleItemConverterTest.cs b/test/UnitTest/Converters/ColumnVisibleItemConverterTest.cs deleted file mode 100644 index a008df3d416..00000000000 --- a/test/UnitTest/Converters/ColumnVisibleItemConverterTest.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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 System.Text.Json; - -namespace UnitTest.Converters; - -public class ColumnVisibleItemConverterTest -{ - [Fact] - public void ColumnVisibleItemConverter_Ok() - { - var item = new ColumnVisibleItem("name", true) { DisplayName = "display" }; - var json = JsonSerializer.Serialize(item); - Assert.Equal("{\"name\":\"name\",\"visible\":true}", json); - - var item2 = JsonSerializer.Deserialize>("[{\"test\":\"test\"}]"); - } -} diff --git a/test/UnitTest/Converters/JsonDescriptionEnumConverterTest.cs b/test/UnitTest/Converters/JsonDescriptionEnumConverterTest.cs index 4e02dc73c57..8e0a87dac86 100644 --- a/test/UnitTest/Converters/JsonDescriptionEnumConverterTest.cs +++ b/test/UnitTest/Converters/JsonDescriptionEnumConverterTest.cs @@ -52,20 +52,6 @@ public void JsonEnumConverter_Ok() Assert.Equal("\"\"", json); } - [Fact] - public void ColumnVisibleItemConverter_Ok() - { - var item = new ColumnVisibleItem("name", true) { DisplayName = "display" }; - var json = JsonSerializer.Serialize(item); - - Assert.Equal("{\"name\":\"name\",\"visible\":true}", json); - - var item1 = JsonSerializer.Deserialize(json); - Assert.NotNull(item1); - Assert.Equal("name", item1.Name); - Assert.True(item1.Visible); - } - [JsonConverter(typeof(JsonDescriptionEnumConverter))] public enum TestEnum { From 3611b79b9cb2442daedf0c37b5b3d00182c08973 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 6 May 2026 17:27:57 +0800 Subject: [PATCH 03/26] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Samples/Table/TablesColumnResizing.razor | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor index 0ff4b8f4a83..ae7cab79d34 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor @@ -1,4 +1,4 @@ -@page "/table/column/resizing" +@page "/table/column/resizing" @inject IStringLocalizer NavMenuLocalizer @inject IStringLocalizer Localizer @inject IStringLocalizer FooLocalizer @@ -9,7 +9,7 @@

@Localizer["TablesColumnDescription"]

-
@@ -49,7 +49,7 @@ - + *@ @@ -58,12 +58,13 @@ IsPagination="true" PageItemsSource="@PageItemsSource" AllowResizing="true" ClientTableName="table-test" IsStriped="true" IsBordered="true" RenderMode="TableRenderMode.Table" - ShowToolbar="false" IsMultipleSelect="true" + ShowToolbar="true" IsMultipleSelect="true" ShowColumnList="true" + AllowDragColumn="true" OnQueryAsync="@OnQueryAsync"> - + From e54025aec854babc0dfb674c51f3fbdac315b677 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 6 May 2026 18:38:39 +0800 Subject: [PATCH 04/26] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E4=B8=8D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84=E6=89=A9=E5=B1=95=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/JsonElementExtesions.cs | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 src/BootstrapBlazor/Extensions/JsonElementExtesions.cs diff --git a/src/BootstrapBlazor/Extensions/JsonElementExtesions.cs b/src/BootstrapBlazor/Extensions/JsonElementExtesions.cs deleted file mode 100644 index 72d65971c0c..00000000000 --- a/src/BootstrapBlazor/Extensions/JsonElementExtesions.cs +++ /dev/null @@ -1,27 +0,0 @@ -// 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 System.Text.Json; - -namespace BootstrapBlazor.Components; - -static class JsonElementExtensions -{ - extension(JsonElement element) - { - public T? Parse(JsonSerializerDefaults options = JsonSerializerDefaults.Web) - { - try - { - return element.Deserialize(new JsonSerializerOptions(options)); - } - catch - { - return default; - } - } - } -} - From 72d1a2b9fe79a562001a54aa36ddff6218f4d8ec Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 7 May 2026 12:35:36 +0800 Subject: [PATCH 05/26] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E6=8C=81=E4=B9=85=E5=8C=96=E9=94=AE=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Table/Table.razor.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.js b/src/BootstrapBlazor/Components/Table/Table.razor.js index afde784cf1d..5e293f901f9 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.js +++ b/src/BootstrapBlazor/Components/Table/Table.razor.js @@ -859,11 +859,12 @@ export function getColumnStates(tableName) { const columnWidthState = getColumnWidthState(tableName); if (columnWidthState) { + removeColumnVisibleState(tableName); + const columnVisibleStates = getColumnVisibleState(tableName); if (columnVisibleStates) { - //removeColumnVisibleState(tableName); + removeColumnWidthState(tableName); - //removeColumnWidthState(tableName); for (const item of columnWidthState.cols) { const { name } = item; const column = columnVisibleStates.find(i => i.name === name); @@ -881,7 +882,7 @@ export function getColumnStates(tableName) { } const getColumnStateFromLocalstorage = tableName => { - const columnStateKey = `bb-table-column-${tableName}`; + const columnStateKey = `bb-table-${tableName}`; return getLocalStorageValue(columnStateKey); } @@ -921,7 +922,7 @@ const getLocalStorageValue = key => { const saveColumnStateToLocalstorage = (table, state) => { const { options: { tableName } } = table; if (tableName) { - const columnStateKey = `bb-table-column-${tableName}`; + const columnStateKey = `bb-table-${tableName}`; const columnState = state ?? getColumnStateObject(table); localStorage.setItem(columnStateKey, JSON.stringify(columnState)); } From d781325d3e0406523ac9419820613d8b256fb1bd Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 7 May 2026 12:36:04 +0800 Subject: [PATCH 06/26] =?UTF-8?q?refactor:=20=E8=B0=83=E6=95=B4=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Table/Table.razor | 32 +-- .../Components/Table/Table.razor.cs | 192 +++++++++++------- 2 files changed, 139 insertions(+), 85 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor b/src/BootstrapBlazor/Components/Table/Table.razor index 76000118321..57f90ec29a1 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor +++ b/src/BootstrapBlazor/Components/Table/Table.razor @@ -20,24 +20,26 @@ } else { - BuildTableColumns(); - RebuildVisibleColumnsCache(); - if (ShowSearch && SearchMode == SearchMode.Top) - { - @RenderSearch - } + + @{ + RebuildTableColumns(); + } - if (ShowToolbar) - { - @RenderToolbar - } + @if (ShowSearch && SearchMode == SearchMode.Top) + { + @RenderSearch + } - if (ShowTopPagination && IsPagination) - { - @RenderPagination - } + @if (ShowToolbar) + { + @RenderToolbar + } + + @if (ShowTopPagination && IsPagination) + { + @RenderPagination + } -
@if (ActiveRenderMode == TableRenderMode.Table) { diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.cs b/src/BootstrapBlazor/Components/Table/Table.razor.cs index 9fc84c54890..9022608d80e 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.cs @@ -1121,18 +1121,8 @@ protected override async Task OnAfterRenderAsync(bool firstRender) // 读取浏览器持久化列状态配置 await ReloadColumnStatesFromBrowserAsync(); - // 列排序回调方法 - if (ColumnOrderCallback != null) - { - Columns.Clear(); - Columns.AddRange(ColumnOrderCallback(Columns)); - } - - // 调用列创建回调方法 - if (OnColumnCreating != null) - { - await OnColumnCreating(Columns); - } + // 构建列信息 + await BuildTableColumns(); // 调用查询方法渲染 UI await QueryAsync(true, 1, false, true, IsAutoQueryFirstRender); @@ -1284,33 +1274,7 @@ private void OnParameterCheckChanged() } } - private async Task ReloadColumnStatesFromBrowserAsync() - { - // 未开启客户端持久化功能直接返回 - if (string.IsNullOrEmpty(ClientTableName)) - { - return; - } - - var state = await InvokeAsync("getColumnStates", ClientTableName); - if (state == null) - { - state = new(); - - // 开启客户端持久化后未设置列状态的列默认使用组件参数值 - state.Columns.AddRange(Columns.Where(i => !i.GetIgnore()).Select(i => new TableColumnState() - { - Name = i.GetFieldName(), - Visible = i.GetVisible(), - DisplayName = i.GetDisplayName(), - Width = i.Width - })); - } - - _tableColumnStateCache = state; - } - - private void BuildTableColumns() + private List GetTableColumns() { // 动态列模式 var cols = new List(); @@ -1327,27 +1291,96 @@ private void BuildTableColumns() cols.AddRange(Columns); } + if (ColumnOrderCallback != null) + { + cols = [.. ColumnOrderCallback(cols)]; + } + + return cols; + } + + private async Task TriggerColumnCreating(List cols) + { + if (OnColumnCreating != null) + { + await OnColumnCreating(cols); + } + } + + private async Task BuildTableColumns() + { + // 构建列信息 + var cols = GetTableColumns(); + + // 触发列创建事件 + await TriggerColumnCreating(cols); + + Columns.Clear(); + Columns.AddRange(cols.OrderFunc()); + + // set default sortName + var col = Columns.Find(i => i is { Sortable: true, DefaultSort: true }); + if (col != null) + { + SortName = col.GetFieldName(); + SortOrder = col.DefaultSortOrder; + } + // 加载客户端持久化列状态 - RebuildTableColumnFromCache(cols); + RebuildTableColumnFromCache(); + } + + private void RebuildTableColumns() + { + // 构建列信息 + var cols = GetTableColumns(); Columns.Clear(); Columns.AddRange(cols.OrderFunc()); // set default sortName - var column = Columns.Find(i => i is { Sortable: true, DefaultSort: true }); - if (column != null) + var col = Columns.Find(i => i is { Sortable: true, DefaultSort: true }); + if (col != null) { - SortName = column.GetFieldName(); - SortOrder = column.DefaultSortOrder; + SortName = col.GetFieldName(); + SortOrder = col.DefaultSortOrder; } + + // 加载客户端持久化列状态 + RebuildTableColumnFromCache(); } - private void RebuildTableColumnFromCache(List cols) + private async Task ReloadColumnStatesFromBrowserAsync() { - if (!string.IsNullOrEmpty(ClientTableName)) + // 未开启客户端持久化功能直接返回 + if (string.IsNullOrEmpty(ClientTableName)) + { + return; + } + + var state = await InvokeAsync("getColumnStates", ClientTableName); + if (state == null) { - // 提高性能避免循环 - foreach (var col in cols) + state = new(); + + // 开启客户端持久化后未设置列状态的列默认使用组件参数值 + state.Columns.AddRange(Columns.Where(i => !i.GetIgnore()).Select(i => new TableColumnState() + { + Name = i.GetFieldName(), + Visible = i.GetVisible(), + DisplayName = i.GetDisplayName(), + Width = i.Width + })); + } + + _tableColumnStateCache = state; + } + + private void RebuildTableColumnFromCache() + { + if (_tableColumnStateCache.Columns.Count != 0) + { + foreach (var col in Columns) { var fieldName = col.GetFieldName(); @@ -1357,7 +1390,6 @@ private void RebuildTableColumnFromCache(List cols) { col.Width = column.Width; col.Visible = column.Visible; - column.DisplayName = col.GetDisplayName(); } } @@ -1365,7 +1397,9 @@ private void RebuildTableColumnFromCache(List cols) // 设置可见列顺序 _columnVisibleItems.Clear(); - _columnVisibleItems.AddRange(GetColumnVisibleItems(cols)); + _columnVisibleItems.AddRange(GetColumnVisibleItems(Columns)); + + RebuildVisibleColumnsCache(); } private List GetColumnVisibleItems(List cols) @@ -1373,7 +1407,7 @@ private List GetColumnVisibleItems(List cols) // 开启客户端持久化后未设置列状态的列默认使用组件参数值 return _tableColumnStateCache.Columns.Count != 0 ? _tableColumnStateCache.Columns - : [.. cols.Where(i => !i.GetIgnore()).Select(i => new TableColumnState() + : [.. cols.Where(i => !i.GetIgnore() && i.ShownWithBreakPoint <= _screenSize).Select(i => new TableColumnState() { Name = i.GetFieldName(), Visible = i.GetVisible(), @@ -1452,21 +1486,19 @@ private async Task OnTableRenderAsync(bool firstRender) /// public void ResetVisibleColumns(IEnumerable columns) { - // https://github.com/dotnetcore/BootstrapBlazor/issues/6823 foreach (var col in columns) { - // 使用 for + break 性能更好 - for (var index = 0; index < _columnVisibleItems.Count; index++) + var column = Columns.Find(i => i.GetFieldName() == col.Name); + if (column != null) { - var item = _columnVisibleItems[index]; - if (item.Name == col.Name) - { - item.Visible = col.Visible; - break; - } + column.Visible = col.Visible; + column.Width = col.Width; } } + // 重置可见缓存 + RebuildTableColumnFromCache(); + _resetColumns = true; _invoke = true; StateHasChanged(); @@ -1862,7 +1894,7 @@ public async Task DragColumnCallback(int originIndex, int currentIndex) if (_columnVisibleItems.Count > originIndex) { var firstColumn = _columnVisibleItems[originIndex]; - if (firstColumn != null && _columnVisibleItems.Count > currentIndex) + if (_columnVisibleItems.Count > currentIndex) { var targetColumn = _columnVisibleItems[currentIndex]; _columnVisibleItems.Remove(firstColumn); @@ -1921,18 +1953,38 @@ public async Task AutoFitColumnWidthCallback(string fieldName, TableColumnClient private void UpdateTableColumnState(TableColumnClientStatus columnState) { - if (!string.IsNullOrEmpty(ClientTableName)) + // 更新缓存数据中列宽度 + foreach (var item in _tableColumnStateCache.Columns) { - // 更新缓存数据中列宽度 - foreach (var item in _tableColumnStateCache.Columns) + var colState = columnState.Columns.Find(i => i.Name == item.Name); + if (colState != null) { - var colState = columnState.Columns.Find(i => i.Name == item.Name); - if (colState != null) - { - item.Width = colState.Width; - } + item.Width = colState.Width; + } + } + _tableColumnStateCache.TableWidth = columnState.TableWidth; + + UpdateTableWidth(); + } + + private void UpdateTableWidth() + { + if (_tableColumnStateCache.TableWidth > 0) + { + if (IsMultipleSelect) + { + _tableColumnStateCache.TableWidth += MultiColumnWidth; + } + + if (ShowLineNo) + { + _tableColumnStateCache.TableWidth += LineNoColumnWidth; + } + + if (ShowDetails()) + { + _tableColumnStateCache.TableWidth += DetailColumnWidth; } - _tableColumnStateCache.TableWidth = columnState.TableWidth; } } From 6b3083aa6ed66790f68d88bea00c978f2a8a89a8 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 7 May 2026 12:36:20 +0800 Subject: [PATCH 07/26] =?UTF-8?q?refactor:=20=E5=AE=8C=E5=96=84=E8=AE=A1?= =?UTF-8?q?=E7=AE=97=E8=A1=A8=E6=A0=BC=E5=AE=BD=E5=BA=A6=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Table/Table.razor.Checkbox.cs | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.Checkbox.cs b/src/BootstrapBlazor/Components/Table/Table.razor.Checkbox.cs index ebaab1c52a0..8c9774f0a1d 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.Checkbox.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.Checkbox.cs @@ -168,21 +168,26 @@ private async Task OnToggleColumnVisible(TableColumnState item, bool visible) column.Visible = visible; } - if (column.Visible) + if (!column.Visible) { - // 重新计算表格宽度 - if (column.Width.HasValue) - { - tableWidth += column.Visible ? column.Width.Value : 0; - } - else - { - useTableWidth = false; - } + continue; + } + + // 重新计算表格宽度 + if (column.Width.HasValue) + { + tableWidth += column.Width.Value; + } + else + { + // 未设置列宽表格自适应 + useTableWidth = false; } } _tableColumnStateCache.TableWidth = useTableWidth ? tableWidth : 0; + + UpdateTableWidth(); } // 触发 OnColumnVisibleChanged 回调 From 691aafa86bd1010eadcb1d9351b7ef790eedd200 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 7 May 2026 12:36:28 +0800 Subject: [PATCH 08/26] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/TableTest.cs | 127 +++++++++++++++++++------- 1 file changed, 96 insertions(+), 31 deletions(-) diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index 4a1ceef4518..9853a2aa53c 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -272,8 +272,8 @@ public void ResetVisibleColumns_Ok() { table.ResetVisibleColumns( [ - new(nameof(Foo.Name), true) { DisplayName = "Name-Display" }, - new(nameof(Foo.Address), false), + new TableColumnState() { Name = nameof(Foo.Name), Visible = true, DisplayName = "Name-Display" }, + new TableColumnState() { Name = nameof(Foo.Address), Visible = false } ]); } return Task.CompletedTask; @@ -897,12 +897,12 @@ public void ResetFilter_Null() public async Task ShowColumnList_Ok() { // 设置客户端存储 - Context.JSInterop.Setup>("reloadColumnList", "test").SetResult( - [ - null, - new("Name", false), - new("Address", true) - ]); + var state = new TableColumnClientStatus(); + state.TableWidth = 500; + state.Columns.Add(new TableColumnState() { Name = nameof(Foo.Name), Visible = false, DisplayName = "Name-Display" }); + state.Columns.Add(new TableColumnState() { Name = nameof(Foo.Address), Visible = true, Width = 120, DisplayName = "Address-Display" }); + + Context.JSInterop.Setup("getColumnStates", "test").SetResult(state); var show = false; var localizer = Context.Services.GetRequiredService>(); var cut = Context.Render(pb => @@ -945,6 +945,7 @@ public async Task ShowColumnList_Ok() var item = cut.FindComponents>()[0]; await cut.InvokeAsync(item.Instance.OnToggleClick); Assert.True(show); + cut.Contains(""); await cut.InvokeAsync(item.Instance.OnToggleClick); Assert.False(show); @@ -955,6 +956,27 @@ public async Task ShowColumnList_Ok() pb.Add(a => a.IsPopoverToolbarDropdownButton, true); }); table.Contains("dropdown-menu-popover"); + + state.Columns[0].Width = 100; + await cut.InvokeAsync(item.Instance.OnToggleClick); + Assert.True(show); + + cut.Contains("style=\"width: 220px;\""); + + table.Render(pb => + { + pb.Add(a => a.IsMultipleSelect, true); + pb.Add(a => a.ShowLineNo, true); + pb.Add(a => a.IsDetails, true); + pb.Add(a => a.DetailRowTemplate, foo => builder => builder.AddContent(0, "test_DetailRowTemplate")); + }); + await cut.InvokeAsync(item.Instance.OnToggleClick); + Assert.False(show); + cut.Contains("style=\"width: 240px;\""); + + await cut.InvokeAsync(item.Instance.OnToggleClick); + Assert.True(show); + cut.Contains("style=\"width: 340px;\""); } [Fact] @@ -6372,7 +6394,7 @@ public async Task OnBreakPointChanged_Ok() Assert.Equal(2, cols.Count); var resp = cut.FindComponent().Instance; - await resp.OnResize(BreakPoint.Small); + await cut.InvokeAsync(() => resp.OnResize(BreakPoint.Small)); var row = table.Find("tbody > tr"); Assert.Equal(1, row.ChildElementCount); @@ -8739,6 +8761,7 @@ public void ShowRowCheckboxCallback_Ok() public async Task OnAutoFitColumnWidthCallback_Ok() { var name = ""; + TableColumnClientStatus? clientState = null; var localizer = Context.Services.GetRequiredService>(); var cut = Context.Render(pb => { @@ -8749,11 +8772,11 @@ public async Task OnAutoFitColumnWidthCallback_Ok() pb.Add(a => a.ClientTableName, "table-unit-test"); pb.Add(a => a.OnQueryAsync, OnQueryAsync(localizer)); pb.Add(a => a.FitColumnWidthIncludeHeader, true); - pb.Add(a => a.OnAutoFitColumnWidthCallback, (fieldName, calcWidth) => + pb.Add(a => a.OnAutoFitColumnWidthCallback, (fieldName, state) => { name = fieldName; - var resWidth = Math.Max(100.65f, calcWidth); - return Task.FromResult(resWidth); + clientState = state; + return Task.CompletedTask; }); pb.Add(a => a.TableColumns, foo => builder => { @@ -8771,17 +8794,14 @@ public async Task OnAutoFitColumnWidthCallback_Ok() }); var table = cut.FindComponent>(); - int? v = 0f; - await cut.InvokeAsync(async () => v = await table.Instance.AutoFitColumnWidthCallback("DateTime", 90)); - Assert.Equal(100.65f, v); + var state = new TableColumnClientStatus() { Columns = [] }; + await cut.InvokeAsync(() => table.Instance.AutoFitColumnWidthCallback(nameof(Foo.DateTime), state)); + Assert.NotNull(clientState); } [Fact] public async Task AllowDragColumn_Ok() { - Context.JSInterop.Setup>("reloadColumnOrder", "table-unit-test").SetResult(["Name", "Address"]); - Context.JSInterop.SetupVoid("saveColumnOrder").SetVoidResult(); - var name = ""; var localizer = Context.Services.GetRequiredService>(); var cut = Context.Render(pb => @@ -8820,10 +8840,7 @@ public async Task AllowDragColumn_Ok() }); var table = cut.FindComponent>(); - await cut.InvokeAsync(async () => - { - await table.Instance.DragColumnCallback(1, 0); - }); + await cut.InvokeAsync(() => table.Instance.DragColumnCallback(1, 0)); Assert.Equal("Address", name); var columns = cut.FindAll("th"); @@ -8851,7 +8868,7 @@ await cut.InvokeAsync(async () => public async Task OnResizeColumnCallback_Ok() { var name = ""; - int? width = null; + TableColumnClientStatus? clientState = null; var localizer = Context.Services.GetRequiredService>(); var cut = Context.Render(pb => { @@ -8859,10 +8876,10 @@ public async Task OnResizeColumnCallback_Ok() { pb.Add(a => a.RenderMode, TableRenderMode.Table); pb.Add(a => a.AllowResizing, true); - pb.Add(a => a.OnResizeColumnAsync, (field, colWidth) => + pb.Add(a => a.OnResizeColumnAsync, (field, state) => { name = field; - width = colWidth; + clientState = state; return Task.CompletedTask; }); pb.Add(a => a.OnQueryAsync, OnQueryAsync(localizer)); @@ -8882,11 +8899,10 @@ public async Task OnResizeColumnCallback_Ok() }); var table = cut.FindComponent>(); - await cut.InvokeAsync(() => table.Instance.ResizeColumnCallback(1, 100)); + var state = new TableColumnClientStatus(); + await cut.InvokeAsync(() => table.Instance.ResizeColumnCallback(nameof(Foo.Address), state)); Assert.Equal("Address", name); - Assert.Equal(100, width); - - await cut.InvokeAsync(() => table.Instance.ResizeColumnCallback(20, 100)); + Assert.NotNull(clientState); } [Theory] @@ -9230,6 +9246,47 @@ public async Task ShowColumnListControls_Ok() await cut.InvokeAsync(() => buttons[0].Click()); } + [Fact] + private async Task UpdateTableState_Ok() + { + var argumentsCount = 0; + object? obj = null; + Context.JSInterop.SetupVoid("updateTableState", invocationMatcher => + { + argumentsCount = invocationMatcher.Arguments.Count; + obj = invocationMatcher.Arguments[1]; + return true; + }); + + var localizer = Context.Services.GetRequiredService>(); + var cut = Context.Render>(pb => + { + pb.Add(a => a.ScrollIntoViewBehavior, ScrollIntoViewBehavior.Auto); + pb.Add(a => a.TableColumns, foo => builder => + { + builder.OpenComponent>(0); + builder.AddAttribute(1, "Field", "Name"); + builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Name", typeof(string))); + builder.CloseComponent(); + + builder.OpenComponent>(0); + builder.AddAttribute(1, "Field", "Address"); + builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string))); + builder.CloseComponent(); + }); + pb.Add(a => a.RenderMode, TableRenderMode.Table); + pb.Add(a => a.Items, Foo.GenerateFoo(localizer)); + }); + + // 更新客户端持久化键值 + cut.Render(pb => + { + pb.Add(a => a.ClientTableName, "table-unit-test"); + }); + + Assert.Equal(2, argumentsCount); + Assert.NotNull(obj); + } class SortableList : ISortableList { } static bool ProhibitEdit(Table @this) @@ -9527,7 +9584,7 @@ private class MockTable : Table { public TableRenderMode ShouldBeTable() { - ScreenSize = BreakPoint.Large; + InvokeScreen(BreakPoint.Large); RenderModeResponsiveWidth = BreakPoint.Medium; RenderMode = TableRenderMode.Auto; return base.ActiveRenderMode; @@ -9535,7 +9592,7 @@ public TableRenderMode ShouldBeTable() public TableRenderMode ShouldBeCardView() { - ScreenSize = BreakPoint.ExtraSmall; + InvokeScreen(BreakPoint.ExtraSmall); RenderModeResponsiveWidth = BreakPoint.Medium; RenderMode = TableRenderMode.Auto; return base.ActiveRenderMode; @@ -9575,6 +9632,14 @@ public async Task TestDeleteAsync() public string? TestGetCellClassString(ITableColumn col) => base.GetCellClassString(col, false, false); public string? TestGetHeaderWrapperClassString(ITableColumn col) => base.GetHeaderWrapperClassString(col); + + private void InvokeScreen(object val) + { + var fieldInfo = GetType().BaseType!.GetField("_screenSize", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(fieldInfo); + + fieldInfo.SetValue(this, val); + } } private class MockRenderCellTable : Table From 7c8eaf85885d533e6e3d45ef26bdea87817081fd Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 7 May 2026 12:56:43 +0800 Subject: [PATCH 09/26] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E4=B8=8D?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/TableTest.cs | 161 -------------------------- 1 file changed, 161 deletions(-) diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index 9853a2aa53c..54c11160392 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -6129,167 +6129,6 @@ public async Task ToggleLoading_Ok() await cut.InvokeAsync(() => table.Instance.QueryAsync()); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void ReloadColumnWidth_Ok(bool fixedHeader) - { - Context.JSInterop.Setup("reloadColumnWidth", "test_client_name").SetResult(""" - { - "cols": [ - { "name": "Name", "width": 20 }, - { "name": "Address", "width": 80 } - ], - "table": 100 - } - """); - var localizer = Context.Services.GetRequiredService>(); - var items = Foo.GenerateFoo(localizer, 2); - var cut = Context.Render(pb => - { - pb.AddChildContent>(pb => - { - pb.Add(a => a.IsFixedHeader, fixedHeader); - pb.Add(a => a.RenderMode, TableRenderMode.Table); - pb.Add(a => a.ClientTableName, "test_client_name"); - pb.Add(a => a.AllowResizing, true); - pb.Add(a => a.Items, items); - pb.Add(a => a.TableColumns, foo => builder => - { - builder.OpenComponent>(0); - builder.AddAttribute(1, "Field", "Name"); - builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Name", typeof(string))); - builder.CloseComponent(); - - builder.OpenComponent>(0); - builder.AddAttribute(1, "Field", "Address"); - builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string))); - builder.CloseComponent(); - }); - }); - }); - var table = cut.FindComponent>(); - Assert.Contains("style=\"width: 100px;\"", table.Markup); - } - - [Fact] - public void ReloadColumnWidth_TableWidth_Invalid() - { - Context.JSInterop.Setup("reloadColumnWidth", "test_client_name").SetResult(""" - { - "cols": [ - { "name": "Name", "width": 20 }, - { "name": "Address", "width": 80 } - ], - "table": 123.12 - } - """); - var localizer = Context.Services.GetRequiredService>(); - var items = Foo.GenerateFoo(localizer, 2); - var cut = Context.Render(pb => - { - pb.AddChildContent>(pb => - { - pb.Add(a => a.RenderMode, TableRenderMode.Table); - pb.Add(a => a.ClientTableName, "test_client_name"); - pb.Add(a => a.AllowResizing, true); - pb.Add(a => a.Items, items); - pb.Add(a => a.TableColumns, foo => builder => - { - builder.OpenComponent>(0); - builder.AddAttribute(1, "Field", "Name"); - builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Name", typeof(string))); - builder.CloseComponent(); - - builder.OpenComponent>(0); - builder.AddAttribute(1, "Field", "Address"); - builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string))); - builder.CloseComponent(); - }); - }); - }); - var table = cut.FindComponent>(); - Assert.Contains("", table.Markup); - } - - [Fact] - public void ReloadColumnWidth_NoTableElement() - { - Context.JSInterop.Setup("reloadColumnWidth", "test_client_name").SetResult(""" - { - "cols": [ - { "name": "Name", "width": 20 }, - { "name": "Address", "width": 80 } - ] - } - """); - var localizer = Context.Services.GetRequiredService>(); - var items = Foo.GenerateFoo(localizer, 2); - var cut = Context.Render(pb => - { - pb.AddChildContent>(pb => - { - pb.Add(a => a.RenderMode, TableRenderMode.Table); - pb.Add(a => a.ClientTableName, "test_client_name"); - pb.Add(a => a.AllowResizing, true); - pb.Add(a => a.Items, items); - pb.Add(a => a.TableColumns, foo => builder => - { - builder.OpenComponent>(0); - builder.AddAttribute(1, "Field", "Name"); - builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Name", typeof(string))); - builder.CloseComponent(); - - builder.OpenComponent>(0); - builder.AddAttribute(1, "Field", "Address"); - builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string))); - builder.CloseComponent(); - }); - }); - }); - var table = cut.FindComponent>(); - Assert.Contains("", table.Markup); - } - - [Fact] - public void ReloadColumnWidth_Columns_Invalid() - { - Context.JSInterop.Setup("reloadColumnWidth", "test_client_name").SetResult(""" - { - "cols": { - "name": "Name", - "name": "Address" - } - } - """); - var localizer = Context.Services.GetRequiredService>(); - var items = Foo.GenerateFoo(localizer, 2); - var cut = Context.Render(pb => - { - pb.AddChildContent>(pb => - { - pb.Add(a => a.RenderMode, TableRenderMode.Table); - pb.Add(a => a.ClientTableName, "test_client_name"); - pb.Add(a => a.AllowResizing, true); - pb.Add(a => a.Items, items); - pb.Add(a => a.TableColumns, foo => builder => - { - builder.OpenComponent>(0); - builder.AddAttribute(1, "Field", "Name"); - builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Name", typeof(string))); - builder.CloseComponent(); - - builder.OpenComponent>(0); - builder.AddAttribute(1, "Field", "Address"); - builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string))); - builder.CloseComponent(); - }); - }); - }); - var table = cut.FindComponent>(); - Assert.DoesNotContain("", table.Markup); - } - [Fact] public async Task FitAllColumnWidth_Ok() { From 5619ac5441d45062f0e1346132ed92ee1343c967 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 7 May 2026 12:57:03 +0800 Subject: [PATCH 10/26] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=20OnResize?= =?UTF-8?q?ColumnCallback=5FOk=20=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/TableTest.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index 54c11160392..41fe8da90cd 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -8706,6 +8706,13 @@ await cut.InvokeAsync(async () => [Fact] public async Task OnResizeColumnCallback_Ok() { + var state = new TableColumnClientStatus(); + state.TableWidth = 500; + state.Columns.Add(new TableColumnState() { Name = nameof(Foo.Name), Visible = false, DisplayName = "Name-Display" }); + state.Columns.Add(new TableColumnState() { Name = nameof(Foo.Address), Visible = true, Width = 120, DisplayName = "Address-Display" }); + + Context.JSInterop.Setup("getColumnStates", "test").SetResult(state); + var name = ""; TableColumnClientStatus? clientState = null; var localizer = Context.Services.GetRequiredService>(); @@ -8713,6 +8720,7 @@ public async Task OnResizeColumnCallback_Ok() { pb.AddChildContent>(pb => { + pb.Add(a => a.ClientTableName, "test"); pb.Add(a => a.RenderMode, TableRenderMode.Table); pb.Add(a => a.AllowResizing, true); pb.Add(a => a.OnResizeColumnAsync, (field, state) => @@ -8738,8 +8746,11 @@ public async Task OnResizeColumnCallback_Ok() }); var table = cut.FindComponent>(); - var state = new TableColumnClientStatus(); - await cut.InvokeAsync(() => table.Instance.ResizeColumnCallback(nameof(Foo.Address), state)); + var newState = new TableColumnClientStatus(); + newState.TableWidth = 100; + newState.Columns.Add(new TableColumnState() { Name = "Name", Width = 50 }); + newState.Columns.Add(new TableColumnState() { Name = "Address", Width = 50 }); + await cut.InvokeAsync(() => table.Instance.ResizeColumnCallback(nameof(Foo.Address), newState)); Assert.Equal("Address", name); Assert.NotNull(clientState); } From b71c7735e088f89047311683023b2d173c2988fa Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 7 May 2026 15:42:13 +0800 Subject: [PATCH 11/26] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/TableTest.cs | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index 41fe8da90cd..b80d3f0be55 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -8755,6 +8755,52 @@ public async Task OnResizeColumnCallback_Ok() Assert.NotNull(clientState); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ReloadColumnWidth_Ok(bool fixedHeader) + { + var state = new TableColumnClientStatus(); + state.TableWidth = 220; + state.Columns.Add(new TableColumnState() { Name = nameof(Foo.Name), Visible = true, Width = 100, DisplayName = "Name-Display" }); + state.Columns.Add(new TableColumnState() { Name = nameof(Foo.Address), Visible = true, Width = 120, DisplayName = "Address-Display" }); + + Context.JSInterop.Setup("getColumnStates", "test_client_name").SetResult(state); + + var localizer = Context.Services.GetRequiredService>(); + var items = Foo.GenerateFoo(localizer, 2); + var cut = Context.Render(pb => + { + pb.AddChildContent>(pb => + { + pb.Add(a => a.IsFixedHeader, fixedHeader); + pb.Add(a => a.RenderMode, TableRenderMode.Table); + pb.Add(a => a.ClientTableName, "test_client_name"); + pb.Add(a => a.AllowResizing, true); + pb.Add(a => a.Items, items); + pb.Add(a => a.TableColumns, foo => builder => + { + builder.OpenComponent>(0); + builder.AddAttribute(1, "Field", "Name"); + builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Name", typeof(string))); + builder.CloseComponent(); + + builder.OpenComponent>(0); + builder.AddAttribute(1, "Field", "Address"); + builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string))); + builder.CloseComponent(); + }); + }); + }); + var table = cut.FindComponent>(); + + Assert.Contains("style=\"width: 220px;\"", table.Markup); + if (fixedHeader) + { + Assert.Contains("style=\"width: 215px;\"", table.Markup); + } + } + [Theory] [InlineData(null, true)] [InlineData(180, true)] @@ -9137,6 +9183,7 @@ private async Task UpdateTableState_Ok() Assert.Equal(2, argumentsCount); Assert.NotNull(obj); } + class SortableList : ISortableList { } static bool ProhibitEdit(Table @this) From a17c7db8a7e81111b511606ccdc359cc323333c6 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 8 May 2026 12:11:41 +0800 Subject: [PATCH 12/26] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20key=20?= =?UTF-8?q?=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Table/Table.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor b/src/BootstrapBlazor/Components/Table/Table.razor index 57f90ec29a1..e761d69a310 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor +++ b/src/BootstrapBlazor/Components/Table/Table.razor @@ -305,7 +305,7 @@ { var fieldName = col.GetFieldName(); var displayName = col.GetDisplayName(); - From 35809e380c637e233909acd2dd087255a3f2959e Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 8 May 2026 14:30:38 +0800 Subject: [PATCH 13/26] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=94=B9=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=B1=BB=E5=9E=8B=E4=B8=BA=20List?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Table/ITable.cs | 2 +- src/BootstrapBlazor/Components/Table/Table.razor.Sort.cs | 4 ++-- src/BootstrapBlazor/Components/Table/Table.razor.Toolbar.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/ITable.cs b/src/BootstrapBlazor/Components/Table/ITable.cs index f9288db74e5..a963524dcdf 100644 --- a/src/BootstrapBlazor/Components/Table/ITable.cs +++ b/src/BootstrapBlazor/Components/Table/ITable.cs @@ -15,7 +15,7 @@ public interface ITable : IColumnCollection /// 获得 ITable 实例配置的可见列集合 /// Gets visible columns collection configured in ITable instance /// - IEnumerable GetVisibleColumns(); + List GetVisibleColumns(); /// /// 获得 过滤条件集合 diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.Sort.cs b/src/BootstrapBlazor/Components/Table/Table.razor.Sort.cs index d45e5f28c86..4cf288eeee7 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.Sort.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.Sort.cs @@ -351,7 +351,7 @@ string GetFixedHeaderStyleString() => IsFixedHeader private string? GetLeftStyle(ITableColumn col) { - var columns = GetVisibleColumns().ToList(); + var columns = GetVisibleColumns(); var defaultWidth = 200; var width = 0; var start = 0; @@ -378,7 +378,7 @@ string GetFixedHeaderStyleString() => IsFixedHeader private string? GetRightStyle(ITableColumn col, int margin) { - var columns = GetVisibleColumns().ToList(); + var columns = GetVisibleColumns(); var defaultWidth = 200; var width = 0; var index = columns.IndexOf(col); diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.Toolbar.cs b/src/BootstrapBlazor/Components/Table/Table.razor.Toolbar.cs index b814a3dacf1..0e90f355d28 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.Toolbar.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.Toolbar.cs @@ -552,7 +552,7 @@ public Func? ShowDeleteButtonCallback /// 获得当前可见列集合 /// Get Visible Columns Collection /// - public IEnumerable GetVisibleColumns() => _visibleColumnsCache; + public List GetVisibleColumns() => _visibleColumnsCache; private void RebuildVisibleColumnsCache() { From 1b7abc4d0cb8e242fd841527411afbe4b4ac940a Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 09:34:22 +0800 Subject: [PATCH 14/26] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Table/Table.razor.Sort.cs | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.Sort.cs b/src/BootstrapBlazor/Components/Table/Table.razor.Sort.cs index 4cf288eeee7..ea6ca48e781 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.Sort.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.Sort.cs @@ -194,7 +194,7 @@ private int MultipleSelectColumnLeft() /// protected string? GetFixedCellClassString(ITableColumn col, string? cellClass = null) => CssBuilder.Default(cellClass) .AddClass("fixed", col.Fixed) - .AddClass("fixed-right", col.Fixed && IsTail(col)) + .AddClass("fixed-right", col.Fixed && IsFixRight(col)) .AddClass("fr", IsLastColumn(col)) .AddClass("fl", IsFirstColumn(col)) .Build(); @@ -243,10 +243,11 @@ private int MultipleSelectColumnLeft() private bool IsLastColumn(ITableColumn col) => LastFixedColumnCache.GetOrAdd(col, col => { var ret = false; - if (col.Fixed && !IsTail(col)) + if (col.Fixed && !IsFixRight(col)) { - var index = Columns.IndexOf(col) + 1; - ret = index < Columns.Count && Columns[index].Fixed == false; + var columns = GetVisibleColumns(); + var index = columns.IndexOf(col) + 1; + ret = index < columns.Count && columns[index].Fixed == false; } return ret; }); @@ -258,12 +259,13 @@ private bool IsLastColumn(ITableColumn col) => LastFixedColumnCache.GetOrAdd(col private bool IsFirstColumn(ITableColumn col) => FirstFixedColumnCache.GetOrAdd(col, col => { var ret = false; - if (col.Fixed && IsTail(col)) + if (col.Fixed && IsFixRight(col)) { - var index = Columns.IndexOf(col) - 1; - if (index > 0) + var columns = GetVisibleColumns(); + var index = columns.IndexOf(col) - 1; + if (index >= 0) { - ret = !Columns[index].Fixed; + ret = !columns[index].Fixed; } } return ret; @@ -310,11 +312,15 @@ private int CalcMargin() return margin; } - private bool IsTail(ITableColumn col) + private bool IsFixRight(ITableColumn col) { - var middle = Math.Floor(GetVisibleColumns().Count() * 1.0 / 2); - var index = Columns.IndexOf(col); - return middle < index; + // 获得所有可见列 + var columns = GetVisibleColumns(); + + // 获得当前列索引 + var index = columns.IndexOf(col); + + return !columns.Take(index).All(i => i.Fixed); } /// @@ -344,7 +350,7 @@ string GetFixedHeaderStyleString() => IsFixedHeader string? ret = null; if (col.Fixed) { - ret = IsTail(col) ? GetRightStyle(col, margin) : GetLeftStyle(col); + ret = IsFixRight(col) ? GetRightStyle(col, margin) : GetLeftStyle(col); } return ret; } @@ -379,24 +385,21 @@ string GetFixedHeaderStyleString() => IsFixedHeader private string? GetRightStyle(ITableColumn col, int margin) { var columns = GetVisibleColumns(); - var defaultWidth = 200; + var defaultWidth = DefaultFixedColumnWidth; var width = 0; var index = columns.IndexOf(col); // after - while (index + 1 < columns.Count) + for (var i = index + 1; i < columns.Count; i++) { - var column = columns[index++]; + var column = columns[i]; width += column.Width ?? defaultWidth; } if (ShowExtendButtons && FixedExtendButtonsColumn) { width += ExtendButtonColumnWidth; } - - // 如果是固定表头时增加滚动条位置 - // Add scroll bar position if it is fixed header - if (IsFixedHeader && (index + 1) == columns.Count) + if (IsFixedHeader) { width += margin; } From 6b604aac0ef36156f10948313d02681945a1c0ae Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 09:35:00 +0800 Subject: [PATCH 15/26] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/TableBoolFilterTest.cs | 2 +- test/UnitTest/Components/TableColumnFilterTest.cs | 2 +- test/UnitTest/Components/TableDateTimeFilterTest.cs | 2 +- test/UnitTest/Components/TableEnumFilterTest.cs | 2 +- test/UnitTest/Components/TableLookupFilterTest.cs | 2 +- test/UnitTest/Components/TableMultiFilterTest.cs | 2 +- test/UnitTest/Components/TableMultiSelectFilterTest.cs | 2 +- test/UnitTest/Components/TableNotSupportFilterTest.cs | 2 +- test/UnitTest/Components/TableNumberFilterTest.cs | 2 +- test/UnitTest/Dynamic/ChangeDetectionCleanTaskTest.cs | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/UnitTest/Components/TableBoolFilterTest.cs b/test/UnitTest/Components/TableBoolFilterTest.cs index 3c8f02a74ca..ceec65c16ae 100644 --- a/test/UnitTest/Components/TableBoolFilterTest.cs +++ b/test/UnitTest/Components/TableBoolFilterTest.cs @@ -77,7 +77,7 @@ class MockTable : ITable public List Columns => []; - public IEnumerable GetVisibleColumns() => Columns; + public List GetVisibleColumns() => Columns; } class MockColumn : TableColumn diff --git a/test/UnitTest/Components/TableColumnFilterTest.cs b/test/UnitTest/Components/TableColumnFilterTest.cs index ea384a513d7..7c872a2033c 100644 --- a/test/UnitTest/Components/TableColumnFilterTest.cs +++ b/test/UnitTest/Components/TableColumnFilterTest.cs @@ -232,7 +232,7 @@ private class MockTable : ITable public List Columns => []; - public IEnumerable GetVisibleColumns() => Columns; + public List GetVisibleColumns() => Columns; } private class MockColumn : TableColumn diff --git a/test/UnitTest/Components/TableDateTimeFilterTest.cs b/test/UnitTest/Components/TableDateTimeFilterTest.cs index 502c9dbe7aa..c4849ffe043 100644 --- a/test/UnitTest/Components/TableDateTimeFilterTest.cs +++ b/test/UnitTest/Components/TableDateTimeFilterTest.cs @@ -85,7 +85,7 @@ class MockTable : ITable public List Columns => []; - public IEnumerable GetVisibleColumns() => Columns; + public List GetVisibleColumns() => Columns; } class MockColumn : TableColumn diff --git a/test/UnitTest/Components/TableEnumFilterTest.cs b/test/UnitTest/Components/TableEnumFilterTest.cs index 548008829b4..eaa08967d34 100644 --- a/test/UnitTest/Components/TableEnumFilterTest.cs +++ b/test/UnitTest/Components/TableEnumFilterTest.cs @@ -98,7 +98,7 @@ class MockTable : ITable public List Columns => []; - public IEnumerable GetVisibleColumns() => Columns; + public List GetVisibleColumns() => Columns; } class MockColumn : TableColumn diff --git a/test/UnitTest/Components/TableLookupFilterTest.cs b/test/UnitTest/Components/TableLookupFilterTest.cs index b290edfe69b..2a1b2d864ea 100644 --- a/test/UnitTest/Components/TableLookupFilterTest.cs +++ b/test/UnitTest/Components/TableLookupFilterTest.cs @@ -118,7 +118,7 @@ class MockTable : ITable public List Columns => []; - public IEnumerable GetVisibleColumns() => Columns; + public List GetVisibleColumns() => Columns; } class MockColumn : TableColumn diff --git a/test/UnitTest/Components/TableMultiFilterTest.cs b/test/UnitTest/Components/TableMultiFilterTest.cs index a79b5069147..baa5cc781ba 100644 --- a/test/UnitTest/Components/TableMultiFilterTest.cs +++ b/test/UnitTest/Components/TableMultiFilterTest.cs @@ -198,6 +198,6 @@ class MockTable : ITable public List Columns => []; - public IEnumerable GetVisibleColumns() => Columns; + public List GetVisibleColumns() => Columns; } } diff --git a/test/UnitTest/Components/TableMultiSelectFilterTest.cs b/test/UnitTest/Components/TableMultiSelectFilterTest.cs index cbd9697a541..c1faf44e412 100644 --- a/test/UnitTest/Components/TableMultiSelectFilterTest.cs +++ b/test/UnitTest/Components/TableMultiSelectFilterTest.cs @@ -93,7 +93,7 @@ class MockTable : ITable public List Columns => []; - public IEnumerable GetVisibleColumns() => Columns; + public List GetVisibleColumns() => Columns; } class MockColumn : TableColumn diff --git a/test/UnitTest/Components/TableNotSupportFilterTest.cs b/test/UnitTest/Components/TableNotSupportFilterTest.cs index 7001e5676e2..d1127fdfed6 100644 --- a/test/UnitTest/Components/TableNotSupportFilterTest.cs +++ b/test/UnitTest/Components/TableNotSupportFilterTest.cs @@ -34,7 +34,7 @@ class MockTable : ITable public List Columns => []; - public IEnumerable GetVisibleColumns() => Columns; + public List GetVisibleColumns() => Columns; } class MockColumn : TableColumn> diff --git a/test/UnitTest/Components/TableNumberFilterTest.cs b/test/UnitTest/Components/TableNumberFilterTest.cs index 3e9301507a6..206bb4c5ae7 100644 --- a/test/UnitTest/Components/TableNumberFilterTest.cs +++ b/test/UnitTest/Components/TableNumberFilterTest.cs @@ -106,7 +106,7 @@ class MockTable : ITable public List Columns => []; - public IEnumerable GetVisibleColumns() => Columns; + public List GetVisibleColumns() => Columns; } class MockColumn : TableColumn diff --git a/test/UnitTest/Dynamic/ChangeDetectionCleanTaskTest.cs b/test/UnitTest/Dynamic/ChangeDetectionCleanTaskTest.cs index 41a8db9be5f..fce7fc4847a 100644 --- a/test/UnitTest/Dynamic/ChangeDetectionCleanTaskTest.cs +++ b/test/UnitTest/Dynamic/ChangeDetectionCleanTaskTest.cs @@ -92,6 +92,6 @@ class MockTable : ITable public List Columns { get; } = []; - public IEnumerable GetVisibleColumns() => []; + public List GetVisibleColumns() => []; } } From f94cd475c2fe73eed8442179d59d18b37061feee Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 09:35:19 +0800 Subject: [PATCH 16/26] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=85=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/TableTest.cs | 101 +++++++++++++++++++++----- 1 file changed, 84 insertions(+), 17 deletions(-) diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index b80d3f0be55..927f16f5b52 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -1797,27 +1797,27 @@ public void ColumnFixed_Ok(bool showExtendButton, bool isFixedHeader) builder.AddAttribute(3, "Width", 100); builder.CloseComponent(); - builder.OpenComponent>(10); - builder.AddAttribute(11, "Field", foo.Address); - builder.AddAttribute(12, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string))); + builder.OpenComponent>(10); + builder.AddAttribute(11, "Field", foo.Complete); + builder.AddAttribute(12, "FieldExpression", Utility.GenerateValueExpression(foo, nameof(Foo.Complete), typeof(bool))); builder.CloseComponent(); builder.OpenComponent>(10); builder.AddAttribute(11, "Field", foo.Address); - builder.AddAttribute(12, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string))); - builder.AddAttribute(13, nameof(TableColumn.Fixed), true); + builder.AddAttribute(12, "FieldExpression", Utility.GenerateValueExpression(foo, nameof(foo.Address), typeof(string))); + builder.AddAttribute(13, nameof(TableColumn<,>.Fixed), true); builder.CloseComponent(); - builder.OpenComponent>(10); - builder.AddAttribute(11, "Field", foo.Address); - builder.AddAttribute(12, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string))); - builder.AddAttribute(13, nameof(TableColumn.Fixed), true); + builder.OpenComponent>(10); + builder.AddAttribute(11, "Field", foo.Education); + builder.AddAttribute(12, "FieldExpression", Utility.GenerateValueExpression(foo, nameof(foo.Education), typeof(EnumEducation?))); + builder.AddAttribute(13, nameof(TableColumn<,>.Fixed), true); builder.AddAttribute(3, "Width", 100); builder.CloseComponent(); - builder.OpenComponent>(10); - builder.AddAttribute(11, "Field", foo.Address); - builder.AddAttribute(12, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string))); + builder.OpenComponent>>(10); + builder.AddAttribute(11, "Field", foo.Hobby); + builder.AddAttribute(12, "FieldExpression", Utility.GenerateValueExpression(foo, nameof(foo.Hobby), typeof(IEnumerable))); builder.AddAttribute(13, nameof(TableColumn.Fixed), true); builder.CloseComponent(); }); @@ -1826,35 +1826,102 @@ public void ColumnFixed_Ok(bool showExtendButton, bool isFixedHeader) cut.Contains("left: 0px;"); cut.Contains("left: 200px;"); - cut.Contains("left: 500px;"); if (showExtendButton) { if (isFixedHeader) { - cut.Contains("right: 238px;"); + cut.Contains("right: 438px;"); + cut.Contains("right: 338px;"); cut.Contains("right: 138px;"); cut.Contains("right: 8px;"); } else { - cut.Contains("right: 230px;"); + cut.Contains("right: 430px;"); + cut.Contains("right: 330px;"); cut.Contains("right: 130px;"); cut.Contains("right: 0px;"); } } if (!showExtendButton) { - cut.Contains("right: 100px;"); + cut.Contains("right: 300px;"); + cut.Contains("right: 200px;"); cut.Contains("right: 0px;"); if (isFixedHeader) { - cut.Contains("right: 108px;"); + cut.Contains("right: 308px;"); + cut.Contains("right: 208px;"); cut.Contains("right: 8px;"); } } } + [Fact] + public void ColumnFixed_TailColumn_Ok() + { + var localizer = Context.Services.GetRequiredService>(); + var cut = Context.Render(pb => + { + pb.AddChildContent>(pb => + { + pb.Add(a => a.RenderMode, TableRenderMode.Table); + pb.Add(a => a.Items, Foo.GenerateFoo(localizer, 2)); + pb.Add(a => a.TableColumns, foo => builder => + { + builder.OpenComponent>(0); + builder.AddAttribute(1, "Field", foo.Name); + builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, nameof(Foo.Name), typeof(string))); + builder.AddAttribute(3, nameof(TableColumn<,>.Fixed), true); + builder.AddAttribute(4, nameof(TableColumn<,>.Width), 100); + builder.CloseComponent(); + + builder.OpenComponent>(5); + builder.AddAttribute(6, "Field", foo.Count); + builder.AddAttribute(7, "FieldExpression", Utility.GenerateValueExpression(foo, nameof(Foo.Count), typeof(int))); + builder.AddAttribute(8, nameof(TableColumn<,>.Fixed), true); + builder.AddAttribute(9, nameof(TableColumn<,>.Width), 100); + builder.CloseComponent(); + + builder.OpenComponent>(10); + builder.AddAttribute(11, "Field", foo.Address); + builder.AddAttribute(12, "FieldExpression", Utility.GenerateValueExpression(foo, nameof(Foo.Address), typeof(string))); + builder.AddAttribute(13, nameof(TableColumn<,>.Width), 100); + builder.CloseComponent(); + + builder.OpenComponent>(14); + builder.AddAttribute(15, "Field", foo.DateTime); + builder.AddAttribute(16, "FieldExpression", Utility.GenerateValueExpression(foo, nameof(Foo.DateTime), typeof(DateTime?))); + builder.AddAttribute(17, nameof(TableColumn<,>.Fixed), true); + builder.AddAttribute(18, nameof(TableColumn<,>.Width), 100); + builder.CloseComponent(); + + builder.OpenComponent>(19); + builder.AddAttribute(20, "Field", foo.Education); + builder.AddAttribute(21, "FieldExpression", Utility.GenerateValueExpression(foo, nameof(Foo.Education), typeof(EnumEducation?))); + builder.AddAttribute(22, nameof(TableColumn<,>.Fixed), true); + builder.AddAttribute(23, nameof(TableColumn<,>.Width), 100); + builder.CloseComponent(); + + builder.OpenComponent>(24); + builder.AddAttribute(25, "Field", foo.Complete); + builder.AddAttribute(26, "FieldExpression", Utility.GenerateValueExpression(foo, nameof(Foo.Complete), typeof(bool))); + builder.AddAttribute(27, nameof(TableColumn<,>.Fixed), true); + builder.AddAttribute(28, nameof(TableColumn<,>.Width), 100); + builder.CloseComponent(); + }); + }); + }); + + cut.Contains("style=\"left: 0px;\""); + cut.Contains("style=\"left: 100px;\""); + cut.Contains("style=\"right: 200px;\""); + cut.Contains("style=\"right: 100px;\""); + cut.Contains("style=\"right: 0px;\""); + cut.DoesNotContain("style=\"left: 300px;\""); + } + [Fact] public void ScrollWidth_Ok() { From 2d68c6ee83aa33ddf25c7c11e07791601be0115c Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 13:36:28 +0800 Subject: [PATCH 17/26] =?UTF-8?q?refactor:=20=E5=A2=9E=E5=8A=A0=E5=8F=96?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Table/Table.razor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.js b/src/BootstrapBlazor/Components/Table/Table.razor.js index 5e293f901f9..7731c5966c2 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.js +++ b/src/BootstrapBlazor/Components/Table/Table.razor.js @@ -672,7 +672,7 @@ const calcCellWidth = cell => { document.body.appendChild(div); const cellStyle = getComputedStyle(cell); - return getWidth(div) + parseFloat(cellStyle.getPropertyValue('padding-left')) + parseFloat(cellStyle.getPropertyValue('padding-right')) + parseFloat(cellStyle.getPropertyValue('border-left-width')) + parseFloat(cellStyle.getPropertyValue('border-right-width')) + 1; + return getWidth(div) + parseFloat(cellStyle.getPropertyValue('padding-left')) + parseFloat(cellStyle.getPropertyValue('padding-right')) + parseFloat(cellStyle.getPropertyValue('border-left-width')) + parseFloat(cellStyle.getPropertyValue('border-right-width')) + 1 | 0; } const closeAllTips = (columns, self) => { From 649272cdb465a14074a95de01ae75c4190092b38 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 13:36:49 +0800 Subject: [PATCH 18/26] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E5=9B=9E=E8=B0=83=E5=AF=BC=E8=87=B4=E5=AE=BD?= =?UTF-8?q?=E5=BA=A6=E8=AE=A1=E7=AE=97=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Table/Table.razor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.cs b/src/BootstrapBlazor/Components/Table/Table.razor.cs index 9022608d80e..cd9898d3e8a 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.cs @@ -1963,8 +1963,6 @@ private void UpdateTableColumnState(TableColumnClientStatus columnState) } } _tableColumnStateCache.TableWidth = columnState.TableWidth; - - UpdateTableWidth(); } private void UpdateTableWidth() From 2638129639def78ec8cdf87809ff526fe064f662 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 13:37:11 +0800 Subject: [PATCH 19/26] =?UTF-8?q?refactor:=20=E8=B0=83=E6=95=B4=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=80=BB=E8=BE=91=E9=98=B2=E6=AD=A2=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E5=AE=BD=E5=BA=A6=E6=8A=96=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Table/Table.razor.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.js b/src/BootstrapBlazor/Components/Table/Table.razor.js index 7731c5966c2..8c856909513 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.js +++ b/src/BootstrapBlazor/Components/Table/Table.razor.js @@ -643,11 +643,13 @@ const autoFitColumnWidth = async (table, col) => { span.style.removeProperty('width'); } - table.style.removeProperty('width'); const tableWidth = getTableWidth(table); if (tableWidth) { table.style.setProperty('width', `${tableWidth}px`); } + else { + table.style.removeProperty('width'); + } }); resetColumnWidthTips(table, col); From 52bea3ac150686f8ef91a74965e11656b5ec1563 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 13:39:06 +0800 Subject: [PATCH 20/26] =?UTF-8?q?refactor:=20=E7=B2=BE=E7=AE=80=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E6=8F=90=E9=AB=98=E5=8F=AF=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Table/Table.razor.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.js b/src/BootstrapBlazor/Components/Table/Table.razor.js index 8c856909513..fc4e958bf87 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.js +++ b/src/BootstrapBlazor/Components/Table/Table.razor.js @@ -963,13 +963,13 @@ const getTableWidth = table => { const colgroup = [...table.children].find(i => i.nodeName === 'COLGROUP'); for (const col of colgroup.children) { const width = parseInt(col.style.width); - if (isNaN(width) === false) { - tableWidth += width; - } - else { + if (isNaN(width)) { tableWidth = null; break; } + else { + tableWidth += width; + } } return (tableWidth ?? getWidth(table)) | 0; } From 2abd9e5e996606964b4c9610233b6fd61b02e199 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 13:39:17 +0800 Subject: [PATCH 21/26] =?UTF-8?q?refactor:=20=E7=B2=BE=E7=AE=80=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Table/Table.razor.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.js b/src/BootstrapBlazor/Components/Table/Table.razor.js index fc4e958bf87..7d86c46490d 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.js +++ b/src/BootstrapBlazor/Components/Table/Table.razor.js @@ -644,12 +644,7 @@ const autoFitColumnWidth = async (table, col) => { } const tableWidth = getTableWidth(table); - if (tableWidth) { - table.style.setProperty('width', `${tableWidth}px`); - } - else { - table.style.removeProperty('width'); - } + table.style.setProperty('width', `${tableWidth}px`); }); resetColumnWidthTips(table, col); From c9b919b96779477f2f1644a2555ad83d19637636 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 13:41:03 +0800 Subject: [PATCH 22/26] =?UTF-8?q?refactor(Table):=20FitColumnWidthIncludeH?= =?UTF-8?q?eader=20=E5=8F=82=E6=95=B0=E9=BB=98=E8=AE=A4=E5=80=BC=E6=9B=B4?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=20true?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Table/Table.razor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.cs b/src/BootstrapBlazor/Components/Table/Table.razor.cs index cd9898d3e8a..385fa7a2f37 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.cs @@ -1866,11 +1866,11 @@ private async Task OnContextMenu(MouseEventArgs e, TItem item) public Func? OnAutoFitColumnWidthCallback { get; set; } /// - /// 获得/设置 列宽自适应时是否包含表头 默认 false - /// Gets or sets Whether to include header when auto fit column width. Default false + /// 获得/设置 列宽自适应时是否包含表头 默认 true + /// Gets or sets Whether to include header when auto fit column width. Default true /// [Parameter] - public bool FitColumnWidthIncludeHeader { get; set; } + public bool FitColumnWidthIncludeHeader { get; set; } = true; /// /// 列宽自适应方法 From 49253b403546fb680ec95040f36f62ecb37825e3 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 13:51:56 +0800 Subject: [PATCH 23/26] =?UTF-8?q?refactor:=20=E6=92=A4=E9=94=80=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/Table/TablesColumnResizing.razor | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor index ae7cab79d34..cce3cc25bf9 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor @@ -9,7 +9,7 @@

@Localizer["TablesColumnDescription"]

-@*
@@ -49,7 +49,7 @@
- *@ + @@ -58,8 +58,6 @@ IsPagination="true" PageItemsSource="@PageItemsSource" AllowResizing="true" ClientTableName="table-test" IsStriped="true" IsBordered="true" RenderMode="TableRenderMode.Table" - ShowToolbar="true" IsMultipleSelect="true" ShowColumnList="true" - AllowDragColumn="true" OnQueryAsync="@OnQueryAsync"> From dd531eb3cc0e29a9a7cb85fa66cea459c9a685f7 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 13:52:37 +0800 Subject: [PATCH 24/26] =?UTF-8?q?refactor:=20=E6=92=A4=E9=94=80=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/Table/TablesColumnResizing.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor index cce3cc25bf9..38467a4df15 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor @@ -62,7 +62,7 @@ - + From 7b9dcbc7e99f2fda02393f6af29c8dae277e99b0 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 9 May 2026 13:54:15 +0800 Subject: [PATCH 25/26] =?UTF-8?q?revert:=20=E6=92=A4=E9=94=80=E5=8F=98?= =?UTF-8?q?=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/Table/TablesColumnResizing.razor | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor index 38467a4df15..ba47a14c81e 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesColumnResizing.razor @@ -58,6 +58,7 @@ IsPagination="true" PageItemsSource="@PageItemsSource" AllowResizing="true" ClientTableName="table-test" IsStriped="true" IsBordered="true" RenderMode="TableRenderMode.Table" + ShowToolbar="false" IsMultipleSelect="true" OnQueryAsync="@OnQueryAsync"> From e434555e9b640488f664cc2c3b6ed206e43d425a Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 11 May 2026 13:04:33 +0800 Subject: [PATCH 26/26] =?UTF-8?q?refactor:=20=E7=B2=BE=E7=AE=80=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Table/Table.razor | 2 +- .../Components/Table/Table.razor.cs | 43 ++++++------------- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor b/src/BootstrapBlazor/Components/Table/Table.razor index e761d69a310..c1aa3a6a454 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor +++ b/src/BootstrapBlazor/Components/Table/Table.razor @@ -22,7 +22,7 @@ { @{ - RebuildTableColumns(); + BuildTableColumns(); } @if (ShowSearch && SearchMode == SearchMode.Top) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.cs b/src/BootstrapBlazor/Components/Table/Table.razor.cs index 385fa7a2f37..10d1f59ad70 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.cs @@ -1122,7 +1122,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) await ReloadColumnStatesFromBrowserAsync(); // 构建列信息 - await BuildTableColumns(); + BuildTableColumns(); // 调用查询方法渲染 UI await QueryAsync(true, 1, false, true, IsAutoQueryFirstRender); @@ -1299,41 +1299,16 @@ private List GetTableColumns() return cols; } - private async Task TriggerColumnCreating(List cols) - { - if (OnColumnCreating != null) - { - await OnColumnCreating(cols); - } - } - - private async Task BuildTableColumns() + private void BuildTableColumns() { // 构建列信息 var cols = GetTableColumns(); // 触发列创建事件 - await TriggerColumnCreating(cols); - - Columns.Clear(); - Columns.AddRange(cols.OrderFunc()); - - // set default sortName - var col = Columns.Find(i => i is { Sortable: true, DefaultSort: true }); - if (col != null) - { - SortName = col.GetFieldName(); - SortOrder = col.DefaultSortOrder; - } - - // 加载客户端持久化列状态 - RebuildTableColumnFromCache(); - } - - private void RebuildTableColumns() - { - // 构建列信息 - var cols = GetTableColumns(); + //if (OnColumnCreating != null) + //{ + // await OnColumnCreating(cols); + //} Columns.Clear(); Columns.AddRange(cols.OrderFunc()); @@ -1415,6 +1390,12 @@ private List GetColumnVisibleItems(List cols) })]; } + private async Task OnTest() + { + var t = Columns; + await Task.CompletedTask; + } + private async Task OnTableRenderAsync(bool firstRender) { if (firstRender)