Skip to content

Commit 789b58d

Browse files
authored
feat(Tasks): add Bootstrap.Tasks.Dashboard project (#530)
* chore: 增加 TaskDashboard 组件 * feat: 实现日志功能 * feat: 增加日志弹窗逻辑 * refactor: 调整顺序 * feat: 实现异常明细弹窗功能 * refactor: 移除测试代码 * doc: 增加异常 UI 格式化逻辑 * doc: 代码格式化 * doc: 代码注释 * refactor: 支持多播 * refactor: 触发器未执行时不显示日志 * chore(Tasks): bump version 9.0.0
1 parent b0d8f58 commit 789b58d

10 files changed

Lines changed: 376 additions & 1 deletion

File tree

BootstrapBlazor.Extensions.sln

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 18
4-
VisualStudioVersion = 18.0.10828.68 main
4+
VisualStudioVersion = 18.0.10828.68
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "components", "components", "{FF1089BE-C704-4374-B629-C57C08E1798F}"
77
EndProject
@@ -200,6 +200,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.OpcDa", "sr
200200
EndProject
201201
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestOpcDa", "test\UnitTestOpcDa\UnitTestOpcDa.csproj", "{835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}"
202202
EndProject
203+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Tasks.Dashboard", "src\components\BootstrapBlazor.Tasks.Dashboard\BootstrapBlazor.Tasks.Dashboard.csproj", "{30C57119-C564-401C-AE3A-6203E2733E1A}"
204+
EndProject
203205
Global
204206
GlobalSection(SolutionConfigurationPlatforms) = preSolution
205207
Debug|Any CPU = Debug|Any CPU
@@ -546,6 +548,10 @@ Global
546548
{835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}.Debug|Any CPU.Build.0 = Debug|Any CPU
547549
{835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}.Release|Any CPU.ActiveCfg = Release|Any CPU
548550
{835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}.Release|Any CPU.Build.0 = Release|Any CPU
551+
{30C57119-C564-401C-AE3A-6203E2733E1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
552+
{30C57119-C564-401C-AE3A-6203E2733E1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
553+
{30C57119-C564-401C-AE3A-6203E2733E1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
554+
{30C57119-C564-401C-AE3A-6203E2733E1A}.Release|Any CPU.Build.0 = Release|Any CPU
549555
EndGlobalSection
550556
GlobalSection(SolutionProperties) = preSolution
551557
HideSolutionNode = FALSE
@@ -640,6 +646,7 @@ Global
640646
{98373A64-E224-4715-AE02-A8C6DAFF3338} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
641647
{01007B10-7C3C-4136-83FF-981CA39AD3D4} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
642648
{835C8BA9-A9CC-4EA0-9002-34A20F8B2E86} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
649+
{30C57119-C564-401C-AE3A-6203E2733E1A} = {FF1089BE-C704-4374-B629-C57C08E1798F}
643650
EndGlobalSection
644651
GlobalSection(ExtensibilityGlobals) = postSolution
645652
SolutionGuid = {D5EB1960-6F30-4CE1-B375-EAE1F787D6FF}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Razor">
2+
3+
<PropertyGroup>
4+
<Version>9.0.0</Version>
5+
</PropertyGroup>
6+
7+
<PropertyGroup>
8+
<PackageTags>Bootstrap Blazor WebAssembly wasm UI Components Task Dashboard</PackageTags>
9+
<Description>Bootstrap UI components extensions of Task</Description>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<Content Remove="Locales\en-US.json" />
14+
<Content Remove="Locales\zh-CN.json" />
15+
<EmbeddedResource Include="Locales\en-US.json" />
16+
<EmbeddedResource Include="Locales\zh-CN.json" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<PackageReference Include="BootstrapBlazor" Version="9.0.0" />
21+
<PackageReference Include="Longbow.Tasks" Version="9.0.2" />
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<Using Include="BootstrapBlazor.Components" />
26+
<Using Include="Longbow.Tasks" />
27+
</ItemGroup>
28+
29+
</Project>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"BootstrapBlazor.Components.Tasks.TaskDashboard": {
3+
"Name": "Name",
4+
"Status": "Status",
5+
"NextRuntime": "NextRuntime",
6+
"LastRuntime": "LastRuntime",
7+
"LastRunResult": "LastRunResult",
8+
"Exception": "Exception",
9+
"Operation": "Operation",
10+
"SchedulerStatus.Running": "Running",
11+
"SchedulerStatus.Ready": "Ready",
12+
"SchedulerStatus.Disabled": "Disabled",
13+
"TriggerResult.Running": "Running",
14+
"TriggerResult.Success": "Success",
15+
"TriggerResult.Cancelled": "Cancelled",
16+
"TriggerResult.Timeout": "Timeout",
17+
"TriggerResult.Error": "Error",
18+
"ButtonPause": "Pause",
19+
"ButtonRun": "Run",
20+
"ButtonLog": "Log",
21+
"LogDialogTitle": "{0} - Log(Latest 20 Items)",
22+
"LogDialogConsoleHeaderText": "Action Log",
23+
"ExceptionDialogTitle": "{0} - Exception Detail",
24+
"None": "None"
25+
}
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"BootstrapBlazor.Components.Tasks.TaskDashboard": {
3+
"Name": "名称",
4+
"Status": "调度状态",
5+
"NextRuntime": "下次运行时间",
6+
"LastRuntime": "上次运行时间",
7+
"LastRunResult": "上次结果",
8+
"Exception": "异常",
9+
"Operation": "操作",
10+
"SchedulerStatus.Running": "运行中",
11+
"SchedulerStatus.Ready": "已停止",
12+
"SchedulerStatus.Disabled": "禁用",
13+
"TriggerResult.Running": "运行",
14+
"TriggerResult.Success": "成功",
15+
"TriggerResult.Cancelled": "取消",
16+
"TriggerResult.Timeout": "超时",
17+
"TriggerResult.Error": "故障",
18+
"ButtonPause": "暂停",
19+
"ButtonRun": "运行",
20+
"ButtonLog": "日志",
21+
"LogDialogTitle": "{0} - 日志窗口(最新 20 条)",
22+
"LogDialogConsoleHeaderText": "执行日志",
23+
"ExceptionDialogTitle": "{0} - 异常明细",
24+
"None": ""
25+
}
26+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
@namespace BootstrapBlazor.Components.Tasks
2+
@inherits BootstrapComponentBase
3+
4+
<div @attributes="AdditionalAttributes" class="@ClassString">
5+
<Table TItem="IScheduler" Items="@_schedulers" IsBordered="true" IsStriped="true">
6+
<TableColumns>
7+
<TableTemplateColumn Text="@Localizer["Name"]">
8+
<Template Context="v">
9+
@v.Row.Name
10+
</Template>
11+
</TableTemplateColumn>
12+
<TableTemplateColumn Text="@Localizer["Status"]">
13+
<Template Context="v">
14+
<Tag Color="GetStatusColor(v.Row.Status)" Icon="@GetStatusIcon(v.Row.Status)">@FormatStatus(v.Row.Status)</Tag>
15+
</Template>
16+
</TableTemplateColumn>
17+
<TableTemplateColumn Text="@Localizer["LastRuntime"]">
18+
<Template Context="v">
19+
@FormatDateTime(v.Row.LastRuntime)
20+
</Template>
21+
</TableTemplateColumn>
22+
<TableTemplateColumn Text="@Localizer["NextRuntime"]">
23+
<Template Context="v">
24+
@FormatDateTime(v.Row.NextRuntime)
25+
</Template>
26+
</TableTemplateColumn>
27+
<TableTemplateColumn Text="@Localizer["LastRunResult"]">
28+
<Template Context="v">
29+
<Tag Color="GetResultColor(v.Row.LastRunResult)">@FormatResult(v.Row.LastRunResult)</Tag>
30+
</Template>
31+
</TableTemplateColumn>
32+
<TableTemplateColumn Text="@Localizer["Exception"]">
33+
<Template Context="v">
34+
@if (v.Row.Exception != null)
35+
{
36+
<Button Color="Color.Danger" Size="Size.ExtraSmall" Icon="fa-solid fa-exclamation-circle"
37+
OnClickWithoutRender="() => OnShowException(v.Row, v.Row.Exception)"></Button>
38+
}
39+
else
40+
{
41+
<Tag Color="Color.Success">@Localizer["None"]</Tag>
42+
}
43+
</Template>
44+
</TableTemplateColumn>
45+
<TableTemplateColumn Text="@Localizer["Operation"]" Width="170">
46+
<Template Context="v">
47+
<div class="btn-group">
48+
<Button Size="Size.ExtraSmall" Color="Color.Warning" Icon="fa-solid fa-pause-circle"
49+
Text="@Localizer["ButtonPause"]" OnClick="() => OnPause(v.Row)" IsDisabled="OnCheckPauseTaskStatus(v.Row)"></Button>
50+
<Button Size="Size.ExtraSmall" Color="Color.Success" Icon="fa-solid fa-play-circle"
51+
Text="@Localizer["ButtonRun"]" OnClick="() => OnRun(v.Row)" IsDisabled="OnCheckRunTaskStatus(v.Row)"></Button>
52+
<Button Size="Size.ExtraSmall" Color="Color.Info" Icon="fa-solid fa-info-circle"
53+
Text="@Localizer["ButtonLog"]" OnClick="() => OnLog(v.Row)"></Button>
54+
</div>
55+
</Template>
56+
</TableTemplateColumn>
57+
</TableColumns>
58+
</Table>
59+
</div>
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Website: https://www.blazor.zone or https://argozhang.github.io/
4+
5+
using Microsoft.AspNetCore.Components;
6+
using Microsoft.Extensions.Localization;
7+
8+
namespace BootstrapBlazor.Components.Tasks;
9+
10+
/// <summary>
11+
/// Task Dashboard Component
12+
/// </summary>
13+
public partial class TaskDashboard
14+
{
15+
[Inject, NotNull]
16+
private IStringLocalizer<TaskDashboard>? Localizer { get; set; }
17+
18+
[Inject, NotNull]
19+
private DialogService? DialogService { get; set; }
20+
21+
private string? ClassString => CssBuilder.Default("bb-tasks-dashboard")
22+
.AddClassFromAttributes(AdditionalAttributes)
23+
.Build();
24+
25+
private IEnumerable<IScheduler> _schedulers = [];
26+
27+
/// <summary>
28+
/// <inheritdoc/>
29+
/// </summary>
30+
protected override void OnParametersSet()
31+
{
32+
base.OnParametersSet();
33+
34+
_schedulers = TaskServicesManager.ToList();
35+
}
36+
37+
private static Color GetStatusColor(SchedulerStatus status) => status switch
38+
{
39+
SchedulerStatus.Running => Color.Success,
40+
SchedulerStatus.Ready => Color.Primary,
41+
_ => Color.Danger
42+
};
43+
44+
private string FormatStatus(SchedulerStatus status) => status switch
45+
{
46+
SchedulerStatus.Running => Localizer["SchedulerStatus.Running"],
47+
SchedulerStatus.Ready => Localizer["SchedulerStatus.Ready"],
48+
_ => Localizer["SchedulerStatus.Disabled"]
49+
};
50+
51+
private static string GetStatusIcon(SchedulerStatus status) => status switch
52+
{
53+
SchedulerStatus.Running => "fa-solid fa-play-circle",
54+
SchedulerStatus.Ready => "fa-solid fa-stop-circle",
55+
_ => "fa-solid fa-times-circle"
56+
};
57+
58+
private static Color GetResultColor(TriggerResult result) => result switch
59+
{
60+
TriggerResult.Running => Color.Primary,
61+
TriggerResult.Success => Color.Success,
62+
TriggerResult.Cancelled => Color.Dark,
63+
TriggerResult.Timeout => Color.Warning,
64+
_ => Color.Danger,
65+
};
66+
67+
private string FormatResult(TriggerResult result) => result switch
68+
{
69+
TriggerResult.Running => Localizer["TriggerResult.Running"],
70+
TriggerResult.Success => Localizer["TriggerResult.Success"],
71+
TriggerResult.Cancelled => Localizer["TriggerResult.Timeout"],
72+
TriggerResult.Timeout => Localizer["TriggerResult.Timeout"],
73+
_ => Localizer["TriggerResult.Error"],
74+
};
75+
76+
private static Task OnPause(IScheduler scheduler)
77+
{
78+
scheduler.Status = SchedulerStatus.Ready;
79+
return Task.CompletedTask;
80+
}
81+
82+
private static Task OnRun(IScheduler scheduler)
83+
{
84+
scheduler.Status = SchedulerStatus.Running;
85+
return Task.CompletedTask;
86+
}
87+
88+
private async Task OnLog(IScheduler scheduler)
89+
{
90+
var option = new DialogOption()
91+
{
92+
Class = "modal-dialog-task-log",
93+
Title = Localizer["LogDialogTitle", scheduler.Name],
94+
Component = BootstrapDynamicComponent.CreateComponent<TaskInfo>(new Dictionary<string, object?>
95+
{
96+
[nameof(TaskInfo.Scheduler)] = scheduler,
97+
[nameof(TaskInfo.HeaderText)] = Localizer["LogDialogConsoleHeaderText"].Value
98+
})
99+
};
100+
await DialogService.Show(option);
101+
}
102+
103+
private static bool OnCheckPauseTaskStatus(IScheduler model) => model.Status != SchedulerStatus.Running;
104+
105+
private static bool OnCheckRunTaskStatus(IScheduler model) => model.Status == SchedulerStatus.Running;
106+
107+
private static string? FormatDateTime(DateTimeOffset? dateTime) => dateTime?.ToString("yyyy-MM-dd HH:mm:ss");
108+
109+
private async Task OnShowException(IScheduler scheduler, Exception ex)
110+
{
111+
var option = new DialogOption()
112+
{
113+
Class = "modal-dialog-task-ex",
114+
IsScrolling = true,
115+
Title = Localizer["ExceptionDialogTitle", scheduler.Name],
116+
BodyTemplate = RenderException(ex)
117+
};
118+
await DialogService.Show(option);
119+
}
120+
121+
private static RenderFragment RenderException(Exception ex) => builder => builder.AddContent(0, ex.FormatMarkupString());
122+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.my-component {
2+
border: 2px dashed red;
3+
padding: 1em;
4+
margin: 1em 0;
5+
background-image: url('background.png');
6+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@namespace BootstrapBlazor.Components.Tasks
2+
3+
<BootstrapBlazor.Components.Console Items="@Messages" Height="280" ShowAutoScroll="true" HeaderText="@HeaderText" />

0 commit comments

Comments
 (0)