Skip to content

Commit b620da2

Browse files
ArgoZhangAApuci
andauthored
refactor(TreeView): support Search function (#4944)
* chore: bump version 9.19-beta07 Co-Authored-By: AApuci <44996451+AApuci@users.noreply.github.com> * doc: 更新搜索示例代码 * doc: 移除扁平化数据源示例 * refactor: 重构 ToFlat 返回值数据结构 * refactor: 重构搜索数据集逻辑 * doc: 更新搜索示例 * refactor: 更改搜索设计 * refactor: 更新滚动脚本 * refactor: 移除 IsReset 参数 * refactor: 更新示例 * refactor: 更新 TreeView 单元测试 * test: 更新 Table 单元测试 * test: 更新 SelectTree 单元测试 * revert: 撤销更新 * test: 更新单元测试 --------- Co-authored-by: AApuci <44996451+AApuci@users.noreply.github.com>
1 parent 0a2a091 commit b620da2

File tree

10 files changed

+74
-157
lines changed

10 files changed

+74
-157
lines changed

src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor

+7-10
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@
3939
<div>@((MarkupString)Localizer["TreeViewCheckboxTips2"].Value)</div>
4040
</section>
4141
<section ignore class="row form-inline">
42-
<div class="col-12 col-lg-auto">
43-
<RadioList IsButton="true" @bind-Value="@IsReset" Items="@ResetItems" Color="Color.Success" ShowLabel="true" DisplayText="@Localizer["TreeViewNormalRadioListDisplayText"]"></RadioList>
44-
</div>
4542
<div class="col-12 col-lg-auto">
4643
<Checkbox DisplayText="@Localizer["TreeViewCheckboxCheckBoxDisplayText1"]" ShowAfterLabel="true" @bind-Value="@AutoCheckChildren"></Checkbox>
4744
<Checkbox DisplayText="@Localizer["TreeViewCheckboxCheckBoxDisplayText2"]" ShowAfterLabel="true" @bind-Value="@AutoCheckParent" class="ms-3"></Checkbox>
@@ -50,7 +47,7 @@
5047
<Button Text="@Localizer["TreeViewCheckboxButtonText"]" OnClick="@OnRefresh"></Button>
5148
</div>
5249
</section>
53-
<TreeView TItem="TreeFoo" Items="@CheckedItems" ShowCheckbox="true" OnTreeItemChecked="@OnTreeItemChecked" IsReset="@IsReset" AutoCheckChildren="@AutoCheckChildren" AutoCheckParent="@AutoCheckParent"></TreeView>
50+
<TreeView TItem="TreeFoo" Items="@CheckedItems" ShowCheckbox="true" OnTreeItemChecked="@OnTreeItemChecked" AutoCheckChildren="@AutoCheckChildren" AutoCheckParent="@AutoCheckParent"></TreeView>
5451
<ConsoleLogger @ref="Logger2"></ConsoleLogger>
5552
</DemoBlock>
5653

@@ -173,26 +170,26 @@
173170
<DemoBlock Title="@Localizer["TreeViewShowSearchTitle"]"
174171
Introduction="@Localizer["TreeViewShowSearchIntro"]"
175172
Name="ShowSearch">
176-
<TreeView TItem="TreeFoo" Items="@SearchItems" ShowSearch="true" OnSearchAsync="@OnSearchAsync"></TreeView>
173+
<TreeView TItem="TreeFoo" Items="@SearchItems1" ShowSearch="true" OnSearchAsync="@OnSearchAsync"></TreeView>
177174
</DemoBlock>
178175

179176
<DemoBlock Title="@Localizer["TreeViewFixedSearchTitle"]"
180177
Introduction="@Localizer["TreeViewFixedSearchIntro"]"
181178
Name="IsFixedSearch">
182-
<TreeView TItem="TreeFoo" Items="@SearchItems" ShowSearch="true" OnSearchAsync="@OnSearchAsync" IsFixedSearch="true" style="height: 180px;"></TreeView>
179+
<TreeView TItem="TreeFoo" Items="@SearchItems2" ShowSearch="true" OnSearchAsync="@OnSearchAsync" IsFixedSearch="true" style="height: 180px;"></TreeView>
183180
</DemoBlock>
184181

185182
<DemoBlock Title="@Localizer["TreeViewMaxSelectedCountTitle"]"
186183
Introduction="@Localizer["TreeViewMaxSelectedCountIntro"]"
187184
Name="MaxSelectedCount">
188185
<section ignore>@((MarkupString)Localizer["TreeViewMaxSelectedCountDesc"].Value)</section>
189-
<TreeView TItem="TreeFoo" Items="@SearchItems" ShowCheckbox="true" AutoCheckChildren="true" AutoCheckParent="true"
186+
<TreeView TItem="TreeFoo" Items="@MaxItems" ShowCheckbox="true" AutoCheckChildren="true" AutoCheckParent="true"
190187
MaxSelectedCount="2" OnMaxSelectedCountExceed="OnMaxSelectedCountExceed"></TreeView>
191188
</DemoBlock>
192189

193190
<DemoBlock Title="@Localizer["TreeViewEnableKeyboardArrowUpDownTitle"]"
194191
Introduction="@Localizer["TreeViewEnableKeyboardArrowUpDownIntro"]"
195-
Name="Normal">
192+
Name="EnableKeyboard">
196193
<section ignore>@_selectedValue</section>
197194
<TreeView TItem="TreeFoo" Items="@KeyboardItems" OnTreeItemClick="@OnTreeItemKeyboardClick" style="height: 160px;"
198195
EnableKeyboard="true" ClickToggleNode="false" ClickToggleCheck="false" ShowCheckbox="true" />
@@ -211,7 +208,7 @@
211208
</div>
212209
</DemoBlock>
213210

214-
<DemoBlock Title="@Localizer["TreeViewFlatTitle"]"
211+
@* <DemoBlock Title="@Localizer["TreeViewFlatTitle"]"
215212
Introduction="@Localizer["TreeViewFlatIntro"]"
216213
Name="FlatItems">
217214
<section ignore>
@@ -220,7 +217,7 @@
220217
<TreeView TItem="TreeFoo" Items="@FlatItems" ShowCheckbox="true"
221218
AutoCheckChildren="true" AutoCheckParent="true"></TreeView>
222219
</DemoBlock>
223-
220+
*@
224221
<AttributeTable Items="@GetAttributes()"></AttributeTable>
225222

226223
<AttributeTable Items="@GetTreeItemAttributes()" Title="@Localizer["TreeViewsAttribute"]"></AttributeTable>

src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor.cs

+17-13
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ public sealed partial class TreeViews
5353

5454
private List<TreeViewItem<TreeFoo>>? AsyncItems { get; set; }
5555

56-
private List<TreeViewItem<TreeFoo>>? SearchItems { get; set; } = TreeFoo.GetTreeItems();
56+
private List<TreeViewItem<TreeFoo>>? MaxItems { get; set; } = TreeFoo.GetTreeItems();
57+
58+
private List<TreeViewItem<TreeFoo>>? SearchItems1 { get; set; } = TreeFoo.GetTreeItems();
59+
60+
private List<TreeViewItem<TreeFoo>>? SearchItems2 { get; set; } = TreeFoo.GetTreeItems();
5761

5862
private List<TreeViewItem<TreeFoo>> VirtualizeItems { get; } = TreeFoo.GetVirtualizeTreeItems();
5963

@@ -89,14 +93,6 @@ private static List<TreeViewItem<TreeFoo>> GetCheckedItems()
8993
return ret;
9094
}
9195

