using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Win32; using PersonalToolBox.Helpers; using PersonalToolBox.Models; using PersonalToolBox.Services; namespace PersonalToolBox.ViewModels; /// /// 工具编辑窗口 ViewModel,管理添加/编辑工具的交互逻辑 /// public partial class ToolEditViewModel : ObservableObject { private readonly IDataService _dataService; private readonly ILogService _logService; private readonly ToolItem? _editingTool; // ───────────────────────────── 可观察属性 ───────────────────────────── [ObservableProperty] private string _windowTitle = "添加工具"; [ObservableProperty] private string _name = string.Empty; [ObservableProperty] private string _executablePath = string.Empty; [ObservableProperty] private string _arguments = string.Empty; [ObservableProperty] private string _hotKey = string.Empty; [ObservableProperty] private Category? _selectedCategory; [ObservableProperty] private IconProvider.IconOption? _selectedIcon; /// /// 分类下拉列表 /// public ObservableCollection Categories { get; } = new(); /// /// 按分类分组的可用图标列表 /// public ListCollectionView AvailableIconsView { get; } /// /// 窗口关闭回调(由 View 层设置) /// public Action? CloseAction { get; set; } /// /// 是否保存成功(供调用方判断是否需要刷新) /// public bool Saved { get; private set; } // ───────────────────────────── 构造函数 ───────────────────────────── public ToolEditViewModel(IDataService dataService, ILogService logService, ToolItem? toolToEdit = null) { _dataService = dataService; _logService = logService; _editingTool = toolToEdit; AvailableIconsView = new ListCollectionView(IconProvider.AvailableIcons); AvailableIconsView.GroupDescriptions.Add(new PropertyGroupDescription("Category")); // 加载分类列表 foreach (var cat in _dataService.Config.Categories) Categories.Add(cat); // 编辑模式 if (toolToEdit != null) { WindowTitle = "编辑工具"; Name = toolToEdit.Name; ExecutablePath = toolToEdit.ExecutablePath; Arguments = toolToEdit.Arguments; HotKey = toolToEdit.HotKey; SelectedCategory = Categories.FirstOrDefault(c => c.Id == toolToEdit.CategoryId); if (!string.IsNullOrEmpty(toolToEdit.IconCode)) { SelectedIcon = IconProvider.AvailableIcons.FirstOrDefault(i => i.Icon.ToString() == toolToEdit.IconCode); } } } // ───────────────────────────── 命令 ───────────────────────────── /// /// 浏览本地文件 /// [RelayCommand] private void BrowseFile() { var dialog = new OpenFileDialog { Title = "选择可执行文件或脚本", Filter = "所有文件|*.*|可执行文件|*.exe|脚本文件|*.bat;*.cmd;*.ps1;*.py|快捷方式|*.lnk" }; if (dialog.ShowDialog() == true) { ExecutablePath = dialog.FileName; if (string.IsNullOrWhiteSpace(Name)) { Name = Path.GetFileNameWithoutExtension(dialog.FileName); } } } /// /// 保存工具 /// [RelayCommand] private void Save() { try { if (string.IsNullOrWhiteSpace(Name)) { _logService.Warning("工具名称不能为空"); return; } if (string.IsNullOrWhiteSpace(ExecutablePath)) { _logService.Warning($"工具 \"{Name}\" 路径不能为空"); return; } // 编辑模式:更新已有工具 if (_editingTool != null) { _editingTool.Name = Name.Trim(); _editingTool.IconCode = SelectedIcon?.Icon.ToString() ?? string.Empty; _editingTool.ExecutablePath = ExecutablePath.Trim(); _editingTool.Arguments = Arguments.Trim(); _editingTool.HotKey = HotKey.Trim(); _editingTool.CategoryId = SelectedCategory?.Id ?? string.Empty; _editingTool.IsValid = IsExecutablePathValid(ExecutablePath.Trim()); _logService.Info($"已更新工具: {Name.Trim()}"); } // 添加模式:创建新工具 else { var newTool = new ToolItem { Name = Name.Trim(), IconCode = SelectedIcon?.Icon.ToString() ?? string.Empty, ExecutablePath = ExecutablePath.Trim(), Arguments = Arguments.Trim(), HotKey = HotKey.Trim(), CategoryId = SelectedCategory?.Id ?? string.Empty, IsValid = IsExecutablePathValid(ExecutablePath.Trim()) }; _dataService.Config.Tools.Add(newTool); _logService.Info($"已添加工具: {Name.Trim()}"); } _dataService.Save(); Saved = true; CloseAction?.Invoke(true); } catch (Exception ex) { _logService.Error($"保存工具失败: {ex.Message}"); } } /// /// 取消编辑 /// [RelayCommand] private void Cancel() { CloseAction?.Invoke(false); } /// /// 验证可执行路径是否有效 /// private static bool IsExecutablePathValid(string path) { if (string.IsNullOrWhiteSpace(path)) return false; // URL 格式(https://...)直接通过 if (Uri.TryCreate(path, UriKind.Absolute, out var uri) && (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) return true; // 纯文件名(无路径分隔符)可能位于系统 PATH 中 if (!path.Contains('\\') && !path.Contains('/')) return true; return File.Exists(path); } }