Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5932ae5
refactor: 调整 UseSearchForm 优先级
ArgoZhang Mar 21, 2026
4037c11
feat: 增加 BuildSearchMetaData 扩展方法
ArgoZhang Mar 21, 2026
34735cf
refactor: 移除 Model 不可为空限制
ArgoZhang Mar 21, 2026
7b603c0
feat(SearchDialog): 增加 UseSearchForm 参数
ArgoZhang Mar 21, 2026
945e377
feat: 增加 Reset 方法用于清空搜索条件
ArgoZhang Mar 21, 2026
7af410f
refactor: 调整参数名称
ArgoZhang Mar 21, 2026
15f5b41
refactor: 增加参数传递逻辑
ArgoZhang Mar 21, 2026
232ee42
feat: 表格高级搜索支持 UseSearchForm 参数
ArgoZhang Mar 21, 2026
0a2cabc
doc: 更新示例
ArgoZhang Mar 21, 2026
cfb0459
test: 更新单元测试
ArgoZhang Mar 21, 2026
529b4e7
refactor: 重构 Items 赋值逻辑
ArgoZhang Mar 21, 2026
229002c
refactor: 提升 UseSearchForm 优先级
ArgoZhang Mar 21, 2026
f250249
test: 增加 EditDialog 单元测试
ArgoZhang Mar 21, 2026
ef7da26
refactor: 更改为虚方法
ArgoZhang Mar 21, 2026
09797d3
feat: 增加 Reset 方法
ArgoZhang Mar 21, 2026
352a70a
refactor: 代码格式化
ArgoZhang Mar 21, 2026
c48c648
test: 提高代码覆盖率
ArgoZhang Mar 21, 2026
2c614dd
refactor: 重构代码增加 RenderButtons 方法
ArgoZhang Mar 22, 2026
cbbff37
feat(SearchForm): 内置自动创建 MetaData 实例逻辑
ArgoZhang Mar 22, 2026
ed88b3a
feat: 增加 ToFilter 扩展方法
ArgoZhang Mar 22, 2026
a9fc310
test: 增加 Reset 方法单元测试
ArgoZhang Mar 22, 2026
69f4d1d
feat: SearchForm 增加 SearchFormLocalizerOptions 参数
ArgoZhang Mar 22, 2026
00f016a
refactor: 方法 BuildSearchMetaData 不返回空值
ArgoZhang Mar 22, 2026
a8dc2f1
refactor: 逻辑下沉到 SearchForm 中简化表格内代码
ArgoZhang Mar 22, 2026
093f3a6
test: 增加 SearchForm 单元测试
ArgoZhang Mar 22, 2026
87148b1
test: 增加 SearchDialog 单元测试
ArgoZhang Mar 22, 2026
658e706
refactor: 增加单元测试
ArgoZhang Mar 22, 2026
72b7462
test: 增加单元测试
ArgoZhang Mar 22, 2026
b88fe58
test: 更新单元测试
ArgoZhang Mar 22, 2026
a1c8f37
refactor: 重构扩展方法 CreateSearchItemComponentByMetadata
ArgoZhang Mar 22, 2026
84a3377
doc: 更新示例
ArgoZhang Mar 22, 2026
a92deec
refactor: 重构 Meta 文件名
ArgoZhang Mar 22, 2026
10f276b
refactor: 重命名类型
ArgoZhang Mar 22, 2026
38626b6
refactor: 属性重命名
ArgoZhang Mar 22, 2026
d32c21f
refactor: 重命名
ArgoZhang Mar 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,29 @@
<p>@((MarkupString)Localizer["SearchTableTips"].Value)</p>
</Tips>

<DemoBlock Title="@Localizer["SearchFormTitle"]"
Introduction="@Localizer["SearchFormIntro"]"
Name="SearchForm">
<section ignore>
<p>@((MarkupString)Localizer["SearchFormDesc"].Value)</p>
<div>@((MarkupString)Localizer["SearchFormTips"].Value)</div>
</section>
<Table TItem="Foo"
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">
<TableColumns>
<TableColumn @bind-Field="@context.DateTime" Width="180" Searchable="true" />
<TableColumn @bind-Field="@context.Name" Searchable="true" SearchFormItemMetadata="_nameSearchFormItemMetadata" />
<TableColumn @bind-Field="@context.Address" Searchable="true" SearchFormItemMetadata="_addressSearchFormItemMetadata" />
<TableColumn @bind-Field="@context.Count" Searchable="true" />
<TableColumn @bind-Field="@context.Education" Searchable="true" />
</TableColumns>
</Table>
</DemoBlock>

