Skip to content

Commit ac8b12d

Browse files
ArgoZhangyhuse
andauthored
refactor(TreeView): redesign TreeNode state cache (#4953)
* chore: bump version 9.19-beta04 Co-Authored-By: Sunny <53289193+yhuse@users.noreply.github.com> * chore: bump version 9.19-beta07 * fix: 修复 scrollTo 语法错误 * refactor: 重构代码 * doc: 更新注释 * doc: 更新示例 * doc: 更新大小写 * doc: 增加追加节点示例 * feat: 移除缓存机制 * doc: 增加 Add Note 示例代码 * doc: 更新手风琴效果示例代码 * doc: 更新 Icon 示例 * doc: 更新 Click 展开节点示例 * doc: 更新表单示例 * refactor: 精简代码 * doc: 更新模板示例 * doc: 删除自定义节点示例 * refactor: 更新 Color 示例 * doc: 更新示例 * test: 更新单元测试 * chore: bump version 9.1.9-beta08 * test: 增加单元测试 * perf: 优化性能 --------- Co-authored-by: Sunny <53289193+yhuse@users.noreply.github.com>
1 parent 5c7e380 commit ac8b12d

File tree

11 files changed

+189
-145
lines changed

11 files changed

+189
-145
lines changed

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

+20-45
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
Introduction="@Localizer["TreeViewNormalIntro"]"
2828
Name="Normal">
2929
<section ignore>@((MarkupString)Localizer["TreeViewNormalDescription"].Value)</section>
30-
<TreeView TItem="TreeFoo" Items="@Items" OnTreeItemClick="@OnTreeItemClick" />
30+
<TreeView Items="@NormalItems" OnTreeItemClick="@OnTreeItemClick" />
3131
<ConsoleLogger @ref="Logger1" />
3232
</DemoBlock>
3333

@@ -45,9 +45,10 @@
4545
</div>
4646
<div class="col-12 col-lg-auto">
4747
<Button Text="@Localizer["TreeViewCheckboxButtonText"]" OnClick="@OnRefresh"></Button>
48+
<Button Text="@Localizer["TreeViewCheckboxAddButtonText"]" OnClick="OnClickAddNode" class="ms-3"></Button>
4849
</div>
4950
</section>
50-
<TreeView TItem="TreeFoo" Items="@CheckedItems" ShowCheckbox="true" OnTreeItemChecked="@OnTreeItemChecked" AutoCheckChildren="@AutoCheckChildren" AutoCheckParent="@AutoCheckParent"></TreeView>
51+
<TreeView Items="@CheckedItems" ShowCheckbox="true" OnTreeItemChecked="@OnTreeItemChecked" AutoCheckChildren="@AutoCheckChildren" AutoCheckParent="@AutoCheckParent"></TreeView>
5152
<ConsoleLogger @ref="Logger2"></ConsoleLogger>
5253
</DemoBlock>
5354

@@ -59,14 +60,14 @@
5960
<Checkbox DisplayText="@Localizer["TreeViewsDisableWholeTreeView"]" ShowAfterLabel="true" @bind-Value="@IsDisabled"></Checkbox>
6061
<Checkbox DisplayText="@Localizer["TreeViewsWhetherToExpandWhenDisable"]" ShowAfterLabel="true" @bind-Value="@DisableCanExpand" class="ms-3"></Checkbox>
6162
</div>
62-
<TreeView TItem="TreeFoo" Items="@DisabledItems" ShowCheckbox="true" IsDisabled="@IsDisabled" CanExpandWhenDisabled="@DisableCanExpand"></TreeView>
63+
<TreeView Items="@DisabledItems" ShowCheckbox="true" IsDisabled="@IsDisabled" CanExpandWhenDisabled="@DisableCanExpand"></TreeView>
6364
</DemoBlock>
6465

6566
<DemoBlock Title="@Localizer["TreeViewAccordionModelTitle"]"
6667
Introduction="@Localizer["TreeViewAccordionModelIntro"]"
6768
Name="AccordionModel">
6869
<section ignore>@((MarkupString)Localizer["TreeViewAccordionModelDescription"].Value)</section>
69-
<TreeView TItem="TreeFoo" Items="@GetAccordionItems()" OnExpandNodeAsync="OnExpandNodeAsync" ShowCheckbox="true" IsAccordion="true"></TreeView>
70+
<TreeView Items="@AccordionItems" OnExpandNodeAsync="TreeFoo.OnExpandAccordionNodeAsync" ShowCheckbox="true" IsAccordion="true"></TreeView>
7071
</DemoBlock>
7172

7273
<DemoBlock Title="@Localizer["TreeViewDefaultExpandTitle"]"
@@ -75,7 +76,7 @@
7576
<section ignore>
7677
@((MarkupString)Localizer["TreeViewDefaultExpandDescription"].Value)
7778
</section>
78-
<TreeView TItem="TreeFoo" Items="@ExpandItems" ShowCheckbox="true"></TreeView>
79+
<TreeView Items="@ExpandItems" ShowCheckbox="true"></TreeView>
7980
</DemoBlock>
8081

8182
<DemoBlock Title="@Localizer["TreeViewTreeDisplayIconTitle"]"
@@ -84,7 +85,7 @@
8485
<section ignore>
8586
@((MarkupString)Localizer["TreeViewTreeDisplayIconDescription"].Value)
8687
</section>
87-
<TreeView TItem="TreeFoo" Items="@GetIconItems()" ShowIcon="true" ShowCheckbox="true"></TreeView>
88+
<TreeView Items="@IconItems" ShowIcon="true" ShowCheckbox="true"></TreeView>
8889
</DemoBlock>
8990

9091
<DemoBlock Title="@Localizer["TreeViewTreeClickExpandTitle"]"
@@ -93,15 +94,15 @@
9394
<section ignore>
9495
@((MarkupString)Localizer["TreeViewTreeClickExpandDescription"].Value)
9596
</section>
96-
<TreeView TItem="TreeFoo" Items="@GetClickExpandItems" ShowIcon="true" ShowCheckbox="true" ClickToggleNode="true"></TreeView>
97+
<TreeView Items="@ClickExpandItems" ShowIcon="true" ShowCheckbox="true" ClickToggleNode="true"></TreeView>
9798
</DemoBlock>
9899

99100
<DemoBlock Title="@Localizer["TreeViewTreeValidationFormTitle"]"
100101
Introduction="@Localizer["TreeViewTreeValidationFormIntro"]"
101102
Name="TreeValidationForm">
102103
<section ignore>@((MarkupString)Localizer["TreeViewTreeValidationFormDescription"].Value)</section>
103104
<ValidateForm Model="@Model">
104-
<TreeView TItem="TreeFoo" Items="@GetFormItems" OnTreeItemClick="@OnFormTreeItemClick" ShowCheckbox="true"></TreeView>
105+
<TreeView Items="@FormItems" OnTreeItemClick="@OnFormTreeItemClick" ShowCheckbox="true"></TreeView>
105106
</ValidateForm>
106107
</DemoBlock>
107108

@@ -111,44 +112,28 @@
111112
<section ignore>
112113
@((MarkupString)Localizer["TreeViewTreeLazyLoadingDescription"].Value)
113114
</section>
114-
<TreeView TItem="TreeFoo" ClickToggleNode="true" Items="@GetLazyItems()" OnExpandNodeAsync="OnExpandNodeAsync"></TreeView>
115+
<TreeView ClickToggleNode="true" Items="@LazyItems" OnExpandNodeAsync="TreeFoo.OnExpandAccordionNodeAsync"></TreeView>
115116
</DemoBlock>
116117

117118
<DemoBlock Title="@Localizer["TreeViewTreeCustomNodeTitle"]"
118119
Introduction="@Localizer["TreeViewTreeCustomNodeIntro"]"
119120
Name="TreeCustomNode">
120-
<TreeView TItem="TreeFoo" ClickToggleNode="true" Items="GetTemplateItems()"></TreeView>
121+
<TreeView ClickToggleNode="true" Items="TemplateItems"></TreeView>
121122
</DemoBlock>
122123

123124
<DemoBlock Title="@Localizer["TreeViewTreeNodeColorTitle"]"
124125
Introduction="@Localizer["TreeViewTreeNodeColorIntro"]"
125126
Name="TreeNodeColor">
126-
<TreeView TItem="TreeFoo" ClickToggleNode="true" Items="GetColorItems()"></TreeView>
127+
<TreeView ClickToggleNode="true" Items="ColorItems"></TreeView>
127128
</DemoBlock>
128129

129130
<DemoBlock Title="@Localizer["TreeViewCheckedItemsTitle"]"
130131
Introduction="@Localizer["TreeViewCheckedItemsIntro"]"
131132
Name="CheckedItems">
132-
<TreeView TItem="TreeFoo" ShowCheckbox="true" Items="@CheckedItems2" OnTreeItemChecked="@OnTreeItemChecked2"></TreeView>
133+
<TreeView ShowCheckbox="true" Items="@CheckedItems2" OnTreeItemChecked="@OnTreeItemChecked2"></TreeView>
133134
<ConsoleLogger @ref="Logger3"></ConsoleLogger>
134135
</DemoBlock>
135136

136-
<DemoBlock Title="@Localizer["TreeViewCustomCheckedItemsTitle"]"
137-
Introduction="@Localizer["TreeViewCustomCheckedItemsIntro"]"
138-
Name="CustomCheckedItems">
139-
<section ignore>
140-
<div>@((MarkupString)Localizer["TreeViewCustomCheckedItemsTips1"].Value)</div>
141-
</section>
142-
<section ignore class="row form-inline">
143-
<div class="col-12 col-lg-auto">
144-
<Checkbox DisplayText="@Localizer["TreeViewCheckboxCheckBoxDisplayText1"]" ShowAfterLabel="true" @bind-Value="@AutoCheckChildren"></Checkbox>
145-
<Checkbox DisplayText="@Localizer["TreeViewCheckboxCheckBoxDisplayText2"]" ShowAfterLabel="true" @bind-Value="@AutoCheckParent" class="ms-3"></Checkbox>
146-
</div>
147-
</section>
148-
<TreeView TItem="TreeFoo" ShowCheckbox="true" ClickToggleNode="true" ClickToggleCheck="false" AutoCheckChildren="@AutoCheckChildren" AutoCheckParent="@AutoCheckParent"
149-
Items="@GetCustomCheckedItems()" OnExpandNodeAsync="CustomCheckedNodeOnExpandNodeAsync"></TreeView>
150-
</DemoBlock>
151-
152137
<DemoBlock Title="@Localizer["TreeViewSetActiveTitle"]"
153138
Introduction="@Localizer["TreeViewSetActiveIntro"]"
154139
Name="SetActive">
@@ -157,41 +142,41 @@
157142
<Select TValue="string" Items="SelectedItems" OnSelectedItemChanged="SelectedItemOnChanged" ShowLabel="true" DisplayText="@Localizer["TreeViewSetActiveDisplayText"]"></Select>
158143
</div>
159144
</section>
160-
<TreeView @ref="SetActiveTreeView" TItem="TreeFoo" Items="@Items" OnTreeItemClick="@OnTreeItemClick"></TreeView>
145+
<TreeView @ref="SetActiveTreeView" Items="@Items" OnTreeItemClick="@OnTreeItemClick"></TreeView>
161146
</DemoBlock>
162147

163148
<DemoBlock Title="@Localizer["TreeViewShowSkeletonTitle"]"
164149
Introduction="@Localizer["TreeViewShowSkeletonIntro"]"
165150
Name="ShowSkeleton">
166151
<Button Text="@Localizer["TreeViewShowSkeletonButtonText"]" IsAsync="true" Icon="fa-solid fa-font-awesome" OnClick="@OnLoadAsyncItems"></Button>
167-
<TreeView TItem="TreeFoo" Items="@AsyncItems" ShowSkeleton="true" OnExpandNodeAsync="OnExpandNodeAsync" class="mt-3"></TreeView>
152+
<TreeView Items="@AsyncItems" ShowSkeleton="true" OnExpandNodeAsync="OnExpandNodeAsync" class="mt-3"></TreeView>
168153
</DemoBlock>
169154

170155
<DemoBlock Title="@Localizer["TreeViewShowSearchTitle"]"
171156
Introduction="@Localizer["TreeViewShowSearchIntro"]"
172157
Name="ShowSearch">
173-
<TreeView TItem="TreeFoo" Items="@SearchItems1" ShowSearch="true" OnSearchAsync="@OnSearchAsync"></TreeView>
158+
<TreeView Items="@SearchItems1" ShowSearch="true" OnSearchAsync="@OnSearchAsync"></TreeView>
174159
</DemoBlock>
175160

176161
<DemoBlock Title="@Localizer["TreeViewFixedSearchTitle"]"
177162
Introduction="@Localizer["TreeViewFixedSearchIntro"]"
178163
Name="IsFixedSearch">
179-
<TreeView TItem="TreeFoo" Items="@SearchItems2" ShowSearch="true" OnSearchAsync="@OnSearchAsync" IsFixedSearch="true" style="height: 180px;"></TreeView>
164+
<TreeView Items="@SearchItems2" ShowSearch="true" OnSearchAsync="@OnSearchAsync" IsFixedSearch="true" style="height: 180px;"></TreeView>
180165
</DemoBlock>
181166

182167
<DemoBlock Title="@Localizer["TreeViewMaxSelectedCountTitle"]"
183168
Introduction="@Localizer["TreeViewMaxSelectedCountIntro"]"
184169
Name="MaxSelectedCount">
185170
<section ignore>@((MarkupString)Localizer["TreeViewMaxSelectedCountDesc"].Value)</section>
186-
<TreeView TItem="TreeFoo" Items="@MaxItems" ShowCheckbox="true" AutoCheckChildren="true" AutoCheckParent="true"
171+
<TreeView Items="@MaxItems" ShowCheckbox="true" AutoCheckChildren="true" AutoCheckParent="true"
187172
MaxSelectedCount="2" OnMaxSelectedCountExceed="OnMaxSelectedCountExceed"></TreeView>
188173
</DemoBlock>
189174

190175
<DemoBlock Title="@Localizer["TreeViewEnableKeyboardArrowUpDownTitle"]"
191176
Introduction="@Localizer["TreeViewEnableKeyboardArrowUpDownIntro"]"
192177
Name="EnableKeyboard">
193178
<section ignore>@_selectedValue</section>
194-
<TreeView TItem="TreeFoo" Items="@KeyboardItems" OnTreeItemClick="@OnTreeItemKeyboardClick" style="height: 160px;"
179+
<TreeView Items="@KeyboardItems" OnTreeItemClick="@OnTreeItemKeyboardClick" style="height: 160px;"
195180
EnableKeyboard="true" ClickToggleNode="false" ClickToggleCheck="false" ShowCheckbox="true" />
196181
</DemoBlock>
197182

@@ -202,22 +187,12 @@
202187
@((MarkupString)Localizer["TreeViewVirtualizeDescription"].Value)
203188
</section>
204189
<div style="height: 400px">
205-
<TreeView TItem="TreeFoo" Items="@VirtualizeItems" ShowCheckbox="true" IsVirtualize="true"
190+
<TreeView Items="@VirtualizeItems" ShowCheckbox="true" IsVirtualize="true"
206191
AutoCheckChildren="true" AutoCheckParent="true"
207192
OnExpandNodeAsync="OnExpandVirtualNodeAsync"></TreeView>
208193
</div>
209194
</DemoBlock>
210195

211-
@* <DemoBlock Title="@Localizer["TreeViewFlatTitle"]"
212-
Introduction="@Localizer["TreeViewFlatIntro"]"
213-
Name="FlatItems">
214-
<section ignore>
215-
@((MarkupString)Localizer["TreeViewFlatItemsDescription"].Value)
216-
</section>
217-
<TreeView TItem="TreeFoo" Items="@FlatItems" ShowCheckbox="true"
218-
AutoCheckChildren="true" AutoCheckParent="true"></TreeView>
219-
</DemoBlock>
220-
*@
221196
<AttributeTable Items="@GetAttributes()"></AttributeTable>
222197

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

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

+19-84
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ public sealed partial class TreeViews
2323
private ConsoleLogger? Logger3 { get; set; }
2424

2525
private bool DisableCanExpand { get; set; }
26+
2627
private bool IsDisabled { get; set; }
2728

29+
private List<TreeViewItem<TreeFoo>> NormalItems { get; } = TreeFoo.GetTreeItems();
30+
2831
private List<TreeViewItem<TreeFoo>> Items { get; } = TreeFoo.GetTreeItems();
2932

3033
private bool AutoCheckChildren { get; set; }
@@ -33,15 +36,17 @@ public sealed partial class TreeViews
3336

3437
private List<TreeViewItem<TreeFoo>> DisabledItems { get; } = GetDisabledItems();
3538

39+
private List<TreeViewItem<TreeFoo>>? AccordionItems { get; } = TreeFoo.GetAccordionItems();
40+
3641
private List<TreeViewItem<TreeFoo>> ExpandItems { get; } = GetExpandItems();
3742

3843
private List<TreeViewItem<TreeFoo>> CheckedItems { get; set; } = GetCheckedItems();
3944

40-
private static List<TreeViewItem<TreeFoo>> GetIconItems() => TreeFoo.GetTreeItems();
45+
private static List<TreeViewItem<TreeFoo>> IconItems { get; set; } = TreeFoo.GetTreeItems();
4146

42-
private List<TreeViewItem<TreeFoo>> GetClickExpandItems { get; } = TreeFoo.GetTreeItems();
47+
private List<TreeViewItem<TreeFoo>> ClickExpandItems { get; } = TreeFoo.GetTreeItems();
4348

44-
private List<TreeViewItem<TreeFoo>> GetFormItems { get; } = TreeFoo.GetTreeItems();
49+
private List<TreeViewItem<TreeFoo>> FormItems { get; } = TreeFoo.GetTreeItems();
4550

4651
private List<TreeViewItem<TreeFoo>> CheckedItems2 { get; } = TreeFoo.GetTreeItems();
4752

@@ -61,7 +66,11 @@ public sealed partial class TreeViews
6166

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

64-
private List<TreeViewItem<TreeFoo>> FlatItems { get; } = TreeFoo.GetFlatItems();
69+
private List<TreeViewItem<TreeFoo>> LazyItems { get; } = TreeFoo.GetLazyItems();
70+
71+
private List<TreeViewItem<TreeFoo>> ColorItems { get; } = TreeFoo.GetColorItems();
72+
73+
private List<TreeViewItem<TreeFoo>> TemplateItems { get; } = TreeFoo.GetTemplateItems();
6574

6675
private Foo Model => Foo.Generate(LocalizerFoo);
6776

@@ -85,6 +94,11 @@ private void OnRefresh()
8594
CheckedItems = GetCheckedItems();
8695
}
8796

