Skip to content

Commit f947524

Browse files
committed
feat: 实现卡片式切换逻辑
1 parent 942db8a commit f947524

5 files changed

Lines changed: 293 additions & 51 deletions

File tree

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

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,99 @@
88
}
99
<div @attributes="AdditionalAttributes" id="@Id" class="@ClassString">
1010
<div class="dropdown-toggle" data-bs-toggle="bb.dropdown" data-bs-placement="@PlacementString" data-bs-offset="@OffsetString" data-bs-custom-class="@CustomClassString">
11-
<input type="text" id="@InputId" disabled="@Disabled" readonly placeholder="@PlaceHolder" class="@InputClassString" value="@CurrentValue" />
11+
<input type="text" id="@InputId" disabled="@Disabled" readonly placeholder="@PlaceHolder" class="@InputClassString" value="@CurrentValueAsString" />
1212
<span class="@AppendClassString"><i class="@DropdownIcon"></i></span>
1313
</div>
14-
@if (GetClearable())
14+
@if (!IsDisabled)
1515
{
1616
<span class="@ClearClassString" @onclick="OnClearValue"><i class="@ClearIcon"></i></span>
1717
}
1818
<div class="dropdown-menu">
19-
<div class="bb-region-header">
20-
<ul>
21-
<li>省/直辖市</li>
22-
<li>市</li>
23-
<li>区/县</li>
24-
<li>乡/镇/街道</li>
25-
</ul>
26-
</div>
19+
<div class="bb-region-header">
20+
<ul>
21+
<li class="@GetHeaderActiveClass(RegionViewMode.Province)" @onclick="@OnSwitchProvinceView">省/直辖市</li>
22+
<li class="@GetHeaderActiveClass(RegionViewMode.City)" @onclick="@OnSwitchCityView">市</li>
23+
<li class="@GetHeaderActiveClass(RegionViewMode.County)" @onclick="@OnSwitchCountyView">区/县</li>
24+
<li class="@GetHeaderActiveClass(RegionViewMode.Detail)" @onclick="@OnSwitchDetailView">乡/镇/街道</li>
25+
</ul>
26+
</div>
2727
<div class="bb-region-body">
28-
@RenderProvinces()
28+
<div class="@GetBodyActiveClass(RegionViewMode.Province)">
29+
@RenderProvinces()
30+
</div>
31+
<div class="@GetBodyActiveClass(RegionViewMode.City)">
32+
@RenderCities()
33+
</div>
34+
<div class="@GetBodyActiveClass(RegionViewMode.County)">
35+
@RenderCounties()
36+
</div>
37+
<div class="@GetBodyActiveClass(RegionViewMode.Detail)">
38+
@RenderDetails()
39+
</div>
2940
</div>
3041
</div>
3142
</div>
3243

3344
@code {
3445
RenderFragment RenderProvinces() =>
46+
@<ul>
47+
@foreach(var item in GetProvinces())
48+
{
49+
<li class="@GetProvinceActiveClass(item)" @onclick="() => OnClickProvince(item)">@item</li>
50+
}
51+
</ul>;
52+
53+
RenderFragment RenderCities() =>
54+
@<ul class="bb-region-province">
55+
@{
56+
var items = GetCities();
57+
}
58+
@if (items.Count == 0)
59+
{
60+
<li>无匹配项目</li>
61+
}
62+
else
63+
{
64+
foreach(var item in items)
65+
{
66+
<li class="@GetCityActiveClass(item)" @onclick="() => OnClickCity(item)">@item</li>
67+
}
68+
}
69+
</ul>;
70+
71+
RenderFragment RenderCounties() =>
72+
@<ul class="bb-region-province">
73+
@{
74+
var items = GetCounties();
75+
}
76+
@if (items.Count == 0)
77+
{
78+
<li>无匹配项目</li>
79+
}
80+
else
81+
{
82+
@foreach(var item in items)
83+
{
84+
<li class="@GetCountyActiveClass(item)" @onclick="() => OnClickCounty(item)">@item.Name</li>
85+
}
86+
}
87+
</ul>;
88+
89+
RenderFragment RenderDetails() =>
3590
@<ul class="bb-region-province">
36-
@foreach(var item in RegionService.GetProvinces())
91+
@{
92+
var items = GetDetails();
93+
}
94+
@if (items.Count == 0)
95+
{
96+
<li>无匹配项目</li>
97+
}
98+
else
3799
{
38-
<li>@item</li>
100+
@foreach(var item in items)
101+
{
102+
<li class="@GetDetailActiveClass(item)" @onclick="() => OnClickDetail(item)">@item</li>
103+
}
39104
}
40105
</ul>;
41106
}

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