<DemoBlock Title="@Localizer["SearchTableTitle"]"
Introduction="@Localizer["SearchTableIntro"]"
Name="SearchTable">
Expand Down Expand Up @@ -65,19 +88,22 @@
items = items.Where(options.Searches.GetFilterFunc&lt;Foo&gt;(FilterLogic.Or));
}</Pre>
<GroupBox Title="@Localizer["AutoGenerateSearchGroupBoxTitle"]" class="mb-3">
<div class="row g-3 form-inline">
<div class="col-12 col-sm-3">
<div class="bb-grid">
<div class="bb-grid-item">
<Switch DisplayText="@Localizer["DisplayText1"]" ShowLabel="true" @bind-Value="SearchModeFlag" />
</div>
<div class="col-12 col-sm-3">
<div class="bb-grid-item">
<Switch DisplayText="@Localizer["DisplayText2"]" ShowLabel="true" @bind-Value="ShowSearchText" />
</div>
<div class="col-12 col-sm-3">
<div class="bb-grid-item">
<Switch DisplayText="@Localizer["DisplayText3"]" ShowLabel="true" @bind-Value="ShowResetButton" />
</div>
<div class="col-12 col-sm-3">
<div class="bb-grid-item">
<Switch DisplayText="@Localizer["DisplayText4"]" ShowLabel="true" @bind-Value="ShowSearchButton" IsDisabled="SearchModeFlag" />
</div>
<div class="bb-grid-item">
<Switch DisplayText="@Localizer["DisplayText5"]" ShowLabel="true" @bind-Value="UseSearchForm" />
</div>
</div>
</GroupBox>
</section>
Expand All @@ -87,6 +113,7 @@
ShowToolbar="true" IsMultipleSelect="true" ShowExtendButtons="true"
ShowSearch="true" ShowResetButton="ShowResetButton" ShowSearchButton="ShowSearchButton" ShowSearchText="ShowSearchText"
AddModalTitle="@Localizer["AddModelTitle"]" EditModalTitle="@Localizer["EditModelTitle"]"
UseSearchForm="UseSearchForm" SearchItems="_searchItems"
OnQueryAsync="@OnQueryAsync" OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
<TableColumns>
<TableColumn @bind-Field="@context.DateTime" Width="180" Filterable="true" />
Expand Down Expand Up @@ -157,25 +184,3 @@
</CustomerSearchTemplate>
</Table>
</DemoBlock>

<DemoBlock Title="@Localizer["SearchFormTitle"]"
Introduction="@Localizer["SearchFormIntro"]"
Name="SearchForm">
<section ignore>
<div>@((MarkupString)Localizer["SearchFormDesc"].Value)</div>
</section>
<Table TItem="Foo"
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">
<TableColumns>
<TableColumn @bind-Field="@context.DateTime" Width="180" Searchable="true" />
<TableColumn @bind-Field="@context.Name" Searchable="true" SearchFormItemMetaData="_nameSearchFormItemMetaData" />
<TableColumn @bind-Field="@context.Address" Searchable="true" SearchFormItemMetaData="_addressSearchFormItemMetaData" />
<TableColumn @bind-Field="@context.Count" Searchable="true" />
<TableColumn @bind-Field="@context.Education" Searchable="true" />
</TableColumns>
</Table>
</DemoBlock>
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,23 @@ public partial class TablesSearch

private SearchMode SearchModeValue { get; set; }

private bool UseSearchForm { get; set; }

private List<ISearchItem> _searchItems = [];

private bool SearchModeFlag
{
get => SearchModeValue == SearchMode.Popup;
set => SearchModeValue = value ? SearchMode.Popup : SearchMode.Top;
}

private ISearchFormItemMetaData _nameSearchFormItemMetaData = new StringSearchMetaData()
private ISearchFormItemMetadata _nameSearchFormItemMetadata = new StringSearchMetadata()
{
PlaceHolder = "请输入名称搜索(支持模糊匹配)",
FilterAction = FilterAction.Contains,
};