97+
private void OnClickAddNode()
98+
{
99+
CheckedItems.Add(new TreeViewItem<TreeFoo>(new TreeFoo() { Id = $"Id-{DateTime.Now.Ticks}" }) { Text = DateTime.Now.ToString() });
100+
}
101+
88102
private static List<TreeViewItem<TreeFoo>> GetCheckedItems()
89103
{
90104
var ret = TreeFoo.GetTreeItems();
@@ -114,35 +128,13 @@ private static List<TreeViewItem<TreeFoo>> GetDisabledItems()
114128
return ret;
115129
}
116130

117-
private static List<TreeViewItem<TreeFoo>> GetAccordionItems()
118-
{
119-
var ret = TreeFoo.GetTreeItems();
120-
ret[1].Items[0].HasChildren = true;
121-
return ret;
122-
}
123-
124131
private static async Task<IEnumerable<TreeViewItem<TreeFoo>>> OnExpandNodeAsync(TreeViewItem<TreeFoo> node)
125132
{
126-
await Task.Delay(800);
133+
await Task.Delay(200);
127134
var item = node.Value;
128135
return new TreeViewItem<TreeFoo>[] { new(new TreeFoo() { Id = $"{item.Id}-101", ParentId = item.Id }) { Text = "懒加载子节点1", HasChildren = true }, new(new TreeFoo() { Id = $"{item.Id}-102", ParentId = item.Id }) { Text = "懒加载子节点2" } };
129136
}
130137

