mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-25 10:05:49 +08:00
feat: 新增采集与锄地一键采集工具页面2
This commit is contained in:
@@ -156,6 +156,7 @@ public partial class App : Application
|
||||
services.AddView<KeyMouseRecordPage, KeyMouseRecordPageViewModel>();
|
||||
services.AddView<JsListPage, JsListViewModel>();
|
||||
services.AddView<MapPathingPage, MapPathingViewModel>();
|
||||
services.AddView<GatheringAndFarmingPage, GatheringAndFarmingPageViewModel>();
|
||||
services.AddView<OneDragonFlowPage, OneDragonFlowViewModel>();
|
||||
services.AddSingleton<PathingConfigViewModel>();
|
||||
// services.AddView<PathingConfigView, PathingConfigViewModel>();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using BetterGenshinImpact.Core.Script.Group;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -24,6 +25,9 @@ public class GearTaskDefinitionData
|
||||
[JsonProperty("order")]
|
||||
public int Order { get; set; } = 0;
|
||||
|
||||
[JsonProperty("group_config")]
|
||||
public ScriptGroupConfig GroupConfig { get; set; } = new();
|
||||
|
||||
[JsonProperty("root_task")]
|
||||
public GearTaskData? RootTask { get; set; }
|
||||
}
|
||||
@@ -65,4 +69,4 @@ public class GearTaskData
|
||||
|
||||
[JsonProperty("children")]
|
||||
public List<GearTaskData> Children { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using BetterGenshinImpact.Model.Gear.Tasks;
|
||||
using BetterGenshinImpact.Model.Gear.Parameter;
|
||||
using BetterGenshinImpact.Core.Script;
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
using BetterGenshinImpact.Core.Script.Group;
|
||||
using BetterGenshinImpact.Service.GearTask;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
@@ -52,7 +53,7 @@ public class GearTaskConverter
|
||||
{
|
||||
_logger.LogInformation("开始转换任务定义: {TaskDefinitionName}", taskDefinition.Name);
|
||||
|
||||
var rootTask = await ConvertTaskDataAsync(taskDefinition.RootTask);
|
||||
var rootTask = await ConvertTaskDataAsync(taskDefinition.RootTask, null, taskDefinition.GroupConfig);
|
||||
tasks.Add(rootTask);
|
||||
|
||||
_logger.LogInformation("任务定义转换完成: {TaskDefinitionName}, 共 {TaskCount} 个任务",
|
||||
@@ -73,7 +74,7 @@ public class GearTaskConverter
|
||||
/// <param name="taskData">任务数据</param>
|
||||
/// <param name="parent">父任务</param>
|
||||
/// <returns>转换后的任务</returns>
|
||||
public async Task<BaseGearTask> ConvertTaskDataAsync(GearTaskData taskData, BaseGearTask? parent = null)
|
||||
public async Task<BaseGearTask> ConvertTaskDataAsync(GearTaskData taskData, BaseGearTask? parent = null, ScriptGroupConfig? groupConfig = null)
|
||||
{
|
||||
if (taskData == null)
|
||||
{
|
||||
@@ -106,7 +107,7 @@ public class GearTaskConverter
|
||||
else
|
||||
{
|
||||
// 使用工厂创建具体的任务实例
|
||||
var preparedTaskData = PrepareTaskDataForExecution(taskData);
|
||||
var preparedTaskData = PrepareTaskDataForExecution(taskData, groupConfig);
|
||||
task = await _taskFactory.CreateTaskAsync(preparedTaskData);
|
||||
task.Father = parent;
|
||||
|
||||
@@ -123,7 +124,7 @@ public class GearTaskConverter
|
||||
{
|
||||
try
|
||||
{
|
||||
var childTask = await ConvertTaskDataAsync(childData, task);
|
||||
var childTask = await ConvertTaskDataAsync(childData, task, groupConfig);
|
||||
task.Children.Add(childTask);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -369,49 +370,54 @@ public class GearTaskConverter
|
||||
return result;
|
||||
}
|
||||
|
||||
private GearTaskData PrepareTaskDataForExecution(GearTaskData taskData)
|
||||
private GearTaskData PrepareTaskDataForExecution(GearTaskData taskData, ScriptGroupConfig? groupConfig)
|
||||
{
|
||||
if (string.Equals(taskData.TaskType, "Javascript", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var javascriptParameters = DeserializeJavascriptParams(taskData.Parameters);
|
||||
javascriptParameters.PathingPartyConfig = groupConfig?.PathingConfig;
|
||||
return BuildPreparedTaskData(taskData, JsonConvert.SerializeObject(javascriptParameters));
|
||||
}
|
||||
|
||||
if (string.Equals(taskData.TaskType, "Shell", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (groupConfig?.EnableShellConfig == true)
|
||||
{
|
||||
return BuildPreparedTaskData(taskData, JsonConvert.SerializeObject(groupConfig.ShellConfig));
|
||||
}
|
||||
|
||||
return taskData;
|
||||
}
|
||||
|
||||
if (!string.Equals(taskData.TaskType, "Pathing", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return taskData;
|
||||
}
|
||||
|
||||
var parameters = DeserializePathingParams(taskData.Parameters);
|
||||
if (!string.IsNullOrWhiteSpace(parameters.Path)
|
||||
&& !TryExtractPathingRepoRelativePath(parameters.Path, out _))
|
||||
var pathingParameters = DeserializePathingParams(taskData.Parameters);
|
||||
pathingParameters.PathingPartyConfig = groupConfig?.PathingConfig;
|
||||
if (!string.IsNullOrWhiteSpace(pathingParameters.Path)
|
||||
&& !TryExtractPathingRepoRelativePath(pathingParameters.Path, out _))
|
||||
{
|
||||
return taskData;
|
||||
return BuildPreparedTaskData(taskData, JsonConvert.SerializeObject(pathingParameters));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(taskData.Path))
|
||||
{
|
||||
return taskData;
|
||||
return BuildPreparedTaskData(taskData, JsonConvert.SerializeObject(pathingParameters));
|
||||
}
|
||||
|
||||
if (TryExtractPathingRepoRelativePath(taskData.Path, out var repoRelativePath)
|
||||
&& repoRelativePath.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
parameters.Path = GetPathingExecutionFilePath(repoRelativePath);
|
||||
pathingParameters.Path = GetPathingExecutionFilePath(repoRelativePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters.Path = taskData.Path.Trim().TrimEnd('\\', '/');
|
||||
pathingParameters.Path = taskData.Path.Trim().TrimEnd('\\', '/');
|
||||
}
|
||||
|
||||
return new GearTaskData
|
||||
{
|
||||
Name = taskData.Name,
|
||||
TaskType = taskData.TaskType,
|
||||
Path = taskData.Path,
|
||||
IsEnabled = taskData.IsEnabled,
|
||||
IsDirectory = taskData.IsDirectory,
|
||||
IsExpanded = taskData.IsExpanded,
|
||||
Parameters = JsonConvert.SerializeObject(parameters),
|
||||
CreatedTime = taskData.CreatedTime,
|
||||
ModifiedTime = taskData.ModifiedTime,
|
||||
Priority = taskData.Priority,
|
||||
Children = taskData.Children
|
||||
};
|
||||
return BuildPreparedTaskData(taskData, JsonConvert.SerializeObject(pathingParameters));
|
||||
}
|
||||
|
||||
private PathingGearTaskParams DeserializePathingParams(string? parametersJson)
|
||||
@@ -431,6 +437,41 @@ public class GearTaskConverter
|
||||
}
|
||||
}
|
||||
|
||||
private JavascriptGearTaskParams DeserializeJavascriptParams(string? parametersJson)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(parametersJson))
|
||||
{
|
||||
return new JavascriptGearTaskParams();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<JavascriptGearTaskParams>(parametersJson) ?? new JavascriptGearTaskParams();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new JavascriptGearTaskParams();
|
||||
}
|
||||
}
|
||||
|
||||
private static GearTaskData BuildPreparedTaskData(GearTaskData taskData, string parametersJson)
|
||||
{
|
||||
return new GearTaskData
|
||||
{
|
||||
Name = taskData.Name,
|
||||
TaskType = taskData.TaskType,
|
||||
Path = taskData.Path,
|
||||
IsEnabled = taskData.IsEnabled,
|
||||
IsDirectory = taskData.IsDirectory,
|
||||
IsExpanded = taskData.IsExpanded,
|
||||
Parameters = parametersJson,
|
||||
CreatedTime = taskData.CreatedTime,
|
||||
ModifiedTime = taskData.ModifiedTime,
|
||||
Priority = taskData.Priority,
|
||||
Children = taskData.Children
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildPathingPlaceholderPath(string repoRelativePath, bool isDirectory)
|
||||
{
|
||||
var normalized = repoRelativePath.Replace('/', Path.DirectorySeparatorChar);
|
||||
|
||||
@@ -185,6 +185,7 @@ public class GearTaskStorageService
|
||||
CreatedTime = viewModel.CreatedTime,
|
||||
ModifiedTime = viewModel.ModifiedTime,
|
||||
Order = viewModel.Order,
|
||||
GroupConfig = viewModel.GroupConfig,
|
||||
RootTask = viewModel.RootTask != null ? ConvertTaskToData(viewModel.RootTask) : null
|
||||
};
|
||||
}
|
||||
@@ -229,6 +230,7 @@ public class GearTaskStorageService
|
||||
CreatedTime = data.CreatedTime,
|
||||
ModifiedTime = data.ModifiedTime,
|
||||
Order = data.Order,
|
||||
GroupConfig = data.GroupConfig ?? new(),
|
||||
RootTask = data.RootTask != null ? ConvertTaskToViewModel(data.RootTask) : null
|
||||
};
|
||||
|
||||
@@ -276,4 +278,4 @@ public class GearTaskStorageService
|
||||
var safeName = string.Join("_", name.Split(invalidChars, StringSplitOptions.RemoveEmptyEntries));
|
||||
return string.IsNullOrWhiteSpace(safeName) ? "unnamed_task" : safeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,13 @@
|
||||
<ui:SymbolIcon Symbol="Map24" />
|
||||
</ui:NavigationViewItem.Icon>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem Content="采集与锄地"
|
||||
NavigationCacheMode="Enabled"
|
||||
TargetPageType="{x:Type pages:GatheringAndFarmingPage}">
|
||||
<ui:NavigationViewItem.Icon>
|
||||
<ui:SymbolIcon Symbol="Map24" />
|
||||
</ui:NavigationViewItem.Icon>
|
||||
</ui:NavigationViewItem>
|
||||
<!-- Script16 -->
|
||||
<ui:NavigationViewItem Content="录制回放"
|
||||
NavigationCacheMode="Enabled"
|
||||
|
||||
@@ -621,11 +621,15 @@
|
||||
<MenuItem Header="Shell" Command="{Binding AddTaskNodeCommand}" CommandParameter="Shell" />
|
||||
<MenuItem Header="C# 方法" Command="{Binding AddTaskNodeCommand}" CommandParameter="CSharp" />
|
||||
</ContextMenu>
|
||||
</ui:DropDownButton.Flyout>
|
||||
</ui:DropDownButton.Flyout>
|
||||
</ui:DropDownButton>
|
||||
<ui:Button Command="{Binding AddTaskGroupCommand}"
|
||||
Content="添加组"
|
||||
Icon="{ui:SymbolIcon Folder24}" />
|
||||
Icon="{ui:SymbolIcon Folder24}"
|
||||
Margin="0,0,10,0" />
|
||||
<ui:Button Command="{Binding OpenSelectedTaskDefinitionSettingsCommand}"
|
||||
Content="设置"
|
||||
Icon="{ui:SymbolIcon Settings24}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- 任务树 -->
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Windows.Controls;
|
||||
using BetterGenshinImpact.Core.Script.Group;
|
||||
using System.Windows.Controls;
|
||||
using BetterGenshinImpact.ViewModel.Pages.View;
|
||||
|
||||
namespace BetterGenshinImpact.View.Pages.View
|
||||
@@ -13,9 +12,8 @@ namespace BetterGenshinImpact.View.Pages.View
|
||||
|
||||
public ScriptGroupConfigView(ScriptGroupConfigViewModel viewModel)
|
||||
{
|
||||
DataContext = ViewModel = viewModel;
|
||||
DataContext = ViewModel = viewModel;
|
||||
InitializeComponent();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using BetterGenshinImpact.Core.Script.Group;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace BetterGenshinImpact.ViewModel.Pages.Component;
|
||||
@@ -39,6 +39,9 @@ public partial class GearTaskDefinitionViewModel : ObservableObject
|
||||
[ObservableProperty]
|
||||
private bool _hasExecutionBadge;
|
||||
|
||||
[ObservableProperty]
|
||||
private ScriptGroupConfig _groupConfig = new();
|
||||
|
||||
/// <summary>
|
||||
/// 任务根节点
|
||||
/// </summary>
|
||||
|
||||
@@ -1,24 +1,32 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
using BetterGenshinImpact.Core.Script;
|
||||
using BetterGenshinImpact.GameTask;
|
||||
using BetterGenshinImpact.Helpers.Ui;
|
||||
using BetterGenshinImpact.Service;
|
||||
using BetterGenshinImpact.Service.GearTask;
|
||||
using BetterGenshinImpact.Service.GearTask.Execution;
|
||||
using BetterGenshinImpact.View.Pages.View;
|
||||
using BetterGenshinImpact.View.Windows;
|
||||
using BetterGenshinImpact.View.Windows.GearTask;
|
||||
using BetterGenshinImpact.ViewModel.Pages.Component;
|
||||
using BetterGenshinImpact.ViewModel.Pages.View;
|
||||
using BetterGenshinImpact.ViewModel.Windows;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wpf.Ui;
|
||||
using Wpf.Ui.Violeta.Controls;
|
||||
|
||||
namespace BetterGenshinImpact.ViewModel.Pages;
|
||||
@@ -432,6 +440,37 @@ public partial class GearTaskListPageViewModel : ViewModel
|
||||
_taskExecutor.StopExecution();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task OpenSelectedTaskDefinitionSettings()
|
||||
{
|
||||
if (SelectedTaskDefinition == null)
|
||||
{
|
||||
Toast.Warning("请先选择一个任务定义");
|
||||
return;
|
||||
}
|
||||
|
||||
var dialogWindow = new Wpf.Ui.Controls.FluentWindow
|
||||
{
|
||||
Title = "任务组设置",
|
||||
Content = new ScriptGroupConfigView(new ScriptGroupConfigViewModel(TaskContext.Instance().Config, SelectedTaskDefinition.GroupConfig)),
|
||||
Width = 800,
|
||||
Height = 600,
|
||||
MinWidth = 800,
|
||||
MaxWidth = 800,
|
||||
MinHeight = 600,
|
||||
Owner = Application.Current.MainWindow,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
ExtendsContentIntoTitleBar = true,
|
||||
WindowBackdropType = Wpf.Ui.Controls.WindowBackdropType.Auto,
|
||||
};
|
||||
dialogWindow.SourceInitialized += (_, _) => WindowHelper.TryApplySystemBackdrop(dialogWindow);
|
||||
|
||||
dialogWindow.ShowDialog();
|
||||
|
||||
SelectedTaskDefinition.ModifiedTime = DateTime.Now;
|
||||
await _storageService.SaveTaskDefinitionAsync(SelectedTaskDefinition);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddTaskNode(string? taskType = null)
|
||||
{
|
||||
@@ -679,6 +718,11 @@ public partial class GearTaskListPageViewModel : ViewModel
|
||||
{
|
||||
SetupTaskPropertyChangeListener(taskDefinition.RootTask, taskDefinition);
|
||||
}
|
||||
|
||||
if (taskDefinition.GroupConfig != null)
|
||||
{
|
||||
SetupTaskGroupConfigChangeListener(taskDefinition.GroupConfig, taskDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -726,6 +770,115 @@ public partial class GearTaskListPageViewModel : ViewModel
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 递归监听任务组配置及其子配置变化,实现自动保存。
|
||||
/// </summary>
|
||||
private void SetupTaskGroupConfigChangeListener(object configObject, GearTaskDefinitionViewModel parentDefinition)
|
||||
{
|
||||
var visited = new HashSet<object>();
|
||||
SubscribeTaskGroupConfigChanges(configObject, parentDefinition, visited);
|
||||
}
|
||||
|
||||
private void SubscribeTaskGroupConfigChanges(object? current, GearTaskDefinitionViewModel parentDefinition, HashSet<object> visited)
|
||||
{
|
||||
if (current == null || !visited.Add(current))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (current is INotifyPropertyChanged notifyPropertyChanged)
|
||||
{
|
||||
notifyPropertyChanged.PropertyChanged += async (_, _) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
parentDefinition.ModifiedTime = DateTime.Now;
|
||||
await _storageService.SaveTaskDefinitionAsync(parentDefinition);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "任务组配置自动保存失败: {TaskName}", parentDefinition.Name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (current is INotifyCollectionChanged notifyCollectionChanged)
|
||||
{
|
||||
notifyCollectionChanged.CollectionChanged += async (_, e) =>
|
||||
{
|
||||
if (e.NewItems != null)
|
||||
{
|
||||
foreach (var item in e.NewItems)
|
||||
{
|
||||
SubscribeTaskGroupConfigChanges(item, parentDefinition, visited);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
parentDefinition.ModifiedTime = DateTime.Now;
|
||||
await _storageService.SaveTaskDefinitionAsync(parentDefinition);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "任务组配置自动保存失败: {TaskName}", parentDefinition.Name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
foreach (var child in EnumerateConfigChildren(current))
|
||||
{
|
||||
SubscribeTaskGroupConfigChanges(child, parentDefinition, visited);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<object> EnumerateConfigChildren(object current)
|
||||
{
|
||||
var properties = current.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||
foreach (var property in properties)
|
||||
{
|
||||
if (!property.CanRead || property.GetIndexParameters().Length > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (property.PropertyType == typeof(string) || property.PropertyType.IsValueType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
object? value;
|
||||
try
|
||||
{
|
||||
value = property.GetValue(current);
|
||||
}
|
||||
catch
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value == null || value is string)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value is IEnumerable enumerable)
|
||||
{
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
if (item != null && item is not string)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshTaskDefinitionExecutionSummariesAsync()
|
||||
{
|
||||
foreach (var taskDefinition in TaskDefinitions)
|
||||
|
||||
Reference in New Issue
Block a user