private ISearchFormItemMetaData _addressSearchFormItemMetaData = new StringSearchMetaData()
private ISearchFormItemMetadata _addressSearchFormItemMetadata = new StringSearchMetadata()
{
PlaceHolder = "请输入地址搜索(支持模糊匹配)",
FilterAction = FilterAction.Contains,
Expand All @@ -58,6 +62,7 @@ private bool SearchModeFlag
protected override void OnInitialized()
{
base.OnInitialized();

Items = Foo.GenerateFoo(FooLocalizer);
SearchItems = new List<SelectedItem>()
{
Expand All @@ -77,6 +82,11 @@ protected override void OnInitialized()
Value = Localizer["SelectedItemValue2"].Value
},
};

_searchItems = [
new SearchItem(nameof(Foo.Name), typeof(string), FooLocalizer[nameof(Foo.Name)]),
new SearchItem(nameof(Foo.DateTime), typeof(DateTime), FooLocalizer[nameof(Foo.DateTime)])
];
}

private static Task<Foo> OnAddAsync() => Task.FromResult(new Foo() { DateTime = DateTime.Now });
Expand Down
4 changes: 3 additions & 1 deletion src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -5061,10 +5061,12 @@
"DisplayText2": "Fuzzy Search",
"DisplayText3": "Display reset",
"DisplayText4": "Display Search",
"DisplayText5": "SearchForm",
"EditModelTitle": "Edit Test Data Window",
"NamePlaceholder": "Please enter your name within 50 characters",
"SearchFormDesc": "When <code>UseSearchForm</code> is enabled and <code>SearchItems</code> is not provided, it will default to using <code>TableColumn</code> with <code>Searchable=\"true\"</code>. You can customize the metadata through the <code>SearchFormItemMetaData</code> property in <code>TableColumn</code>",
"SearchFormDesc": "When <code>UseSearchForm</code> is enabled and <code>SearchItems</code> is not provided, it will default to using <code>TableColumn</code> with <code>Searchable=\"true\"</code>. You can customize the metadata through the <code>SearchFormItemMetadata</code> property in <code>TableColumn</code>",
"SearchFormIntro": "Enable the search form feature by setting <code>UseSearchForm=\"true\"</code>, and configure the search items within the form using <code>SearchItems</code>, suitable for scenarios with custom complex search conditions",
"SearchFormTips": "Enabling <code>UseSearchForm</code> will prevent <code>SearchTemplate</code>, <code>CustomerSearchModel</code>, and <code>CustomerSearchTemplate</code> from taking effect.",
"SearchFormTitle": "Search Form",
"SearchTableGroupBoxText": "Search Criteria",
"SearchTableIntro": "Set <code>ShowSearch</code> to display the query component, customize the search UI by setting the <code>SearchTemplate</code> template",
Expand Down
4 changes: 3 additions & 1 deletion src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -5061,9 +5061,11 @@
"DisplayText2": "模糊搜索",
"DisplayText3": "显示清空",
"DisplayText4": "显示搜索",
"DisplayText5": "搜索表单",
"EditModelTitle": "编辑测试数据窗口",
"NamePlaceholder": "请输入姓名,50字以内",
"SearchFormDesc": "使用 <code>UseSearchForm</code> 开启搜索表单时未提供 <code>SearchItems</code> 默认尝试使用设置 <code>Searchable=\"true\"</code> 的 <code>TableColumn</code> 进行构建,可以通过 <code>TableColumn</code> 中的 <code>SearchFormItemMetaData</code> 属性定制化元数据",
"SearchFormDesc": "使用 <code>UseSearchForm</code> 开启搜索表单时未提供 <code>SearchItems</code> 默认尝试使用设置 <code>Searchable=\"true\"</code> 的 <code>TableColumn</code> 进行构建,可以通过 <code>TableColumn</code> 中的 <code>SearchFormItemMetadata</code> 属性定制化元数据",
"SearchFormTips": "开启 <code>UseSearchForm</code> 后 <code>SearchTemplate</code> <code>CustomerSearchModel</code> <code>CustomerSearchTemplate</code> 均不生效",
"SearchFormIntro": "通过设置 <code>UseSearchForm=\"true\"</code> 开启搜索表单功能,通过 <code>SearchItems</code> 配置搜索表单内搜索项,适用于自定义复杂搜索条件的场景",
"SearchFormTitle": "搜索表单",
"SearchTableGroupBoxText": "搜索条件",
Expand Down
18 changes: 18 additions & 0 deletions src/BootstrapBlazor.Server/wwwroot/css/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -398,3 +398,21 @@ code {
.icon-list .bb-iconpark-icon {
--bb-svg-icon-width: 16px;
}

.bb-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
}

.bb-grid-item {
display: flex;
flex-direction: row;
align-items: center;
}

