Merge remote-tracking branch 'origin/main'

This commit is contained in:
辉鸭蛋
2025-01-30 08:08:09 +08:00
4 changed files with 225 additions and 35 deletions

View File

@@ -386,7 +386,7 @@ namespace LogParse
return "Invalid input time format. Please use 'yyyy-MM-dd HH:mm:ss'.";
}
}
public static string GenerHtmlByConfigGroupEntity(List<ConfigGroupEntity> configGroups, GameInfo gameInfo,LogParseConfig.ScriptGroupLogParseConfig scriptGroupLogParseConfig)
public static string GenerHtmlByConfigGroupEntity(List<ConfigGroupEntity> configGroups, GameInfo? gameInfo,LogParseConfig.ScriptGroupLogParseConfig scriptGroupLogParseConfig)
{
(string name, Func<ConfigTask, string> value)[] colConfigs =
[

View File

@@ -1,4 +1,7 @@
using BetterGenshinImpact.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using BetterGenshinImpact.Model;
using System.IO;
using System.Linq;
@@ -54,4 +57,103 @@ public class FileTreeNodeHelper
parentNode.Children.Add(fileNode);
}
}
/// <summary>
/// 根据路径过滤树形结构,排除指定路径的节点
/// </summary>
/// <typeparam name="T">节点数据类型</typeparam>
/// <param name="nodes">树形结构的根节点集合</param>
/// <param name="folderNames">要排除的路径集合</param>
/// <returns>过滤后的树形结构</returns>
public static ObservableCollection<FileTreeNode<T>> FilterTree<T>(
ObservableCollection<FileTreeNode<T>> nodes,
List<string> folderNames)
{
// 递归过滤节点
ObservableCollection<FileTreeNode<T>> FilterNodes(ObservableCollection<FileTreeNode<T>> inputNodes, List<string> paths)
{
var filteredNodes = new ObservableCollection<FileTreeNode<T>>();
foreach (var node in inputNodes)
{
// 获取当前层需要排除的路径
var matchedPaths = paths
.Where(path => IsPathMatch(node.FileName ??"", path))
.Select(path => GetRemainingPath(node.FileName ?? "" , path))
.Where(remainingPath => remainingPath != null)
.ToList();
// 如果当前路径完全匹配,跳过当前节点
if (matchedPaths.Any(path => path == ""))
{
continue;
}
// 递归对子节点过滤
node.Children = FilterNodes(node.Children, matchedPaths);
// 添加过滤后的节点
filteredNodes.Add(node);
}
return filteredNodes;
}
return FilterNodes(nodes, folderNames);
}
/// <summary>
/// 判断文件路径是否匹配多级路径的前缀
/// </summary>
/// <param name="fileName">当前节点路径</param>
/// <param name="path">目标路径</param>
/// <returns>是否匹配</returns>
private static bool IsPathMatch(string fileName, string path)
{
// 匹配路径是否以指定前缀开始,路径分隔符对齐
return path.StartsWith(fileName, StringComparison.OrdinalIgnoreCase)
&& (path.Length == fileName.Length || path[fileName.Length] == '\\');
}
/// <summary>
/// 获取路径中去掉当前节点后的剩余路径
/// </summary>
/// <param name="fileName">当前节点路径</param>
/// <param name="path">完整路径</param>
/// <returns>剩余路径,如果不匹配返回 null</returns>
private static string? GetRemainingPath(string fileName, string path)
{
if (IsPathMatch(fileName, path))
{
return path.Length > fileName.Length ? path.Substring(fileName.Length + 1) : "";
}
return null;
}
public static ObservableCollection<FileTreeNode<T>> FilterEmptyNodes<T>(ObservableCollection<FileTreeNode<T>> nodes)
{
// 递归过滤节点
ObservableCollection<FileTreeNode<T>> Filter(ObservableCollection<FileTreeNode<T>> inputNodes)
{
var filteredNodes = new ObservableCollection<FileTreeNode<T>>();
foreach (var node in inputNodes)
{
// 递归处理子节点
node.Children = Filter(node.Children);
// 如果是目录并且没有子节点,跳过当前节点
if (node.IsDirectory && !node.Children.Any())
{
continue;
}
// 其他情况保留节点
filteredNodes.Add(node);
}
return filteredNodes;
}
return Filter(nodes);
}
}

