feat(app): 初始化 OmniScheduler WPF 调度器
基于 PRD 搭建 .NET 8 WPF 桌面应用,包含主控制台、任务编辑、全局设置、系统托盘和应用图标集成。 实现本地任务模型、触发器规则、JSON 状态持久化、OmniNotify HTTP 推送、执行日志记录、动态变量替换以及基础 Cron 预览能力。 补充 .gitignore,排除构建产物和本地 IDE 文件。 BREAKING CHANGE: 首次提交,建立项目初始结构
This commit is contained in:
225
OmniScheduler/MainWindow.xaml.cs
Normal file
225
OmniScheduler/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using Forms = System.Windows.Forms;
|
||||
|
||||
namespace OmniScheduler;
|
||||
|
||||
public partial class MainWindow : Window, INotifyPropertyChanged
|
||||
{
|
||||
private readonly StateStore _store = new();
|
||||
private readonly NotifyClient _client = new();
|
||||
private readonly SchedulerService _scheduler;
|
||||
private readonly Forms.NotifyIcon _trayIcon;
|
||||
private bool _allowExit;
|
||||
private ScheduledTask? _selectedTask;
|
||||
private string _statusText = "就绪";
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
State = _store.Load();
|
||||
_scheduler = new SchedulerService(State, _client, AddLog);
|
||||
_trayIcon = BuildTrayIcon();
|
||||
DataContext = this;
|
||||
_scheduler.Start();
|
||||
|
||||
if (Environment.GetCommandLineArgs().Any(a => a.Equals("--tray", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
|
||||
public SchedulerState State { get; }
|
||||
|
||||
public ScheduledTask? SelectedTask
|
||||
{
|
||||
get => _selectedTask;
|
||||
set
|
||||
{
|
||||
_selectedTask = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedTask)));
|
||||
}
|
||||
}
|
||||
|
||||
public string StatusText
|
||||
{
|
||||
get => _statusText;
|
||||
set
|
||||
{
|
||||
_statusText = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StatusText)));
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
private Forms.NotifyIcon BuildTrayIcon()
|
||||
{
|
||||
var menu = new Forms.ContextMenuStrip();
|
||||
menu.Items.Add("打开主界面", null, (_, _) => ShowDashboard());
|
||||
menu.Items.Add("暂停全部任务", null, (_, _) =>
|
||||
{
|
||||
_scheduler.IsPaused = !_scheduler.IsPaused;
|
||||
PauseToggle.IsChecked = _scheduler.IsPaused;
|
||||
});
|
||||
menu.Items.Add("退出", null, (_, _) => ExitApplication());
|
||||
|
||||
var iconPath = System.IO.Path.Combine(AppContext.BaseDirectory, "app.ico");
|
||||
var icon = new Forms.NotifyIcon
|
||||
{
|
||||
Icon = new System.Drawing.Icon(iconPath),
|
||||
Text = "OmniScheduler",
|
||||
Visible = true,
|
||||
ContextMenuStrip = menu
|
||||
};
|
||||
icon.DoubleClick += (_, _) => ShowDashboard();
|
||||
return icon;
|
||||
}
|
||||
|
||||
private void AddTask_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var task = new ScheduledTask();
|
||||
task.Triggers.Add(new TaskTrigger());
|
||||
if (TaskEditorWindow.Edit(this, task, State.Settings, _client))
|
||||
{
|
||||
State.Tasks.Add(task);
|
||||
SaveAndRefresh("已创建任务");
|
||||
}
|
||||
}
|
||||
|
||||
private void EditTask_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SelectedTask is null)
|
||||
{
|
||||
StatusText = "请先选择一个任务";
|
||||
return;
|
||||
}
|
||||
|
||||
if (TaskEditorWindow.Edit(this, SelectedTask, State.Settings, _client))
|
||||
{
|
||||
SaveAndRefresh("任务已保存");
|
||||
}
|
||||
}
|
||||
|
||||
private void TasksGrid_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
=> EditTask_Click(sender, e);
|
||||
|
||||
private void DeleteTask_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SelectedTask is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (System.Windows.MessageBox.Show(this, $"删除任务“{SelectedTask.Name}”?", "确认删除", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
|
||||
{
|
||||
State.Tasks.Remove(SelectedTask);
|
||||
SaveAndRefresh("任务已删除");
|
||||
}
|
||||
}
|
||||
|
||||
private void CloneTask_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SelectedTask is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var clone = SelectedTask.Clone();
|
||||
State.Tasks.Add(clone);
|
||||
SelectedTask = clone;
|
||||
SaveAndRefresh("任务已克隆");
|
||||
}
|
||||
|
||||
private async void FireTask_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SelectedTask is null)
|
||||
{
|
||||
StatusText = "请先选择一个任务";
|
||||
return;
|
||||
}
|
||||
|
||||
StatusText = "正在手动触发...";
|
||||
await _scheduler.FireNowAsync(SelectedTask);
|
||||
SaveAndRefresh("手动触发完成");
|
||||
}
|
||||
|
||||
private void Settings_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SettingsWindow.Edit(this, State.Settings))
|
||||
{
|
||||
StartupRegistration.Apply(State.Settings.StartWithWindows);
|
||||
SaveAndRefresh("设置已保存");
|
||||
}
|
||||
}
|
||||
|
||||
private void PauseToggle_Changed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_scheduler.IsPaused = PauseToggle.IsChecked == true;
|
||||
StatusText = _scheduler.IsPaused ? "全部任务已暂停" : "调度已恢复";
|
||||
}
|
||||
|
||||
private void ClearLogs_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
State.Logs.Clear();
|
||||
SaveAndRefresh("日志已清空");
|
||||
}
|
||||
|
||||
private void LogsGrid_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
if (LogsGrid.SelectedItem is ExecutionLog log)
|
||||
{
|
||||
System.Windows.MessageBox.Show(this, $"请求 JSON:\n{log.RequestJson}\n\n响应:\n{log.Response}", "日志详情", MessageBoxButton.OK, log.Level == "ERROR" ? MessageBoxImage.Error : MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddLog(ExecutionLog log)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
State.Logs.Insert(0, log);
|
||||
StatusText = $"{log.Timestamp:HH:mm:ss} {log.TaskName}: {log.Message}";
|
||||
_store.Save(State);
|
||||
});
|
||||
}
|
||||
|
||||
private void SaveAndRefresh(string status)
|
||||
{
|
||||
_scheduler.RecalculateNextRuns();
|
||||
foreach (var task in State.Tasks)
|
||||
{
|
||||
task.Notify(nameof(ScheduledTask.TriggerSummary));
|
||||
}
|
||||
TasksGrid.Items.Refresh();
|
||||
_store.Save(State);
|
||||
StatusText = status;
|
||||
}
|
||||
|
||||
private void Window_Closing(object? sender, CancelEventArgs e)
|
||||
{
|
||||
if (_allowExit)
|
||||
{
|
||||
_trayIcon.Visible = false;
|
||||
_trayIcon.Dispose();
|
||||
_store.Save(State);
|
||||
return;
|
||||
}
|
||||
|
||||
e.Cancel = true;
|
||||
Hide();
|
||||
StatusText = "已隐藏到系统托盘";
|
||||
}
|
||||
|
||||
private void ShowDashboard()
|
||||
{
|
||||
Show();
|
||||
WindowState = WindowState.Normal;
|
||||
Activate();
|
||||
}
|
||||
|
||||
private void ExitApplication()
|
||||
{
|
||||
_allowExit = true;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user