92-
private bool IsReset { get; set; }
93-
94-
private List<SelectedItem> ResetItems { get; } =
95-
[
96-
new("True", "Reset"),
97-
new("False", "Keep")
98-
];
99-
10096
[Inject, NotNull]
10197
private ToastService? ToastService { get; set; }
10298

@@ -226,11 +222,19 @@ private Task SelectedItemOnChanged(SelectedItem selectedItem)
226222
return ret;
227223
}
228224

229-
private Task OnSearchAsync(string searchText)
225+
private static async Task<List<TreeViewItem<TreeFoo>>?> OnSearchAsync(string searchText)
230226
{
231-
SearchItems = string.IsNullOrEmpty(searchText) ? TreeFoo.GetTreeItems() : [];
232-
StateHasChanged();
233-
return Task.CompletedTask;
227+
await Task.Delay(20);
228+
229+
List<TreeViewItem<TreeFoo>>? items = null;
230+
if (!string.IsNullOrEmpty(searchText))
231+
{
232+
items =
233+
[
234+
new TreeViewItem<TreeFoo>(new TreeFoo() { Text = searchText }) { Text = searchText },
235+
];
236+
}
237+
return items;
234238
}
235239

236240
private static async Task<IEnumerable<TreeViewItem<TreeFoo>>> OnExpandVirtualNodeAsync(TreeViewItem<TreeFoo> node)

