zcbot/windows-node/Zcbot.WindowsNode/Presentation/ViewModels/JobPageViewModel.cs

121 lines
3.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.ObjectModel;
using System.Windows.Input;
namespace Zcbot.WindowsNode;
internal sealed class LocalJobItemViewModel(JobDisplaySnapshot snapshot)
{
internal JobDisplaySnapshot Snapshot { get; } = snapshot;
public Guid JobId => Snapshot.JobId;
public string StageText => FormatStage(Snapshot.Stage);
public string Title => Snapshot.Title;
public string InputFilename => Snapshot.InputFilename;
public string ProgressText => Snapshot.Stage == "software_running"
? $"已执行 {FormatElapsed(DateTimeOffset.UtcNow - Snapshot.UpdatedAt)}"
: $"{Snapshot.Progress}%";
public string AcceptedAtText => Snapshot.AcceptedAt.LocalDateTime.ToString("MM-dd HH:mm");
public string ShortId => Snapshot.JobId.ToString("N")[..8];
public string Tone => Snapshot.Stage switch
{
"failed" or "cloud_terminal" => "error",
"succeeded" => "success",
"cancelled" => "neutral",
_ => "active",
};
public string DetailText => string.Join("\n", [
$"{StageText} · {Snapshot.Detail}",
$"能力:{Snapshot.Capability}",
$"Job ID{Snapshot.JobId}",
$"更新时间:{Snapshot.UpdatedAt.LocalDateTime:yyyy-MM-dd HH:mm:ss}",
]);
private static string FormatElapsed(TimeSpan elapsed)
{
if (elapsed < TimeSpan.Zero) elapsed = TimeSpan.Zero;
return elapsed.TotalHours >= 1
? $"{(int)elapsed.TotalHours}:{elapsed.Minutes:00}:{elapsed.Seconds:00}"
: $"{elapsed.Minutes:00}:{elapsed.Seconds:00}";
}
private static string FormatStage(string stage) => stage switch
{
"accepted" => "等待处理",
"downloading_inputs" => "下载输入",
"ready_to_run" => "准备执行",
"software_running" => "软件执行中",
"uploading_outputs" => "上传结果",
"succeeded" => "成功",
"failed" => "失败",
"cloud_terminal" => "云端已终止",
"cancelled" => "已取消",
_ => stage,
};
}
internal sealed class JobPageViewModel : ObservableObject
{
private readonly IJobMonitorService service;
private LocalJobItemViewModel? selectedJob;
private string summary = "正在读取本机任务…";
internal JobPageViewModel(IJobMonitorService service)
{
this.service = service;
RefreshCommand = new RelayCommand(Refresh);
}
public ObservableCollection<LocalJobItemViewModel> Jobs { get; } = [];
public LocalJobItemViewModel? SelectedJob
{
get => selectedJob;
set
{
if (SetProperty(ref selectedJob, value))
{
OnPropertyChanged(nameof(SelectedDetail));
}
}
}
public string Summary
{
get => summary;
private set => SetProperty(ref summary, value);
}
public string SelectedDetail => SelectedJob?.DetailText
?? "选择任务后可查看执行阶段、更新时间和完整 Job ID。";
public ICommand RefreshCommand { get; }
internal void Refresh()
{
var selectedId = SelectedJob?.JobId;
IReadOnlyList<JobDisplaySnapshot> snapshots;
try
{
snapshots = service.ReadSnapshots();
}
catch (Exception exception) when (
exception is IOException or UnauthorizedAccessException)
{
Summary = $"无法读取本机任务:{exception.Message}";
return;
}
Jobs.Clear();
foreach (var snapshot in snapshots)
{
Jobs.Add(new LocalJobItemViewModel(snapshot));
}
var active = snapshots.Count(item => item.IsActive);
Summary = snapshots.Count == 0
? "暂无本机任务"
: $"活动任务 {active} 个 · 最近记录 {snapshots.Count} 条";
SelectedJob = Jobs.FirstOrDefault(item => item.JobId == selectedId)
?? Jobs.FirstOrDefault();
}
}