.bb-grid-item > .form-label {
min-width: 80px;
text-align: right;
margin-right: .5rem;
margin-bottom: 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ public class AutoGenerateColumnAttribute : AutoGenerateBaseAttribute, ITableColu

bool? ITableColumn.Searchable { get => Searchable; set => Searchable = value ?? false; }

ISearchFormItemMetaData? ITableColumn.SearchFormItemMetaData { get; set; }
ISearchFormItemMetadata? ITableColumn.SearchFormItemMetadata { get; set; }

bool? ITableColumn.Filterable { get => Filterable; set => Filterable = value ?? false; }

Expand Down
15 changes: 4 additions & 11 deletions src/BootstrapBlazor/Components/Dialog/DialogBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,9 @@ public abstract class DialogBase<TModel> : BootstrapModuleComponentBase
public bool ShowUnsetGroupItemsOnTop { get; set; }

/// <summary>
/// <para lang="zh">OnInitialized 方法</para>
/// <para lang="en">OnInitialized Method</para>
/// <para lang="zh">通过模型标签获得所有搜索列集合</para>
/// <para lang="en">Gets all searchable columns by model attributes</para>
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();

if (Model == null)
{
throw new InvalidOperationException("Model value not set to null");
}
}
/// <returns></returns>
protected IEnumerable<IEditorItem> GetItemsByColumns() => Utility.GenerateColumns<TModel>(item => item.GetSearchable());
}
10 changes: 10 additions & 0 deletions src/BootstrapBlazor/Components/Dialog/EditDialog.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ protected override void OnParametersSet()
{
base.OnParametersSet();

if (Model == null)
{
throw new InvalidOperationException($"参数 {nameof(Model)} 未赋值; {nameof(Model)} can not be null.");
}

CloseButtonIcon ??= IconTheme.GetIconByKey(ComponentIcons.DialogCloseButtonIcon);
SaveButtonIcon ??= IconTheme.GetIconByKey(ComponentIcons.DialogSaveButtonIcon);

Expand All @@ -173,6 +178,11 @@ protected override void OnParametersSet()

CloseConfirmTitle ??= Localizer[nameof(CloseConfirmTitle)];
CloseConfirmContent ??= Localizer[nameof(CloseConfirmContent)];

if (BodyTemplate == null)
{
Items ??= GetItemsByColumns();
}
}

private async Task<bool> OnClosingCallback()
Expand Down
19 changes: 13 additions & 6 deletions src/BootstrapBlazor/Components/Dialog/SearchDialog.razor
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
@namespace BootstrapBlazor.Components
@namespace BootstrapBlazor.Components
@typeparam TModel
@inherits DialogBase<TModel>

