Skip to content

Commit c33fc36

Browse files
authored
fix(Table): incorrect column order when switching to multiple languages (#8223)
* fix(Table): 修复切换多语言时列错位问题 * test: 更新表格单元测试 * chore: bump version 10.8.1
1 parent d9269e9 commit c33fc36

3 files changed

Lines changed: 132 additions & 2 deletions

File tree

src/BootstrapBlazor/BootstrapBlazor.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<Version>10.8.1-beta02</Version>
4+
<Version>10.8.1</Version>
55
</PropertyGroup>
66

77
<ItemGroup>

src/BootstrapBlazor/Components/Table/Table.razor.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,9 @@ private void ResetTableColumns()
14601460
}
14611461
}
14621462

1463+
// 未匹配到状态的列按其在 Columns 中的相对位置插入而不是追加到末尾
1464+
// 防止多语言切换等场景列字段名变化导致仅部分列匹配时列顺序错乱
1465+
var insertIndex = 0;
14631466
foreach (var col in Columns)
14641467
{
14651468
if (col.GetIgnore())
@@ -1471,11 +1474,13 @@ private void ResetTableColumns()
14711474
if (stateMap.TryGetValue(name, out var item))
14721475
{
14731476
item.DisplayName = col.GetDisplayName();
1477+
insertIndex = _tableColumnStates.IndexOf(item) + 1;
14741478
}
14751479
else
14761480
{
14771481
item = CreateTableColumnState(col);
1478-
_tableColumnStates.Add(item);
1482+
_tableColumnStates.Insert(insertIndex, item);
1483+
insertIndex++;
14791484
}
14801485
}
14811486

test/UnitTest/Components/TableTest.cs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9071,6 +9071,131 @@ public async Task ClearTableColumnClientStatus_Ok()
90719071
Assert.Contains("style=\"width: 80px;\"", colGroup.ToMarkup());
90729072
}
90739073

