Skip to content

Commit 61ca51d

Browse files
authored
feat(SelectCity): add ShowSearch parameter (#610)
* refactor: 规范样式名称 * feat: 增加 ShowSearch 参数 * refactor: 增加搜索功能 * feat: 内置拼音搜索 * refactor: 增加数据结构 * refactor: 增加拼音服务 * doc: 移除 value 值 * feat: 增加搜索逻辑 * feat: 增加搜索预览样式 * feat: 增加搜索栏自动获得焦点功能 * feat: 完善搜索功能 * refactor: 处理重庆无法搜索问题 * chore: bump version 9.0.4
1 parent dacbe4c commit 61ca51d

13 files changed

Lines changed: 764 additions & 96 deletions

src/components/BootstrapBlazor.Region/BootstrapBlazor.Region.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<Version>9.0.3</Version>
4+
<Version>9.0.4</Version>
55
</PropertyGroup>
66

77
<PropertyGroup>
Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@namespace BootstrapBlazor.Components
22
@inherits SelectRegionBase
3-
@attribute [JSModuleAutoLoader("./_content/BootstrapBlazor.Region/Components/SelectRegion.razor.js")]
3+
@attribute [JSModuleAutoLoader("./_content/BootstrapBlazor.Region/Components/SelectCity.razor.js", JSObjectReference = true)]
44

55
@if (IsShowLabel)
66
{
@@ -15,18 +15,34 @@
1515
{
1616
<span class="@ClearClassString" @onclick="OnClearValue"><i class="@ClearIcon"></i></span>
1717
}
18-
<div class="dropdown-menu">
19-
@foreach (var item in GetProvinces())
18+
<div class="dropdown-menu dropdown-menu-city">
19+
@if (ShowSearch)
2020
{
21-
<div class="bb-region-city-item">
22-
<div class="bb-region-city-title" @onclick="() => OnSelectProvince(item)">@item</div>
23-
<ul>
24-
@foreach (var city in GetCities(item))
25-
{
26-
<li class="@GetActiveClass(city)" @onclick="() => OnSelectCity(city)">@city</li>
27-
}
28-
</ul>
21+
<div class="dropdown-menu-search">
22+
<input type="text" class="search-text form-control" autocomplete="off" aria-label="search" />
23+
<i class="@SearchIconString"></i>
24+
<i class="@ClearIconString"></i>
2925
</div>
26+
<div class="dropdown-menu-search-body">
27+
@RenderCities()
28+
</div>
29+
}
30+
else
31+
{
32+
@RenderCities()
3033
}
3134
</div>
3235
</div>
36+
37+
@code {
38+
RenderFragment RenderItem(string item) =>
39+
@<div class="bb-region-city-item">
40+
<div class="bb-region-city-title" @onclick="() => OnSelectProvince(item)">@item</div>
41+
<ul>
42+
@foreach (var city in GetCities(item))
43+
{
44+
<li class="@GetActiveClass(city)" @onclick="() => OnSelectCity(city)">@city</li>
45+
}
46+
</ul>
47+
</div>;
48+
}

src/components/BootstrapBlazor.Region/Components/SelectCity.razor.cs

Lines changed: 154 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
// Website: https://www.blazor.zone or https://argozhang.github.io/
44

5+
using System.Runtime.CompilerServices;
6+
57
namespace BootstrapBlazor.Components;
68

79
/// <summary>
@@ -15,14 +17,75 @@ public partial class SelectCity
1517
[Parameter]
1618
public bool IsMultiple { get; set; }
1719

20+
/// <summary>
21+
/// 获得/设置 是否开启搜索功能 默认 true 开启
22+
/// </summary>
23+
[Parameter]
24+
public bool ShowSearch { get; set; } = true;
25+
26+
/// <summary>
27+
/// Gets or sets the search icon.
28+
/// </summary>
29+
[Parameter]
30+
public string? SearchIcon { get; set; }
31+
32+
/// <summary>
33+
/// 获得/设置 单选时选择后是否自动关闭 默认 true
34+
/// </summary>
35+
[Parameter]
36+
public bool AutoClose { get; set; } = true;
37+
1838
private string? ClassString => CssBuilder.Default("select bb-city")
1939
.AddClass("disabled", IsDisabled)
2040
.AddClassFromAttributes(AdditionalAttributes)
2141
.Build();
2242

43+
private string? SearchIconString => CssBuilder.Default("icon search-icon")
44+
.AddClass(SearchIcon)
45+
.Build();
46+
47+
private string? ClearIconString => CssBuilder.Default("icon clear-icon")
48+
.AddClass(ClearIcon)
49+
.Build();
50+
2351
private readonly HashSet<string> _values = [];
52+
private string? _searchText;
53+
54+
private string? GetActiveClass(string item) => CssBuilder.Default()
55+
.AddClass("active", _values.Contains(item) || CurrentValue == item)
56+
.AddClass("prev", !string.IsNullOrEmpty(_searchText) && PinYinService.GetFirstLetters(item).StartsWith(_searchText))
57+
.Build();
2458

25-
private string? GetActiveClass(string item) => _values.Contains(item) || CurrentValue == item ? "active" : null;
59+
/// <summary>
60+
/// <inheritdoc/>
61+
/// </summary>
62+
protected override void OnParametersSet()
63+
{
64+
base.OnParametersSet();
65+
66+
SearchIcon ??= IconTheme.GetIconByKey(ComponentIcons.SelectSearchIcon);
67+
}
68+
69+
/// <summary>
70+
/// <inheritdoc/>
71+
/// </summary>
72+
/// <returns></returns>
73+
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, new
74+
{
75+
TriggerSearch = nameof(TriggerSearch)
76+
});
77+
78+
/// <summary>
79+
/// 触发过滤方法 由 JavaScript 触发
80+
/// </summary>
81+
/// <param name="v"></param>
82+
/// <returns></returns>
83+
[JSInvokable]
84+
public void TriggerSearch(string v)
85+
{
86+
_searchText = v.ToUpperInvariant();
87+
StateHasChanged();
88+
}
2689

2790
private async Task OnClearValue()
2891
{
@@ -38,18 +101,26 @@ private async Task OnClearValue()
38101
}
39102
}
40103