131-
private static async Task<IEnumerable<TreeViewItem<TreeFoo>>> CustomCheckedNodeOnExpandNodeAsync(TreeViewItem<TreeFoo> node)
132-
{
133-
await Task.Delay(800);
134-
var item = node.Value;
135-
return TreeFoo.GetCheckedTreeItems(item.Id);
136-
}
137-
138-
private static List<TreeViewItem<TreeFoo>> GetCustomCheckedItems()
139-
{
140-
var ret = TreeFoo.GetCheckedTreeItems();
141-
ret[0].IsExpand = true;
142-
ret[0].Items = TreeFoo.GetCheckedTreeItems();
143-
return ret;
144-
}
145-
146138
private static List<TreeViewItem<TreeFoo>> GetExpandItems()
147139
{
148140
var ret = TreeFoo.GetTreeItems();
@@ -155,31 +147,6 @@ private static Task OnFormTreeItemClick(TreeViewItem<TreeFoo> item)
155147
return Task.CompletedTask;
156148
}
157149

158-
private static List<TreeViewItem<TreeFoo>> GetLazyItems()
159-
{
160-
var ret = TreeFoo.GetTreeItems();
161-
ret[1].Items[0].IsExpand = true;
162-
ret[2].Text = "懒加载延时";
163-
ret[2].HasChildren = true;
164-
return ret;
165-
}
166-
167-
private static List<TreeViewItem<TreeFoo>> GetTemplateItems()
168-
{
169-
var ret = TreeFoo.GetTreeItems();
170-
ret[0].Template = foo => BootstrapDynamicComponent.CreateComponent<CustomerTreeItem>(new Dictionary<string, object?>() { [nameof(CustomerTreeItem.Foo)] = foo }).Render();
171-
return ret;
172-
}
173-
174-
private static List<TreeViewItem<TreeFoo>> GetColorItems()
175-
{
176-
var ret = TreeFoo.GetTreeItems();
177-
ret[0].CssClass = "text-primary";
178-
ret[1].CssClass = "text-success";
179-
ret[2].CssClass = "text-danger";
180-
return ret;
181-
}
182-
183150
private Task OnTreeItemChecked2(List<TreeViewItem<TreeFoo>> items)
184151
{
185152
Logger3.Log($"当前共选中{items.Count}项");
@@ -249,38 +216,6 @@ private static async Task<IEnumerable<TreeViewItem<TreeFoo>>> OnExpandVirtualNod
249216
return items;
250217
}
251218

