diff --git a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesSearch.razor b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesSearch.razor index 368acfc6cf2..d3527ae19d7 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesSearch.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesSearch.razor @@ -165,11 +165,11 @@
@((MarkupString)Localizer["SearchFormDesc"].Value)
+ IsPagination="true" PageItemsSource="@PageItemsSource" SearchMode="SearchMode.Top" + IsStriped="true" IsBordered="true" + ShowSearch="true" UseSearchForm="true" + ShowToolbar="true" IsMultipleSelect="true" ShowExtendButtons="true" + OnQueryAsync="@OnQueryAsync" OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync"> diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index c0f46107146..01c5e9ecd6d 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -5063,6 +5063,9 @@ "DisplayText4": "Display Search", "EditModelTitle": "Edit Test Data Window", "NamePlaceholder": "Please enter your name within 50 characters", + "SearchFormDesc": "When UseSearchForm is enabled and SearchItems is not provided, it will default to using TableColumn with Searchable=\"true\". You can customize the metadata through the SearchFormItemMetaData property in TableColumn", + "SearchFormIntro": "Enable the search form feature by setting UseSearchForm=\"true\", and configure the search items within the form using SearchItems, suitable for scenarios with custom complex search conditions", + "SearchFormTitle": "Search Form", "SearchTableGroupBoxText": "Search Criteria", "SearchTableIntro": "Set ShowSearch to display the query component, customize the search UI by setting the SearchTemplate template", "SearchTableLi1": "Enable no data display function by setting ShowEmpty=\"true\"", @@ -5076,10 +5079,7 @@ "SelectedItemValue1": "Name1", "SelectedItemValue2": "Name2", "TablesSearchDesc": "Commonly used for single table maintenance, simple addition, deletion, modification, search, sorting, filtering, search and other common functions can be realized through attribute configuration, and very complex can be realized through the advanced usage of Template business needs functions", - "TablesSearchTitle": "Table Search", - "SearchFormTitle": "Search Form", - "SearchFormIntro": "Enable the search form feature by setting UseSearchForm=\"true\", and configure the search items within the form using SearchItems, suitable for scenarios with custom complex search conditions", - "SearchFormDesc": "When UseSearchForm is enabled and SearchItems is not provided, it will default to using TableColumn with Searchable=\"true\". You can customize the metadata through the SearchFormItemMetaData property in TableColumn" + "TablesSearchTitle": "Table Search" }, "BootstrapBlazor.Server.Components.Samples.Table.TablesSelection": { "TablesSelectionCountText": "Count:{0}", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 1a4220b9ff1..98b254d1041 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -5063,6 +5063,9 @@ "DisplayText4": "显示搜索", "EditModelTitle": "编辑测试数据窗口", "NamePlaceholder": "请输入姓名,50字以内", + "SearchFormDesc": "使用 UseSearchForm 开启搜索表单时未提供 SearchItems 默认尝试使用设置 Searchable=\"true\"TableColumn 进行构建,可以通过 TableColumn 中的 SearchFormItemMetaData 属性定制化元数据", + "SearchFormIntro": "通过设置 UseSearchForm=\"true\" 开启搜索表单功能,通过 SearchItems 配置搜索表单内搜索项,适用于自定义复杂搜索条件的场景", + "SearchFormTitle": "搜索表单", "SearchTableGroupBoxText": "搜索条件", "SearchTableIntro": "设置 ShowSearch 显示查询组件,通过设置 SearchTemplate 模板自定义搜索 UI", "SearchTableLi1": "通过设置 ShowEmpty=\"true\" 开启无数据显示功能", @@ -5076,10 +5079,7 @@ "SelectedItemValue1": "姓名1", "SelectedItemValue2": "姓名2", "TablesSearchDesc": "常用于单表维护,通过属性配置实现简单的增、删、改、查、排序、过滤、搜索等常用功能,通过 Template 的高级用法能实现非常复杂的业务需求功能", - "TablesSearchTitle": "Table 表格", - "SearchFormTitle": "搜索表单", - "SearchFormIntro": "通过设置 UseSearchForm=\"true\" 开启搜索表单功能,通过 SearchItems 配置搜索表单内搜索项,适用于自定义复杂搜索条件的场景", - "SearchFormDesc": "使用 UseSearchForm 开启搜索表单时未提供 SearchItems 默认尝试使用设置 Searchable=\"true\"TableColumn 进行构建,可以通过 TableColumn 中的 SearchFormItemMetaData 属性定制化元数据" + "TablesSearchTitle": "Table 表格" }, "BootstrapBlazor.Server.Components.Samples.Table.TablesSelection": { "TablesSelectionCountText": "选中的行数:{0}", diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index b8adf8922a4..36ecc7b6dbd 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@  - 10.4.1-beta02 + 10.4.1 diff --git a/src/BootstrapBlazor/Components/SearchForm/ISearchItem.cs b/src/BootstrapBlazor/Components/SearchForm/ISearchItem.cs index 5ce2ee610eb..b2e4b8389b1 100644 --- a/src/BootstrapBlazor/Components/SearchForm/ISearchItem.cs +++ b/src/BootstrapBlazor/Components/SearchForm/ISearchItem.cs @@ -68,7 +68,7 @@ public interface ISearchItem /// 获得/设置 搜索元数据 /// Gets or sets the search metadata /// - public ISearchFormItemMetaData? MetaData { get; set; } + ISearchFormItemMetaData? MetaData { get; set; } /// /// 获得 过滤器实例 @@ -78,5 +78,5 @@ public interface ISearchItem /// 过滤器实例 /// Filter instance /// - public FilterKeyValueAction? GetFilter() => MetaData?.GetFilter(FieldName); + FilterKeyValueAction? GetFilter(); } diff --git a/src/BootstrapBlazor/Components/SearchForm/SearchItem.cs b/src/BootstrapBlazor/Components/SearchForm/SearchItem.cs index bd6edc8c4f1..6cc8596a880 100644 --- a/src/BootstrapBlazor/Components/SearchForm/SearchItem.cs +++ b/src/BootstrapBlazor/Components/SearchForm/SearchItem.cs @@ -63,4 +63,10 @@ public class SearchItem(string fieldName, Type fieldType, string? fieldText = nu /// /// public ISearchFormItemMetaData? MetaData { get; set; } + + /// + /// + /// + /// + public FilterKeyValueAction? GetFilter() => MetaData?.GetFilter(FieldName); } diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.Localization.cs b/src/BootstrapBlazor/Components/Table/Table.razor.Localization.cs index dc8efdd2d20..20da0210bac 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.Localization.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.Localization.cs @@ -416,6 +416,10 @@ public partial class Table [NotNull] private IStringLocalizer>? Localizer { get; set; } + [Inject] + [NotNull] + private IStringLocalizer? SearchFormLocalizer { get; set; } + private void OnInitLocalization() { AddButtonText ??= Localizer[nameof(AddButtonText)]; diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.Search.cs b/src/BootstrapBlazor/Components/Table/Table.razor.Search.cs index 92fa0300a81..ae8fefa59aa 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.Search.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.Search.cs @@ -94,8 +94,8 @@ public partial class Table public bool ShowSearchButton { get; set; } = true; /// - /// 获得/设置 是否显示高级搜索按钮,默认值为 true - /// Gets or sets Whether to show advanced search button. Default true. + /// 获得/设置 是否显示高级搜索按钮,默认值为 true 设置 时生效 + /// Gets or sets Whether to show advanced search button. Default true. Effective when is set to Popup /// [Parameter] public bool ShowAdvancedSearch { get; set; } = true; @@ -128,6 +128,13 @@ public partial class Table [Parameter] public IEnumerable? SearchItems { get; set; } + /// + /// 获得/设置 搜索表单本地化配置项 + /// Gets or sets Search Form Localization Options + /// + [Parameter] + public SearchFormLocalizerOptions? SearchFormLocalizerOptions { get; set; } + /// /// 获得/设置 每行显示组件数量 默认为 2 /// Gets or sets Items per row. Default 2 @@ -163,8 +170,19 @@ private IEnumerable SearchFormItems { get { - // TODO: 增加多语言支持 - _searchItems ??= SearchItems ?? GetSearchColumns().Select(i => i.ParseSearchItem()).ToList(); + if (SearchFormLocalizerOptions is null) + { + SearchFormLocalizerOptions = new SearchFormLocalizerOptions() + { + SelectAllText = SearchFormLocalizer[nameof(Components.SearchFormLocalizerOptions.SelectAllText)], + BooleanAllText = SearchFormLocalizer[nameof(Components.SearchFormLocalizerOptions.BooleanAllText)], + BooleanTrueText = SearchFormLocalizer[nameof(Components.SearchFormLocalizerOptions.BooleanTrueText)], + BooleanFalseText = SearchFormLocalizer[nameof(Components.SearchFormLocalizerOptions.BooleanFalseText)], + NumberStartValueLabelText = SearchFormLocalizer[nameof(Components.SearchFormLocalizerOptions.NumberStartValueLabelText)], + NumberEndValueLabelText = SearchFormLocalizer[nameof(Components.SearchFormLocalizerOptions.NumberEndValueLabelText)] + }; + } + _searchItems ??= SearchItems ?? GetSearchColumns().Select(i => i.ParseSearchItem(SearchFormLocalizerOptions.Value)).ToList(); return _searchItems; } } @@ -315,7 +333,7 @@ protected IEnumerable GetCustomerSearches() protected List GetAdvanceSearches() { var searches = new List(); - if (ShowAdvancedSearch && CustomerSearchModel == null && UseSearchForm == false) + if (ShowAdvancedSearch && SearchMode == SearchMode.Popup && CustomerSearchModel == null) { var callback = GetAdvancedSearchFilterCallback ?? new Func?>((p, model) => { diff --git a/src/BootstrapBlazor/Extensions/ITableColumnExtensions.cs b/src/BootstrapBlazor/Extensions/ITableColumnExtensions.cs index 36935cd2b87..edc909651a9 100644 --- a/src/BootstrapBlazor/Extensions/ITableColumnExtensions.cs +++ b/src/BootstrapBlazor/Extensions/ITableColumnExtensions.cs @@ -18,8 +18,9 @@ public static class IEditItemExtensions /// Convert ITableColumn to ISearchItem /// /// + /// /// - public static ISearchItem ParseSearchItem(this ITableColumn column) + public static ISearchItem ParseSearchItem(this ITableColumn column, SearchFormLocalizerOptions options) { var item = new SearchItem(column.GetFieldName(), column.PropertyType, column.GetDisplayName()) { @@ -28,13 +29,13 @@ public static ISearchItem ParseSearchItem(this ITableColumn column) GroupName = column.GroupName, GroupOrder = column.GroupOrder, Order = column.Order, - MetaData = column.BuildSearchMetaData() + MetaData = column.BuildSearchMetaData(options) }; return item; } - private static ISearchFormItemMetaData BuildSearchMetaData(this ITableColumn column) + private static ISearchFormItemMetaData BuildSearchMetaData(this ITableColumn column, SearchFormLocalizerOptions options) { // 自定义搜索项逻辑 if (column.SearchFormItemMetaData is not null) @@ -61,38 +62,34 @@ private static ISearchFormItemMetaData BuildSearchMetaData(this ITableColumn col } else if (type.IsEnum) { - // TODO: 缺少本地化工作 metaData = new SelectSearchMetaData() { - Items = type.ToSelectList(new SelectedItem() { Value = "", Text = "全选" }), + Items = type.ToSelectList(new SelectedItem() { Value = "", Text = options.SelectAllText }), }; } else if (fieldType.IsNumberWithDotSeparator()) { - // TODO: 缺少本地化工作 metaData = new NumberSearchMetaData() { - StartValueLabelText = "开始数量", - EndValueLabelText = "截止数量", + StartValueLabelText = options.NumberStartValueLabelText, + EndValueLabelText = options.NumberEndValueLabelText, ValueType = type }; } else if (fieldType.IsBoolean()) { - // TODO: 缺少本地化工作 metaData = new SelectSearchMetaData() { Items = new List() { - new SelectedItem() { Value = "", Text = "全部" }, - new SelectedItem() { Value = "True", Text = "完成" }, - new SelectedItem() { Value = "False", Text = "未完成" } + new SelectedItem() { Value = "", Text = options.BooleanAllText }, + new SelectedItem() { Value = "True", Text = options.BooleanTrueText }, + new SelectedItem() { Value = "False", Text = options.BooleanFalseText } } }; } else if (fieldType.IsDateTime()) { - // TODO: 缺少默认值设置 metaData = new DateTimeRangeSearchMetaData(); } else diff --git a/src/BootstrapBlazor/Locales/en.json b/src/BootstrapBlazor/Locales/en.json index c9415894ae8..47f7aad6bc0 100644 --- a/src/BootstrapBlazor/Locales/en.json +++ b/src/BootstrapBlazor/Locales/en.json @@ -407,5 +407,13 @@ }, "BootstrapBlazor.Components.CardUpload": { "DeleteConfirmContent": "Are you sure you want to delete the current data?" + }, + "BootstrapBlazor.Components.SearchFormLocalizerOptions": { + "SelectAllText": "All", + "BooleanAllText": "All", + "BooleanTrueText": "True", + "BooleanFalseText": "False", + "NumberStartValueLabelText": "Start", + "NumberEndValueLabelText": "End" } } diff --git a/src/BootstrapBlazor/Locales/zh.json b/src/BootstrapBlazor/Locales/zh.json index bc44d15d56a..cabcefdbfbd 100644 --- a/src/BootstrapBlazor/Locales/zh.json +++ b/src/BootstrapBlazor/Locales/zh.json @@ -407,5 +407,13 @@ }, "BootstrapBlazor.Components.CardUpload": { "DeleteConfirmContent": "确定删除当前数据吗?" + }, + "BootstrapBlazor.Components.SearchFormLocalizerOptions": { + "SelectAllText": "全部", + "BooleanAllText": "全部", + "BooleanTrueText": "是", + "BooleanFalseText": "否", + "NumberStartValueLabelText": "开始数量", + "NumberEndValueLabelText": "截止数量" } } diff --git a/src/BootstrapBlazor/Options/SearchFormLocalizerOptions.cs b/src/BootstrapBlazor/Options/SearchFormLocalizerOptions.cs new file mode 100644 index 00000000000..fb7c8f2f8fc --- /dev/null +++ b/src/BootstrapBlazor/Options/SearchFormLocalizerOptions.cs @@ -0,0 +1,49 @@ +// 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 + +namespace BootstrapBlazor.Components; + +/// +/// 搜索表单项本地化配置类 +/// Search form item localization configuration class +/// +public readonly record struct SearchFormLocalizerOptions +{ + /// + /// 全选文本 + /// Select all text + /// + public string SelectAllText { get; init; } + + /// + /// 布尔值全部文本 + /// Boolean all text + /// + public string BooleanAllText { get; init; } + + /// + /// 布尔值为真文本 + /// Boolean true text + /// + public string BooleanTrueText { get; init; } + + /// + /// 布尔值为假文本 + /// Boolean false text + /// + public string BooleanFalseText { get; init; } + + /// + /// 数字起始值标签文本 + /// Number start value label text + /// + public string NumberStartValueLabelText { get; init; } + + /// + /// 数字结束值标签文本 + /// Number end value label text + /// + public string NumberEndValueLabelText { get; init; } +} diff --git a/test/UnitTest/Components/SearchFormTest.cs b/test/UnitTest/Components/SearchFormTest.cs index 25a85aa753c..2581f88fa74 100644 --- a/test/UnitTest/Components/SearchFormTest.cs +++ b/test/UnitTest/Components/SearchFormTest.cs @@ -165,4 +165,16 @@ public void MetaData_Ok() }); }); } + + [Fact] + public void GetFilter_Ok() + { + var item = new SearchItem(nameof(Foo.Education), typeof(string), nameof(Foo.Education)); + var action = item.GetFilter(); + Assert.Null(action); + + item.MetaData = new StringSearchMetaData() { Value = "test" }; + action = item.GetFilter(); + Assert.NotNull(action); + } } diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index ba5c64c44fc..1da1c5d7d7c 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -471,6 +471,34 @@ public async Task CustomSearch_Ok() await cut.InvokeAsync(() => table.Instance.QueryAsync()); } + [Fact] + public async Task GetAdvanceSearches_OK() + { + var localizer = Context.Services.GetRequiredService>(); + var searchModel = new Foo() { Name = "test" }; + var cut = Context.Render(pb => + { + pb.AddChildContent>(pb => + { + pb.Add(a => a.ShowToolbar, true); + pb.Add(a => a.ShowSearch, true); + pb.Add(a => a.SearchModel, searchModel); + pb.Add(a => a.CustomerSearchTemplate, foo => builder => builder.AddContent(0, "test_CustomerSearchTemplate")); + pb.Add(a => a.ShowAdvancedSearch, true); + pb.Add(a => a.SearchMode, SearchMode.Popup); + pb.Add(a => a.OnQueryAsync, OnQueryAsync(localizer)); + pb.Add(a => a.TableColumns, foo => builder => + { + builder.OpenComponent>(0); + builder.AddAttribute(1, "Field", foo.Name); + builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Name", typeof(string))); + builder.AddAttribute(3, "Searchable", true); + builder.CloseComponent(); + }); + }); + }); + } + [Fact] public async Task ShowTopSearch_Ok() {