Lines changed: 121 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,6 @@ public partial class Region
4343
[NotNull]
4444
public string? ClearIcon { get; set; }
4545

46-
/// <summary>
47-
/// Gets or sets whether the select component is clearable. Default is false.
48-
/// </summary>
49-
[Parameter]
50-
public bool IsClearable { get; set; }
51-
5246
/// <summary>
5347
/// Gets or sets the <see cref="IIconTheme"/> service instance.
5448
/// </summary>
@@ -79,14 +73,36 @@ public partial class Region
7973
.AddClass($"text-danger", IsValid.HasValue && !IsValid.Value)
8074
.Build();
8175

82-
private bool GetClearable() => IsClearable && !IsDisabled;
83-
8476
private string? ClearClassString => CssBuilder.Default("clear-icon")
8577
.AddClass($"text-{Color.ToDescriptionString()}", Color != Color.None)
8678
.AddClass($"text-success", IsValid.HasValue && IsValid.Value)
8779
.AddClass($"text-danger", IsValid.HasValue && !IsValid.Value)
8880
.Build();
8981

82+
private string? GetHeaderActiveClass(RegionViewMode type) => _currentViewMode == type ? "active" : null;
83+
84+
private string? GetBodyActiveClass(RegionViewMode type) => CssBuilder.Default("bb-region-body-item")
85+
.AddClass("active", _currentViewMode == type)
86+
.Build();
87+
88+
private string? GetProvinceActiveClass(string item) => _provinceValue == item ? "active" : null;
89+
90+
private string? GetCityActiveClass(string item) => _cityValue == item ? "active" : null;
91+
92+
private string? GetCountyActiveClass(CountyItem item) => _countyValue == item ? "active" : null;
93+
94+
private string? GetDetailActiveClass(string item) => _detailValue == item ? "active" : null;
95+
96+
private RegionViewMode _currentViewMode = RegionViewMode.Province;
97+
98+
private string? _provinceValue;
99+
100+
private string? _cityValue;
101+
102+
private CountyItem _countyValue;
103+
104+
private string? _detailValue;
105+
90106
/// <summary>
91107
/// <inheritdoc/>
92108
/// </summary>
@@ -98,8 +114,104 @@ protected override void OnParametersSet()
98114
ClearIcon ??= IconTheme.GetIconByKey(ComponentIcons.SelectClearIcon);
99115
}
100116

117+
/// <summary>
118+
/// <inheritdoc/>
119+
/// </summary>
120+
/// <param name="value"></param>
121+
/// <returns></returns>
122+
protected override string? FormatValueAsString(string? value) => $"{_provinceValue}{_cityValue}{_countyValue.Name}{_detailValue}";
123+
101124
private void OnClearValue()
102125
{
103-
CurrentValue = "";
126+
_provinceValue = "";
127+
_cityValue = "";
128+
_countyValue = new();
129+
_detailValue = "";
130+
131+
_currentViewMode = RegionViewMode.Province;
132+
}
133+
134+
private IReadOnlySet<string> GetProvinces() => RegionService.GetProvinces();
135+
136+
private IReadOnlySet<string> GetCities()
137+
{
138+
if (string.IsNullOrEmpty(_provinceValue))
139+
{
140+
return new HashSet<string>();
141+
}
142+
143+
return RegionService.GetCities(_provinceValue);
144+
}
145+
146+
private IReadOnlySet<CountyItem> GetCounties()
147+
{
148+
if (string.IsNullOrEmpty(_cityValue))
149+
{
150+
return new HashSet<CountyItem>();
151+
}
152+
153+
return RegionService.GetCounties(_cityValue);
154+
}
155+
156+
private IReadOnlySet<string> GetDetails()
157+
{
158+
if (string.IsNullOrEmpty(_countyValue.Code))
159+
{
160+
return new HashSet<string>();
161+
}
162+
163+
return RegionService.GetDetails(_countyValue.Code);
164+
}
165+
166+
private void OnClickProvince(string value)
167+
{
168+
_provinceValue = value;
169+
_currentViewMode = RegionViewMode.City;
170+
}
171+
172+
private void OnClickCity(string value)
173+
{
174+
_cityValue = value;
175+
_currentViewMode = RegionViewMode.County;
176+
}
177+
178+
private void OnClickCounty(CountyItem item)
179+
{
180+
_countyValue = item;
181+
_currentViewMode = RegionViewMode.Detail;
182+
}
183+
184+
private async Task OnClickDetail(string value)
185+
{
186+
_detailValue = value;
187+
_currentViewMode = RegionViewMode.Detail;
188+
189+
await InvokeVoidAsync("hide", Id);
190+
}
191+
192+
private void OnSwitchProvinceView()
193+
{
194+
_currentViewMode = RegionViewMode.Province;
195+
_cityValue = "";
196+
_countyValue = new();
197+
_detailValue = "";
198+
}
199+
200+
private void OnSwitchCityView()
201+
{
202+
_currentViewMode = RegionViewMode.City;
203+
_countyValue = new();
204+
_detailValue = "";
205+
}
206+
207+
private void OnSwitchCountyView()
208+
{
209+
_currentViewMode = RegionViewMode.County;
210+
_detailValue = "";
211+
}
212+
213+
private void OnSwitchDetailView()
214+
{
215+
_currentViewMode = RegionViewMode.Detail;
104216
}
105217
}