src/BootstrapBlazor/Components/Select/SelectTree.razor

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<span class="@AppendClassName"><i class="@DropdownIcon"></i></span>
2121
</div>
2222
<div class="dropdown-menu">
23-
<TreeView TItem="TValue" Items="@Items" IsReset="IsReset" ShowIcon="ShowIcon" OnTreeItemClick="OnItemClick" ModelEqualityComparer="@ModelEqualityComparer" CustomKeyAttribute="@CustomKeyAttribute" OnExpandNodeAsync="@OnExpandNodeAsync" />
23+
<TreeView TItem="TValue" Items="@Items" ShowIcon="ShowIcon" OnTreeItemClick="OnItemClick" ModelEqualityComparer="@ModelEqualityComparer" CustomKeyAttribute="@CustomKeyAttribute" OnExpandNodeAsync="@OnExpandNodeAsync" />
2424
</div>
2525
@if (!IsPopover)
2626
{

src/BootstrapBlazor/Components/Select/SelectTree.razor.cs

-6
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,6 @@ public partial class SelectTree<TValue> : IModelEqualityComparer<TValue>
120120
[Parameter]
121121
public bool IsEditable { get; set; }
122122

123-
/// <summary>
124-
/// 获得/设置 页面刷新是否重置已加载数据 默认 false
125-
/// </summary>
126-
[Parameter]
127-
public bool IsReset { get; set; }
128-
129123
[Inject]
130124
[NotNull]
131125
private IStringLocalizer<SelectTree<TValue>>? Localizer { get; set; }

src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs

+36-44
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,7 @@ public partial class TreeView<TItem> : IModelEqualityComparer<TItem>
151151
/// </summary>
152152
/// <remarks>通过设置 <see cref="ShowSearch"/> 开启</remarks>
153153
[Parameter]
154-
public Func<string?, Task>? OnSearchAsync { get; set; }
155-
156-
/// <summary>
157-
/// 获得/设置 页面刷新是否重置已加载数据 默认 false
158-
/// </summary>
159-
[Parameter]
160-
public bool IsReset { get; set; }
154+
public Func<string?, Task<List<TreeViewItem<TItem>>?>>? OnSearchAsync { get; set; }
161155

162156
/// <summary>
163157
/// 获得/设置 带层次数据集合
@@ -345,11 +339,6 @@ protected override void OnParametersSet()
345339
SearchIcon ??= IconTheme.GetIconByKey(ComponentIcons.TreeViewSearchIcon);
346340
ClearSearchIcon ??= IconTheme.GetIconByKey(ComponentIcons.TreeViewResetSearchIcon);
347341
LoadingIcon ??= IconTheme.GetIconByKey(ComponentIcons.TreeViewLoadingIcon);
348-
349-
if (IsReset)
350-
{
351-
_rows = null;
352-
}
353342
}
354343

355344
/// <summary>
@@ -358,35 +347,32 @@ protected override void OnParametersSet()
358347
/// <returns></returns>
359348
protected override async Task OnParametersSetAsync()
360349
{
361-
if (Items == null)
362-
{
363-
// 未提供数据显示 loading
364-
return;
365-
}
366-
367-
if (Items.Count > 0)
350+
if (Items != null)
368351
{
369-
await CheckExpand(Items);
370-
}
352+
if (Items.Count > 0)
353+
{
354+
await CheckExpand(Items);
355+
}
371356

372-
if (ShowCheckbox && (AutoCheckParent || AutoCheckChildren))
373-
{
374-
// 开启 Checkbox 功能时初始化选中节点
375-
TreeNodeStateCache.IsChecked(Items);
376-
}
357+
if (ShowCheckbox && (AutoCheckParent || AutoCheckChildren))
358+
{
359+
// 开启 Checkbox 功能时初始化选中节点
360+
TreeNodeStateCache.IsChecked(Items);
361+
}
377362

378-
// 从数据源中恢复当前 active 节点
379-
if (_activeItem != null)
380-
{
381-
_activeItem = TreeNodeStateCache.Find(Items, _activeItem.Value, out _);
382-
}
363+
// 从数据源中恢复当前 active 节点
364+
if (_activeItem != null)
365+
{
366+
_activeItem = TreeNodeStateCache.Find(Items, _activeItem.Value, out _);
367+
}
383368

384-
if (_init == false)
385-
{
386-
// 设置 ActiveItem 默认值
387-
_activeItem ??= Items.FirstOrDefaultActiveItem();
388-
_activeItem?.SetParentExpand<TreeViewItem<TItem>, TItem>(true);
389-
_init = true;
369+
if (_init == false)
370+
{
371+
// 设置 ActiveItem 默认值
372+
_activeItem ??= Items.FirstOrDefaultActiveItem();
373+
_activeItem?.SetParentExpand<TreeViewItem<TItem>, TItem>(true);
374+
_init = true;
375+
}
390376
}
391377
}
392378

