using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Media; 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 icon = new Forms.NotifyIcon { Icon = System.Drawing.Icon.ExtractAssociatedIcon(Environment.ProcessPath ?? string.Empty) ?? System.Drawing.SystemIcons.Application, 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) { var row = FindVisualParent(e.OriginalSource as DependencyObject); if (row?.Item is not ScheduledTask task) { return; } SelectedTask = task; 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)) { 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(); } private static T? FindVisualParent(DependencyObject? child) where T : DependencyObject { while (child is not null) { if (child is T parent) { return parent; } child = VisualTreeHelper.GetParent(child); } return null; } }