|
@@ -0,0 +1,558 @@
|
|
|
|
|
+# 开机自启动功能实施计划
|
|
|
|
|
+
|
|
|
|
|
+> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
|
+
|
|
|
|
|
+**Goal:** 为 YZWater3 添加跨平台开机自启动功能,用户可在参数设置页面切换开关。
|
|
|
|
|
+
|
|
|
|
|
+**Architecture:** 创建 `StartupService` 静态类,通过 `RuntimeInformation.IsOSPlatform()` 判断平台,分别操作 Windows 注册表、Linux `.desktop` 文件、macOS LaunchAgent。在 `SystemConfig` 中新增 `RunAtStartup` 属性持久化到 JSON,在 `ViewBViewModel` 中添加属性和切换逻辑,在 `ViewBView.axaml` 中添加 ToggleSwitch。
|
|
|
|
|
+
|
|
|
|
|
+**Tech Stack:** .NET 8, Avalonia 12, CommunityToolkit.Mvvm, Microsoft.Win32.Registry
|
|
|
|
|
+
|
|
|
|
|
+**Spec:** `docs/superpowers/specs/2026-09-01-autostart-design.md`
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 文件结构
|
|
|
|
|
+
|
|
|
|
|
+| 操作 | 文件路径 | 职责 |
|
|
|
|
|
+|------|----------|------|
|
|
|
|
|
+| 新建 | `src/YZWater.Core/Services/StartupService.cs` | 跨平台开机自启动管理 |
|
|
|
|
|
+| 修改 | `src/YZWater.Core/Models/SystemConfig.cs` | 新增 `RunAtStartup` 属性 |
|
|
|
|
|
+| 修改 | `src/YZWater.Core/ViewModels/ViewBViewModel.cs` | 新增 `RunAtStartup` 属性 + 保存逻辑 + 修复 SaveConfig bug |
|
|
|
|
|
+| 修改 | `src/YZWater.Avalonia/Views/ViewBView.axaml` | 新增系统设置区块 + ToggleSwitch |
|
|
|
|
|
+| 修改 | `src/YZWater.Core/Services/LanguageService.cs` | 新增中英文 i18n 键 |
|
|
|
|
|
+| 修改 | `src/YZWater.Core/YZWater.Core.csproj` | 添加 `Microsoft.Win32.Registry` 包引用 |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## Task 1: 添加 NuGet 包引用
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Modify: `src/YZWater.Core/YZWater.Core.csproj:10-23`
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: 添加 Microsoft.Win32.Registry 包**
|
|
|
|
|
+
|
|
|
|
|
+在 `<ItemGroup>` 的 PackageReference 列表中添加:
|
|
|
|
|
+
|
|
|
|
|
+```xml
|
|
|
|
|
+<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+添加到 `Serilog.Sinks.File` 之后(第 20 行后)。
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: 还原依赖**
|
|
|
|
|
+
|
|
|
|
|
+Run: `dotnet restore src/YZWater.Core`
|
|
|
|
|
+Expected: 成功还原,无错误
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: 提交**
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+git add src/YZWater.Core/YZWater.Core.csproj
|
|
|
|
|
+git commit -m "chore: 添加 Microsoft.Win32.Registry 包引用"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## Task 2: SystemConfig 模型新增属性
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Modify: `src/YZWater.Core/Models/SystemConfig.cs:130-141`
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: 在 `_plcPollingIntervalMs` 字段后添加新属性**
|
|
|
|
|
+
|
|
|
|
|
+在第 141 行(`private int _plcPollingIntervalMs = 1000;`)之后,添加:
|
|
|
|
|
+
|
|
|
|
|
+```csharp
|
|
|
|
|
+/// <summary>
|
|
|
|
|
+/// 是否开机自启动
|
|
|
|
|
+/// </summary>
|
|
|
|
|
+[ObservableProperty]
|
|
|
|
|
+private bool _runAtStartup;
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: 构建验证**
|
|
|
|
|
+
|
|
|
|
|
+Run: `dotnet build src/YZWater.Core`
|
|
|
|
|
+Expected: 编译成功
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: 提交**
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+git add src/YZWater.Core/Models/SystemConfig.cs
|
|
|
|
|
+git commit -m "feat: SystemConfig 新增 RunAtStartup 属性"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## Task 3: 创建 StartupService
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Create: `src/YZWater.Core/Services/StartupService.cs`
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: 创建 StartupService.cs**
|
|
|
|
|
+
|
|
|
|
|
+创建文件 `src/YZWater.Core/Services/StartupService.cs`,内容如下:
|
|
|
|
|
+
|
|
|
|
|
+```csharp
|
|
|
|
|
+using System.Runtime.InteropServices;
|
|
|
|
|
+using Microsoft.Win32;
|
|
|
|
|
+using Serilog;
|
|
|
|
|
+
|
|
|
|
|
+namespace YZWater.Core.Services;
|
|
|
|
|
+
|
|
|
|
|
+/// <summary>
|
|
|
|
|
+/// 跨平台开机自启动管理服务
|
|
|
|
|
+/// </summary>
|
|
|
|
|
+public static class StartupService
|
|
|
|
|
+{
|
|
|
|
|
+ private const string AppName = "YZWater";
|
|
|
|
|
+ private const string WindowsRegistryPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
|
|
|
|
|
+ private const string LinuxDesktopFileName = "yzwater.desktop";
|
|
|
|
|
+ private const string MacPlistFileName = "com.yxwater.yzwater.plist";
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 设置开机自启动
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public static bool SetAutoStart(bool enabled)
|
|
|
|
|
+ {
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
|
|
|
+ return SetWindowsAutoStart(enabled);
|
|
|
|
|
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
|
|
|
+ return SetLinuxAutoStart(enabled);
|
|
|
|
|
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
|
|
|
|
+ return SetMacAutoStart(enabled);
|
|
|
|
|
+
|
|
|
|
|
+ Log.Warning("StartupService: 不支持的操作系统");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (Exception ex)
|
|
|
|
|
+ {
|
|
|
|
|
+ Log.Error(ex, "StartupService: 设置开机自启动失败 (enabled={Enabled})", enabled);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 检查是否已启用开机自启动
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public static bool IsAutoStartEnabled()
|
|
|
|
|
+ {
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
|
|
|
+ return IsWindowsAutoStartEnabled();
|
|
|
|
|
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
|
|
|
+ return IsLinuxAutoStartEnabled();
|
|
|
|
|
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
|
|
|
|
+ return IsMacAutoStartEnabled();
|
|
|
|
|
+
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (Exception ex)
|
|
|
|
|
+ {
|
|
|
|
|
+ Log.Error(ex, "StartupService: 检查开机自启动状态失败");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 获取当前可执行文件路径
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private static string GetExecutablePath()
|
|
|
|
|
+ {
|
|
|
|
|
+ return Environment.ProcessPath ?? Environment.GetCommandLineArgs()[0];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #region Windows
|
|
|
|
|
+
|
|
|
|
|
+ private static bool SetWindowsAutoStart(bool enabled)
|
|
|
|
|
+ {
|
|
|
|
|
+ using var key = Registry.CurrentUser.OpenSubKey(WindowsRegistryPath, true);
|
|
|
|
|
+ if (key == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ Log.Error("StartupService: 无法打开注册表路径 {Path}", WindowsRegistryPath);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (enabled)
|
|
|
|
|
+ {
|
|
|
|
|
+ key.SetValue(AppName, GetExecutablePath());
|
|
|
|
|
+ Log.Information("StartupService: Windows 开机自启动已启用");
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ key.DeleteValue(AppName, false);
|
|
|
|
|
+ Log.Information("StartupService: Windows 开机自启动已禁用");
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static bool IsWindowsAutoStartEnabled()
|
|
|
|
|
+ {
|
|
|
|
|
+ using var key = Registry.CurrentUser.OpenSubKey(WindowsRegistryPath);
|
|
|
|
|
+ return key?.GetValue(AppName) != null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region Linux
|
|
|
|
|
+
|
|
|
|
|
+ private static string GetLinuxDesktopFilePath()
|
|
|
|
|
+ {
|
|
|
|
|
+ var configDir = Path.Combine(
|
|
|
|
|
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
|
|
|
|
+ ".config", "autostart");
|
|
|
|
|
+ return Path.Combine(configDir, LinuxDesktopFileName);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static bool SetLinuxAutoStart(bool enabled)
|
|
|
|
|
+ {
|
|
|
|
|
+ var filePath = GetLinuxDesktopFilePath();
|
|
|
|
|
+
|
|
|
|
|
+ if (enabled)
|
|
|
|
|
+ {
|
|
|
|
|
+ var dir = Path.GetDirectoryName(filePath)!;
|
|
|
|
|
+ Directory.CreateDirectory(dir);
|
|
|
|
|
+
|
|
|
|
|
+ var content = $"""
|
|
|
|
|
+ [Desktop Entry]
|
|
|
|
|
+ Type=Application
|
|
|
|
|
+ Name={AppName}
|
|
|
|
|
+ Exec={GetExecutablePath()}
|
|
|
|
|
+ X-GNOME-Autostart-enabled=true
|
|
|
|
|
+ """;
|
|
|
|
|
+ File.WriteAllText(filePath, content);
|
|
|
|
|
+ Log.Information("StartupService: Linux 开机自启动已启用 ({Path})", filePath);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ if (File.Exists(filePath))
|
|
|
|
|
+ File.Delete(filePath);
|
|
|
|
|
+ Log.Information("StartupService: Linux 开机自启动已禁用");
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static bool IsLinuxAutoStartEnabled()
|
|
|
|
|
+ {
|
|
|
|
|
+ return File.Exists(GetLinuxDesktopFilePath());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region macOS
|
|
|
|
|
+
|
|
|
|
|
+ private static string GetMacPlistPath()
|
|
|
|
|
+ {
|
|
|
|
|
+ var libraryDir = Path.Combine(
|
|
|
|
|
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
|
|
|
|
+ "Library", "LaunchAgents");
|
|
|
|
|
+ return Path.Combine(libraryDir, MacPlistFileName);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static bool SetMacAutoStart(bool enabled)
|
|
|
|
|
+ {
|
|
|
|
|
+ var filePath = GetMacPlistPath();
|
|
|
|
|
+
|
|
|
|
|
+ if (enabled)
|
|
|
|
|
+ {
|
|
|
|
|
+ var dir = Path.GetDirectoryName(filePath)!;
|
|
|
|
|
+ Directory.CreateDirectory(dir);
|
|
|
|
|
+
|
|
|
|
|
+ var content = $"""
|
|
|
|
|
+ <?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
|
|
|
+ <plist version="1.0">
|
|
|
|
|
+ <dict>
|
|
|
|
|
+ <key>Label</key>
|
|
|
|
|
+ <string>com.yxwater.yzwater</string>
|
|
|
|
|
+ <key>ProgramArguments</key>
|
|
|
|
|
+ <array>
|
|
|
|
|
+ <string>{GetExecutablePath()}</string>
|
|
|
|
|
+ </array>
|
|
|
|
|
+ <key>RunAtLoad</key>
|
|
|
|
|
+ <true/>
|
|
|
|
|
+ </dict>
|
|
|
|
|
+ </plist>
|
|
|
|
|
+ """;
|
|
|
|
|
+ File.WriteAllText(filePath, content);
|
|
|
|
|
+ Log.Information("StartupService: macOS 开机自启动已启用 ({Path})", filePath);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ if (File.Exists(filePath))
|
|
|
|
|
+ File.Delete(filePath);
|
|
|
|
|
+ Log.Information("StartupService: macOS 开机自启动已禁用");
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static bool IsMacAutoStartEnabled()
|
|
|
|
|
+ {
|
|
|
|
|
+ return File.Exists(GetMacPlistPath());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: 构建验证**
|
|
|
|
|
+
|
|
|
|
|
+Run: `dotnet build src/YZWater.Core`
|
|
|
|
|
+Expected: 编译成功
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: 提交**
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+git add src/YZWater.Core/Services/StartupService.cs
|
|
|
|
|
+git commit -m "feat: 添加 StartupService 跨平台开机自启动服务"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## Task 4: ViewBViewModel 添加属性和逻辑
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Modify: `src/YZWater.Core/ViewModels/ViewBViewModel.cs`
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: 添加 RunAtStartup 属性**
|
|
|
|
|
+
|
|
|
|
|
+在 `_isConnecting` 字段(第 40 行)之后添加:
|
|
|
|
|
+
|
|
|
|
|
+```csharp
|
|
|
|
|
+[ObservableProperty]
|
|
|
|
|
+private bool _runAtStartup;
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: 添加翻译文本属性**
|
|
|
|
|
+
|
|
|
|
|
+在 `_connectingText` 字段(第 62 行)之后添加:
|
|
|
|
|
+
|
|
|
|
|
+```csharp
|
|
|
|
|
+[ObservableProperty] private string _runAtStartupText;
|
|
|
|
|
+[ObservableProperty] private string _systemSettingsText;
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: 在 UpdateTexts() 方法中添加赋值**
|
|
|
|
|
+
|
|
|
|
|
+在 `UpdateTexts()` 方法中 `ConnectingText = _lang.Get("Connecting");`(第 92 行)之后添加:
|
|
|
|
|
+
|
|
|
|
|
+```csharp
|
|
|
|
|
+RunAtStartupText = _lang.Get("RunAtStartup");
|
|
|
|
|
+SystemSettingsText = _lang.Get("SystemSettings");
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 4: 添加 OnRunAtStartupChanged 部分方法**
|
|
|
|
|
+
|
|
|
|
|
+在 `UpdateTexts()` 方法之后(第 93 行后),添加:
|
|
|
|
|
+
|
|
|
|
|
+```csharp
|
|
|
|
|
+partial void OnRunAtStartupChanged(bool value)
|
|
|
|
|
+{
|
|
|
|
|
+ var success = StartupService.SetAutoStart(value);
|
|
|
|
|
+ if (!success)
|
|
|
|
|
+ {
|
|
|
|
|
+ _runAtStartup = !value;
|
|
|
|
|
+ OnPropertyChanged(nameof(RunAtStartup));
|
|
|
|
|
+ Log.Warning("开机自启动设置失败,已回滚 UI 状态");
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ var config = ConfigService.GetConfig();
|
|
|
|
|
+ if (config != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ config.RunAtStartup = value;
|
|
|
|
|
+ ConfigService.SaveConfig();
|
|
|
|
|
+ Log.Information("开机自启动已{Status}", value ? "启用" : "禁用");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 5: 修改 LoadConfig() 加载 RunAtStartup**
|
|
|
|
|
+
|
|
|
|
|
+在 `LoadConfig()` 方法中,`PumpFrequency = config.PumpFrequency;`(第 110 行)之后添加:
|
|
|
|
|
+
|
|
|
|
|
+```csharp
|
|
|
|
|
+RunAtStartup = config.RunAtStartup;
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 6: 修复 SaveConfig() bug**
|
|
|
|
|
+
|
|
|
|
|
+将 `SaveConfig()` 方法(第 118-134 行)替换为:
|
|
|
|
|
+
|
|
|
|
|
+```csharp
|
|
|
|
|
+/// <summary>
|
|
|
|
|
+/// 保存配置
|
|
|
|
|
+/// </summary>
|
|
|
|
|
+[RelayCommand]
|
|
|
|
|
+private void SaveConfig()
|
|
|
|
|
+{
|
|
|
|
|
+ var config = ConfigService.GetConfig();
|
|
|
|
|
+ if (config == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ Log.Warning("配置为空,无法保存");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ config.PlcIp = PlcIp;
|
|
|
|
|
+ config.PlcPort = PlcPort;
|
|
|
|
|
+ config.AutoConnect = AutoConnect;
|
|
|
|
|
+ config.LevelHighAlarm = LevelHighAlarm;
|
|
|
|
|
+ config.LevelLowAlarm = LevelLowAlarm;
|
|
|
|
|
+ config.FlowHighAlarm = FlowHighAlarm;
|
|
|
|
|
+ config.PumpFrequency = PumpFrequency;
|
|
|
|
|
+ // RunAtStartup 在 OnRunAtStartupChanged 中已单独保存
|
|
|
|
|
+
|
|
|
|
|
+ ConfigService.SaveConfig();
|
|
|
|
|
+ Log.Information("配置已保存");
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 7: 构建验证**
|
|
|
|
|
+
|
|
|
|
|
+Run: `dotnet build src/YZWater.Core`
|
|
|
|
|
+Expected: 编译成功
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 8: 提交**
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+git add src/YZWater.Core/ViewModels/ViewBViewModel.cs
|
|
|
|
|
+git commit -m "feat: ViewBViewModel 添加开机自启动属性和逻辑"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## Task 5: LanguageService 添加 i18n 键
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Modify: `src/YZWater.Core/Services/LanguageService.cs`
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: 在中文词典中添加键**
|
|
|
|
|
+
|
|
|
|
|
+在 `["Connecting"] = "连接中...",`(第 145 行)之后添加:
|
|
|
|
|
+
|
|
|
|
|
+```csharp
|
|
|
|
|
+["RunAtStartup"] = "开机自启动",
|
|
|
|
|
+["SystemSettings"] = "系统设置",
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: 在英文词典中添加键**
|
|
|
|
|
+
|
|
|
|
|
+在 `["Connecting"] = "Connecting...";`(约第 392 行)之后添加:
|
|
|
|
|
+
|
|
|
|
|
+```csharp
|
|
|
|
|
+["RunAtStartup"] = "START WITH WINDOWS",
|
|
|
|
|
+["SystemSettings"] = "SYSTEM SETTINGS",
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: 构建验证**
|
|
|
|
|
+
|
|
|
|
|
+Run: `dotnet build src/YZWater.Core`
|
|
|
|
|
+Expected: 编译成功
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 4: 提交**
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+git add src/YZWater.Core/Services/LanguageService.cs
|
|
|
|
|
+git commit -m "feat: 添加开机自启动中英文翻译"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## Task 6: ViewBView 添加 UI
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Modify: `src/YZWater.Avalonia/Views/ViewBView.axaml`
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: 调整 Grid RowDefinitions**
|
|
|
|
|
+
|
|
|
|
|
+将第 24 行的 `RowDefinitions="Auto,Auto"` 改为 `RowDefinitions="Auto,Auto,Auto"`,为新区块增加一行。
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: 将"泵频率设置"移到 Row 1**
|
|
|
|
|
+
|
|
|
|
|
+将"泵频率设置"的 `Border`(第 80-95 行)中的 `Grid.Row="1"` 改为 `Grid.Row="1"`(保持不变),但将 `Margin` 从 `"0,4,4,0"` 改为 `"0,4,4,4"`。
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: 将"操作按钮"移到 Row 2**
|
|
|
|
|
+
|
|
|
|
|
+将"操作按钮"的 `Border`(第 97-108 行)中的 `Grid.Row="1"` 改为 `Grid.Row="2"`,`Margin` 从 `"4,4,0,0"` 改为 `"4,4,0,4"`。
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 4: 在 Row 1 Column 1 添加系统设置区块**
|
|
|
|
|
+
|
|
|
|
|
+在"泵频率设置" `Border`(第 95 行 `</Border>`)之后,"操作按钮" `Border` 之前,插入:
|
|
|
|
|
+
|
|
|
|
|
+```xml
|
|
|
|
|
+<!-- 系统设置 -->
|
|
|
|
|
+<Border Grid.Row="1" Grid.Column="1" Background="{DynamicResource SurfaceBgBrush}" CornerRadius="2"
|
|
|
|
|
+ BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" Padding="12" Margin="4,4,0,4">
|
|
|
|
|
+ <StackPanel>
|
|
|
|
|
+ <StackPanel Orientation="Horizontal" Spacing="6" Margin="0,0,0,12">
|
|
|
|
|
+ <Border Background="{DynamicResource PrimaryBrush}" Width="3" Height="14" CornerRadius="1"/>
|
|
|
|
|
+ <TextBlock Text="{Binding SystemSettingsText}" FontFamily="{DynamicResource MonoFont}" FontSize="12" FontWeight="Bold"
|
|
|
|
|
+ Foreground="{DynamicResource TextSecondaryBrush}"/>
|
|
|
|
|
+ </StackPanel>
|
|
|
|
|
+ <Grid ColumnDefinitions="120,*" Margin="4,0">
|
|
|
|
|
+ <TextBlock Grid.Column="0" Text="{Binding RunAtStartupText}" FontFamily="{DynamicResource MonoFont}" FontSize="11" Foreground="{DynamicResource HeaderSubtextBrush}" VerticalAlignment="Center"/>
|
|
|
|
|
+ <ToggleSwitch Grid.Column="1" IsChecked="{Binding RunAtStartup}" Margin="0,2"/>
|
|
|
|
|
+ </Grid>
|
|
|
|
|
+ </StackPanel>
|
|
|
|
|
+</Border>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 5: 构建验证**
|
|
|
|
|
+
|
|
|
|
|
+Run: `dotnet build src/YZWater.Avalonia`
|
|
|
|
|
+Expected: 编译成功
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 6: 提交**
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+git add src/YZWater.Avalonia/Views/ViewBView.axaml
|
|
|
|
|
+git commit -m "feat: ViewBView 添加开机自启动 ToggleSwitch"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## Task 7: 集成测试
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: 全量构建**
|
|
|
|
|
+
|
|
|
|
|
+Run: `dotnet build`
|
|
|
|
|
+Expected: 编译成功,无错误
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: 运行应用验证**
|
|
|
|
|
+
|
|
|
|
|
+Run: `dotnet run --project src/YZWater.Avalonia`
|
|
|
|
|
+
|
|
|
|
|
+手动验证:
|
|
|
|
|
+1. 登录应用
|
|
|
|
|
+2. 导航到参数设置页面(ViewB)
|
|
|
|
|
+3. 确认"系统设置"区块可见,包含"开机自启动"标签和 ToggleSwitch
|
|
|
|
|
+4. 切换 ToggleSwitch 为开启状态
|
|
|
|
|
+5. (Windows)打开注册表编辑器,检查 `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` 下是否有 `YZWater` 键
|
|
|
|
|
+6. 关闭应用,重新打开,确认 ToggleSwitch 状态已恢复
|
|
|
|
|
+7. 切换 ToggleSwitch 为关闭状态,确认注册表键已删除
|
|
|
|
|
+8. 切换语言为英文,确认"开机自启动"变为"START WITH WINDOWS"
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: 提交(如果有修改)**
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+git add -A
|
|
|
|
|
+git commit -m "fix: 开机自启动集成测试修复"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 完成标准
|
|
|
|
|
+
|
|
|
|
|
+- [ ] `SystemConfig` 包含 `RunAtStartup` 属性
|
|
|
|
|
+- [ ] `StartupService` 支持 Windows/Linux/macOS 三平台
|
|
|
|
|
+- [ ] ViewB 页面显示"系统设置"区块,包含开机自启动开关
|
|
|
|
|
+- [ ] 开关切换即时生效(写入注册表/文件 + 持久化到 JSON)
|
|
|
|
|
+- [ ] 重启应用后状态正确恢复
|
|
|
|
|
+- [ ] 中英文翻译完整
|
|
|
|
|
+- [ ] `SaveConfig()` bug 已修复
|
|
|
|
|
+- [ ] 全量构建无错误
|