Files
better-genshin-impact/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs
辉鸭蛋 a4bc632ae7 ui test
新增任务类属性和配置页面,优化日志记录

在 `BetterGenshinImpact.csproj` 文件中,添加了两个新文件夹路径 `GameTask\OneDragon\` 和 `User\AutoPathing\`。

在多个任务类文件中(如 `AutoDomainTask.cs`、`AutoFightTask.cs`、`AutoGeniusInvokationTask.cs`、`AutoMusicGameTask.cs`、`AutoWoodTask.cs`),新增了 `Name` 属性。

在 `PickAroundHandler.cs` 文件中,优化了 `RunAsync` 方法的日志记录,并添加了超时检查。

在 `ISoloTask.cs` 文件中,新增了 `Name` 属性和 `Start` 方法的接口定义。

在 `OneDragonTaskItem.cs` 文件中,新增了 `ViewModel` 属性。

在 `OneDragonFlowPage.xaml` 文件中,右侧配置部分从 `StackPanel` 改为 `ContentControl`,并添加了 `DataTemplate` 以支持不同任务类型的配置页面。

在 `IViewModel.cs` 文件中,将 `IViewModel` 接口的访问修饰符从 `internal` 改为 `public`。

在 `OneDragonFlowViewModel.cs` 文件中,初始化了任务项的 `ViewModel` 属性。

在 `ScriptControlViewModel.cs` 文件中,移除了构造函数的 `HomePageViewModel` 参数。

新增了 `LoginConfigViewModel.cs` 和 `MailConfigViewModel.cs` 文件,定义了相应的 ViewModel 类。

新增了 `LoginConfigPage.xaml` 和 `MailConfigPage.xaml` 文件,定义了相应的 XAML 布局及其交互逻辑。
2024-10-19 17:42:58 +08:00

582 lines
17 KiB
C#

using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Script.Group;
using BetterGenshinImpact.Core.Script.Project;
using BetterGenshinImpact.GameTask.AutoPathing.Model;
using BetterGenshinImpact.Helpers.Ui;
using BetterGenshinImpact.Model;
using BetterGenshinImpact.Service.Interface;
using BetterGenshinImpact.View.Windows;
using BetterGenshinImpact.View.Windows.Editable;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Wpf.Ui;
using Wpf.Ui.Controls;
using Wpf.Ui.Violeta.Controls;
using StackPanel = Wpf.Ui.Controls.StackPanel;
namespace BetterGenshinImpact.ViewModel.Pages;
public partial class ScriptControlViewModel : ObservableObject, INavigationAware, IViewModel
{
private readonly ISnackbarService _snackbarService;
private readonly ILogger<ScriptControlViewModel> _logger = App.GetLogger<ScriptControlViewModel>();
private readonly IScriptService _scriptService;
/// <summary>
/// 配置组配置
/// </summary>
[ObservableProperty]
private ObservableCollection<ScriptGroup> _scriptGroups = [];
/// <summary>
/// 当前选中的配置组
/// </summary>
[ObservableProperty]
private ScriptGroup? _selectedScriptGroup;
public readonly string ScriptGroupPath = Global.Absolute(@"User\ScriptGroup");
public void OnNavigatedFrom()
{
}
public void OnNavigatedTo()
{
ReadScriptGroup();
}
public ScriptControlViewModel(ISnackbarService snackbarService, IScriptService scriptService)
{
_snackbarService = snackbarService;
_scriptService = scriptService;
ScriptGroups.CollectionChanged += ScriptGroupsCollectionChanged;
}
[RelayCommand]
private void OnAddScriptGroup()
{
var str = PromptDialog.Prompt("请输入配置组名称", "新增配置组");
if (!string.IsNullOrEmpty(str))
{
// 检查是否已存在
if (ScriptGroups.Any(x => x.Name == str))
{
_snackbarService.Show(
"配置组已存在",
$"配置组 {str} 已经存在,请勿重复添加",
ControlAppearance.Caution,
null,
TimeSpan.FromSeconds(2)
);
}
else
{
ScriptGroups.Add(new ScriptGroup { Name = str });
}
}
}
[RelayCommand]
public void OnDeleteScriptGroup(ScriptGroup? item)
{
if (item == null)
{
return;
}
try
{
ScriptGroups.Remove(item);
File.Delete(Path.Combine(ScriptGroupPath, $"{item.Name}.json"));
_snackbarService.Show(
"配置组删除成功",
$"配置组 {item.Name} 已经被删除",
ControlAppearance.Success,
null,
TimeSpan.FromSeconds(2)
);
}
catch (Exception e)
{
_logger.LogDebug(e, "删除配置组配置时失败");
_snackbarService.Show(
"删除配置组配置失败",
$"配置组 {item.Name} 删除失败!",
ControlAppearance.Danger,
null,
TimeSpan.FromSeconds(3)
);
}
}
[RelayCommand]
private void OnAddJsScript()
{
var list = LoadAllJsScriptProjects();
var combobox = new ComboBox();
foreach (var scriptProject in list)
{
combobox.Items.Add(scriptProject.FolderName + " - " + scriptProject.Manifest.Name);
}
var str = PromptDialog.Prompt("请选择需要添加的JS脚本", "请选择需要添加的JS脚本", combobox);
if (!string.IsNullOrEmpty(str))
{
var folderName = str.Split(" - ")[0];
SelectedScriptGroup?.Projects.Add(new ScriptGroupProject(new ScriptProject(folderName)));
}
}
[RelayCommand]
private void OnAddKmScript()
{
var list = LoadAllKmScripts();
var combobox = new ComboBox();
foreach (var fileInfo in list)
{
combobox.Items.Add(fileInfo.Name);
}
var str = PromptDialog.Prompt("请选择需要添加的键鼠脚本", "请选择需要添加的键鼠脚本", combobox);
if (!string.IsNullOrEmpty(str))
{
SelectedScriptGroup?.Projects.Add(ScriptGroupProject.BuildKeyMouseProject(str));
}
}
[RelayCommand]
private void OnAddPathing()
{
var root = FileTreeNodeHelper.LoadDirectory<PathingTask>(MapPathingViewModel.PathJsonPath);
var stackPanel = CreatePathingScriptSelectionPanel(root.Children);
var result = PromptDialog.Prompt("请选择需要添加的路径追踪任务", "请选择需要添加的路径追踪任务", stackPanel, new Size(500, 600));
if (!string.IsNullOrEmpty(result))
{
AddSelectedPathingScripts((StackPanel)stackPanel.Content);
}
}
private ScrollViewer CreatePathingScriptSelectionPanel(IEnumerable<FileTreeNode<PathingTask>> list)
{
var stackPanel = new StackPanel();
AddNodesToPanel(stackPanel, list, 0);
var scrollViewer = new ScrollViewer
{
Content = stackPanel,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
Height = 435 // 固定高度
};
return scrollViewer;
}
private void AddNodesToPanel(StackPanel parentPanel, IEnumerable<FileTreeNode<PathingTask>> nodes, int depth)
{
foreach (var node in nodes)
{
var checkBox = new CheckBox
{
Content = node.FileName,
Tag = node.FilePath,
Margin = new Thickness(depth * 30, 0, 0, 0) // 根据深度计算Margin
};
if (node.IsDirectory)
{
var childPanel = new StackPanel();
AddNodesToPanel(childPanel, node.Children, depth + 1); // 递归调用时深度加1
checkBox.Checked += (s, e) => SetChildCheckBoxesState(childPanel, true);
checkBox.Unchecked += (s, e) => SetChildCheckBoxesState(childPanel, false);
parentPanel.Children.Add(checkBox);
parentPanel.Children.Add(childPanel);
}
else
{
parentPanel.Children.Add(checkBox);
}
}
}
private void SetChildCheckBoxesState(StackPanel childStackPanel, bool state)
{
foreach (var child in childStackPanel.Children)
{
if (child is CheckBox checkBox)
{
checkBox.IsChecked = state;
}
else if (child is StackPanel nestedStackPanel)
{
SetChildCheckBoxesState(nestedStackPanel, state);
}
}
}
private void AddSelectedPathingScripts(StackPanel stackPanel)
{
foreach (var child in stackPanel.Children)
{
if (child is StackPanel childStackPanel)
{
foreach (var grandChild in childStackPanel.Children)
{
if (grandChild is CheckBox { IsChecked: true } checkBox)
{
var fileInfo = new FileInfo((string)checkBox.Tag);
SelectedScriptGroup?.Projects.Add(ScriptGroupProject.BuildPathingProject(fileInfo.Name, fileInfo.Directory!.Name));
}
}
}
}
}
private Dictionary<string, List<FileInfo>> LoadAllPathingScripts()
{
var folder = Global.Absolute(@"User\AutoPathing");
var directories = Directory.GetDirectories(folder);
var result = new Dictionary<string, List<FileInfo>>();
foreach (var directory in directories)
{
var dirInfo = new DirectoryInfo(directory);
var files = dirInfo.GetFiles("*.*", SearchOption.TopDirectoryOnly).ToList();
result.Add(dirInfo.Name, files);
}
return result;
}
private List<ScriptProject> LoadAllJsScriptProjects()
{
var path = Global.ScriptPath();
// 获取所有脚本项目
var projects = Directory.GetDirectories(path)
.Select(x => new ScriptProject(Path.GetFileName(x)))
.ToList();
return projects;
}
private List<FileInfo> LoadAllKmScripts()
{
var folder = Global.Absolute(@"User\KeyMouseScript");
// 获取所有脚本项目
var files = Directory.GetFiles(folder, "*.*",
SearchOption.AllDirectories);
return files.Select(file => new FileInfo(file)).ToList();
}
[RelayCommand]
public void OnEditScriptCommon(ScriptGroupProject? item)
{
if (item == null)
{
return;
}
ShowEditWindow(item);
// foreach (var group in ScriptGroups)
// {
// WriteScriptGroup(group);
// }
}
public static void ShowEditWindow(object viewModel)
{
var uiMessageBox = new Wpf.Ui.Controls.MessageBox
{
Title = "修改通用设置",
Content = new ScriptGroupProjectEditor { DataContext = viewModel },
CloseButtonText = "关闭",
Owner = Application.Current.MainWindow,
};
uiMessageBox.ShowDialogAsync();
}
[RelayCommand]
public void OnEditJsScriptSettings(ScriptGroupProject? item)
{
if (item == null)
{
return;
}
if (item.Project == null)
{
item.BuildScriptProjectRelation();
}
if (item.Project == null)
{
return;
}
if (item.Type == "Javascript")
{
if (item.JsScriptSettingsObject == null)
{
item.JsScriptSettingsObject = new ExpandoObject();
}
var ui = item.Project.LoadSettingUi(item.JsScriptSettingsObject);
if (ui == null)
{
Toast.Warning("此脚本未提供自定义配置");
return;
}
var uiMessageBox = new Wpf.Ui.Controls.MessageBox
{
Title = "修改JS脚本自定义设置 ",
Content = ui,
CloseButtonText = "关闭",
Owner = Application.Current.MainWindow,
};
uiMessageBox.ShowDialogAsync();
// 由于 JsScriptSettingsObject 的存在,这里只能手动再次保存配置
foreach (var group in ScriptGroups)
{
WriteScriptGroup(group);
}
}
else
{
Toast.Warning("只有JS脚本才有自定义配置");
}
}
[RelayCommand]
public void OnDeleteScript(ScriptGroupProject? item)
{
if (item == null)
{
return;
}
SelectedScriptGroup?.Projects.Remove(item);
_snackbarService.Show(
"脚本配置移除成功",
$"{item.Name} 的关联配置已经移除",
ControlAppearance.Success,
null,
TimeSpan.FromSeconds(2)
);
}
private void ScriptGroupsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (ScriptGroup newItem in e.NewItems)
{
newItem.Projects.CollectionChanged += ScriptProjectsCollectionChanged;
foreach (var project in newItem.Projects)
{
project.PropertyChanged += ScriptProjectsPChanged;
}
}
}
if (e.OldItems != null)
{
foreach (ScriptGroup oldItem in e.OldItems)
{
foreach (var project in oldItem.Projects)
{
project.PropertyChanged -= ScriptProjectsPChanged;
}
oldItem.Projects.CollectionChanged -= ScriptProjectsCollectionChanged;
}
}
// 补充排序字段
var i = 1;
foreach (var group in ScriptGroups)
{
group.Index = i++;
}
// 保存配置组配置
foreach (var group in ScriptGroups)
{
WriteScriptGroup(group);
}
}
private void ScriptProjectsPChanged(object? sender, PropertyChangedEventArgs e)
{
foreach (var group in ScriptGroups)
{
WriteScriptGroup(group);
}
}
private void ScriptProjectsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
// 补充排序字段
if (SelectedScriptGroup is { Projects.Count: > 0 })
{
var i = 1;
foreach (var project in SelectedScriptGroup.Projects)
{
project.Index = i++;
}
}
// 保存配置组配置
if (SelectedScriptGroup != null)
{
WriteScriptGroup(SelectedScriptGroup);
}
}
private void WriteScriptGroup(ScriptGroup scriptGroup)
{
try
{
if (!Directory.Exists(ScriptGroupPath))
{
Directory.CreateDirectory(ScriptGroupPath);
}
var file = Path.Combine(ScriptGroupPath, $"{scriptGroup.Name}.json");
File.WriteAllText(file, scriptGroup.ToJson());
}
catch (Exception e)
{
_logger.LogDebug(e, "保存配置组配置时失败");
_snackbarService.Show(
"保存配置组配置失败",
$"{scriptGroup.Name} 保存失败!",
ControlAppearance.Danger,
null,
TimeSpan.FromSeconds(3)
);
}
}
private void ReadScriptGroup()
{
try
{
if (!Directory.Exists(ScriptGroupPath))
{
Directory.CreateDirectory(ScriptGroupPath);
}
ScriptGroups.Clear();
var files = Directory.GetFiles(ScriptGroupPath, "*.json");
List<ScriptGroup> groups = [];
foreach (var file in files)
{
try
{
var json = File.ReadAllText(file);
var group = ScriptGroup.FromJson(json);
groups.Add(group);
}
catch (Exception e)
{
_logger.LogDebug(e, "读取单个配置组配置时失败");
_snackbarService.Show(
"读取配置组配置失败",
"读取配置组配置失败:" + e.Message,
ControlAppearance.Danger,
null,
TimeSpan.FromSeconds(3)
);
}
}
// 按index排序
groups.Sort((a, b) => a.Index.CompareTo(b.Index));
foreach (var group in groups)
{
ScriptGroups.Add(group);
}
}
catch (Exception e)
{
_logger.LogDebug(e, "读取配置组配置时失败");
_snackbarService.Show(
"读取配置组配置失败",
"读取配置组配置失败!",
ControlAppearance.Danger,
null,
TimeSpan.FromSeconds(3)
);
}
}
[RelayCommand]
public void OnGoToScriptGroupUrl()
{
Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/autos/dispatcher.html") { UseShellExecute = true });
}
[RelayCommand]
public void OnImportScriptGroup(string scriptGroupExample)
{
ScriptGroup group = new();
if ("AutoCrystalflyExampleGroup" == scriptGroupExample)
{
group.Name = "晶蝶示例组";
group.Projects.Add(new ScriptGroupProject(new ScriptProject("AutoCrystalfly")));
}
if (ScriptGroups.Any(x => x.Name == group.Name))
{
_snackbarService.Show(
"配置组已存在",
$"配置组 {group.Name} 已经存在,请勿重复添加",
ControlAppearance.Caution,
null,
TimeSpan.FromSeconds(2)
);
return;
}
ScriptGroups.Add(group);
}
[RelayCommand]
public async Task OnStartScriptGroupAsync()
{
if (SelectedScriptGroup == null)
{
_snackbarService.Show(
"未选择配置组",
"请先选择一个配置组",
ControlAppearance.Caution,
null,
TimeSpan.FromSeconds(2)
);
return;
}
await _scriptService.RunMulti(SelectedScriptGroup.Projects, SelectedScriptGroup.Name);
}
}