@if (BodyTemplate != null)
@if (UseSearchForm)
{
<SearchForm Items="SearchItems" RowType="RowType" ItemsPerRow="ItemsPerRow" LabelAlign="LabelAlign" ShowLabel="ShowLabel" ShowUnsetGroupItemsOnTop="ShowUnsetGroupItemsOnTop"
SearchFormLocalizerOptions="SearchFormLocalizerOptions" OnChanged="OnSearchFormFilterChanged">
<Buttons>
@RenderButtons
</Buttons>
</SearchForm>
}
else if (BodyTemplate != null)
{
<div class="form-body">
@BodyTemplate.Invoke(Model)
</div>
<div class="form-footer">
<DialogCloseButton Icon="@ClearIcon" Text="@ResetButtonText" OnClickWithoutRender="@OnResetSearchClick" />
<DialogCloseButton Color="Color.Primary" Icon="@SearchIcon" Text="@QueryButtonText" OnClickWithoutRender="@OnSearchClick" />
@RenderButtons
</div>
}
else
{
<CascadingValue Value="true" IsFixed="true" Name="IsSearch">
<EditorForm TModel="TModel" Model="Model" Items="Items" RowType="RowType" ItemsPerRow="ItemsPerRow" LabelAlign="LabelAlign" ShowLabel="ShowLabel" ShowUnsetGroupItemsOnTop="ShowUnsetGroupItemsOnTop">
<Buttons>
<DialogCloseButton Icon="@ClearIcon" Text="@ResetButtonText" OnClickWithoutRender="@OnResetSearchClick" />
<DialogCloseButton Color="Color.Primary" Icon="@SearchIcon" Text="@QueryButtonText" OnClickWithoutRender="@OnSearchClick" />
@RenderButtons
</Buttons>
</EditorForm>
</CascadingValue>
Expand Down
68 changes: 66 additions & 2 deletions src/BootstrapBlazor/Components/Dialog/SearchDialog.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,34 @@ public partial class SearchDialog<TModel>
[Parameter]
public string? SearchIcon { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 是否使用 SearchForm 组件进行搜索条件编辑 默认 false 不使用</para>
/// <para lang="en">Gets or sets Whether to use SearchForm component for editing search conditions. Default is false</para>
/// </summary>
[Parameter]
public bool UseSearchForm { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 搜索表单项集合</para>
/// <para lang="en">Gets or sets Search Form Items collection</para>
/// </summary>
[Parameter]
public List<ISearchItem>? SearchItems { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 过滤器改变回调事件 Func 版本</para>
/// <para lang="en">Gets or sets the filter changed callback event Func version</para>
/// </summary>
[Parameter]
public Func<FilterKeyValueAction, Task>? OnChanged { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 搜索表单本地化配置项</para>
/// <para lang="en">Gets or sets Search Form Localization Options</para>
/// </summary>
[Parameter]
public SearchFormLocalizerOptions? SearchFormLocalizerOptions { get; set; }

[Inject]
[NotNull]
private IStringLocalizer<SearchDialog<TModel>>? Localizer { get; set; }
Expand All @@ -68,8 +96,7 @@ public partial class SearchDialog<TModel>
private IIconTheme? IconTheme { get; set; }

/// <summary>
/// <para lang="zh">OnParametersSet 方法</para>
/// <para lang="en">OnParametersSet Method</para>
/// <inheritdoc/>
/// </summary>
protected override void OnParametersSet()
{
Expand All @@ -80,5 +107,42 @@ protected override void OnParametersSet()

ClearIcon ??= IconTheme.GetIconByKey(ComponentIcons.SearchDialogClearIcon);
SearchIcon ??= IconTheme.GetIconByKey(ComponentIcons.SearchDialogSearchIcon);

if (UseSearchForm)
{
return;
}

if (BodyTemplate != null)
{
return;
}

Items ??= GetItemsByColumns();
}

private async Task OnSearchFormFilterChanged(FilterKeyValueAction action)
{
// 通知父组件过滤器改变事件,此时并没有触发 OnSearchClick 搜索事件,父组件可以在 OnChanged 事件中获取当前过滤器状态并决定是否触发搜索事件
if (OnChanged != null)
{
await OnChanged(action);
}
}

private RenderFragment RenderButtons => builder =>
{
builder.OpenComponent<DialogCloseButton>(0);
builder.AddAttribute(10, nameof(DialogCloseButton.Icon), ClearIcon);
builder.AddAttribute(20, nameof(DialogCloseButton.Text), ResetButtonText);
builder.AddAttribute(30, nameof(DialogCloseButton.OnClickWithoutRender), OnResetSearchClick);
builder.CloseComponent();

builder.OpenComponent<DialogCloseButton>(100);
builder.AddAttribute(101, nameof(DialogCloseButton.Color), Color.Primary);
builder.AddAttribute(110, nameof(DialogCloseButton.Icon), SearchIcon);
builder.AddAttribute(120, nameof(DialogCloseButton.Text), QueryButtonText);
builder.AddAttribute(130, nameof(DialogCloseButton.OnClickWithoutRender), OnSearchClick);
builder.CloseComponent();
};
}
24 changes: 24 additions & 0 deletions src/BootstrapBlazor/Components/Dialog/SearchDialogOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,28 @@ public SearchDialogOption()
/// <para lang="en">Gets or sets Search Callback Delegate</para>
/// </summary>
public Func<Task>? OnSearchClick { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 是否使用 SearchForm 组件进行搜索条件编辑 默认 false 不使用</para>
/// <para lang="en">Gets or sets Whether to use SearchForm component for editing search conditions. Default is false</para>
/// </summary>
public bool UseSearchForm { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 搜索表单项集合</para>
/// <para lang="en">Gets or sets Search Form Items collection</para>
/// </summary>
public List<ISearchItem>? SearchItems { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 过滤器改变回调事件 Func 版本</para>
/// <para lang="en">Gets or sets the filter changed callback event Func version</para>
/// </summary>
public Func<FilterKeyValueAction, Task>? OnFilterChanged { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 搜索表单本地化配置项</para>
/// <para lang="en">Gets or sets Search Form Localization Options</para>
/// </summary>
public SearchFormLocalizerOptions? SearchFormLocalizerOptions { get; set; }
}
Loading
Loading