src/components/BootstrapBlazor.Region/Components/Region.razor.css

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,77 @@
22
position: relative;
33
}
44

5-
.bb-region-province {
6-
display: flex;
7-
flex-wrap: wrap;
8-
gap: 5px;
9-
}
10-
11-
.bb-region-province > li {
12-
padding: 3px 12px;
13-
border-radius: var(--bs-border-radius);
14-
cursor: pointer;
5+
.bb-region:not(.disabled):hover .form-select-append {
6+
display: none;
157
}
168

17-
.bb-region-province > li:hover {
18-
background-color: var(--bs-secondary-bg-subtle);
19-
}
20-
219
.popover-region ul {
2210
margin: 0;
2311
padding: 0;
2412
}
2513

2614
.popover-region li {
2715
list-style: none;
16+
transition: background-color .3s linear, color .3s linear;
2817
}
2918

30-
.bb-region-header ul {
31-
display: flex;
32-
justify-content: space-between;
33-
flex-wrap: nowrap;
34-
padding: .5rem;
35-
font-weight: bold;
19+
.bb-region-header {
20+
width: 400px;
21+
border-bottom: 1px solid var(--bs-border-color);
3622
}
3723

38-
.bb-region-header ul li {
39-
cursor: pointer;
40-
padding: 3px 12px;
41-
border-radius: var(--bs-border-radius);
24+
.bb-region-header ul {
25+
display: flex;
26+
justify-content: space-between;
27+
flex-wrap: nowrap;
28+
padding: .5rem;
29+
font-weight: bold;
4230
}
4331

44-
.bb-region-header ul li:hover {
45-
background-color: var(--bs-secondary-bg-subtle);
32+
.bb-region-header ul li {
33+
cursor: pointer;
34+
padding: 3px 12px;
35+
border-radius: var(--bs-border-radius);
4636
}
4737

38+
.bb-region-header ul li.active {
39+
background-color: #e3e3e3;
40+
color: #000;
41+
}
42+
43+
.bb-region-header ul li:hover {
44+
background-color: #e3e3e3;
45+
color: #000;
46+
}
47+
4848
.bb-region-body {
4949
padding: .5rem;
50-
max-width: 400px;
50+
width: 400px;
5151
}
52+
53+
.bb-region-body ul {
54+
display: flex;
55+
flex-wrap: wrap;
56+
gap: 5px;
57+
}
58+
59+
.bb-region-body .bb-region-body-item:not(.active) {
60+
display: none;
61+
}
62+
63+
.bb-region-body ul li {
64+
padding: 3px 12px;
65+
border-radius: var(--bs-border-radius);
66+
cursor: pointer;
67+
color: #777;
68+
}
69+
70+
.bb-region-body ul li.active {
71+
background-color: #e3e3e3;
72+
color: #000;
73+
}
74+
75+
.bb-region-body ul li:hover {
76+
background-color: #e3e3e3;
77+
color: #000;
78+
}

0 commit comments

Comments
 (0)