View File

@@ -331,6 +331,22 @@
<MenuItem Command="{Binding DeleteScriptCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"
Header="移除" />
<MenuItem Command="{Binding DeleteScriptByFolderCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"
Header="根据文件夹移除">
<MenuItem.Style>
<Style TargetType="MenuItem">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem.Type}"
Value="Pathing">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
<ListView.Style>

View File

@@ -11,7 +11,6 @@ using System.Text.Json;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Script;
using BetterGenshinImpact.Core.Script.Group;
@@ -30,6 +29,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LogParse;
using Microsoft.Extensions.Logging;
using SharpCompress;
using Wpf.Ui;
using Wpf.Ui.Controls;
using Wpf.Ui.Violeta.Controls;
@@ -107,25 +107,32 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
[RelayCommand]
private void ClearTasks()
{
if (SelectedScriptGroup == null)
{
return;
}
SelectedScriptGroup.Projects.Clear();
WriteScriptGroup(SelectedScriptGroup);
}
[RelayCommand]
private async Task OpenLogParse()
{
GameInfo gameInfo = null;
if (SelectedScriptGroup == null)
{
return;
}
GameInfo? gameInfo = null;
var config = LogParse.LogParse.LoadConfig();
if (!string.IsNullOrEmpty(config.Cookie))
{
config.CookieDictionary.TryGetValue(config.Cookie, out gameInfo);
}
LogParseConfig.ScriptGroupLogParseConfig sgpc;
if (!config.ScriptGroupLogDictionary.TryGetValue(_selectedScriptGroup.Name,out sgpc))
LogParseConfig.ScriptGroupLogParseConfig? sgpc;
if (!config.ScriptGroupLogDictionary.TryGetValue(SelectedScriptGroup.Name,out sgpc))
{
sgpc=new LogParseConfig.ScriptGroupLogParseConfig();
}
// 创建 StackPanel
var stackPanel = new StackPanel
@@ -265,9 +272,9 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
PrimaryButtonText = "确定",
Owner = Application.Current.MainWindow,
};
questionButton.Click += (sender, args) =>
{
void OnQuestionButtonOnClick(object sender, RoutedEventArgs args)
{
WebpageWindow cookieWin = new()
{
Title = "日志分析",
@@ -278,8 +285,9 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
};
cookieWin.NavigateToHtml(TravelsDiaryDetailManager.generHtmlMessage());
cookieWin.Show();
}
};
questionButton.Click += OnQuestionButtonOnClick;
//对象赋值
rangeComboBox.SelectedValue = sgpc.RangeValue;
@@ -305,7 +313,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
sgpc.HoeingDelay = hoeingDelayTextBox.Text;
config.Cookie = cookieValue;
config.ScriptGroupLogDictionary[_selectedScriptGroup.Name]=sgpc;
config.ScriptGroupLogDictionary[SelectedScriptGroup.Name]=sgpc;
LogParse.LogParse.WriteConfigFile(config);
@@ -339,7 +347,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
Toast.Warning("未填写cookie此次将不启用锄地统计");
}
//真正存储的gameinfo
GameInfo realGameInfo = gameInfo;
GameInfo? realGameInfo = gameInfo;
//统计锄地开关打开并且不为cookie不为空
if ((hoeingStatsSwitch.IsChecked ?? false) && !string.IsNullOrEmpty(cookieValue))
{
@@ -350,7 +358,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
Toast.Success($"米游社数据获取成功,开始进行解析,请耐心等待!");
}
catch (Exception e)
catch (Exception)
{
if (realGameInfo!=null)
{
@@ -378,7 +386,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
var configGroupEntities = LogParse.LogParse.ParseFile(fs);
if (rangeValue == "CurrentConfig") {
//Toast.Success(_selectedScriptGroup.Name);
configGroupEntities =configGroupEntities.Where(item => _selectedScriptGroup.Name == item.Name).ToList();
configGroupEntities =configGroupEntities.Where(item => SelectedScriptGroup.Name == item.Name).ToList();
}
if (configGroupEntities.Count == 0)
{
@@ -414,11 +422,11 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
ScriptRepoUpdater.Instance.OpenLocalRepoInWebView();
}
[RelayCommand]
private async Task UpdateTasks()
private void UpdateTasks()
{
List<ScriptGroupProject> projects = new();
List<ScriptGroupProject> oldProjects = new();
oldProjects.AddRange(SelectedScriptGroup?.Projects);
oldProjects.AddRange(SelectedScriptGroup?.Projects ?? []);
var oldcount = oldProjects.Count;
List<string> folderNames = new();
foreach (var project in oldProjects)
@@ -451,25 +459,25 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
}
}
SelectedScriptGroup.Projects.Clear();
SelectedScriptGroup?.Projects.Clear();
foreach (var scriptGroupProject in projects)
{
SelectedScriptGroup?.AddProject(scriptGroupProject);
}
Toast.Success($"增加了{projects.Count - oldcount}个路径追踪任务");
WriteScriptGroup(SelectedScriptGroup);
if (SelectedScriptGroup != null) WriteScriptGroup(SelectedScriptGroup);
}
[RelayCommand]
private void ReverseTaskOrder()
{
List<ScriptGroupProject> projects = new();
projects.AddRange(SelectedScriptGroup?.Projects.Reverse());
SelectedScriptGroup.Projects.Clear();
projects.ForEach(item=>SelectedScriptGroup.Projects.Add(item));
WriteScriptGroup(SelectedScriptGroup);
projects.AddRange(SelectedScriptGroup?.Projects.Reverse() ?? []);
SelectedScriptGroup?.Projects.Clear();
projects.ForEach(item=>SelectedScriptGroup?.Projects.Add(item));
if (SelectedScriptGroup != null) WriteScriptGroup(SelectedScriptGroup);
}
[RelayCommand]
@@ -498,8 +506,12 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
else
{
var newScriptGroup =JsonSerializer.Deserialize<ScriptGroup>(JsonSerializer.Serialize(item)) ;
newScriptGroup.Name = str;
ScriptGroups.Add(newScriptGroup);
if (newScriptGroup != null)
{
newScriptGroup.Name = str;
ScriptGroups.Add(newScriptGroup);
}
//WriteScriptGroup(newScriptGroup);
}
}
@@ -628,12 +640,26 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
private ScrollViewer CreatePathingScriptSelectionPanel(IEnumerable<FileTreeNode<PathingTask>> list)
{
var stackPanel = new StackPanel();
CheckBox excludeCheckBox = new CheckBox
{
Content = "排除已选择过的目录",
VerticalAlignment = VerticalAlignment.Center,
};
stackPanel.Children.Add(excludeCheckBox);
var filterTextBox = new TextBox
{
Margin = new Thickness(0, 0, 0, 10),
PlaceholderText = "输入筛选条件..."
PlaceholderText = "输入筛选条件...",
};
filterTextBox.TextChanged += delegate
{
ApplyFilter(stackPanel, list, filterTextBox.Text, excludeCheckBox.IsChecked);
};
excludeCheckBox.Click += delegate
{
ApplyFilter(stackPanel, list, filterTextBox.Text, excludeCheckBox.IsChecked);
};
filterTextBox.TextChanged += (s, e) => ApplyFilter(stackPanel, list, filterTextBox.Text);
stackPanel.Children.Add(filterTextBox);
AddNodesToPanel(stackPanel, list, 0, filterTextBox.Text);
@@ -647,14 +673,50 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
return scrollViewer;
}
private void ApplyFilter(StackPanel parentPanel, IEnumerable<FileTreeNode<PathingTask>> nodes, string filter)
private void ApplyFilter(StackPanel parentPanel, IEnumerable<FileTreeNode<PathingTask>> nodes, string filter,bool? excludeSelectedFolder = false)
{
if (parentPanel.Children.Count > 0 && parentPanel.Children[0] is TextBox filterTextBox)
if (parentPanel.Children.Count > 0)
{
List<UIElement> removeElements = new List<UIElement>();
foreach (UIElement parentPanelChild in parentPanel.Children)
{
if (parentPanelChild is FrameworkElement frameworkElement && frameworkElement.Name.StartsWith("dynamic_"))
{
removeElements.Add(frameworkElement);
}
}
removeElements.ForEach(parentPanel.Children.Remove);
}
if (excludeSelectedFolder ?? false)
{
List<string> skipFolderNames = SelectedScriptGroup?.Projects.ToList().Select(item=>item.FolderName).Distinct().ToList() ?? [];
//复制Nodes
string jsonString = JsonSerializer.Serialize(nodes);
var copiedNodes = JsonSerializer.Deserialize<ObservableCollection<FileTreeNode<PathingTask>>>(jsonString);
if (copiedNodes!=null)
{
//路径过滤
copiedNodes = FileTreeNodeHelper.FilterTree(copiedNodes, skipFolderNames);
copiedNodes = FileTreeNodeHelper.FilterEmptyNodes(copiedNodes);
AddNodesToPanel(parentPanel, copiedNodes, 0,filter);
}
}
else
{
AddNodesToPanel(parentPanel, nodes, 0,filter);
}
/*if (parentPanel.Children.Count > 0 && parentPanel.Children[1] is TextBox filterTextBox)
{
parentPanel.Children.Clear();
parentPanel.Children.Add(filterTextBox); // 保留筛选框
AddNodesToPanel(parentPanel, nodes, 0, filter);
}
}*/
}
private void AddNodesToPanel(StackPanel parentPanel, IEnumerable<FileTreeNode<PathingTask>> nodes, int depth, string filter)
@@ -671,6 +733,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
Content = node.FileName,
Tag = node.FilePath,
Margin = new Thickness(depth * 30, 0, 0, 0) // 根据深度计算Margin
,Name = "dynamic_"+Guid.NewGuid().ToString().Replace("-","_")
};
if (node.IsDirectory)
@@ -683,6 +746,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
Header = checkBox,
Content = childPanel,
IsExpanded = false // 默认不展开
,Name = "dynamic_"+Guid.NewGuid().ToString().Replace("-","_")
};
checkBox.Checked += (s, e) => SetChildCheckBoxesState(childPanel, true);
@@ -793,7 +857,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
[RelayCommand]
private void AddNextFlag(ScriptGroupProject? item)
{
if (item == null)
if (item == null || SelectedScriptGroup == null)
{
return;
}
@@ -805,8 +869,8 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
nextScheduledTask.Remove(nst);
}
nextScheduledTask.Add((SelectedScriptGroup?.Name, item.Index, item.FolderName, item.Name));
foreach (var item1 in SelectedScriptGroup.Projects)
nextScheduledTask.Add((SelectedScriptGroup?.Name ?? "", item.Index, item.FolderName, item.Name));
foreach (var item1 in SelectedScriptGroup?.Projects ?? [])
{
item1.NextFlag = false;
}
@@ -878,7 +942,15 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
Toast.Warning("只有JS脚本才有自定义配置");
}
}
[RelayCommand]
public void OnDeleteScriptByFolder(ScriptGroupProject? item)
{
if (item == null)
{
return;
}
SelectedScriptGroup?.Projects.ToList().Where(item2=>item2.FolderName == item.FolderName).ForEach(OnDeleteScript);
}
[RelayCommand]
public void OnDeleteScript(ScriptGroupProject? item)
{