Skip to content

Commit f5000ed

Browse files
committed
!563 feat(#I1XH0X): 增加倒计时组件
* doc: 增加组件属性说明表格 * doc: 增加颜色示例文档 * doc: 增加颜色示例 * doc: 更新示例文档文件 * refactor: 删除冗余代码 * doc: 增加回调方法示例 * fix: 修复 OnTimeout 回调不刷新界面问题 * feat: 增加取消计时回调委托方法 * feat: 增加倒计时结束时回调委托 * feat: 增加倒计时动画效果 * doc: 增加 Timer 示例文档文件 * doc: 增加 Timer 示例 * style: 增加 Timer 组件样式 * feat: 增加 Timer 组件 * Merge branch 'dev' into dev-timer * doc: 增加计时器菜单
1 parent 63ff7d2 commit f5000ed

File tree

7 files changed

+338
-0
lines changed

7 files changed

+338
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Timer OnTimeout="@OnTimeout" OnCancel="OnCancel" />
2+
<Logger @ref="Trace" class="mt-3" />
3+
4+
@code {
5+
private Logger? Trace { get; set; }
6+
7+
private Task OnTimeout()
8+
{
9+
Trace?.Log("计时器时间到");
10+
return Task.CompletedTask;
11+
}
12+
13+
private Task OnCancel()
14+
{
15+
Trace?.Log("计时器取消");
16+
return Task.CompletedTask;
17+
}
18+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<Timer Color="Color.Warning" />
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
@page "/timers"
2+
3+
<h3>Timer 计时器</h3>
4+
5+
<h4>用于时间倒计时</h4>
6+
7+
<Block Title="基本用法" Introduction="通过设置 <code>Value</code> 属性设定倒计时时间" CodeFile="timer.1.html">
8+
<Timer OnTimeout="@OnTimeout" OnCancel="OnCancel" />
9+
<Logger @ref="Trace" class="mt-3" />
10+
</Block>
11+
12+
<Block Title="颜色" Introduction="通过设置 <code>Color</code> 属性设定圆形进度条颜色" CodeFile="timer.2.html">
13+
<Timer Color="Color.Warning" />
14+
</Block>
15+
16+
<AttributeTable Items="@GetAttributes()" />
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using BootstrapBlazor.Shared.Common;
2+
using BootstrapBlazor.Shared.Pages.Components;
3+
using System.Collections.Generic;
4+
using System.Threading.Tasks;
5+
6+
namespace BootstrapBlazor.Shared.Pages
7+
{
8+
/// <summary>
9+
///
10+
/// </summary>
11+
public sealed partial class Timers
12+
{
13+
private Logger? Trace { get; set; }
14+
15+
private Task OnTimeout()
16+
{
17+
Trace?.Log("计时器时间到");
18+
return Task.CompletedTask;
19+
}
20+
21+
private Task OnCancel()
22+
{
23+
Trace?.Log("计时器取消");
24+
return Task.CompletedTask;
25+
}
26+
27+
/// <summary>
28+
///
29+
/// </summary>
30+
/// <returns></returns>
31+
private IEnumerable<AttributeItem> GetAttributes()
32+
{
33+
return new AttributeItem[]
34+
{
35+
new AttributeItem(){
36+
Name = "Width",
37+
Description = "组件宽度",
38+
Type = "int",
39+
ValueList = " - ",
40+
DefaultValue = "300"
41+
},
42+
new AttributeItem(){
43+
Name = "StrokeWidth",
44+
Description = "进度条宽度",
45+
Type = "int",
46+
ValueList = " - ",
47+
DefaultValue = "6"
48+
},
49+
new AttributeItem()
50+
{
51+
Name = "Value",
52+
Description = "倒计时时间",
53+
Type = "Timespan",
54+
ValueList = " - ",
55+
DefaultValue = " - "
56+
},
57+
new AttributeItem()
58+
{
59+
Name = "Color",
60+
Description = "进度条颜色",
61+
Type = "Color",
62+
ValueList = "Primary / Secondary / Success / Danger / Warning / Info / Dark",
63+
DefaultValue = "Primary"
64+
}
65+
};
66+
}
67+
}
68+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@namespace BootstrapBlazor.Components
2+
@inherits CircleBase
3+
4+
<div @attributes="@AdditionalAttributes" class="@ClassString">
5+
@if (Value > TimeSpan.Zero)
6+
{
7+
<svg class="circle-body" style="@PrevStyleString">
8+
<circle r="@CircleR" cy="@CircleDiameter" cx="@CircleDiameter" stroke-width="2" stroke="#EAEFF4" stroke-linejoin="round" stroke-linecap="round" fill="none" />
9+
<circle class="@ProgressClassString" r="@CircleR" cy="@CircleDiameter" cx="@CircleDiameter" stroke-dashoffset="@ValueString" stroke-dasharray="@DashString" stroke-width="@StrokeWidth" stroke="#1593FF" stroke-linejoin="round" stroke-linecap="round" fill="none" />
10+
</svg>
11+
<div class="@TitleClassString">
12+
<span class="timer-body">@ValueTitleString</span>
13+
<span class="timer-alert"><i class="fa fa-bell"></i>@AlertTime</span>
14+
</div>
15+
<div class="timer-buttons">
16+
<button @onclick="@OnClickCancel" class="btn btn-secondary">取消</button>
17+
<button @onclick="@OnClickPause" class="@PauseClassString">@PauseText</button>
18+
</div>
19+
}
20+
else
21+
{
22+
<TimePickerBody @bind-Value="@Value" OnConfirm="@OnStart" ConfirmText="开始计时" />
23+
}
24+
</div>
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
using Microsoft.AspNetCore.Components;
2+
using System;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
6+
namespace BootstrapBlazor.Components
7+
{
8+
/// <summary>
9+
///
10+
/// </summary>
11+
public sealed partial class Timer
12+
{
13+
/// <summary>
14+
/// 获得 组件样式字符串
15+
/// </summary>
16+
protected override string? ClassString => CssBuilder.Default("circle timer")
17+
.AddClassFromAttributes(AdditionalAttributes)
18+
.Build();
19+
20+
private string? PauseClassString => CssBuilder.Default("btn")
21+
.AddClass("btn-warning", !IsPause)
22+
.AddClass("btn-success", IsPause)
23+
.Build();
24+
25+
/// <summary>
26+
/// 获得/设置 当前进度值
27+
/// </summary>
28+
private string? ValueString => $"{Math.Round(((1 - CurrentTimespan.TotalSeconds * 1.0 / Value.TotalSeconds) * CircleLength), 2)}";
29+
30+
private TimeSpan CurrentTimespan { get; set; }
31+
32+
private string PauseText { get; set; } = "暂停";
33+
34+
private bool IsPause { get; set; }
35+
36+
/// <summary>
37+
/// 获得/设置 Title 字符串
38+
/// </summary>
39+
private string ValueTitleString => CurrentTimespan.Hours == 0 ? $"{CurrentTimespan:mm\\:ss}" : $"{CurrentTimespan:hh\\:mm\\:ss}";
40+
41+
private string AlertTime { get; set; } = "";
42+
43+
private CancellationTokenSource? CancelTokenSource { get; set; }
44+
45+
private ManualResetEventSlim? ResetEvent { get; set; } = new ManualResetEventSlim(true);
46+
47+
/// <summary>
48+
/// 获得/设置 当前值
49+
/// </summary>
50+
[Parameter]
51+
public TimeSpan Value { get; set; }
52+
53+
/// <summary>
54+
/// 获得/设置 文件预览框宽度
55+
/// </summary>
56+
[Parameter]
57+
public override int Width { get; set; } = 300;
58+
59+
/// <summary>
60+
/// 获得/设置 倒计时结束时回调委托
61+
/// </summary>
62+
[Parameter]
63+
public Func<Task>? OnTimeout { get; set; }
64+
65+
/// <summary>
66+
/// 获得/设置 取消时回调委托
67+
/// </summary>
68+
[Parameter]
69+
public Func<Task>? OnCancel { get; set; }
70+
71+
/// <summary>
72+
/// 获得/设置 进度条宽度 默认为 2
73+
/// </summary>
74+
[Parameter]
75+
public override int StrokeWidth { get; set; } = 6;
76+
77+
private void OnStart()
78+
{
79+
IsPause = false;
80+
CurrentTimespan = Value;
81+
AlertTime = DateTime.Now.Add(CurrentTimespan).ToString("HH:mm");
82+
83+
if (CancelTokenSource != null)
84+
{
85+
CancelTokenSource.Dispose();
86+
}
87+
88+
CancelTokenSource = new CancellationTokenSource();
89+
90+
StateHasChanged();
91+
92+
Task.Run(async () =>
93+
{
94+
do
95+
{
96+
await Task.Delay(1000, CancelTokenSource.Token);
97+
98+
ResetEvent?.Wait();
99+
100+
CurrentTimespan = CurrentTimespan.Subtract(TimeSpan.FromSeconds(1));
101+
await InvokeAsync(StateHasChanged);
102+
}
103+
while (!CancelTokenSource.IsCancellationRequested && CurrentTimespan > TimeSpan.Zero);
104+
105+
if (CurrentTimespan == TimeSpan.Zero)
106+
{
107+
await Task.Delay(1000);
108+
Value = TimeSpan.Zero;
109+
await InvokeAsync(() =>
110+
{
111+
StateHasChanged();
112+
OnTimeout?.Invoke();
113+
});
114+
}
115+
});
116+
}
117+
118+
private Task OnClickPause()
119+
{
120+
IsPause = !IsPause;
121+
PauseText = IsPause ? "继续" : "暂停";
122+
if (IsPause)
123+
{
124+
ResetEvent?.Reset();
125+
}
126+
else
127+
{
128+
AlertTime = DateTime.Now.Add(CurrentTimespan).ToString("HH:mm");
129+
if (!ResetEvent?.IsSet ?? false) ResetEvent?.Set();
130+
}
131+
return Task.CompletedTask;
132+
}
133+
134+
private async Task OnClickCancel()
135+
{
136+
Value = TimeSpan.Zero;
137+
CancelTokenSource?.Cancel();
138+
if (OnCancel != null) await OnCancel.Invoke();
139+
}
140+
141+
/// <summary>
142+
/// Dispose 方法
143+
/// </summary>
144+
/// <param name="disposing"></param>
145+
protected override void Dispose(bool disposing)
146+
{
147+
if (disposing)
148+
{
149+
CancelTokenSource?.Dispose();
150+
ResetEvent?.Dispose();
151+
ResetEvent = null;
152+
}
153+
154+
base.Dispose(disposing);
155+
}
156+
}
157+
}

src/BootstrapBlazor/wwwroot/css/bootstrap.blazor.css

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5464,3 +5464,57 @@ input:disabled,
54645464
display: none;
54655465
}
54665466
/*end camera*/
5467+
5468+
/*timer*/
5469+
.timer {
5470+
}
5471+
5472+
.timer .time-panel-footer {
5473+
display: flex;
5474+
justify-content: space-between;
5475+
}
5476+
5477+
.timer .time-panel-btn {
5478+
border: 1px solid transparent;
5479+
border-radius: 4px;
5480+
transition: border .3s linear;
5481+
padding: 0 0.65rem;
5482+
}
5483+
5484+
.timer .time-panel-btn:hover {
5485+
border-color: #ddd;
5486+
}
5487+
5488+
.timer .time-panel-btn.confirm {
5489+
color: #28a745;
5490+
}
5491+
5492+
.timer .circle-title {
5493+
flex-flow: column nowrap;
5494+
bottom: 66px;
5495+
}
5496+
5497+
.timer .timer-body {
5498+
font-size: 3.5rem;
5499+
}
5500+
5501+
.timer .timer-alert {
5502+
font-size: 1rem;
5503+
}
5504+
5505+
.timer .timer-alert i {
5506+
margin-right: 0.5rem;
5507+
}
5508+
5509+
.timer .timer-buttons {
5510+
display: flex;
5511+
justify-content: space-between;
5512+
}
5513+
5514+
.timer .timer-buttons .btn {
5515+
border: 5px double #949496;
5516+
border-radius: 50%;
5517+
height: 66px;
5518+
width: 66px;
5519+
}
5520+
/*end timer*/

0 commit comments

Comments
 (0)