Files
better-genshin-impact/BetterGenshinImpact/ViewModel/Pages/GearTaskListPageViewModel.cs
2026-05-04 13:46:52 +08:00

703 lines
23 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 CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.Logging;
using System.Linq;
using System;
using System.Collections.Generic;
using BetterGenshinImpact.ViewModel.Pages.Component;
using BetterGenshinImpact.Service;
using System.Threading.Tasks;
using System.Collections.Specialized;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.View.Windows;
using BetterGenshinImpact.View.Windows.GearTask;
using BetterGenshinImpact.ViewModel.Windows;
using Wpf.Ui.Violeta.Controls;
namespace BetterGenshinImpact.ViewModel.Pages;
/// <summary>
/// 任务列表页面ViewModel
/// </summary>
public partial class GearTaskListPageViewModel : ViewModel
{
private readonly ILogger<GearTaskListPageViewModel> _logger;
private readonly GearTaskStorageService _storageService;
/// <summary>
/// 任务定义列表(左侧)
/// </summary>
[ObservableProperty] private ObservableCollection<GearTaskDefinitionViewModel> _taskDefinitions = new();
/// <summary>
/// 当前选中的任务定义
/// </summary>
[ObservableProperty] private GearTaskDefinitionViewModel? _selectedTaskDefinition;
/// <summary>
/// 当前任务树根节点(右侧)
/// </summary>
[ObservableProperty] private GearTaskViewModel _currentTaskTreeRoot = new();
/// <summary>
/// 当前选中的任务节点
/// </summary>
[ObservableProperty] private GearTaskViewModel? _selectedTaskNode;
public GearTaskListPageViewModel(ILogger<GearTaskListPageViewModel> logger, GearTaskStorageService storageService)
{
_logger = logger;
_storageService = storageService;
InitializeData();
// 监听集合变化,实现自动保存
TaskDefinitions.CollectionChanged += OnTaskDefinitionsChanged;
// 监听当前任务树根节点的子集合变化,用于拖拽后自动保存
CurrentTaskTreeRoot.Children.CollectionChanged += OnCurrentTaskTreeChanged;
}
/// <summary>
/// 任务定义集合变化时的处理
/// </summary>
private async void OnTaskDefinitionsChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
// 当集合发生变化时包括拖拽重排序更新Order属性并保存
try
{
// 更新所有任务定义的Order属性以反映当前顺序
for (int i = 0; i < TaskDefinitions.Count; i++)
{
if (TaskDefinitions[i].Order != i)
{
TaskDefinitions[i].Order = i;
TaskDefinitions[i].ModifiedTime = DateTime.Now;
}
}
// 保存所有受影响的任务定义
foreach (var taskDef in TaskDefinitions)
{
await _storageService.SaveTaskDefinitionAsync(taskDef);
}
_logger.LogInformation("任务定义列表顺序已更新并保存");
}
catch (Exception ex)
{
_logger.LogError(ex, "保存任务定义列表顺序时发生错误");
}
}
/// <summary>
/// 当前任务树集合变化时的处理(用于拖拽后自动保存)
/// </summary>
private async void OnCurrentTaskTreeChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (SelectedTaskDefinition != null)
{
try
{
SelectedTaskDefinition.ModifiedTime = DateTime.Now;
await _storageService.SaveTaskDefinitionAsync(SelectedTaskDefinition);
_logger.LogInformation("任务树根级别结构变化,已自动保存任务定义 '{TaskName}'", SelectedTaskDefinition.Name);
}
catch (Exception ex)
{
_logger.LogError(ex, "自动保存任务定义 {TaskName} 时发生错误", SelectedTaskDefinition.Name);
}
}
}
/// <summary>
/// 初始化数据
/// </summary>
private async void InitializeData()
{
try
{
// 从 JSON 文件加载任务定义
var loadedTasks = await _storageService.LoadAllTaskDefinitionsAsync();
// 按order字段排序
var sortedTasks = loadedTasks.OrderBy(t => t.Order).ToList();
foreach (var task in sortedTasks)
{
TaskDefinitions.Add(task);
// 为每个任务定义设置属性变化监听
SetupTaskDefinitionPropertyChanged(task);
}
// 如果没有加载到任何任务,创建一个示例任务
if (TaskDefinitions.Count == 0)
{
await CreateSampleTaskAsync();
}
}
catch (Exception ex)
{
_logger.LogError(ex, "初始化任务数据时发生错误");
// 发生错误时创建示例任务
await CreateSampleTaskAsync();
}
}
/// <summary>
/// 创建示例任务
/// </summary>
private async Task CreateSampleTaskAsync()
{
var sampleTask = new GearTaskDefinitionViewModel("示例任务组", "这是一个示例任务组");
if (sampleTask.RootTask != null)
{
sampleTask.RootTask.AddChild(new GearTaskViewModel("采集任务1") { TaskType = "采集任务" });
sampleTask.RootTask.AddChild(new GearTaskViewModel("战斗任务1") { TaskType = "战斗任务" });
var subGroup = new GearTaskViewModel("子任务组", true);
subGroup.AddChild(new GearTaskViewModel("传送任务1") { TaskType = "传送任务" });
subGroup.AddChild(new GearTaskViewModel("交互任务1") { TaskType = "交互任务" });
sampleTask.RootTask.AddChild(subGroup);
}
TaskDefinitions.Add(sampleTask);
SetupTaskDefinitionPropertyChanged(sampleTask);
// 保存示例任务到文件
await _storageService.SaveTaskDefinitionAsync(sampleTask);
}
/// <summary>
/// 选中任务定义时的处理
/// </summary>
partial void OnSelectedTaskDefinitionChanged(GearTaskDefinitionViewModel? value)
{
// 清除之前选中项的状态
foreach (var task in TaskDefinitions)
{
task.IsSelected = false;
}
// 设置当前选中项
if (value != null)
{
value.IsSelected = true;
}
// 先解除之前的事件绑定
CurrentTaskTreeRoot.Children.CollectionChanged -= OnCurrentTaskTreeChanged;
// 设置当前任务树根节点
if (value?.RootTask != null)
{
CurrentTaskTreeRoot = value.RootTask;
}
else
{
CurrentTaskTreeRoot = new GearTaskViewModel();
}
// 重新绑定事件
CurrentTaskTreeRoot.Children.CollectionChanged += OnCurrentTaskTreeChanged;
}
/// <summary>
/// 选择任务定义命令
/// </summary>
[RelayCommand]
private void SelectTaskDefinition(GearTaskDefinitionViewModel? taskDefinition)
{
SelectedTaskDefinition = taskDefinition;
}
/// <summary>
/// 添加新的任务定义
/// </summary>
[RelayCommand]
private async Task AddTaskDefinition()
{
var editViewModel = App.GetService<TaskDefinitionEditWindowViewModel>();
if (editViewModel == null) return;
editViewModel.Name = $"新任务组{TaskDefinitions.Count + 1}";
editViewModel.Description = "";
var editWindow = App.GetService<TaskDefinitionEditWindow>();
if (editWindow == null) return;
editWindow.ViewModel.Name = editViewModel.Name;
editWindow.ViewModel.Description = editViewModel.Description;
editWindow.Owner = Application.Current.MainWindow;
if (editWindow.ShowDialog() == true)
{
var newTask = new GearTaskDefinitionViewModel(editWindow.ViewModel.Name, editWindow.ViewModel.Description);
// 设置新任务的order为当前最大值+1
newTask.Order = TaskDefinitions.Count > 0 ? TaskDefinitions.Max(t => t.Order) + 1 : 0;
TaskDefinitions.Add(newTask);
SetupTaskDefinitionPropertyChanged(newTask);
SelectedTaskDefinition = newTask;
// 自动保存到文件
await _storageService.SaveTaskDefinitionAsync(newTask);
}
}
/// <summary>
/// 删除任务定义
/// </summary>
[RelayCommand]
private async Task DeleteTaskDefinition(GearTaskDefinitionViewModel? taskDefinition)
{
if (taskDefinition == null) return;
var result = MessageBox.Show($"确定要删除任务定义 '{taskDefinition.Name}' 吗?", "确认删除",
MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
var taskName = taskDefinition.Name;
TaskDefinitions.Remove(taskDefinition);
if (SelectedTaskDefinition == taskDefinition)
{
SelectedTaskDefinition = TaskDefinitions.FirstOrDefault();
}
// 删除对应的 JSON 文件
await _storageService.DeleteTaskDefinitionAsync(taskName);
}
}
/// <summary>
/// 编辑选中的任务定义
/// </summary>
[RelayCommand]
private async Task EditSelectedTaskDefinition()
{
if (SelectedTaskDefinition == null) return;
var editViewModel = App.GetService<TaskDefinitionEditWindowViewModel>();
if (editViewModel == null) return;
editViewModel.Name = SelectedTaskDefinition.Name;
editViewModel.Description = SelectedTaskDefinition.Description;
var editWindow = App.GetService<TaskDefinitionEditWindow>();
if (editWindow == null) return;
editWindow.ViewModel.Name = editViewModel.Name;
editWindow.ViewModel.Description = editViewModel.Description;
editWindow.Owner = Application.Current.MainWindow;
if (editWindow.ShowDialog() == true)
{
SelectedTaskDefinition.Name = editWindow.ViewModel.Name;
SelectedTaskDefinition.Description = editWindow.ViewModel.Description;
SelectedTaskDefinition.ModifiedTime = DateTime.Now;
// 自动保存到文件
await _storageService.SaveTaskDefinitionAsync(SelectedTaskDefinition);
_logger.LogInformation("编辑了任务定义: {Name}", SelectedTaskDefinition.Name);
}
}
/// <summary>
/// 删除选中的任务定义
/// </summary>
[RelayCommand]
private async Task DeleteSelectedTaskDefinition()
{
if (SelectedTaskDefinition == null) return;
await DeleteTaskDefinition(SelectedTaskDefinition);
}
/// <summary>
/// 执行选中的任务定义(整组执行)
/// </summary>
[RelayCommand]
private async Task ExecuteSelectedTaskDefinition()
{
await ExecuteTaskDefinition(SelectedTaskDefinition);
}
/// <summary>
/// 执行指定任务定义(整组执行)
/// </summary>
[RelayCommand]
private async Task ExecuteTaskDefinition(GearTaskDefinitionViewModel? taskDefinition)
{
taskDefinition ??= SelectedTaskDefinition;
if (taskDefinition == null)
{
Toast.Warning("请先选择要执行的任务组");
return;
}
try
{
var executor = App.GetRequiredService<GearTaskExecutor>();
if (executor.IsExecuting)
{
Toast.Warning("已有任务组正在执行,请稍后再试");
return;
}
await executor.ExecuteTaskDefinitionAsync(taskDefinition.Name);
}
catch (Exception ex)
{
_logger.LogError(ex, "执行任务定义失败: {TaskName}", taskDefinition.Name);
Toast.Error($"执行任务组失败:{ex.Message}");
}
}
/// <summary>
/// 添加任务节点
/// </summary>
[RelayCommand]
private async Task AddTaskNode(string? taskType = null)
{
if (SelectedTaskDefinition?.RootTask == null)
{
Toast.Warning("请先选择一个任务定义");
return;
}
// 检查选中的节点是否为任务组,任务节点下不允许添加任务节点
if (SelectedTaskNode != null && !SelectedTaskNode.IsDirectory)
{
Toast.Warning("只有任务组下能够添加任务!");
return;
}
// 如果没有指定任务类型默认为Javascript
taskType ??= "Javascript";
GearTaskViewModel newTask;
// 如果是JS脚本类型使用JS脚本选择窗口
if (taskType == "Javascript")
{
var jsSelectionWindow = new JsScriptSelectionWindow
{
Owner = Application.Current.MainWindow
};
jsSelectionWindow.ShowDialog();
if (jsSelectionWindow.DialogResult && jsSelectionWindow.ViewModel.SelectedScript != null)
{
var selectedScript = jsSelectionWindow.ViewModel.SelectedScript;
newTask = new GearTaskViewModel(string.IsNullOrWhiteSpace(selectedScript.Name) ? selectedScript.FolderName : selectedScript.Name)
{
TaskType = "Javascript",
Path = @$"{{jsUserFolder}}\{selectedScript.FolderName}\"
};
}
else
{
return; // 用户取消了操作
}
}
// 如果是地图追踪类型,使用地图追踪任务选择窗口
else if (taskType == "Pathing")
{
var pathingSelectionWindow = new PathingTaskSelectionWindow
{
Owner = Application.Current.MainWindow
};
pathingSelectionWindow.ShowDialog();
if (pathingSelectionWindow.DialogResult && pathingSelectionWindow.SelectedGearTask != null)
{
newTask = pathingSelectionWindow.SelectedGearTask;
}
else
{
return; // 用户取消了操作
}
}
else if (taskType == "Shell")
{
var list = LoadAllKmScripts();
var folder = KeyMouseRecordPageViewModel.ScriptPath;
var combobox = new ComboBox
{
VerticalAlignment = VerticalAlignment.Top
};
foreach (var fileInfo in list)
{
// 计算相对于 KeyMouseScript 文件夹的相对路径
var relativePath = Path.GetRelativePath(folder, fileInfo.FullName);
combobox.Items.Add(relativePath);
}
var str = PromptDialog.Prompt("请选择需要添加的键鼠脚本", "请选择需要添加的键鼠脚本", combobox);
if (!string.IsNullOrEmpty(str))
{
newTask = new GearTaskViewModel(str)
{
TaskType = "Javascript",
Path = @$"{{kmUserFolder}}\{str}\"
};
}
else
{
return;
}
}
else if (taskType == "Shell")
{
var str = PromptDialog.Prompt("执行 shell 操作存在极大风险!请勿输入你看不懂的指令!以免引发安全隐患并损坏系统!\n执行 shell 的时候,游戏可能会失去焦点", "请输入需要执行的shell");
if (!string.IsNullOrEmpty(str))
{
newTask = new GearTaskViewModel(str)
{
TaskType = "Shell",
Parameters = str
};
}
else
{
return;
}
}
else
{
// 其他类型使用原有的对话框
var dialogResult = AddTaskNodeDialog.ShowDialog(taskType, Application.Current.MainWindow);
if (dialogResult == null)
{
return; // 用户取消了操作
}
newTask = new GearTaskViewModel(dialogResult.TaskName)
{
TaskType = dialogResult.TaskType,
};
}
// 如果有选中的节点,则在选中节点下新增
// 如果未选择节点,则在根节点下直接新增
var targetParent = SelectedTaskNode ?? SelectedTaskDefinition.RootTask;
targetParent.AddChild(newTask);
// 展开父节点
targetParent.IsExpanded = true;
SelectedTaskDefinition.ModifiedTime = DateTime.Now;
// 自动保存到文件
await _storageService.SaveTaskDefinitionAsync(SelectedTaskDefinition);
}
private List<FileInfo> LoadAllKmScripts()
{
var folder = KeyMouseRecordPageViewModel.ScriptPath;
// 获取所有脚本项目
var files = Directory.GetFiles(folder, "*.*",
SearchOption.AllDirectories);
return files.Select(file => new FileInfo(file)).ToList();
}
/// <summary>
/// 添加任务组
/// </summary>
[RelayCommand]
private async Task AddTaskGroup()
{
if (SelectedTaskDefinition?.RootTask == null)
{
Toast.Warning("请先选择一个任务定义");
return;
}
// 检查选中的节点是否为任务组,任务节点下不允许添加任务组
if (SelectedTaskNode != null && !SelectedTaskNode.IsDirectory)
{
Toast.Warning("任务节点下不允许添加任务组,只有任务组下能够添加任务组");
return;
}
// 弹出对话框输入任务组名称
var groupName = PromptDialog.Prompt("请输入任务组名称:", "添加任务组", $"新任务组{DateTime.Now:HHmmss}");
if (string.IsNullOrWhiteSpace(groupName))
{
return; // 用户取消了操作
}
var newGroup = new GearTaskViewModel(groupName, true);
// 如果有选中的节点,则在选中节点下新增
// 如果未选择节点,则在根节点下直接新增
var targetParent = SelectedTaskNode ?? SelectedTaskDefinition.RootTask;
targetParent.AddChild(newGroup);
// 展开父节点
targetParent.IsExpanded = true;
SelectedTaskDefinition.ModifiedTime = DateTime.Now;
// 自动保存到文件
await _storageService.SaveTaskDefinitionAsync(SelectedTaskDefinition);
}
/// <summary>
/// 删除任务节点
/// </summary>
[RelayCommand]
private async Task DeleteTaskNode(GearTaskViewModel? taskNode)
{
if (taskNode == null || SelectedTaskDefinition?.RootTask == null) return;
var result = MessageBox.Show($"确定要删除任务 '{taskNode.Name}' 吗?", "确认删除",
MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
RemoveTaskFromTree(SelectedTaskDefinition.RootTask, taskNode);
SelectedTaskDefinition.ModifiedTime = DateTime.Now;
// 自动保存到文件
await _storageService.SaveTaskDefinitionAsync(SelectedTaskDefinition);
}
}
/// <summary>
/// 从树中移除任务节点
/// </summary>
private bool RemoveTaskFromTree(GearTaskViewModel parent, GearTaskViewModel target)
{
if (parent.Children.Contains(target))
{
parent.RemoveChild(target);
return true;
}
foreach (var child in parent.Children)
{
if (RemoveTaskFromTree(child, target))
{
return true;
}
}
return false;
}
/// <summary>
/// 从JSON文件重新加载所有任务定义内部使用
/// </summary>
private async Task LoadFromJsonInternal()
{
try
{
TaskDefinitions.Clear();
var loadedTasks = await _storageService.LoadAllTaskDefinitionsAsync();
foreach (var task in loadedTasks)
{
TaskDefinitions.Add(task);
SetupTaskDefinitionPropertyChanged(task);
}
_logger.LogInformation("从JSON文件重新加载了 {Count} 个任务定义", loadedTasks.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "从JSON文件加载任务定义时发生错误");
}
}
/// <summary>
/// 为任务定义设置属性变化监听器,实现自动保存
/// </summary>
private void SetupTaskDefinitionPropertyChanged(GearTaskDefinitionViewModel taskDefinition)
{
taskDefinition.PropertyChanged += async (sender, e) =>
{
if (sender is GearTaskDefinitionViewModel task)
{
try
{
await _storageService.SaveTaskDefinitionAsync(task);
}
catch (Exception ex)
{
_logger.LogError(ex, "自动保存任务定义 {TaskName} 时发生错误", task.Name);
}
}
};
// 为根任务及其所有子任务设置监听器
if (taskDefinition.RootTask != null)
{
SetupTaskPropertyChangeListener(taskDefinition.RootTask, taskDefinition);
}
}
/// <summary>
/// 递归为任务及其子任务设置属性变化监听器
/// </summary>
private void SetupTaskPropertyChangeListener(GearTaskViewModel task, GearTaskDefinitionViewModel parentDefinition)
{
task.PropertyChanged += async (sender, e) =>
{
try
{
parentDefinition.ModifiedTime = DateTime.Now;
await _storageService.SaveTaskDefinitionAsync(parentDefinition);
}
catch (Exception ex)
{
_logger.LogError(ex, "自动保存任务定义 {TaskName} 时发生错误", parentDefinition.Name);
}
};
// 为子任务设置监听器
foreach (var child in task.Children)
{
SetupTaskPropertyChangeListener(child, parentDefinition);
}
// 监听子任务集合变化
task.Children.CollectionChanged += async (sender, e) =>
{
if (e.NewItems != null)
{
foreach (GearTaskViewModel newTask in e.NewItems)
{
SetupTaskPropertyChangeListener(newTask, parentDefinition);
}
}
// 任何集合变化都触发保存(包括拖拽重排序)
try
{
parentDefinition.ModifiedTime = DateTime.Now;
await _storageService.SaveTaskDefinitionAsync(parentDefinition);
_logger.LogInformation("任务树结构变化,已自动保存任务定义 '{TaskName}'", parentDefinition.Name);
}
catch (Exception ex)
{
_logger.LogError(ex, "自动保存任务定义 {TaskName} 时发生错误", parentDefinition.Name);
}
};
}
/// <summary>
/// 刷新当前任务树显示
/// </summary>
}