- 安装 FontAwesome.Sharp v6.6.0,新增 Helpers/IconProvider.cs(190+ 图标,11 分类) - ToolEditWindow/GroupEditWindow: 添加图标选择 ComboBox,按分类分组,带图标预览 - MainWindow 卡片模板: 使用 fa:IconBlock 渲染图标,IconCode 为空时回退首字母 - 组合角标从 emoji 改为 FontAwesome Cubes 图标,放置于卡片右上角 - ComboBox 样式修复: TextBlock→ContentPresenter,支持 ItemTemplate+DisplayMemberPath - ComboBox 滚轮修复: CanContentScroll=False 解决分组模式下滑轮跳组问题 - 编辑弹窗: 所有输入控件添加 VerticalContentAlignment=Center - 组合可包含其他组合: GetAncestorIds 递归排除自身及祖先防止循环引用 - ProcessExecutionService: 支持嵌套组合递归执行,visited 集合防死循环 - MainWindow 右键菜单新增删除功能,工具和组合均支持 - 移除侧边栏顶部个人工具箱标题文字 - 更新测试: 适配组合嵌套逻辑,新增祖先排除测试 (83/83 通过)
116 lines
3.3 KiB
C#
116 lines
3.3 KiB
C#
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using PersonalToolBox.Models;
|
|
|
|
namespace PersonalToolBox.Services;
|
|
|
|
/// <summary>
|
|
/// 进程执行服务,负责启动外部工具进程并处理异常
|
|
/// 支持一键多开:当 IsGroup 为 true 时批量启动子工具(支持嵌套组合,带循环检测)
|
|
/// </summary>
|
|
public class ProcessExecutionService : IProcessExecutionService
|
|
{
|
|
private readonly ILogService _logService;
|
|
private readonly IDataService _dataService;
|
|
|
|
public ProcessExecutionService(ILogService logService, IDataService dataService)
|
|
{
|
|
_logService = logService;
|
|
_dataService = dataService;
|
|
}
|
|
|
|
public async Task ExecuteAsync(ToolItem tool)
|
|
{
|
|
if (tool == null)
|
|
{
|
|
_logService.Error("尝试执行空工具项");
|
|
return;
|
|
}
|
|
|
|
if (tool.IsGroup)
|
|
{
|
|
await ExecuteGroupAsync(tool, new HashSet<string>());
|
|
return;
|
|
}
|
|
|
|
if (!tool.IsValid)
|
|
{
|
|
_logService.Warning($"无法运行工具 \"{tool.Name}\",路径失效: {tool.ExecutablePath}");
|
|
return;
|
|
}
|
|
|
|
LaunchSingleTool(tool);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 批量启动组合中的所有子工具,支持嵌套组合,每次间隔 500ms 防止系统卡顿
|
|
/// </summary>
|
|
private async Task ExecuteGroupAsync(ToolItem group, HashSet<string> visited)
|
|
{
|
|
if (!visited.Add(group.Id))
|
|
{
|
|
_logService.Warning($"组合启动跳过:检测到循环引用 ({group.Name})");
|
|
return;
|
|
}
|
|
|
|
var subTools = new List<ToolItem>();
|
|
foreach (var subId in group.SubToolIds)
|
|
{
|
|
var subTool = _dataService.Config.Tools.FirstOrDefault(t => t.Id == subId);
|
|
if (subTool == null)
|
|
{
|
|
_logService.Warning($"组合启动跳过:找不到 ID 为 {subId} 的工具");
|
|
continue;
|
|
}
|
|
subTools.Add(subTool);
|
|
}
|
|
|
|
_logService.Info($"开始启动组合 \"{group.Name}\",共 {subTools.Count} 个工具");
|
|
|
|
foreach (var subTool in subTools)
|
|
{
|
|
if (subTool.IsGroup)
|
|
{
|
|
await ExecuteGroupAsync(subTool, visited);
|
|
}
|
|
else
|
|
{
|
|
if (!subTool.IsValid)
|
|
{
|
|
_logService.Warning($"组合启动跳过 \"{subTool.Name}\",路径失效: {subTool.ExecutablePath}");
|
|
continue;
|
|
}
|
|
LaunchSingleTool(subTool);
|
|
}
|
|
await Task.Delay(500);
|
|
}
|
|
|
|
_logService.Info($"组合 \"{group.Name}\" 启动完成");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 启动单个工具进程
|
|
/// </summary>
|
|
private void LaunchSingleTool(ToolItem tool)
|
|
{
|
|
try
|
|
{
|
|
var startInfo = new ProcessStartInfo
|
|
{
|
|
FileName = tool.ExecutablePath,
|
|
Arguments = tool.Arguments ?? string.Empty,
|
|
UseShellExecute = true
|
|
};
|
|
|
|
Process.Start(startInfo);
|
|
_logService.Info($"成功启动: {tool.Name}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logService.Error($"启动工具 \"{tool.Name}\" 失败: {ex.Message}");
|
|
}
|
|
}
|
|
}
|