@@ -609,21 +595,25 @@ private async Task OnEnterAsync(string? searchText)
609595

610596
private Task OnEscAsync(string? searchText) => OnClickResetSearch();
611597

598+
private List<TreeViewItem<TItem>>? _searchItems;
599+
612600
private async Task OnClickSearch()
613601
{
614602
if (OnSearchAsync != null)
615603
{
616-
await OnSearchAsync(_searchText);
604+
_searchItems = await OnSearchAsync(_searchText);
605+
_rows = null;
606+
StateHasChanged();
617607
}
618608
}
619609

620-
private async Task OnClickResetSearch()
610+
private Task OnClickResetSearch()
621611
{
622612
_searchText = null;
623-
if (OnSearchAsync != null)
624-
{
625-
await OnSearchAsync(_searchText);
626-
}
613+
_searchItems = null;
614+
_rows = null;
615+
StateHasChanged();
616+
return Task.CompletedTask;
627617
}
628618

629619
/// <summary>
@@ -862,11 +852,13 @@ private List<TreeViewItem<TItem>> Rows
862852
get
863853
{
864854
// 扁平化数据集合
865-
_rows ??= Items.ToFlat<TItem>().ToList();
855+
_rows ??= GetItems().ToFlat<TItem>();
866856
return _rows;
867857
}
868858
}
869859

860+
private List<TreeViewItem<TItem>> GetItems() => _searchItems ?? Items;
861+
870862
private static string? GetTreeRowStyle(TreeViewItem<TItem> item)
871863
{
872864
var level = 0;

src/BootstrapBlazor/Components/TreeView/TreeView.razor.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ export function init(id, options) {
3030

3131
export function scroll(id, options) {
3232
const el = document.getElementById(id);
33-
const item = el.querySelector(".active .tree-node");
33+
const item = el.querySelector(".tree-content.active");
3434
if (item) {
35-
item.scrollIntoView(options ?? { behavior: 'smooth', block: 'start', inline: 'nearest' });
35+
item.parentElement.scrollTo(0, item.offsetTop, options || { behavior: 'smooth' });
3636
}
3737
}
3838

src/BootstrapBlazor/Extensions/TreeViewExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public static class TreeViewExtensions
5050
/// <typeparam name="TItem"></typeparam>
5151
/// <param name="source"></param>
5252
/// <returns></returns>
53-
public static IEnumerable<TreeViewItem<TItem>> ToFlat<TItem>(this IEnumerable<TreeViewItem<TItem>> source)
53+
public static List<TreeViewItem<TItem>> ToFlat<TItem>(this IEnumerable<TreeViewItem<TItem>> source)
5454
{
5555
var rows = new List<TreeViewItem<TItem>>();
5656
if (source != null)

test/UnitTest/Components/SelectTreeTest.cs

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ public void Items_Ok()
1313
var cut = Context.RenderComponent<SelectTree<string>>(builder =>
1414
{
1515
builder.Add(p => p.ShowIcon, true);
16-
builder.Add(p => p.IsReset, false);
1716
builder.Add(p => p.ModelEqualityComparer, (s1, s2) => { return true; });
1817
builder.Add(p => p.OnExpandNodeAsync, (s) => { return Task.FromResult(new List<TreeViewItem<string>>().AsEnumerable()); });
1918
builder.Add(p => p.CustomKeyAttribute, typeof(string));

test/UnitTest/Components/TableTest.cs

-2
Original file line numberDiff line numberDiff line change
@@ -7700,7 +7700,6 @@ public async Task GetValue_LookupServiceKey_Null()
77007700

77017701
var table = cut.FindComponent<MockTable>();
77027702
await cut.InvokeAsync(() => table.Instance.QueryAsync());
7703-
cut.WaitForElement("[data-bs-original-title=\"True\"]");
77047703
}
77057704

77067705
[Fact]
@@ -7728,7 +7727,6 @@ public async Task GetValue_LookupServiceKey_NullText()
77287727

77297728
var table = cut.FindComponent<MockTable>();
77307729
await cut.InvokeAsync(() => table.Instance.QueryAsync());
7731-
cut.WaitForElement("[data-bs-original-title=\"True\"]");
77327730
}
77337731

77347732
[Fact]

0 commit comments

Comments
 (0)