diff --git a/Ink Canvas/Helpers/ConfigProfileManager.cs b/Ink Canvas/Helpers/ConfigProfileManager.cs new file mode 100644 index 00000000..00575196 --- /dev/null +++ b/Ink Canvas/Helpers/ConfigProfileManager.cs @@ -0,0 +1,161 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Ink_Canvas.Helpers +{ + /// + /// 提供多配置文件保存、切换与热重载支持。 + /// 方案保存在 Configs/Profiles 目录下,当前生效的配置仍为 Configs/Settings.json。 + /// + public static class ConfigProfileManager + { + private static readonly string ProfilesDir = Path.Combine(App.RootPath, "Configs", "Profiles"); + private static readonly string SettingsFilePath = Path.Combine(App.RootPath, "Configs", "Settings.json"); + private const string ProfileExtension = ".json"; + + /// 将配置文件名称转为安全文件名(去掉非法字符)。 + private static string ToSafeFileName(string profileName) + { + if (string.IsNullOrWhiteSpace(profileName)) return "未命名"; + var invalid = Path.GetInvalidFileNameChars(); + var name = string.Join("_", profileName.Trim().Split(invalid, StringSplitOptions.RemoveEmptyEntries)); + return string.IsNullOrEmpty(name) ? "未命名" : name; + } + + /// 确保配置文件目录存在。 + public static void EnsureProfilesDirectory() + { + try + { + if (!Directory.Exists(ProfilesDir)) + { + ProcessProtectionManager.WithWriteAccess(ProfilesDir, () => Directory.CreateDirectory(ProfilesDir)); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"创建配置文件目录失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// 获取所有配置文件名称(不含扩展名),按名称排序。 + public static IReadOnlyList ListProfileNames() + { + try + { + EnsureProfilesDirectory(); + if (!Directory.Exists(ProfilesDir)) return Array.Empty(); + var files = Directory.GetFiles(ProfilesDir, "*" + ProfileExtension); + return files + .Select(f => Path.GetFileNameWithoutExtension(f)) + .Where(n => !string.IsNullOrEmpty(n)) + .OrderBy(n => n, StringComparer.OrdinalIgnoreCase) + .ToList(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"列举配置文件失败: {ex.Message}", LogHelper.LogType.Error); + return Array.Empty(); + } + } + + /// 获取某配置文件对应的文件路径。 + public static string GetProfilePath(string profileName) + { + var safe = ToSafeFileName(profileName); + return Path.Combine(ProfilesDir, safe + ProfileExtension); + } + + /// 将当前配置的 JSON 内容保存为指定名称的配置文件。 + /// 配置文件显示名称(会转为安全文件名)。 + /// 已序列化好的 Settings JSON 字符串。 + /// 成功返回 true。 + public static bool SaveAsProfile(string profileName, string settingsJson) + { + try + { + if (string.IsNullOrWhiteSpace(settingsJson)) + { + LogHelper.WriteLogToFile("配置文件保存失败:内容为空", LogHelper.LogType.Warning); + return false; + } + EnsureProfilesDirectory(); + var path = GetProfilePath(profileName); + ProcessProtectionManager.WithWriteAccess(path, () => File.WriteAllText(path, settingsJson)); + LogHelper.WriteLogToFile($"配置文件已保存: {ToSafeFileName(profileName)}", LogHelper.LogType.Event); + return true; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"保存配置文件失败: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + + /// 将指定配置文件应用到当前配置(覆盖 Configs/Settings.json),供主窗口随后热重载。 + /// 配置文件名称(与 ListProfileNames 中一致,或与保存时使用的显示名一致)。 + /// 成功返回 true;文件不存在或复制失败返回 false。 + public static bool ApplyProfile(string profileName) + { + try + { + var path = GetProfilePath(profileName); + if (!File.Exists(path)) + { + LogHelper.WriteLogToFile($"配置文件文件不存在: {path}", LogHelper.LogType.Warning); + return false; + } + var json = File.ReadAllText(path); + if (string.IsNullOrWhiteSpace(json)) + { + LogHelper.WriteLogToFile("配置文件内容为空", LogHelper.LogType.Warning); + return false; + } + // 可选:校验是否为合法 Settings JSON + try + { + JsonConvert.DeserializeObject(json); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"配置文件格式无效: {ex.Message}", LogHelper.LogType.Error); + return false; + } + var configsDir = Path.GetDirectoryName(SettingsFilePath); + if (!string.IsNullOrEmpty(configsDir) && !Directory.Exists(configsDir)) + { + ProcessProtectionManager.WithWriteAccess(configsDir, () => Directory.CreateDirectory(configsDir)); + } + ProcessProtectionManager.WithWriteAccess(SettingsFilePath, () => File.WriteAllText(SettingsFilePath, json)); + LogHelper.WriteLogToFile($"已应用配置文件: {profileName}(请热重载以生效)", LogHelper.LogType.Event); + return true; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"应用配置文件失败: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + + /// 删除指定名称的配置文件。 + public static bool DeleteProfile(string profileName) + { + try + { + var path = GetProfilePath(profileName); + if (!File.Exists(path)) return true; + ProcessProtectionManager.WithWriteAccess(path, () => File.Delete(path)); + LogHelper.WriteLogToFile($"已删除配置文件: {profileName}", LogHelper.LogType.Event); + return true; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"删除配置文件失败: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + } +} diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 03a1f3b4..4e87fbed 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -2873,6 +2873,31 @@