Files
omni-scheduler/OmniScheduler/MainWindow.xaml.cs
gamewhale d33fe30569 feat: 优化触发器配置体验
- 根据触发器类型动态展示对应配置区域,减少无关字段干扰。
- 将单次、每日、每周、每月和生效时间范围改为日期选择器与时分秒下拉选择,避免手动输入时间格式。
- 为单次执行增加延后执行快捷设置,支持常用快捷按钮和自定义分钟、小时、天后执行。
- 移除开机自启设置、注册表写入逻辑和相关配置字段,降低对用户系统的影响。
- 同步优化部分任务状态、触发摘要和设置界面文案。
2026-05-21 10:54:20 +08:00

251 lines
6.8 KiB
C#

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 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)
{
var row = FindVisualParent<DataGridRow>(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<T>(DependencyObject? child) where T : DependencyObject
{
while (child is not null)
{
if (child is T parent)
{
return parent;
}
child = VisualTreeHelper.GetParent(child);
}
return null;
}
}