9074+
[Fact]
9075+
public void ColumnStates_PartialMatch_KeepColumnOrder_Ok()
9076+
{
9077+
// 持久化状态中前两列字段名与当前列不匹配 仅 Count 列匹配
9078+
// 模拟多语言切换后列字段名变化场景
9079+
var state = new TableColumnClientStatus();
9080+
state.Columns.Add(new TableColumnState() { Name = "OldName1", Visible = true });
9081+
state.Columns.Add(new TableColumnState() { Name = "OldName2", Visible = true });
9082+
state.Columns.Add(new TableColumnState() { Name = nameof(Foo.Count), Visible = true });
9083+
9084+
Context.JSInterop.Setup<TableColumnClientStatus>("getColumnStates", "test_partial_match").SetResult(state);
9085+
9086+
var localizer = Context.Services.GetRequiredService<IStringLocalizer<Foo>>();
9087+
var cut = Context.Render<BootstrapBlazorRoot>(pb =>
9088+
{
9089+
pb.AddChildContent<Table<Foo>>(pb =>
9090+
{
9091+
pb.Add(a => a.ClientTableName, "test_partial_match");
9092+
pb.Add(a => a.RenderMode, TableRenderMode.Table);
9093+
pb.Add(a => a.Items, Foo.GenerateFoo(localizer, 2));
9094+
pb.Add(a => a.TableColumns, foo => builder =>
9095+
{
9096+
builder.OpenComponent<TableColumn<Foo, string>>(0);
9097+
builder.AddAttribute(1, "Field", foo.Name);
9098+
builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Name", typeof(string)));
9099+
builder.CloseComponent();
9100+
9101+
builder.OpenComponent<TableColumn<Foo, string>>(3);
9102+
builder.AddAttribute(4, "Field", foo.Address);
9103+
builder.AddAttribute(5, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string)));
9104+
builder.CloseComponent();
9105+
9106+
builder.OpenComponent<TableColumn<Foo, int>>(6);
9107+
builder.AddAttribute(7, "Field", foo.Count);
9108+
builder.AddAttribute(8, "FieldExpression", Utility.GenerateValueExpression(foo, "Count", typeof(int)));
9109+
builder.CloseComponent();
9110+
});
9111+
});
9112+
});
9113+
9114+
// 未匹配的列应按声明相对位置插入 Count 列不应跳到最前
9115+
var table = cut.FindComponent<Table<Foo>>();
9116+
var order = string.Join(",", table.Instance.GetVisibleColumns().Select(i => i.GetFieldName()));
9117+
Assert.Equal("Name,Address,Count", order);
9118+
}
9119+
9120+
[Fact]
9121+
public void ColumnStates_DataTableLocalizedColumnName_Ok()
9122+
{
9123+
// 模拟英文会话保存的持久化列状态
9124+
var state = new TableColumnClientStatus();
9125+
state.Columns.Add(new TableColumnState() { Name = "Name", Visible = true });
9126+
state.Columns.Add(new TableColumnState() { Name = "Address", Visible = true });
9127+
state.Columns.Add(new TableColumnState() { Name = "Id", Visible = true });
9128+
9129+
Context.JSInterop.Setup<TableColumnClientStatus>("getColumnStates", "test_localized_datatable").SetResult(state);
9130+
9131+
// 切换中文后 DataTable 列名变化 仅 Id 列与持久化状态匹配
9132+
var data = new DataTable();
9133+
data.Columns.Add("姓名", typeof(string));
9134+
data.Columns.Add("地址", typeof(string));
9135+
data.Columns.Add("Id", typeof(int));
9136+
data.Rows.Add("张三", "某地", 1);
9137+
data.AcceptChanges();
9138+
9139+
var cut = Context.Render<BootstrapBlazorRoot>(pb =>
9140+
{
9141+
pb.AddChildContent<Table<DynamicObject>>(pb =>
9142+
{
9143+
pb.Add(a => a.ClientTableName, "test_localized_datatable");
9144+
pb.Add(a => a.RenderMode, TableRenderMode.Table);
9145+
pb.Add(a => a.DynamicContext, new DataTableDynamicContext(data));
9146+
});
9147+
});
9148+
9149+
// 列顺序应保持 DataTable 列声明顺序 Id 列不应跳到最前
9150+
var table = cut.FindComponent<Table<DynamicObject>>();
9151+
var order = string.Join(",", table.Instance.GetVisibleColumns().Select(i => i.GetFieldName()));
9152+
Assert.Equal("姓名,地址,Id", order);
9153+
}
9154+
9155+
[Fact]
9156+
public void ColumnStates_FullMatch_KeepPersistedOrder_Ok()
9157+
{
9158+
// 持久化状态全部匹配时保持持久化顺序 模拟用户拖拽列后的自定义顺序
9159+
var state = new TableColumnClientStatus();
9160+
state.Columns.Add(new TableColumnState() { Name = nameof(Foo.Count), Visible = true });
9161+
state.Columns.Add(new TableColumnState() { Name = nameof(Foo.Name), Visible = true });
9162+
state.Columns.Add(new TableColumnState() { Name = nameof(Foo.Address), Visible = true });
9163+
9164+
Context.JSInterop.Setup<TableColumnClientStatus>("getColumnStates", "test_full_match").SetResult(state);
9165+
9166+
var localizer = Context.Services.GetRequiredService<IStringLocalizer<Foo>>();
9167+
var cut = Context.Render<BootstrapBlazorRoot>(pb =>
9168+
{
9169+
pb.AddChildContent<Table<Foo>>(pb =>
9170+
{
9171+
pb.Add(a => a.ClientTableName, "test_full_match");
9172+
pb.Add(a => a.RenderMode, TableRenderMode.Table);
9173+
pb.Add(a => a.Items, Foo.GenerateFoo(localizer, 2));
9174+
pb.Add(a => a.TableColumns, foo => builder =>
9175+
{
9176+
builder.OpenComponent<TableColumn<Foo, string>>(0);
9177+
builder.AddAttribute(1, "Field", foo.Name);
9178+
builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Name", typeof(string)));
9179+
builder.CloseComponent();
9180+
9181+
builder.OpenComponent<TableColumn<Foo, string>>(3);
9182+
builder.AddAttribute(4, "Field", foo.Address);
9183+
builder.AddAttribute(5, "FieldExpression", Utility.GenerateValueExpression(foo, "Address", typeof(string)));
9184+
builder.CloseComponent();
9185+
9186+
builder.OpenComponent<TableColumn<Foo, int>>(6);
9187+
builder.AddAttribute(7, "Field", foo.Count);
9188+
builder.AddAttribute(8, "FieldExpression", Utility.GenerateValueExpression(foo, "Count", typeof(int)));
9189+
builder.CloseComponent();
9190+
});
9191+
});
9192+
});
9193+
9194+
var table = cut.FindComponent<Table<Foo>>();
9195+
var order = string.Join(",", table.Instance.GetVisibleColumns().Select(i => i.GetFieldName()));
9196+
Assert.Equal("Count,Name,Address", order);
9197+
}
9198+
90749199
[Theory]
90759200
[InlineData(true)]
90769201
[InlineData(false)]

0 commit comments

Comments
 (0)