@@ -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