41-
private void OnSelectProvince(string province)
104+
private RenderFragment RenderCities() => builder =>
105+
{
106+
foreach (var item in GetProvinces())
107+
{
108+
builder.AddContent(0, RenderItem(item));
109+
}
110+
};
111+
112+
private void OnSelectProvince(string provinceName)
42113
{
43114
if (!IsMultiple)
44115
{
45116
return;
46117
}
47118

48-
HashSet<string> cities = province switch
119+
HashSet<string> cities = provinceName switch
49120
{
50121
"直辖市" => Municipalities,
51122
"特别行政区" => SpecialAdministrativeRegions,
52-
_ => GetCities(province)
123+
_ => GetCities(provinceName)
53124
};
54125
foreach (var city in cities.Where(city => !_values.Remove(city)))
55126
{
@@ -58,7 +129,7 @@ private void OnSelectProvince(string province)
58129
CurrentValue = string.Join(",", _values);
59130
}
60131

61-
private void OnSelectCity(string item)
132+
private async Task OnSelectCity(string item)
62133
{
63134
if (IsMultiple)
64135
{
@@ -72,61 +143,93 @@ private void OnSelectCity(string item)
72143
{
73144
CurrentValue = item;
74145
}
75-
}
76146

77-
private static HashSet<string> GetProvinces()
78-
{
79-
return
80-
[
81-
"直辖市",
82-
"河北省",
83-
"山西省",
84-
"辽宁省",
85-
"吉林省",
86-
"黑龙江省",
87-
"江苏省",
88-
"浙江省",
89-
"安徽省",
90-
"福建省",
91-
"江西省",
92-
"山东省",
93-
"河南省",
94-
"湖北省",
95-
"湖南省",
96-
"广东省",
97-
"海南省",
98-
"四川省",
99-
"贵州省",
100-
"云南省",
101-
"陕西省",
102-
"甘肃省",
103-
"青海省",
104-
"内蒙古自治区",
105-
"广西壮族自治区",
106-
"西藏自治区",
107-
"宁夏回族自治区",
108-
"新疆维吾尔自治区",
109-
"台湾省",
110-
"特别行政区"
111-
];
147+
if (AutoClose)
148+
{
149+
await InvokeVoidAsync("hide", Id);
150+
}
112151
}
113152

114-
private static readonly HashSet<string> Municipalities = ["北京市", "天津市", "上海市", "重庆市"];
115-
116-
private static readonly HashSet<string> SpecialAdministrativeRegions = ["香港特别行政区", "澳门特别行政区"];
117-
118-
private HashSet<string> GetCities(string provinceName)
153+
private HashSet<string> GetProvinces()
119154
{
120-
if (provinceName == "直辖市")
155+
if (string.IsNullOrEmpty(_searchText))
121156
{
122-
return Municipalities;
157+
return Provinces;
123158
}
124159

125-
if (provinceName == "特别行政区")
160+
if (IsChinese(_searchText))
126161
{
127-
return SpecialAdministrativeRegions;
162+
return [.. Provinces.Where(i => i.Contains(_searchText) || GetCities(i).Any(city => city.Contains(_searchText)))];
128163
}
129164

130-
return RegionService.GetCities(provinceName);
165+
return [.. GenerateProvincePinYin().Where(i => FilterProvince(i, _searchText)).Select(i => i.Name)];
131166
}
167+
168+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
169+
private static bool FilterProvince(ProvinceItem item, string searchText) => item.PinYin.StartsWith(searchText) || item.Cities.Any(city => city.PinYin.StartsWith(searchText));
170+
171+
private static HashSet<ProvinceItem>? _provinceItems;
172+
173+
private HashSet<ProvinceItem> GenerateProvincePinYin()
174+
{
175+
_provinceItems ??= [.. Provinces.Select(i => new ProvinceItem()
176+
{
177+
PinYin = PinYinService.GetFirstLetters(i),
178+
Name = i,
179+
Cities = GenerateCityPinYin(i)
180+
})];
181+
return _provinceItems;
182+
}
183+
184+
private HashSet<string> GetCities(string provinceName) => provinceName switch
185+
{
186+
"直辖市" => Municipalities,
187+
"特别行政区" => SpecialAdministrativeRegions,
188+
_ => RegionService.GetCities(provinceName)
189+
};
190+
191+
private HashSet<CityItem> GenerateCityPinYin(string provinceName) => [.. GetCities(provinceName).Select(i => new CityItem()
192+
{
193+
PinYin = PinYinService.GetFirstLetters(i),
194+
Name = i
195+
})];
196+
197+
private bool IsChinese(string text) => text.Any(i => i >= 0x4E00 && i <= 0x9FFF);
198+
199+
private static readonly HashSet<string> Provinces = [
200+
"直辖市",
201+
"河北省",
202+
"山西省",
203+
"辽宁省",
204+
"吉林省",
205+
"黑龙江省",
206+
"江苏省",
207+
"浙江省",
208+
"安徽省",
209+
"福建省",
210+
"江西省",
211+
"山东省",
212+
"河南省",
213+
"湖北省",
214+
"湖南省",
215+
"广东省",
216+
"海南省",
217+
"四川省",
218+
"贵州省",
219+
"云南省",
220+
"陕西省",
221+
"甘肃省",
222+
"青海省",
223+
"内蒙古自治区",
224+
"广西壮族自治区",
225+
"西藏自治区",
226+
"宁夏回族自治区",
227+
"新疆维吾尔自治区",
228+
"台湾省",
229+
"特别行政区"
230+
];
231+
232+
private static readonly HashSet<string> Municipalities = ["北京市", "天津市", "上海市", "重庆市"];
233+
234+
private static readonly HashSet<string> SpecialAdministrativeRegions = ["香港特别行政区", "澳门特别行政区"];
132235
}

0 commit comments

Comments
 (0)