252-
private class CustomerTreeItem : ComponentBase
253-
{
254-
[Inject]
255-
[NotNull]
256-
private ToastService? ToastService { get; set; }
257-
258-
[Parameter]
259-
[NotNull]
260-
public TreeFoo? Foo { get; set; }
261-
262-
/// <summary>
263-
/// BuildRenderTree
264-
/// </summary>
265-
/// <param name="builder"></param>
266-
protected override void BuildRenderTree(RenderTreeBuilder builder)
267-
{
268-
builder.OpenElement(3, "span");
269-
builder.AddAttribute(4, "class", "me-3");
270-
builder.AddContent(5, Foo.Text);
271-
builder.CloseElement();
272-
273-
builder.OpenComponent<Button>(0);
274-
builder.AddAttribute(1, nameof(Button.Icon), "fa-solid fa-font-awesome");
275-
builder.AddAttribute(2, nameof(Button.Text), "Click");
276-
builder.AddAttribute(3, nameof(Button.OnClick), EventCallback.Factory.Create<MouseEventArgs>(this, e =>
277-
{
278-
ToastService.Warning("自定义 TreeViewItem", "测试 TreeViewItem 按钮点击事件");
279-
}));
280-
builder.CloseComponent();
281-
}
282-
}
283-
284219
/// <summary>
285220
/// 获得属性方法
286221
/// </summary>

0 commit comments

Comments
 (0)