mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
feat.新增了插件开发文档
This commit is contained in:
118
docs/Plugins develop/00-索引与导航.md
Normal file
118
docs/Plugins develop/00-索引与导航.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# 🧭 阑山桌面插件开发文档导航
|
||||
|
||||
欢迎来到 **LanMountainDesktop(阑山桌面)** 插件开发文档!
|
||||
|
||||
这套文档将帮助你从零开始,一步步掌握插件开发的完整流程,最终发布你的作品到插件市场。
|
||||
|
||||
---
|
||||
|
||||
## 📖 文档概述
|
||||
|
||||
**目标读者:**
|
||||
- 有一定 .NET/C# 基础的开发者
|
||||
- 熟悉或愿意学习 Avalonia UI 框架的开发者
|
||||
- 想要为阑山桌面扩展功能的创意开发者
|
||||
|
||||
**你能学到什么:**
|
||||
- 🚀 快速搭建插件开发环境
|
||||
- 🧩 创建桌面组件(Widgets)
|
||||
- ⚙️ 集成设置页面
|
||||
- 🎨 适配主题和外观
|
||||
- 🐛 调试和故障排除
|
||||
- 🚀 CI/CD 自动化构建
|
||||
- 📦 发布到插件市场
|
||||
|
||||
---
|
||||
|
||||
## 🛤️ 推荐阅读路径
|
||||
|
||||
### 🌱 新手路径(从零开始)
|
||||
|
||||
如果你从未开发过阑山桌面插件,请按以下顺序阅读:
|
||||
|
||||
1. **[01-开发环境准备](01-快速开始/01-开发环境准备.md)** - 安装必要工具和模板
|
||||
2. **[02-三分钟创建第一个插件](01-快速开始/02-三分钟创建第一个插件.md)** - 快速上手,建立信心
|
||||
3. **[03-插件项目结构详解](01-快速开始/03-插件项目结构详解.md)** - 理解项目组成
|
||||
4. **[04-调试运行指南](01-快速开始/04-调试运行指南.md)** - 学会调试技巧
|
||||
5. **[01-插件生命周期](02-核心概念与原理/01-插件生命周期.md)** - 理解运行原理
|
||||
6. **[02-桌面组件系统](02-核心概念与原理/02-桌面组件系统.md)** - 创建你的第一个组件
|
||||
7. **[01-开发天气组件](04-实战案例/01-开发天气组件.md)** - 完整实战案例
|
||||
|
||||
**预计时间:** 2-3 小时即可开发出第一个可用插件
|
||||
|
||||
### 🚀 有经验路径(已有 .NET/Avalonia 基础)
|
||||
|
||||
如果你已有相关经验,可以跳过基础部分:
|
||||
|
||||
1. **[01-开发环境准备](01-快速开始/01-开发环境准备.md)** - 快速配置环境
|
||||
2. **[02-核心概念与原理/](02-核心概念与原理/)** - 了解阑山桌面的特殊机制
|
||||
3. **[03-API实践指南/](03-API实践指南/)** - 查阅具体 API 用法
|
||||
4. **[04-实战案例/](04-实战案例/)** - 参考完整示例
|
||||
|
||||
---
|
||||
|
||||
## 🔍 快速问题索引
|
||||
|
||||
| 我想知道... | 查看文档 |
|
||||
|------------|---------|
|
||||
| 如何搭建开发环境? | [01-开发环境准备](01-快速开始/01-开发环境准备.md) |
|
||||
| 如何创建第一个插件? | [02-三分钟创建第一个插件](01-快速开始/02-三分钟创建第一个插件.md) |
|
||||
| plugin.json 各字段含义? | [03-插件项目结构详解](01-快速开始/03-插件项目结构详解.md) |
|
||||
| 如何调试插件代码? | [04-调试运行指南](01-快速开始/04-调试运行指南.md) |
|
||||
| 插件什么时候初始化?能做什么? | [01-插件生命周期](02-核心概念与原理/01-插件生命周期.md) |
|
||||
| 什么是桌面组件?如何创建? | [02-桌面组件系统](02-核心概念与原理/02-桌面组件系统.md) |
|
||||
| 如何添加设置页面? | [03-设置系统集成](02-核心概念与原理/03-设置系统集成.md) + [04-开发设置页面](04-实战案例/04-开发设置页面.md) |
|
||||
| 如何适配暗色模式? | [04-外观与主题系统](02-核心概念与原理/04-外观与主题系统.md) |
|
||||
| 插件之间如何通信? | [05-插件间通信](02-核心概念与原理/05-插件间通信.md) |
|
||||
| 完整的组件开发示例? | [01-开发天气组件](04-实战案例/01-开发天气组件.md) |
|
||||
| 如何排查插件不加载的问题? | [03-常见问题排查](05-调试与故障排除/03-常见问题排查.md) |
|
||||
| 如何配置 GitHub Actions? | [01-GitHub Actions入门](06-CI-CD与自动化/01-GitHub Actions入门.md) |
|
||||
| 如何自动打包 .laapp? | [03-自动打包与发布](06-CI-CD与自动化/03-自动打包与发布.md) |
|
||||
| 如何发布到插件市场? | [03-发布到插件市场](07-发布与运营/03-发布到插件市场.md) |
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关资源
|
||||
|
||||
### 官方资源
|
||||
|
||||
| 资源 | 位置 | 说明 |
|
||||
|-----|------|------|
|
||||
| **Plugin SDK 源码** | `LanMountainDesktop.PluginSdk/` | SDK 的完整源码和 XML 注释 |
|
||||
| **插件模板** | `LanMountainDesktop.PluginTemplate/` | `dotnet new` 模板源码 |
|
||||
| **共享契约** | `LanMountainDesktop.Shared.Contracts/` | 宿主与插件共享的类型定义 |
|
||||
| **架构文档** | `docs/ARCHITECTURE.md` | 宿主应用架构说明 |
|
||||
| **视觉规范** | `docs/VISUAL_SPEC.md` | UI 设计规范 |
|
||||
| **圆角规范** | `docs/CORNER_RADIUS_SPEC.md` | 圆角设计系统 |
|
||||
| **开发指南** | `docs/DEVELOPMENT.md` | 宿主开发指南 |
|
||||
|
||||
### 外部资源
|
||||
|
||||
| 资源 | 链接 | 说明 |
|
||||
|-----|------|------|
|
||||
| **示例插件仓库** | `LanMountainDesktop.SamplePlugin` | 官方示例插件(独立仓库) |
|
||||
| **Avalonia UI 文档** | https://docs.avaloniaui.net/ | UI 框架官方文档 |
|
||||
| **FluentAvalonia** | https://github.com/amwx/FluentAvalonia | 主题控件库 |
|
||||
| **.NET 文档** | https://learn.microsoft.com/dotnet/ | .NET 官方文档 |
|
||||
|
||||
---
|
||||
|
||||
## 💡 获取帮助
|
||||
|
||||
如果在开发过程中遇到问题:
|
||||
|
||||
1. **查阅本文档** - 使用上方快速索引找到相关章节
|
||||
2. **查看示例代码** - 参考 `LanMountainDesktop.PluginTemplate/content/` 中的模板代码
|
||||
3. **阅读 SDK 源码** - `LanMountainDesktop.PluginSdk/` 中有详细的 XML 注释
|
||||
4. **搜索 Issues** - 在 GitHub 仓库搜索是否有人遇到类似问题
|
||||
5. **提交 Issue** - 如果确认是 bug,欢迎提交 Issue
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
准备好开始了吗?点击 **[01-开发环境准备](01-快速开始/01-开发环境准备.md)** 开始你的插件开发之旅!
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
220
docs/Plugins develop/01-快速开始/01-开发环境准备.md
Normal file
220
docs/Plugins develop/01-快速开始/01-开发环境准备.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# 01-开发环境准备
|
||||
|
||||
在开始开发阑山桌面插件之前,你需要准备好开发环境。本指南将带你完成所有必要的安装和配置。
|
||||
|
||||
---
|
||||
|
||||
## ✅ 系统要求
|
||||
|
||||
### 支持的操作系统
|
||||
|
||||
| 操作系统 | 版本要求 | 备注 |
|
||||
|---------|---------|------|
|
||||
| **Windows** | Windows 10 版本 1809 或更高 | 推荐开发平台 |
|
||||
| **Windows** | Windows 11 | 最佳体验 |
|
||||
| **Linux** | Ubuntu 20.04+ / Debian 10+ | 支持开发和运行 |
|
||||
| **macOS** | macOS 12+ | 支持开发和运行 |
|
||||
|
||||
### 硬件要求
|
||||
|
||||
- **处理器**:x64 或 ARM64 架构
|
||||
- **内存**:至少 4GB RAM(推荐 8GB)
|
||||
- **磁盘空间**:至少 2GB 可用空间
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 安装 .NET SDK
|
||||
|
||||
阑山桌面插件基于 **.NET 10** 开发,你需要安装对应版本的 SDK。
|
||||
|
||||
### 下载安装
|
||||
|
||||
1. 访问 [.NET 10 下载页面](https://dotnet.microsoft.com/download/dotnet/10.0)
|
||||
2. 下载适合你操作系统的 SDK 安装包
|
||||
3. 运行安装程序,按提示完成安装
|
||||
|
||||
### 验证安装
|
||||
|
||||
打开终端(PowerShell、CMD 或 Bash),运行以下命令:
|
||||
|
||||
```powershell
|
||||
# 检查 .NET SDK 版本
|
||||
dotnet --version
|
||||
```
|
||||
|
||||
✅ **预期输出示例:**
|
||||
```
|
||||
10.0.100
|
||||
```
|
||||
|
||||
⚠️ **如果版本低于 10.0**,请重新下载安装最新版 .NET 10 SDK。
|
||||
|
||||
---
|
||||
|
||||
## 💻 安装 IDE(集成开发环境)
|
||||
|
||||
你可以选择以下任一 IDE 进行开发:
|
||||
|
||||
### 选项 1:Visual Studio 2022(推荐 Windows 用户)
|
||||
|
||||
**优点:** 功能最全,调试体验最佳
|
||||
|
||||
1. 下载 [Visual Studio 2022](https://visualstudio.microsoft.com/vs/)
|
||||
2. 安装时选择以下工作负载:
|
||||
- ✅ **.NET 桌面开发**
|
||||
- ✅ **Avalonia UI 开发**(可选,如需 Avalonia 设计器)
|
||||
|
||||
### 选项 2:JetBrains Rider(跨平台推荐)
|
||||
|
||||
**优点:** 跨平台,智能提示强大,Avalonia 支持好
|
||||
|
||||
1. 下载 [Rider](https://www.jetbrains.com/rider/)
|
||||
2. 安装后打开,会自动检测 .NET SDK
|
||||
|
||||
### 选项 3:Visual Studio Code(轻量级)
|
||||
|
||||
**优点:** 免费,轻量,插件丰富
|
||||
|
||||
1. 下载 [VS Code](https://code.visualstudio.com/)
|
||||
2. 安装以下扩展:
|
||||
- **C# Dev Kit**(Microsoft 官方)
|
||||
- **Avalonia for VS Code**(可选)
|
||||
|
||||
---
|
||||
|
||||
## 📦 安装插件模板
|
||||
|
||||
阑山桌面提供了官方的 `dotnet new` 模板,帮助你快速创建插件项目。
|
||||
|
||||
### 安装模板
|
||||
|
||||
```powershell
|
||||
# 安装最新版插件模板
|
||||
dotnet new install LanMountainDesktop.PluginTemplate
|
||||
```
|
||||
|
||||
✅ **成功提示:**
|
||||
```
|
||||
模板名 短名称 语言 标签
|
||||
------------------------------------- ---------- ---- ------------
|
||||
LanMountainDesktop Plugin lmd-plugin C# LanMountainDesktop/Plugin
|
||||
```
|
||||
|
||||
### 验证安装
|
||||
|
||||
```powershell
|
||||
# 列出已安装的模板,查找 lmd-plugin
|
||||
dotnet new list | findstr lmd
|
||||
```
|
||||
|
||||
或(Linux/macOS):
|
||||
```bash
|
||||
dotnet new list | grep lmd
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎮 获取宿主应用
|
||||
|
||||
插件需要在阑山桌面宿主中运行,你需要获取宿主应用:
|
||||
|
||||
### 方式 1:下载 Release 版本(推荐)
|
||||
|
||||
1. 访问 GitHub Releases 页面
|
||||
2. 下载最新版本的安装包(.exe / .deb / .dmg)
|
||||
3. 安装并运行阑山桌面
|
||||
|
||||
### 方式 2:从源码构建
|
||||
|
||||
如果你想调试宿主或了解内部机制:
|
||||
|
||||
```powershell
|
||||
# 克隆仓库
|
||||
git clone https://github.com/your-org/LanMountainDesktop.git
|
||||
cd LanMountainDesktop
|
||||
|
||||
# 还原依赖
|
||||
dotnet restore
|
||||
|
||||
# 构建项目
|
||||
dotnet build LanMountainDesktop.slnx -c Debug
|
||||
|
||||
# 运行宿主
|
||||
dotnet run --project LanMountainDesktop/LanMountainDesktop.csproj
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 环境验证清单
|
||||
|
||||
在继续之前,请确认以下检查项都已完成:
|
||||
|
||||
| 检查项 | 验证命令 | 预期结果 |
|
||||
|-------|---------|---------|
|
||||
| ✅ .NET SDK 版本 | `dotnet --version` | 10.0.xxx |
|
||||
| ✅ 模板已安装 | `dotnet new list \| findstr lmd` | 显示 lmd-plugin |
|
||||
| ✅ IDE 可创建项目 | 在 IDE 中新建项目 | 能看到 C# 项目模板 |
|
||||
| ✅ 宿主可运行 | 双击 LanMountainDesktop.exe | 应用正常启动 |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 常见问题
|
||||
|
||||
### 问题 1:dotnet 命令找不到
|
||||
|
||||
**现象:** 运行 `dotnet` 提示不是内部或外部命令
|
||||
|
||||
**解决:**
|
||||
1. 确认 .NET SDK 已正确安装
|
||||
2. 重启终端或 IDE
|
||||
3. 检查环境变量 PATH 是否包含 `C:\Program Files\dotnet\`
|
||||
|
||||
### 问题 2:模板安装失败
|
||||
|
||||
**现象:** `dotnet new install` 报错或卡住
|
||||
|
||||
**解决:**
|
||||
1. 检查网络连接(需要访问 nuget.org)
|
||||
2. 尝试指定版本号:
|
||||
```powershell
|
||||
dotnet new install LanMountainDesktop.PluginTemplate::1.0.0
|
||||
```
|
||||
3. 清除模板缓存后重试:
|
||||
```powershell
|
||||
dotnet new uninstall LanMountainDesktop.PluginTemplate
|
||||
dotnet new install LanMountainDesktop.PluginTemplate
|
||||
```
|
||||
|
||||
### 问题 3:SDK 版本不匹配
|
||||
|
||||
**现象:** 构建时提示 SDK 版本不符合 global.json 要求
|
||||
|
||||
**解决:**
|
||||
1. 检查项目根目录的 `global.json` 文件
|
||||
2. 安装对应版本的 .NET SDK
|
||||
3. 或使用以下命令使用已安装的版本:
|
||||
```powershell
|
||||
dotnet new globaljson --sdk-version 10.0.100 --roll-forward latestFeature
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
环境准备完成!接下来:
|
||||
|
||||
👉 **[02-三分钟创建第一个插件](02-三分钟创建第一个插件.md)** - 立即开始创建你的第一个插件!
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [.NET 10 下载](https://dotnet.microsoft.com/download/dotnet/10.0)
|
||||
- [Visual Studio 2022](https://visualstudio.microsoft.com/vs/)
|
||||
- [JetBrains Rider](https://www.jetbrains.com/rider/)
|
||||
- [VS Code](https://code.visualstudio.com/)
|
||||
- [Avalonia UI 文档](https://docs.avaloniaui.net/)
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
236
docs/Plugins develop/01-快速开始/02-三分钟创建第一个插件.md
Normal file
236
docs/Plugins develop/01-快速开始/02-三分钟创建第一个插件.md
Normal file
@@ -0,0 +1,236 @@
|
||||
# 02-三分钟创建第一个插件
|
||||
|
||||
本指南将帮助你在三分钟内创建并运行你的第一个阑山桌面插件。让我们开始吧!
|
||||
|
||||
---
|
||||
|
||||
## 🎯 目标
|
||||
|
||||
完成本指南后,你将:
|
||||
- ✅ 创建一个可运行的插件项目
|
||||
- ✅ 在宿主中成功加载插件
|
||||
- ✅ 在插件列表中看到你的插件
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 步骤一:创建项目(30秒)
|
||||
|
||||
打开终端,运行以下命令:
|
||||
|
||||
```powershell
|
||||
# 创建插件项目
|
||||
dotnet new lmd-plugin -n MyFirstPlugin
|
||||
|
||||
# 进入项目目录
|
||||
cd MyFirstPlugin
|
||||
```
|
||||
|
||||
✅ **成功标志:** 命令执行后没有报错,且生成了 `MyFirstPlugin` 文件夹。
|
||||
|
||||
---
|
||||
|
||||
## 📝 步骤二:配置插件信息(30秒)
|
||||
|
||||
打开 `plugin.json` 文件,修改以下字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "com.yourname.myfirstplugin",
|
||||
"name": "我的第一个插件",
|
||||
"description": "这是一个测试插件",
|
||||
"author": "你的名字",
|
||||
"version": "1.0.0",
|
||||
"apiVersion": "4.0.1",
|
||||
"entranceAssembly": "MyFirstPlugin.dll",
|
||||
"sharedContracts": []
|
||||
}
|
||||
```
|
||||
|
||||
⚠️ **重要提示:**
|
||||
- `id` 必须是唯一的,建议使用反向域名格式(如 `com.yourname.pluginname`)
|
||||
- `apiVersion` 必须与 SDK 版本匹配
|
||||
- 保存文件时使用 **UTF-8** 编码
|
||||
|
||||
---
|
||||
|
||||
## 🔨 步骤三:构建项目(30秒)
|
||||
|
||||
在终端中运行:
|
||||
|
||||
```powershell
|
||||
# 构建项目
|
||||
dotnet build
|
||||
```
|
||||
|
||||
✅ **成功标志:** 看到类似以下的输出:
|
||||
```
|
||||
生成成功。
|
||||
0 个警告
|
||||
0 个错误
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 步骤四:找到插件包(15秒)
|
||||
|
||||
构建完成后,插件包位于:
|
||||
|
||||
```
|
||||
MyFirstPlugin/
|
||||
└── bin/
|
||||
└── Debug/
|
||||
└── net10.0/
|
||||
└── MyFirstPlugin.laapp <-- 这就是插件包!
|
||||
```
|
||||
|
||||
⚠️ **什么是 .laapp 文件?**
|
||||
- `.laapp` 是阑山桌面的插件包格式
|
||||
- 本质上是一个 ZIP 压缩包,包含插件 DLL 和资源文件
|
||||
- 不要解压,直接安装即可
|
||||
|
||||
---
|
||||
|
||||
## 🚀 步骤五:安装到宿主(30秒)
|
||||
|
||||
1. **启动阑山桌面**(如果尚未运行)
|
||||
|
||||
2. **打开设置**:
|
||||
- 右键点击桌面上的阑山桌面图标
|
||||
- 选择「设置」
|
||||
|
||||
3. **进入插件管理**:
|
||||
- 在设置窗口左侧选择「插件」
|
||||
|
||||
4. **安装本地插件**:
|
||||
- 点击「安装本地插件」按钮
|
||||
- 选择刚才生成的 `.laapp` 文件
|
||||
- 点击「打开」
|
||||
|
||||
5. **重启宿主**:
|
||||
- 安装完成后,点击「重启」按钮
|
||||
- 阑山桌面将重新启动
|
||||
|
||||
---
|
||||
|
||||
## ✅ 步骤六:验证安装(15秒)
|
||||
|
||||
重启后,再次打开设置 → 插件:
|
||||
|
||||
🎉 **成功标志:**
|
||||
- 在插件列表中看到「我的第一个插件」
|
||||
- 状态显示为「已启用」
|
||||
- 作者显示为你设置的名字
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📂 生成的项目结构
|
||||
|
||||
你的项目现在包含以下文件:
|
||||
|
||||
```
|
||||
MyFirstPlugin/
|
||||
├── plugin.json # 插件清单文件
|
||||
├── MyFirstPlugin.csproj # 项目文件
|
||||
├── Plugin.cs # 插件入口类
|
||||
├── README.md # 项目说明
|
||||
└── Localization/ # 本地化文件夹
|
||||
├── zh-CN.json # 中文资源
|
||||
└── en-US.json # 英文资源
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 查看插件代码
|
||||
|
||||
打开 `Plugin.cs`,你会看到:
|
||||
|
||||
```csharp
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace MyFirstPlugin;
|
||||
|
||||
[PluginEntrance]
|
||||
public sealed class Plugin : PluginBase
|
||||
{
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 插件初始化代码
|
||||
// 在这里注册组件、设置页面等
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**关键点:**
|
||||
- `[PluginEntrance]` 特性标记入口类
|
||||
- 继承 `PluginBase` 基类
|
||||
- `Initialize` 方法是插件的初始化入口
|
||||
|
||||
---
|
||||
|
||||
## 🎉 恭喜!
|
||||
|
||||
你已经成功创建并安装了第一个阑山桌面插件!
|
||||
|
||||
虽然这个插件目前还没有任何功能,但你已经掌握了:
|
||||
- ✅ 使用模板创建项目
|
||||
- ✅ 配置插件信息
|
||||
- ✅ 构建插件包
|
||||
- ✅ 安装到宿主
|
||||
|
||||
---
|
||||
|
||||
## 🚦 常见问题
|
||||
|
||||
### 问题 1:构建失败,提示找不到 SDK
|
||||
|
||||
**现象:** 错误信息包含 "SDK not found"
|
||||
|
||||
**解决:**
|
||||
1. 确认已安装 .NET 10 SDK:`dotnet --version`
|
||||
2. 检查 `global.json` 中的版本要求
|
||||
|
||||
### 问题 2:宿主提示插件安装失败
|
||||
|
||||
**现象:** 安装时弹出错误对话框
|
||||
|
||||
**排查步骤:**
|
||||
1. 检查 `plugin.json` 是否为有效的 JSON 格式
|
||||
2. 确认 `id` 字段唯一且合法(只能包含字母、数字、点号)
|
||||
3. 确认 `apiVersion` 与 SDK 版本匹配
|
||||
|
||||
### 问题 3:插件列表中不显示
|
||||
|
||||
**现象:** 安装后重启,但列表中没有
|
||||
|
||||
**排查步骤:**
|
||||
1. 确认已点击「重启」按钮
|
||||
2. 检查日志文件:`%LOCALAPPDATA%\LanMountainDesktop\logs\`
|
||||
3. 确认 `.laapp` 文件完整未损坏
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
现在你的插件已经能运行了,接下来学习:
|
||||
|
||||
👉 **[03-插件项目结构详解](03-插件项目结构详解.md)** - 深入理解每个文件的作用
|
||||
|
||||
或者直接进入实战:
|
||||
|
||||
👉 **[02-桌面组件系统](../02-核心概念与原理/02-桌面组件系统.md)** - 创建你的第一个桌面组件!
|
||||
|
||||
---
|
||||
|
||||
## 💡 小贴士
|
||||
|
||||
- **快速重建**:修改代码后,只需运行 `dotnet build` 即可重新生成 `.laapp`
|
||||
- **自动安装**:可以在 IDE 中配置构建后自动复制到宿主插件目录
|
||||
- **日志调试**:使用 `ILogger` 记录日志,在 `%LOCALAPPDATA%\LanMountainDesktop\logs\` 查看
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
350
docs/Plugins develop/01-快速开始/03-插件项目结构详解.md
Normal file
350
docs/Plugins develop/01-快速开始/03-插件项目结构详解.md
Normal file
@@ -0,0 +1,350 @@
|
||||
# 03-插件项目结构详解
|
||||
|
||||
了解插件项目的每个文件和文件夹的作用,是开发高质量插件的基础。本文将详细解析插件项目的完整结构。
|
||||
|
||||
---
|
||||
|
||||
## 📂 项目结构概览
|
||||
|
||||
使用模板创建的插件项目结构如下:
|
||||
|
||||
```
|
||||
MyPlugin/
|
||||
├── plugin.json # 插件清单(必需)
|
||||
├── MyPlugin.csproj # 项目文件(必需)
|
||||
├── Plugin.cs # 入口类(必需)
|
||||
├── README.md # 项目说明(推荐)
|
||||
├── .gitignore # Git忽略文件(可选)
|
||||
└── Localization/ # 本地化文件夹(可选)
|
||||
├── zh-CN.json # 中文资源
|
||||
└── en-US.json # 英文资源
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 plugin.json - 插件清单
|
||||
|
||||
这是插件最重要的配置文件,定义了插件的元数据。
|
||||
|
||||
### 完整示例
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "com.example.myplugin",
|
||||
"name": "我的插件",
|
||||
"description": "这是一个示例插件",
|
||||
"author": "作者名称",
|
||||
"version": "1.0.0",
|
||||
"apiVersion": "4.0.1",
|
||||
"entranceAssembly": "MyPlugin.dll",
|
||||
"sharedContracts": [],
|
||||
"website": "https://example.com",
|
||||
"icon": "icon.png",
|
||||
"tags": ["工具", "实用"]
|
||||
}
|
||||
```
|
||||
|
||||
### 字段详解
|
||||
|
||||
| 字段 | 必需 | 说明 | 示例 |
|
||||
|-----|------|------|------|
|
||||
| `id` | ✅ | 唯一标识符,反向域名格式 | `com.yourname.plugin` |
|
||||
| `name` | ✅ | 显示名称 | `天气插件` |
|
||||
| `description` | ✅ | 简短描述 | `显示实时天气信息` |
|
||||
| `author` | ✅ | 作者名称 | `张三` |
|
||||
| `version` | ✅ | 版本号(语义化版本) | `1.0.0` |
|
||||
| `apiVersion` | ✅ | SDK API 版本 | `4.0.1` |
|
||||
| `entranceAssembly` | ✅ | 入口程序集文件名 | `MyPlugin.dll` |
|
||||
| `sharedContracts` | ✅ | 共享契约类型列表 | `[]` |
|
||||
| `website` | ❌ | 项目网站 | `https://github.com/...` |
|
||||
| `icon` | ❌ | 图标文件名 | `icon.png` |
|
||||
| `tags` | ❌ | 标签数组 | `["天气", "工具"]` |
|
||||
|
||||
### 重要规则
|
||||
|
||||
⚠️ **id 字段规则:**
|
||||
- 只能包含小写字母、数字、点号(`.`)
|
||||
- 必须全局唯一
|
||||
- 建议使用反向域名格式:`com.yourname.pluginname`
|
||||
- 一经发布不可更改
|
||||
|
||||
⚠️ **version 字段规则:**
|
||||
- 使用语义化版本格式:`主版本.次版本.修订号`
|
||||
- 示例:`1.0.0`、`2.1.3-beta`
|
||||
|
||||
⚠️ **apiVersion 字段规则:**
|
||||
- 必须与引用的 SDK 版本兼容
|
||||
- 当前最新版本:`4.0.1`
|
||||
- 不兼容时宿主将拒绝加载插件
|
||||
|
||||
---
|
||||
|
||||
## 🔧 .csproj - 项目文件
|
||||
|
||||
定义了项目的构建配置和依赖项。
|
||||
|
||||
### 完整示例
|
||||
|
||||
```xml
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="plugin.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Localization\**\*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
```
|
||||
|
||||
### 关键配置项
|
||||
|
||||
| 配置项 | 说明 | 推荐值 |
|
||||
|-------|------|--------|
|
||||
| `TargetFramework` | 目标框架 | `net10.0` |
|
||||
| `LangVersion` | C# 语言版本 | `latest` |
|
||||
| `Nullable` | 可空引用类型 | `enable` |
|
||||
|
||||
### SDK 引用
|
||||
|
||||
```xml
|
||||
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="4.0.1" />
|
||||
```
|
||||
|
||||
⚠️ **版本必须匹配:**
|
||||
- SDK 版本必须与 `plugin.json` 中的 `apiVersion` 兼容
|
||||
- 建议使用最新稳定版
|
||||
|
||||
### 资源文件配置
|
||||
|
||||
确保 `plugin.json` 和本地化文件被正确复制到输出目录:
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<None Update="plugin.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Localization\**\*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚪 Plugin.cs - 入口类
|
||||
|
||||
插件的入口点,负责初始化逻辑。
|
||||
|
||||
### 基本结构
|
||||
|
||||
```csharp
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace MyPlugin;
|
||||
|
||||
[PluginEntrance]
|
||||
public sealed class Plugin : PluginBase
|
||||
{
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 在这里注册组件、设置页面、服务等
|
||||
|
||||
// 示例:注册桌面组件
|
||||
// services.AddPluginDesktopComponent<MyWidget>(...);
|
||||
|
||||
// 示例:注册设置页面
|
||||
// services.AddPluginSettingsSection(...);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 关键特性
|
||||
|
||||
| 特性/类 | 说明 |
|
||||
|--------|------|
|
||||
| `[PluginEntrance]` | 标记插件入口类,必须有且仅有一个 |
|
||||
| `PluginBase` | 插件基类,提供基础功能和日志访问 |
|
||||
| `Initialize` | 初始化方法,宿主启动时调用 |
|
||||
|
||||
### Initialize 方法参数
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
```
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|-----|------|------|
|
||||
| `context` | `HostBuilderContext` | 宿主构建上下文,可访问配置 |
|
||||
| `services` | `IServiceCollection` | 依赖注入服务集合,用于注册组件和服务 |
|
||||
|
||||
---
|
||||
|
||||
## 🌍 Localization - 本地化文件夹
|
||||
|
||||
存放多语言资源文件,支持插件的国际化。
|
||||
|
||||
### 文件夹结构
|
||||
|
||||
```
|
||||
Localization/
|
||||
├── zh-CN.json # 简体中文
|
||||
├── zh-TW.json # 繁体中文
|
||||
├── en-US.json # 英文(美国)
|
||||
├── ja-JP.json # 日文
|
||||
└── ko-KR.json # 韩文
|
||||
```
|
||||
|
||||
### 资源文件格式
|
||||
|
||||
```json
|
||||
{
|
||||
"PluginName": "我的插件",
|
||||
"Settings": {
|
||||
"Title": "设置",
|
||||
"RefreshInterval": "刷新间隔"
|
||||
},
|
||||
"Messages": {
|
||||
"Loading": "加载中...",
|
||||
"Error": "出错了:{0}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 在代码中使用
|
||||
|
||||
```csharp
|
||||
// 获取本地化字符串
|
||||
var localizer = serviceProvider.GetRequiredService<IStringLocalizer<MyPlugin>>();
|
||||
var pluginName = localizer["PluginName"];
|
||||
var message = localizer["Messages.Error", errorDetails];
|
||||
```
|
||||
|
||||
### 支持的语言代码
|
||||
|
||||
| 语言 | 代码 |
|
||||
|-----|------|
|
||||
| 简体中文 | `zh-CN` |
|
||||
| 繁体中文 | `zh-TW` |
|
||||
| 英文 | `en-US` |
|
||||
| 日文 | `ja-JP` |
|
||||
| 韩文 | `ko-KR` |
|
||||
|
||||
---
|
||||
|
||||
## 📦 构建输出结构
|
||||
|
||||
运行 `dotnet build` 后,生成的输出结构:
|
||||
|
||||
```
|
||||
bin/Debug/net10.0/
|
||||
├── MyPlugin.dll # 插件程序集
|
||||
├── MyPlugin.pdb # 调试符号
|
||||
├── plugin.json # 插件清单(复制)
|
||||
├── Localization/ # 本地化文件夹(复制)
|
||||
│ └── zh-CN.json
|
||||
├── MyPlugin.laapp # 插件包(由 SDK 自动生成)
|
||||
└── ...(依赖项 DLL)
|
||||
```
|
||||
|
||||
### .laapp 包结构
|
||||
|
||||
`.laapp` 文件本质是一个 ZIP 压缩包,包含:
|
||||
|
||||
```
|
||||
MyPlugin.laapp
|
||||
├── plugin.json # 清单文件
|
||||
├── MyPlugin.dll # 主程序集
|
||||
├── Localization/ # 本地化资源
|
||||
└── ...(其他依赖 DLL)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔗 与其他 .NET 项目的区别
|
||||
|
||||
| 特性 | 普通 .NET 应用 | 阑山桌面插件 |
|
||||
|-----|---------------|-------------|
|
||||
| 入口点 | `Program.cs` 的 `Main` | `Plugin.cs` 的 `Initialize` |
|
||||
| 运行方式 | 独立运行 | 由宿主加载运行 |
|
||||
| 依赖注入 | 自行配置 | 使用宿主提供的 `IServiceCollection` |
|
||||
| 输出格式 | `.exe` 或 `.dll` | `.laapp` 包 |
|
||||
| 资源访问 | 直接访问 | 通过 SDK API 访问宿主资源 |
|
||||
| 热重载 | 支持 | 不支持(需重启宿主) |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 最佳实践
|
||||
|
||||
### 项目组织建议
|
||||
|
||||
```
|
||||
MyPlugin/
|
||||
├── plugin.json
|
||||
├── MyPlugin.csproj
|
||||
├── Plugin.cs # 入口类(保持简洁)
|
||||
├── README.md
|
||||
├── .gitignore
|
||||
├── Localization/ # 本地化资源
|
||||
├── Services/ # 服务类文件夹
|
||||
│ ├── WeatherService.cs
|
||||
│ └── DataService.cs
|
||||
├── Views/ # 视图文件夹
|
||||
│ ├── WeatherWidget.axaml
|
||||
│ ├── WeatherWidget.axaml.cs
|
||||
│ └── SettingsPage.axaml
|
||||
└── ViewModels/ # 视图模型文件夹
|
||||
├── WeatherViewModel.cs
|
||||
└── SettingsViewModel.cs
|
||||
```
|
||||
|
||||
### 文件命名规范
|
||||
|
||||
| 类型 | 命名约定 | 示例 |
|
||||
|-----|---------|------|
|
||||
| 入口类 | `Plugin` | `Plugin.cs` |
|
||||
| 组件视图 | `{Name}Widget` | `WeatherWidget.axaml` |
|
||||
| 设置页面 | `{Name}SettingsPage` | `WeatherSettingsPage.axaml` |
|
||||
| 服务类 | `{Name}Service` | `WeatherService.cs` |
|
||||
| 视图模型 | `{Name}ViewModel` | `WeatherViewModel.cs` |
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [Plugin SDK 源码](../../LanMountainDesktop.PluginSdk/)
|
||||
- [插件模板](../../LanMountainDesktop.PluginTemplate/content/)
|
||||
- [02-桌面组件系统](../02-核心概念与原理/02-桌面组件系统.md)
|
||||
- [03-设置系统集成](../02-核心概念与原理/03-设置系统集成.md)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
理解了项目结构后,接下来学习:
|
||||
|
||||
👉 **[04-调试运行指南](04-调试运行指南.md)** - 掌握调试技巧
|
||||
|
||||
或者深入了解核心概念:
|
||||
|
||||
👉 **[01-插件生命周期](../02-核心概念与原理/01-插件生命周期.md)** - 理解插件运行机制
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
380
docs/Plugins develop/01-快速开始/04-调试运行指南.md
Normal file
380
docs/Plugins develop/01-快速开始/04-调试运行指南.md
Normal file
@@ -0,0 +1,380 @@
|
||||
# 04-调试运行指南
|
||||
|
||||
掌握插件调试技巧,能大幅提升开发效率。本文介绍阑山桌面插件的各种调试方法和常见问题排查。
|
||||
|
||||
---
|
||||
|
||||
## 🔄 调试方式概述
|
||||
|
||||
阑山桌面插件有两种主要调试方式:
|
||||
|
||||
| 方式 | 适用场景 | 优点 | 缺点 |
|
||||
|-----|---------|------|------|
|
||||
| **附加到进程** | 日常开发调试 | 不改变项目结构 | 每次需手动附加 |
|
||||
| **独立调试** | 深度调试、单元测试 | 启动即调试 | 配置较复杂 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 方式一:附加到进程(推荐)
|
||||
|
||||
这是日常开发中最常用的调试方式。
|
||||
|
||||
### 步骤
|
||||
|
||||
1. **启动阑山桌面**
|
||||
- 正常启动宿主应用(非调试模式)
|
||||
- 确保你的插件已安装
|
||||
|
||||
2. **在 IDE 中打开插件项目**
|
||||
- 使用 Visual Studio / Rider / VS Code 打开项目
|
||||
|
||||
3. **设置断点**
|
||||
- 在你想要调试的代码行左侧点击,设置断点
|
||||
- 常见断点位置:
|
||||
- `Plugin.Initialize()` - 插件初始化
|
||||
- 组件构造函数
|
||||
- 设置页面加载方法
|
||||
|
||||
4. **附加到进程**
|
||||
|
||||
**Visual Studio:**
|
||||
- 菜单:`调试` → `附加到进程`
|
||||
- 或快捷键:`Ctrl+Alt+P`
|
||||
- 在列表中找到 `LanMountainDesktop.exe`
|
||||
- 点击`附加`
|
||||
|
||||
**Rider:**
|
||||
- 菜单:`Run` → `Attach to Process`
|
||||
- 或快捷键:`Ctrl+Alt+F5`
|
||||
- 选择 `LanMountainDesktop.exe`
|
||||
|
||||
**VS Code:**
|
||||
- 按 `Ctrl+Shift+D` 打开调试面板
|
||||
- 点击`创建 launch.json 文件`
|
||||
- 选择 `.NET Core Attach`
|
||||
- 选择 `LanMountainDesktop` 进程
|
||||
|
||||
5. **触发调试**
|
||||
- 在阑山桌面中操作,触发插件代码
|
||||
- 例如:添加组件、打开设置页面等
|
||||
- 程序会在断点处暂停
|
||||
|
||||
### 附加配置(VS Code)
|
||||
|
||||
创建 `.vscode/launch.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "附加到阑山桌面",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processName": "LanMountainDesktop"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 方式二:独立调试
|
||||
|
||||
适用于深度调试或单元测试。
|
||||
|
||||
### 配置步骤
|
||||
|
||||
1. **修改 .csproj 临时引用宿主**
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<!-- 临时添加,仅用于调试 -->
|
||||
<ProjectReference Include="..\LanMountainDesktop\LanMountainDesktop.csproj" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
2. **创建调试启动配置**
|
||||
|
||||
**Visual Studio:**
|
||||
- 右键项目 → `属性` → `调试`
|
||||
- 启动外部程序:选择 `LanMountainDesktop.exe`
|
||||
- 工作目录:设为宿主输出目录
|
||||
|
||||
**VS Code launch.json:**
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "启动阑山桌面(调试)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/../LanMountainDesktop/bin/Debug/net10.0/LanMountainDesktop.exe",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/../LanMountainDesktop/bin/Debug/net10.0",
|
||||
"stopAtEntry": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
3. **启动调试**
|
||||
- 按 `F5` 启动
|
||||
- 宿主会以调试模式启动
|
||||
- 插件代码中的断点会直接命中
|
||||
|
||||
⚠️ **注意:** 发布插件前务必移除临时引用!
|
||||
|
||||
---
|
||||
|
||||
## 📝 日志调试
|
||||
|
||||
当断点调试不方便时,日志是最有效的调试手段。
|
||||
|
||||
### 使用 ILogger
|
||||
|
||||
```csharp
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public class MyService
|
||||
{
|
||||
private readonly ILogger<MyService> _logger;
|
||||
|
||||
public MyService(ILogger<MyService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void DoWork()
|
||||
{
|
||||
_logger.LogInformation("开始执行任务");
|
||||
|
||||
try
|
||||
{
|
||||
// 业务逻辑
|
||||
_logger.LogDebug("处理数据: {Data}", data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "任务执行失败");
|
||||
}
|
||||
|
||||
_logger.LogInformation("任务完成");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 日志级别
|
||||
|
||||
| 级别 | 使用场景 |
|
||||
|-----|---------|
|
||||
| `LogTrace` | 最详细的跟踪信息 |
|
||||
| `LogDebug` | 调试信息 |
|
||||
| `LogInformation` | 一般信息 |
|
||||
| `LogWarning` | 警告信息 |
|
||||
| `LogError` | 错误信息 |
|
||||
| `LogCritical` | 严重错误 |
|
||||
|
||||
### 查看日志文件
|
||||
|
||||
日志文件位置:
|
||||
|
||||
```
|
||||
Windows: %LOCALAPPDATA%\LanMountainDesktop\logs\
|
||||
Linux: ~/.local/share/LanMountainDesktop/logs/
|
||||
macOS: ~/Library/Application Support/LanMountainDesktop/logs/
|
||||
```
|
||||
|
||||
日志文件命名格式:
|
||||
```
|
||||
log-20240413.txt
|
||||
log-20240413_001.txt
|
||||
```
|
||||
|
||||
### 实时查看日志
|
||||
|
||||
**Windows PowerShell:**
|
||||
```powershell
|
||||
Get-Content "$env:LOCALAPPDATA\LanMountainDesktop\logs\log-$(Get-Date -Format 'yyyyMMdd').txt" -Wait
|
||||
```
|
||||
|
||||
**Linux/macOS:**
|
||||
```bash
|
||||
tail -f ~/.local/share/LanMountainDesktop/logs/log-$(date +%Y%m%d).txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚫 热重载限制
|
||||
|
||||
⚠️ **重要:** 阑山桌面插件**不支持**热重载(Hot Reload)
|
||||
|
||||
### 原因
|
||||
|
||||
插件运行在独立的 `AssemblyLoadContext` 中,.NET 不支持卸载已加载的程序集。因此:
|
||||
|
||||
- 修改代码后必须重新构建
|
||||
- 必须重启宿主才能加载新版本
|
||||
- 无法使用 `dotnet watch`
|
||||
|
||||
### 高效开发流程
|
||||
|
||||
```
|
||||
修改代码 → dotnet build → 重启宿主 → 测试
|
||||
```
|
||||
|
||||
**加速技巧:**
|
||||
|
||||
1. **创建批处理脚本**(`rebuild-and-run.ps1`):
|
||||
```powershell
|
||||
dotnet build
|
||||
Stop-Process -Name "LanMountainDesktop" -ErrorAction SilentlyContinue
|
||||
Start-Process "C:\Path\To\LanMountainDesktop.exe"
|
||||
```
|
||||
|
||||
2. **使用 Rider 的外部工具**:
|
||||
- 配置构建后自动复制 `.laapp` 到插件目录
|
||||
|
||||
---
|
||||
|
||||
## 🐛 常见问题排查
|
||||
|
||||
### 问题 1:断点不命中
|
||||
|
||||
**可能原因:**
|
||||
- 插件未重新构建
|
||||
- PDB 符号文件未生成
|
||||
- 附加到了错误的进程
|
||||
|
||||
**解决步骤:**
|
||||
1. 确认已重新构建:`dotnet build`
|
||||
2. 检查输出目录是否有 `.pdb` 文件
|
||||
3. 确认附加的是 `LanMountainDesktop.exe`(不是 `LanMountainDesktop.dll`)
|
||||
4. 尝试清理重建:
|
||||
```powershell
|
||||
dotnet clean
|
||||
dotnet build
|
||||
```
|
||||
|
||||
### 问题 2:插件不加载
|
||||
|
||||
**排查步骤:**
|
||||
|
||||
1. **检查日志**
|
||||
```powershell
|
||||
Get-Content "$env:LOCALAPPDATA\LanMountainDesktop\logs\log-$(Get-Date -Format 'yyyyMMdd').txt" | Select-String "MyPlugin"
|
||||
```
|
||||
|
||||
2. **验证 plugin.json**
|
||||
- JSON 格式是否有效
|
||||
- `id` 是否合法(只含小写字母、数字、点号)
|
||||
- `apiVersion` 是否与 SDK 版本匹配
|
||||
|
||||
3. **检查 .laapp 包**
|
||||
- 用压缩软件打开,确认文件完整
|
||||
- 确认 `plugin.json` 和 DLL 存在
|
||||
|
||||
### 问题 3:依赖项找不到
|
||||
|
||||
**现象:** `FileNotFoundException` 或 `Could not load file or assembly`
|
||||
|
||||
**解决:**
|
||||
1. 确保所有依赖项都复制到输出目录
|
||||
2. 在 `.csproj` 中添加:
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
### 问题 4:调试时宿主卡顿
|
||||
|
||||
**原因:** 断点暂停导致 UI 线程阻塞
|
||||
|
||||
**解决:**
|
||||
- 使用 `Debugger.Break()` 代替断点
|
||||
- 或使用日志代替断点调试
|
||||
|
||||
---
|
||||
|
||||
## 💡 调试技巧
|
||||
|
||||
### 1. 条件断点
|
||||
|
||||
当需要在特定条件下暂停时使用:
|
||||
|
||||
**Visual Studio:**
|
||||
- 右键断点 → `条件`
|
||||
- 输入条件表达式,如:`count > 10`
|
||||
|
||||
### 2. 日志点(Tracepoint)
|
||||
|
||||
不暂停程序,只输出日志:
|
||||
|
||||
**Visual Studio:**
|
||||
- 右键断点 → `操作`
|
||||
- 勾选 `将消息输出到输出窗口`
|
||||
- 输入消息模板:`变量值: {variableName}`
|
||||
|
||||
### 3. 异常设置
|
||||
|
||||
自动在抛出异常时中断:
|
||||
|
||||
**Visual Studio:**
|
||||
- `调试` → `窗口` → `异常设置`
|
||||
- 勾选 `Common Language Runtime Exceptions`
|
||||
|
||||
### 4. 立即窗口
|
||||
|
||||
在调试时执行代码:
|
||||
|
||||
**Visual Studio:**
|
||||
- 快捷键:`Ctrl+Alt+I`
|
||||
- 可查看变量值、调用方法
|
||||
|
||||
---
|
||||
|
||||
## 📊 性能调试
|
||||
|
||||
### 使用 Diagnostic Tools
|
||||
|
||||
**Visual Studio:**
|
||||
- 调试时自动显示 CPU 和内存使用情况
|
||||
- `调试` → `窗口` → `诊断工具`
|
||||
|
||||
### 内存泄漏排查
|
||||
|
||||
```csharp
|
||||
// 在可疑位置添加诊断代码
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
var memory = GC.GetTotalMemory(true);
|
||||
Debug.WriteLine($"内存使用: {memory / 1024 / 1024} MB");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
掌握了调试技巧后,接下来学习核心概念:
|
||||
|
||||
👉 **[01-插件生命周期](../02-核心概念与原理/01-插件生命周期.md)** - 理解插件运行机制
|
||||
|
||||
或者查看实战案例:
|
||||
|
||||
👉 **[01-开发天气组件](../04-实战案例/01-开发天气组件.md)** - 完整开发流程
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [PluginBase 源码](../../LanMountainDesktop.PluginSdk/PluginBase.cs)
|
||||
- [docs/DEVELOPMENT.md](../../docs/DEVELOPMENT.md)
|
||||
- [Visual Studio 调试文档](https://docs.microsoft.com/visualstudio/debugger/)
|
||||
- [Rider 调试文档](https://www.jetbrains.com/help/rider/Debugging.html)
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
398
docs/Plugins develop/02-核心概念与原理/01-插件生命周期.md
Normal file
398
docs/Plugins develop/02-核心概念与原理/01-插件生命周期.md
Normal file
@@ -0,0 +1,398 @@
|
||||
# 01-插件生命周期
|
||||
|
||||
理解插件的生命周期,是开发稳定可靠插件的基础。本文详细讲解插件从加载到卸载的完整过程。
|
||||
|
||||
---
|
||||
|
||||
## 🔄 生命周期概览
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 阑山桌面启动 │
|
||||
└───────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 1. 发现插件 │
|
||||
│ - 扫描插件目录 │
|
||||
│ - 解析 plugin.json │
|
||||
│ - 验证 API 版本兼容性 │
|
||||
└───────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 2. 加载插件 │
|
||||
│ - 创建 AssemblyLoadContext │
|
||||
│ - 加载插件 DLL │
|
||||
│ - 查找入口类(带 [PluginEntrance] 特性) │
|
||||
└───────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 3. 初始化(Initialize) │
|
||||
│ - 调用 Plugin.Initialize() │
|
||||
│ - 注册组件、设置页面、服务 │
|
||||
│ - ⚠️ 此时 UI 尚未完全就绪 │
|
||||
└───────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 4. 运行中 │
|
||||
│ - 组件被添加到桌面 │
|
||||
│ - 用户与组件交互 │
|
||||
│ - 设置页面被打开 │
|
||||
└───────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 5. 停用/卸载 │
|
||||
│ - 用户禁用插件 │
|
||||
│ - 或关闭阑山桌面 │
|
||||
│ - 释放资源(当前版本无显式卸载回调) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 各阶段详解
|
||||
|
||||
### 阶段 1:发现插件
|
||||
|
||||
**时机:** 阑山桌面启动时
|
||||
|
||||
**过程:**
|
||||
1. 扫描 `%LOCALAPPDATA%\LanMountainDesktop\plugins\` 目录
|
||||
2. 读取每个 `.laapp` 包中的 `plugin.json`
|
||||
3. 验证 `apiVersion` 是否与宿主兼容
|
||||
4. 检查 `id` 是否唯一
|
||||
|
||||
**可能失败的原因:**
|
||||
- `plugin.json` 格式错误
|
||||
- `apiVersion` 不兼容
|
||||
- `id` 与其他插件冲突
|
||||
|
||||
---
|
||||
|
||||
### 阶段 2:加载插件
|
||||
|
||||
**时机:** 发现成功后
|
||||
|
||||
**过程:**
|
||||
1. 创建独立的 `AssemblyLoadContext`
|
||||
2. 加载插件 DLL 及其依赖项
|
||||
3. 查找带有 `[PluginEntrance]` 特性的类
|
||||
4. 实例化插件入口类
|
||||
|
||||
**代码示例:**
|
||||
```csharp
|
||||
[PluginEntrance] // ← 这个特性标记入口类
|
||||
public sealed class Plugin : PluginBase
|
||||
{
|
||||
// 插件实例在此阶段被创建
|
||||
}
|
||||
```
|
||||
|
||||
⚠️ **重要:** 此阶段**不要**执行耗时操作,只应进行简单的字段初始化。
|
||||
|
||||
---
|
||||
|
||||
### 阶段 3:初始化(Initialize)
|
||||
|
||||
**时机:** 插件加载完成后
|
||||
|
||||
**这是插件开发中最重要的阶段!**
|
||||
|
||||
#### 方法签名
|
||||
|
||||
```csharp
|
||||
public override void Initialize(
|
||||
HostBuilderContext context, // 宿主构建上下文
|
||||
IServiceCollection services) // 服务注册集合
|
||||
```
|
||||
|
||||
#### 可执行的操作
|
||||
|
||||
✅ **可以做的:**
|
||||
- 注册桌面组件
|
||||
- 注册设置页面
|
||||
- 注册服务到依赖注入容器
|
||||
- 读取配置
|
||||
- 初始化资源
|
||||
|
||||
❌ **不应该做的:**
|
||||
- 访问 UI 元素(UI 尚未就绪)
|
||||
- 执行耗时阻塞操作
|
||||
- 创建窗口或对话框
|
||||
|
||||
#### 典型初始化代码
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 1. 注册服务
|
||||
services.AddSingleton<IWeatherService, WeatherService>();
|
||||
|
||||
// 2. 注册桌面组件
|
||||
services.AddPluginDesktopComponent<WeatherWidget>(
|
||||
new PluginDesktopComponentOptions
|
||||
{
|
||||
ComponentId = "MyPlugin.Weather",
|
||||
DisplayName = "天气",
|
||||
IconKey = "Weather",
|
||||
Category = "工具",
|
||||
MinWidthCells = 4,
|
||||
MinHeightCells = 3
|
||||
});
|
||||
|
||||
// 3. 注册设置页面
|
||||
services.AddPluginSettingsSection(
|
||||
"myplugin-settings",
|
||||
"天气设置",
|
||||
section => section
|
||||
.AddToggle("auto_refresh", "自动刷新", defaultValue: true)
|
||||
.AddNumber("interval", "刷新间隔(分钟)", defaultValue: 30),
|
||||
iconKey: "Settings");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 阶段 4:运行中
|
||||
|
||||
**时机:** 初始化完成后,直到插件被禁用或宿主关闭
|
||||
|
||||
**特点:**
|
||||
- 组件可以被添加到桌面
|
||||
- 用户可以与组件交互
|
||||
- 设置页面可以被打开
|
||||
- 定时器可以运行
|
||||
|
||||
#### 组件生命周期
|
||||
|
||||
```
|
||||
用户添加组件
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ 创建组件实例 │ ← 调用构造函数
|
||||
│ (Dependency │ 注入 IServiceProvider
|
||||
│ Injection) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ 组件初始化 │ ← 可在此时加载数据
|
||||
│ (Loaded事件) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ 渲染显示 │ ← 用户看到组件
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────┴────┐
|
||||
▼ ▼
|
||||
用户交互 定时更新
|
||||
│ │
|
||||
└────┬────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ 组件移除 │ ← 用户删除组件
|
||||
│ (Unloaded事件) │ 或关闭宿主
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 阶段 5:停用/卸载
|
||||
|
||||
**时机:**
|
||||
- 用户在设置中禁用插件
|
||||
- 卸载插件
|
||||
- 关闭阑山桌面
|
||||
|
||||
**当前限制:**
|
||||
- SDK v4 暂无显式的卸载回调方法
|
||||
- 资源释放依赖 .NET 垃圾回收
|
||||
- 建议:
|
||||
- 使用 `IDisposable` 模式管理资源
|
||||
- 在组件卸载事件中清理资源
|
||||
|
||||
---
|
||||
|
||||
## ⏱️ 启动时序图
|
||||
|
||||
```
|
||||
阑山桌面 插件系统 你的插件
|
||||
│ │ │
|
||||
│── 启动 ───────►│ │
|
||||
│ │ │
|
||||
│ │── 发现插件 ───►│
|
||||
│ │ │ (读取 plugin.json)
|
||||
│ │◄───────────────│
|
||||
│ │ │
|
||||
│ │── 加载 DLL ───►│
|
||||
│ │ │ (AssemblyLoadContext)
|
||||
│ │◄───────────────│
|
||||
│ │ │
|
||||
│ │── 创建实例 ───►│
|
||||
│ │ │ (调用构造函数)
|
||||
│ │◄───────────────│
|
||||
│ │ │
|
||||
│ │── Initialize ─►│
|
||||
│ │ │ (注册组件/服务)
|
||||
│ │◄───────────────│
|
||||
│ │ │
|
||||
│◄───────────────│ │
|
||||
│ │ │
|
||||
│── UI就绪 ─────►│ │
|
||||
│ │ │
|
||||
│ │── 用户添加组件 ─►│
|
||||
│ │ │ (创建组件实例)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. Initialize 方法保持轻量
|
||||
|
||||
```csharp
|
||||
// ✅ 好的做法:快速注册
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IDataService, DataService>();
|
||||
services.AddPluginDesktopComponent<MyWidget>(options);
|
||||
}
|
||||
|
||||
// ❌ 避免:耗时操作
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 不要这样做!
|
||||
var data = FetchDataFromInternet().Result; // 阻塞!
|
||||
services.AddSingleton(data);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 延迟加载数据
|
||||
|
||||
```csharp
|
||||
public class MyWidget : Border
|
||||
{
|
||||
private readonly IDataService _dataService;
|
||||
|
||||
public MyWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
_dataService = context.ServiceProvider.GetRequiredService<IDataService>();
|
||||
|
||||
// 在 Loaded 事件中加载数据,而不是构造函数
|
||||
Loaded += async (_, _) =>
|
||||
{
|
||||
await LoadDataAsync();
|
||||
};
|
||||
}
|
||||
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
var data = await _dataService.GetDataAsync();
|
||||
// 更新 UI
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 正确处理资源释放
|
||||
|
||||
```csharp
|
||||
public class MyWidget : Border, IDisposable
|
||||
{
|
||||
private readonly Timer _timer;
|
||||
private bool _disposed;
|
||||
|
||||
public MyWidget()
|
||||
{
|
||||
_timer = new Timer(OnTimerTick, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
|
||||
|
||||
// 在卸载时释放资源
|
||||
Unloaded += (_, _) => Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
_timer?.Dispose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 避免循环依赖
|
||||
|
||||
```csharp
|
||||
// ❌ 避免:服务之间相互依赖
|
||||
public class ServiceA
|
||||
{
|
||||
public ServiceA(ServiceB b) { } // 循环依赖风险
|
||||
}
|
||||
|
||||
public class ServiceB
|
||||
{
|
||||
public ServiceB(ServiceA a) { }
|
||||
}
|
||||
|
||||
// ✅ 好的做法:使用接口解耦
|
||||
public class ServiceA
|
||||
{
|
||||
public ServiceA(IServiceB b) { }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### 问题 1:Initialize 中访问 UI 报错
|
||||
|
||||
**现象:** `InvalidOperationException` 或空引用
|
||||
|
||||
**原因:** Initialize 在 UI 就绪前调用
|
||||
|
||||
**解决:** 延迟到组件创建后再访问 UI
|
||||
|
||||
### 问题 2:服务注册顺序问题
|
||||
|
||||
**现象:** 依赖注入找不到服务
|
||||
|
||||
**原因:** 服务注册顺序不正确
|
||||
|
||||
**解决:** 先注册服务,再注册依赖这些服务的组件
|
||||
|
||||
### 问题 3:插件加载慢
|
||||
|
||||
**现象:** 宿主启动变慢
|
||||
|
||||
**原因:** Initialize 中执行耗时操作
|
||||
|
||||
**解决:** 将耗时操作移到后台线程或延迟执行
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [PluginBase 源码](../../LanMountainDesktop.PluginSdk/PluginBase.cs)
|
||||
- [IPlugin 接口](../../LanMountainDesktop.PluginSdk/IPlugin.cs)
|
||||
- [02-桌面组件系统](02-桌面组件系统.md)
|
||||
- [03-设置系统集成](03-设置系统集成.md)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
理解生命周期后,学习如何创建桌面组件:
|
||||
|
||||
👉 **[02-桌面组件系统](02-桌面组件系统.md)** - 创建可视化组件
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
393
docs/Plugins develop/02-核心概念与原理/02-桌面组件系统.md
Normal file
393
docs/Plugins develop/02-核心概念与原理/02-桌面组件系统.md
Normal file
@@ -0,0 +1,393 @@
|
||||
# 02-桌面组件系统
|
||||
|
||||
桌面组件(Desktop Component)是阑山桌面插件的核心功能。本文详细讲解组件系统的工作原理和开发方法。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 什么是桌面组件
|
||||
|
||||
桌面组件是显示在阑山桌面上的可视化元素,用户可以自由:
|
||||
- 添加/删除组件
|
||||
- 拖动调整位置
|
||||
- 调整大小
|
||||
- 配置属性
|
||||
|
||||
**常见组件示例:**
|
||||
- 时钟组件 - 显示当前时间
|
||||
- 天气组件 - 显示天气信息
|
||||
- 日历组件 - 显示日期和日程
|
||||
- 系统监控 - 显示 CPU/内存使用率
|
||||
|
||||
---
|
||||
|
||||
## 📐 网格系统
|
||||
|
||||
阑山桌面使用网格系统管理组件布局。
|
||||
|
||||
### 网格概念
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
|
||||
│ │ 2x2│ │ 2x2│ │ 2x2│ │ 2x2│ │
|
||||
│ │ 格 │ │ 格 │ │ 格 │ │ 格 │ │
|
||||
│ └────┘ └────┘ └────┘ └────┘ │
|
||||
│ ┌────┐ ┌────────┐ ┌────┐ │
|
||||
│ │ 2x2│ │ 4x2 │ │ 2x2│ │
|
||||
│ │ 格 │ │ 格 │ │ 格 │ │
|
||||
│ └────┘ └────────┘ └────┘ │
|
||||
│ ┌────────┐ ┌────────┐ │
|
||||
│ │ 4x3 │ │ 4x3 │ │
|
||||
│ │ 格 │ │ 格 │ │
|
||||
│ └────────┘ └────────┘ │
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
每格大小:约 60-80 像素(根据 DPI 自动调整)
|
||||
```
|
||||
|
||||
### 组件尺寸
|
||||
|
||||
组件尺寸以**格数**为单位:
|
||||
|
||||
| 属性 | 说明 | 示例 |
|
||||
|-----|------|------|
|
||||
| `MinWidthCells` | 最小宽度(格数) | 4 = 4格宽 |
|
||||
| `MinHeightCells` | 最小高度(格数) | 3 = 3格高 |
|
||||
|
||||
**常见尺寸参考:**
|
||||
|
||||
| 组件类型 | 推荐尺寸 | 实际像素(约) |
|
||||
|---------|---------|--------------|
|
||||
| 小图标 | 2x2 | 120x120 |
|
||||
| 天气卡片 | 4x3 | 240x180 |
|
||||
| 时钟 | 4x4 | 240x240 |
|
||||
| 日历 | 6x4 | 360x240 |
|
||||
| 宽面板 | 8x3 | 480x180 |
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 创建组件
|
||||
|
||||
### 步骤 1:创建组件类
|
||||
|
||||
组件是继承自 Avalonia 控件的类:
|
||||
|
||||
```csharp
|
||||
using Avalonia.Controls;
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
|
||||
namespace MyPlugin;
|
||||
|
||||
public class WeatherWidget : Border // 继承自 Border 或其他控件
|
||||
{
|
||||
public WeatherWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 组件初始化
|
||||
InitializeComponent(context);
|
||||
}
|
||||
|
||||
private void InitializeComponent(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 设置背景
|
||||
Background = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
// 设置圆角(使用宿主主题)
|
||||
CornerRadius = context.Appearance.ResolveCornerRadius(
|
||||
PluginCornerRadiusPreset.Component);
|
||||
|
||||
// 创建内容
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = "天气组件",
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
Child = textBlock;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 2:注册组件
|
||||
|
||||
在 `Plugin.Initialize` 中注册:
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
services.AddPluginDesktopComponent<WeatherWidget>(
|
||||
new PluginDesktopComponentOptions
|
||||
{
|
||||
ComponentId = "MyPlugin.Weather", // 唯一标识
|
||||
DisplayName = "天气", // 显示名称
|
||||
IconKey = "Weather", // 图标(Fluent 图标名)
|
||||
Category = "工具", // 分类
|
||||
MinWidthCells = 4, // 最小宽度(格)
|
||||
MinHeightCells = 3, // 最小高度(格)
|
||||
CornerRadiusPreset = PluginCornerRadiusPreset.Component // 圆角预设
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### PluginDesktopComponentOptions 详解
|
||||
|
||||
| 属性 | 必需 | 说明 | 示例 |
|
||||
|-----|------|------|------|
|
||||
| `ComponentId` | ✅ | 唯一标识符 | `"MyPlugin.Weather"` |
|
||||
| `DisplayName` | ✅ | 显示名称 | `"天气"` |
|
||||
| `IconKey` | ✅ | 图标键名 | `"Weather"`、`"Clock"` |
|
||||
| `Category` | ✅ | 分类 | `"工具"`、`"信息"` |
|
||||
| `MinWidthCells` | ✅ | 最小宽度(格) | `4` |
|
||||
| `MinHeightCells` | ✅ | 最小高度(格) | `3` |
|
||||
| `CornerRadiusPreset` | ❌ | 圆角预设 | `PluginCornerRadiusPreset.Component` |
|
||||
| `ResizeMode` | ❌ | 调整大小模式 | `PluginDesktopComponentResizeMode.Free` |
|
||||
|
||||
### 常用 Fluent 图标
|
||||
|
||||
| 图标键名 | 用途 |
|
||||
|---------|------|
|
||||
| `Weather` | 天气相关 |
|
||||
| `Clock` | 时钟、时间 |
|
||||
| `Calendar` | 日历、日期 |
|
||||
| `Settings` | 设置 |
|
||||
| `Home` | 主页 |
|
||||
| `Search` | 搜索 |
|
||||
| `Star` | 收藏 |
|
||||
| `Heart` | 喜欢 |
|
||||
| `Info` | 信息 |
|
||||
| `Warning` | 警告 |
|
||||
|
||||
完整图标列表:[Fluent UI System Icons](https://github.com/microsoft/fluentui-system-icons)
|
||||
|
||||
---
|
||||
|
||||
## 🎨 组件外观
|
||||
|
||||
### 圆角设置
|
||||
|
||||
插件必须使用宿主提供的圆角系统,以保持视觉一致性:
|
||||
|
||||
```csharp
|
||||
public WeatherWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 获取组件标准圆角
|
||||
var cornerRadius = context.Appearance.ResolveCornerRadius(
|
||||
PluginCornerRadiusPreset.Component);
|
||||
|
||||
CornerRadius = cornerRadius;
|
||||
}
|
||||
```
|
||||
|
||||
**可用的圆角预设:**
|
||||
|
||||
| 预设 | 用途 |
|
||||
|-----|------|
|
||||
| `Micro` | 微小元素 |
|
||||
| `Xs` | 小元素 |
|
||||
| `Sm` | 小卡片 |
|
||||
| `Md` | 普通按钮/卡片 |
|
||||
| `Lg` | 大面板 |
|
||||
| `Xl` | 强调容器 |
|
||||
| `Component` | **桌面组件标准** |
|
||||
| `Default` | 自适应 |
|
||||
|
||||
### 背景与透明
|
||||
|
||||
```csharp
|
||||
// 透明背景(推荐,让宿主壁纸透出)
|
||||
Background = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
// 毛玻璃效果
|
||||
Background = new SolidColorBrush(Color.Parse("#40FFFFFF"));
|
||||
|
||||
// 纯色背景
|
||||
Background = new SolidColorBrush(Color.Parse("#FF2D2D2D"));
|
||||
```
|
||||
|
||||
### 响应主题变化
|
||||
|
||||
```csharp
|
||||
public WeatherWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 订阅主题变化
|
||||
context.Appearance.AppearanceChanged += (_, _) =>
|
||||
{
|
||||
UpdateAppearance();
|
||||
};
|
||||
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
private void UpdateAppearance()
|
||||
{
|
||||
// 根据当前主题更新颜色
|
||||
var isDark = Application.Current?.ActualThemeVariant == ThemeVariant.Dark;
|
||||
|
||||
Foreground = new SolidColorBrush(isDark ? Colors.White : Colors.Black);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📏 尺寸与布局
|
||||
|
||||
### 获取实际尺寸
|
||||
|
||||
```csharp
|
||||
public WeatherWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 订阅尺寸变化
|
||||
SizeChanged += OnSizeChanged;
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object? sender, SizeChangedEventArgs e)
|
||||
{
|
||||
// 获取当前实际尺寸(像素)
|
||||
var width = Bounds.Width;
|
||||
var height = Bounds.Height;
|
||||
|
||||
// 根据尺寸调整内容
|
||||
if (width < 200)
|
||||
{
|
||||
// 小尺寸模式
|
||||
ShowCompactView();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 完整模式
|
||||
ShowFullView();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 自适应布局
|
||||
|
||||
```csharp
|
||||
private void UpdateLayout()
|
||||
{
|
||||
var width = Bounds.Width;
|
||||
var height = Bounds.Height;
|
||||
|
||||
// 根据宽高比调整布局
|
||||
if (width > height * 2)
|
||||
{
|
||||
// 宽屏模式 - 水平排列
|
||||
_layout.Orientation = Orientation.Horizontal;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 正常模式 - 垂直排列
|
||||
_layout.Orientation = Orientation.Vertical;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 组件生命周期事件
|
||||
|
||||
```csharp
|
||||
public WeatherWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 组件加载完成(此时已添加到视觉树)
|
||||
Loaded += OnLoaded;
|
||||
|
||||
// 组件卸载(用户删除或关闭宿主)
|
||||
Unloaded += OnUnloaded;
|
||||
|
||||
// 尺寸变化
|
||||
SizeChanged += OnSizeChanged;
|
||||
}
|
||||
|
||||
private async void OnLoaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
// 加载数据
|
||||
await LoadDataAsync();
|
||||
|
||||
// 启动定时器
|
||||
_timer = new Timer(OnTimerTick, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
|
||||
}
|
||||
|
||||
private void OnUnloaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
// 清理资源
|
||||
_timer?.Dispose();
|
||||
_httpClient?.Dispose();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💾 组件设置持久化
|
||||
|
||||
组件可以保存自己的设置:
|
||||
|
||||
```csharp
|
||||
public class WeatherWidget : Border
|
||||
{
|
||||
private readonly IComponentSettingsAccessor _settings;
|
||||
|
||||
public WeatherWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 获取设置访问器
|
||||
_settings = context.Settings;
|
||||
|
||||
// 读取设置
|
||||
var city = _settings.GetValue<string>("city", defaultValue: "北京");
|
||||
var autoRefresh = _settings.GetValue<bool>("auto_refresh", defaultValue: true);
|
||||
|
||||
// 保存设置
|
||||
_settings.SetValue("city", "上海");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### 问题 1:组件不显示在库中
|
||||
|
||||
**排查:**
|
||||
1. 确认已调用 `AddPluginDesktopComponent`
|
||||
2. 检查 `ComponentId` 是否唯一
|
||||
3. 确认组件类是 `public`
|
||||
|
||||
### 问题 2:组件显示异常
|
||||
|
||||
**排查:**
|
||||
1. 检查构造函数参数是否正确(需要 `PluginDesktopComponentContext`)
|
||||
2. 确认没有抛出未处理异常
|
||||
3. 查看日志文件
|
||||
|
||||
### 问题 3:圆角不生效
|
||||
|
||||
**原因:** 插件无法访问宿主 XAML 资源
|
||||
|
||||
**解决:** 使用代码设置圆角(见上文)
|
||||
|
||||
### 问题 4:尺寸不正确
|
||||
|
||||
**排查:**
|
||||
1. 检查 `MinWidthCells` 和 `MinHeightCells` 设置
|
||||
2. 确认内容没有强制尺寸
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [PluginDesktopComponentOptions 源码](../../LanMountainDesktop.PluginSdk/PluginDesktopComponentOptions.cs)
|
||||
- [04-外观与主题系统](04-外观与主题系统.md)
|
||||
- [01-开发天气组件](../04-实战案例/01-开发天气组件.md)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
学习如何添加设置页面:
|
||||
|
||||
👉 **[03-设置系统集成](03-设置系统集成.md)** - 让用户配置你的组件
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
353
docs/Plugins develop/02-核心概念与原理/03-设置系统集成.md
Normal file
353
docs/Plugins develop/02-核心概念与原理/03-设置系统集成.md
Normal file
@@ -0,0 +1,353 @@
|
||||
# 03-设置系统集成
|
||||
|
||||
设置系统允许插件在阑山桌面的设置窗口中添加自己的配置页面,让用户可以自定义插件行为。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 设置系统概述
|
||||
|
||||
阑山桌面提供两种设置页面模式:
|
||||
|
||||
| 模式 | 适用场景 | 复杂度 | 灵活性 |
|
||||
|-----|---------|--------|--------|
|
||||
| **声明式设置** | 简单的键值配置 | 低 | 中 |
|
||||
| **自定义设置页** | 复杂交互、自定义控件 | 中 | 高 |
|
||||
|
||||
---
|
||||
|
||||
## 📝 声明式设置
|
||||
|
||||
通过链式 API 声明配置项,宿主自动生成设置页面。
|
||||
|
||||
### 基本用法
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
services.AddPluginSettingsSection(
|
||||
sectionId: "myplugin-settings", // 设置节唯一标识
|
||||
displayName: "我的插件设置", // 显示名称
|
||||
configure: section => // 配置设置项
|
||||
{
|
||||
section
|
||||
.AddToggle("enabled", "启用插件", defaultValue: true)
|
||||
.AddText("api_key", "API密钥", defaultValue: "")
|
||||
.AddNumber("interval", "刷新间隔(秒)", defaultValue: 60, minimum: 10, maximum: 3600)
|
||||
.AddSelect("theme", "主题", new[]
|
||||
{
|
||||
new SettingsOptionChoice("light", "浅色"),
|
||||
new SettingsOptionChoice("dark", "深色"),
|
||||
new SettingsOptionChoice("auto", "跟随系统")
|
||||
}, defaultValue: "auto");
|
||||
},
|
||||
iconKey: "Settings"); // 图标
|
||||
}
|
||||
```
|
||||
|
||||
### 支持的设置类型
|
||||
|
||||
| 方法 | 类型 | 用途 | 示例 |
|
||||
|-----|------|------|------|
|
||||
| `AddToggle` | 布尔 | 开关选项 | 启用/禁用 |
|
||||
| `AddText` | 字符串 | 文本输入 | API密钥、用户名 |
|
||||
| `AddNumber` | 数值 | 数字输入 | 刷新间隔、数量 |
|
||||
| `AddSelect` | 枚举 | 下拉选择 | 主题、语言 |
|
||||
| `AddPath` | 路径 | 文件/文件夹选择 | 保存路径 |
|
||||
| `AddList` | 列表 | 字符串列表 | 服务器地址列表 |
|
||||
|
||||
### 各类型详解
|
||||
|
||||
#### Toggle(开关)
|
||||
|
||||
```csharp
|
||||
.AddToggle(
|
||||
key: "auto_update", // 设置键
|
||||
displayName: "自动更新", // 显示名称
|
||||
defaultValue: true, // 默认值
|
||||
description: "启动时检查更新" // 可选描述
|
||||
)
|
||||
```
|
||||
|
||||
#### Text(文本)
|
||||
|
||||
```csharp
|
||||
.AddText(
|
||||
key: "api_key",
|
||||
displayName: "API密钥",
|
||||
defaultValue: "",
|
||||
placeholder: "请输入API密钥", // 占位符
|
||||
isPassword: true // 密码输入(掩码显示)
|
||||
)
|
||||
```
|
||||
|
||||
#### Number(数值)
|
||||
|
||||
```csharp
|
||||
.AddNumber(
|
||||
key: "refresh_interval",
|
||||
displayName: "刷新间隔",
|
||||
defaultValue: 60,
|
||||
minimum: 10, // 最小值
|
||||
maximum: 3600, // 最大值
|
||||
increment: 10 // 步进值
|
||||
)
|
||||
```
|
||||
|
||||
#### Select(选择)
|
||||
|
||||
```csharp
|
||||
.AddSelect(
|
||||
key: "display_mode",
|
||||
displayName: "显示模式",
|
||||
choices: new[]
|
||||
{
|
||||
new SettingsOptionChoice("compact", "紧凑"),
|
||||
new SettingsOptionChoice("normal", "标准"),
|
||||
new SettingsOptionChoice("detailed", "详细")
|
||||
},
|
||||
defaultValue: "normal"
|
||||
)
|
||||
```
|
||||
|
||||
#### Path(路径)
|
||||
|
||||
```csharp
|
||||
.AddPath(
|
||||
key: "save_location",
|
||||
displayName: "保存位置",
|
||||
defaultValue: "",
|
||||
pathType: SettingsPathType.Folder, // Folder 或 File
|
||||
dialogTitle: "选择保存文件夹"
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 自定义设置页
|
||||
|
||||
当声明式设置无法满足需求时,可以创建自定义设置页面。
|
||||
|
||||
### 步骤 1:创建设置页类
|
||||
|
||||
```csharp
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
|
||||
namespace MyPlugin;
|
||||
|
||||
public class MySettingsPage : SettingsPageBase
|
||||
{
|
||||
private readonly IPluginSettingsService _settingsService;
|
||||
|
||||
public MySettingsPage(IPluginSettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
// 创建页面内容
|
||||
var panel = new StackPanel { Spacing = 16 };
|
||||
|
||||
// 添加自定义控件
|
||||
var expander = new SettingsExpander
|
||||
{
|
||||
Header = "高级设置",
|
||||
Description = "配置插件的高级选项"
|
||||
};
|
||||
|
||||
var toggle = new ToggleSwitch
|
||||
{
|
||||
Content = "启用实验性功能"
|
||||
};
|
||||
|
||||
expander.Items.Add(toggle);
|
||||
panel.Children.Add(expander);
|
||||
|
||||
// 添加颜色选择器示例
|
||||
var colorPicker = new ColorPicker
|
||||
{
|
||||
Header = "主题颜色"
|
||||
};
|
||||
panel.Children.Add(colorPicker);
|
||||
|
||||
Content = panel;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 2:注册自定义设置页
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
services.AddPluginSettingsSection<MySettingsPage>(
|
||||
sectionId: "myplugin-advanced",
|
||||
displayName: "高级设置",
|
||||
iconKey: "Settings");
|
||||
}
|
||||
```
|
||||
|
||||
### 混合模式
|
||||
|
||||
可以同时使用声明式设置和自定义视图:
|
||||
|
||||
```csharp
|
||||
services.AddPluginSettingsSection(
|
||||
sectionId: "myplugin-settings",
|
||||
displayName: "插件设置",
|
||||
configure: section => section
|
||||
.SetCustomView<MyCustomSettingsPage>() // 设置自定义视图
|
||||
.AddToggle("enabled", "启用") // 同时声明设置项
|
||||
.AddText("api_key", "API密钥"),
|
||||
iconKey: "Settings");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💾 读取和保存设置
|
||||
|
||||
### 在服务中读取设置
|
||||
|
||||
```csharp
|
||||
public class WeatherService
|
||||
{
|
||||
private readonly IPluginSettingsService _settings;
|
||||
|
||||
public WeatherService(IPluginSettingsService settings)
|
||||
{
|
||||
_settings = settings;
|
||||
|
||||
// 读取设置
|
||||
var apiKey = _settings.GetValue<string>("api_key", "");
|
||||
var autoRefresh = _settings.GetValue<bool>("auto_update", true);
|
||||
var interval = _settings.GetValue<int>("refresh_interval", 60);
|
||||
|
||||
// 监听设置变化
|
||||
_settings.SettingsChanged += (sender, e) =>
|
||||
{
|
||||
if (e.Key == "refresh_interval")
|
||||
{
|
||||
UpdateTimerInterval(e.NewValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 在组件中读取设置
|
||||
|
||||
```csharp
|
||||
public class WeatherWidget : Border
|
||||
{
|
||||
public WeatherWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 通过 context 获取设置
|
||||
var settings = context.Settings;
|
||||
|
||||
var city = settings.GetValue<string>("city", "北京");
|
||||
var unit = settings.GetValue<string>("temperature_unit", "celsius");
|
||||
|
||||
// 监听设置变化
|
||||
context.Settings.SettingsChanged += (_, e) =>
|
||||
{
|
||||
if (e.Key == "city")
|
||||
{
|
||||
RefreshWeather(e.NewValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 保存设置
|
||||
|
||||
```csharp
|
||||
// 在设置页面中保存
|
||||
private void SaveButton_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_settingsService.SetValue("api_key", ApiKeyTextBox.Text);
|
||||
_settingsService.SetValue("auto_update", AutoUpdateToggle.IsChecked ?? false);
|
||||
|
||||
// 设置会自动持久化,无需手动保存文件
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔔 设置变更通知
|
||||
|
||||
### 订阅变更事件
|
||||
|
||||
```csharp
|
||||
public class MyService
|
||||
{
|
||||
public MyService(IPluginSettingsService settings)
|
||||
{
|
||||
settings.SettingsChanged += OnSettingsChanged;
|
||||
}
|
||||
|
||||
private void OnSettingsChanged(object? sender, SettingsChangedEvent e)
|
||||
{
|
||||
Console.WriteLine($"设置变更: {e.Key}");
|
||||
Console.WriteLine($"旧值: {e.OldValue}");
|
||||
Console.WriteLine($"新值: {e.NewValue}");
|
||||
|
||||
// 根据变更的键执行相应操作
|
||||
switch (e.Key)
|
||||
{
|
||||
case "refresh_interval":
|
||||
UpdateRefreshTimer((int)e.NewValue);
|
||||
break;
|
||||
case "theme":
|
||||
ApplyTheme((string)e.NewValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### 问题 1:设置不保存
|
||||
|
||||
**排查:**
|
||||
1. 确认 `sectionId` 唯一且合法
|
||||
2. 检查设置键名是否正确
|
||||
3. 查看日志是否有权限错误
|
||||
|
||||
### 问题 2:设置页面不显示
|
||||
|
||||
**排查:**
|
||||
1. 确认已调用 `AddPluginSettingsSection`
|
||||
2. 检查 `sectionId` 是否唯一
|
||||
3. 确认设置页类是 `public`
|
||||
|
||||
### 问题 3:设置变更通知不触发
|
||||
|
||||
**原因:** 可能订阅的是不同实例
|
||||
|
||||
**解决:** 确保使用注入的 `IPluginSettingsService`
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [IPluginSettingsService 源码](../../LanMountainDesktop.PluginSdk/IPluginSettingsService.cs)
|
||||
- [SettingsPageBase 源码](../../LanMountainDesktop.PluginSdk/SettingsPageBase.cs)
|
||||
- [04-开发设置页面](../04-实战案例/04-开发设置页面.md)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
学习外观系统:
|
||||
|
||||
👉 **[04-外观与主题系统](04-外观与主题系统.md)** - 适配宿主主题
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
308
docs/Plugins develop/02-核心概念与原理/04-外观与主题系统.md
Normal file
308
docs/Plugins develop/02-核心概念与原理/04-外观与主题系统.md
Normal file
@@ -0,0 +1,308 @@
|
||||
# 04-外观与主题系统
|
||||
|
||||
阑山桌面支持暗色/浅色主题切换,插件需要适配宿主的视觉风格,保持界面一致性。
|
||||
|
||||
---
|
||||
|
||||
## 🎨 主题系统概述
|
||||
|
||||
阑山桌面使用 Avalonia UI 的主题系统,支持:
|
||||
|
||||
- **浅色主题** - 明亮背景,深色文字
|
||||
- **深色主题** - 深色背景,浅色文字
|
||||
- **跟随系统** - 自动匹配 Windows/macOS 主题
|
||||
|
||||
---
|
||||
|
||||
## 🌗 检测当前主题
|
||||
|
||||
### 在组件中检测
|
||||
|
||||
```csharp
|
||||
using Avalonia;
|
||||
using Avalonia.Styling;
|
||||
|
||||
public class MyWidget : Border
|
||||
{
|
||||
public MyWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 检测当前主题
|
||||
var isDark = Application.Current?.ActualThemeVariant == ThemeVariant.Dark;
|
||||
|
||||
// 根据主题设置颜色
|
||||
UpdateTheme(isDark);
|
||||
|
||||
// 监听主题变化
|
||||
if (Application.Current != null)
|
||||
{
|
||||
Application.Current.ActualThemeVariantChanged += (_, _) =>
|
||||
{
|
||||
var newIsDark = Application.Current.ActualThemeVariant == ThemeVariant.Dark;
|
||||
UpdateTheme(newIsDark);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTheme(bool isDark)
|
||||
{
|
||||
Background = new SolidColorBrush(
|
||||
isDark ? Color.Parse("#FF1E1E1E") : Color.Parse("#FFFFFFFF"));
|
||||
|
||||
Foreground = new SolidColorBrush(
|
||||
isDark ? Colors.White : Colors.Black);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📐 圆角系统
|
||||
|
||||
插件必须使用宿主提供的圆角系统,确保与内置组件视觉一致。
|
||||
|
||||
### 为什么插件不能使用 XAML 资源
|
||||
|
||||
插件运行在独立的 `AssemblyLoadContext` 中,无法直接访问宿主的资源字典。因此 `{DynamicResource DesignCornerRadiusComponent}` 在插件 XAML 中无效。
|
||||
|
||||
### 使用代码设置圆角
|
||||
|
||||
```csharp
|
||||
public class MyWidget : Border
|
||||
{
|
||||
public MyWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 方法 1:使用预设(推荐)
|
||||
CornerRadius = context.Appearance.ResolveCornerRadius(
|
||||
PluginCornerRadiusPreset.Component);
|
||||
|
||||
// 方法 2:带最小/最大值限制
|
||||
CornerRadius = context.Appearance.ResolveCornerRadius(
|
||||
PluginCornerRadiusPreset.Component,
|
||||
minimum: new CornerRadius(8),
|
||||
maximum: new CornerRadius(24));
|
||||
|
||||
// 方法 3:自定义基础值,应用全局缩放
|
||||
CornerRadius = context.Appearance.ResolveScaledCornerRadius(
|
||||
baseRadius: 16,
|
||||
minimum: 8,
|
||||
maximum: 32);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 圆角预设
|
||||
|
||||
| 预设 | 默认值 | 用途 |
|
||||
|-----|-------|------|
|
||||
| `Micro` | 6px | 微小元素 |
|
||||
| `Xs` | 12px | 小元素、图标容器 |
|
||||
| `Sm` | 14px | 小卡片 |
|
||||
| `Md` | 20px | 普通按钮/卡片 |
|
||||
| `Lg` | 28px | 大面板 |
|
||||
| `Xl` | 32px | 强调容器 |
|
||||
| `Island` | 36px | 大型容器 |
|
||||
| `Component` | 18px | **桌面组件标准** |
|
||||
| `Default` | 自适应 | 根据尺寸自动计算 |
|
||||
|
||||
### 内部元素圆角
|
||||
|
||||
组件内部的卡片、按钮应使用更小的圆角:
|
||||
|
||||
```csharp
|
||||
// 组件根容器 - 使用 Component 预设
|
||||
CornerRadius = context.ResolveCornerRadius(PluginCornerRadiusPreset.Component);
|
||||
|
||||
// 内部卡片 - 使用 Md 预设
|
||||
var innerCard = new Border
|
||||
{
|
||||
CornerRadius = context.ResolveCornerRadius(PluginCornerRadiusPreset.Md),
|
||||
Background = new SolidColorBrush(Colors.LightGray)
|
||||
};
|
||||
|
||||
// 按钮 - 使用 Sm 预设
|
||||
var button = new Button
|
||||
{
|
||||
CornerRadius = context.ResolveCornerRadius(PluginCornerRadiusPreset.Sm)
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 颜色系统
|
||||
|
||||
### 推荐的颜色策略
|
||||
|
||||
```csharp
|
||||
// 透明背景(推荐)- 让宿主壁纸透出
|
||||
Background = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
// 毛玻璃效果
|
||||
Background = new SolidColorBrush(Color.Parse(isDark ? "#40FFFFFF" : "#40000000"));
|
||||
|
||||
// 卡片背景
|
||||
Background = new SolidColorBrush(Color.Parse(isDark ? "#FF2D2D2D" : "#FFFFFFFF"));
|
||||
|
||||
// 强调色(使用系统强调色)
|
||||
var accentColor = Color.Parse("#FF0078D4"); // 阑山桌面主色调
|
||||
```
|
||||
|
||||
### 文字颜色
|
||||
|
||||
```csharp
|
||||
// 主要文字
|
||||
Foreground = new SolidColorBrush(isDark ? Colors.White : Colors.Black);
|
||||
|
||||
// 次要文字
|
||||
Foreground = new SolidColorBrush(isDark ? Color.Parse("#FFCCCCCC") : Color.Parse("#FF666666"));
|
||||
|
||||
// 禁用文字
|
||||
Foreground = new SolidColorBrush(isDark ? Color.Parse("#FF666666") : Color.Parse("#FF999999"));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 响应外观变化
|
||||
|
||||
### 订阅外观变化事件
|
||||
|
||||
```csharp
|
||||
public class MyWidget : Border
|
||||
{
|
||||
public MyWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 订阅外观变化
|
||||
context.Appearance.AppearanceChanged += (_, _) =>
|
||||
{
|
||||
UpdateAppearance();
|
||||
};
|
||||
|
||||
// 初始化
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
private void UpdateAppearance()
|
||||
{
|
||||
// 重新应用圆角(用户可能调整了全局圆角设置)
|
||||
var context = ...; // 获取 context
|
||||
CornerRadius = context.Appearance.ResolveCornerRadius(
|
||||
PluginCornerRadiusPreset.Component);
|
||||
|
||||
// 更新主题颜色
|
||||
var isDark = Application.Current?.ActualThemeVariant == ThemeVariant.Dark;
|
||||
UpdateThemeColors(isDark);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧩 使用 FluentAvalonia 控件
|
||||
|
||||
推荐使用 FluentAvalonia 控件库,它们自动适配主题:
|
||||
|
||||
```xml
|
||||
<Window xmlns:ui="using:FluentAvalonia.UI.Controls">
|
||||
<ui:SettingsExpander Header="设置项">
|
||||
<ui:SettingsExpander.IconSource>
|
||||
<ui:FontIconSource Glyph="" />
|
||||
</ui:SettingsExpander.IconSource>
|
||||
</ui:SettingsExpander>
|
||||
</Window>
|
||||
```
|
||||
|
||||
### 常用 FluentAvalonia 控件
|
||||
|
||||
| 控件 | 用途 |
|
||||
|-----|------|
|
||||
| `SettingsExpander` | 设置项展开器 |
|
||||
| `SettingsCard` | 设置卡片 |
|
||||
| `ColorPicker` | 颜色选择器 |
|
||||
| `NumberBox` | 数字输入框 |
|
||||
| `ToggleSwitch` | 开关 |
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 始终使用透明背景
|
||||
|
||||
```csharp
|
||||
// ✅ 好的做法
|
||||
Background = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
// ❌ 避免硬编码背景色
|
||||
Background = new SolidColorBrush(Colors.White);
|
||||
```
|
||||
|
||||
### 2. 组件根容器必须使用 Component 圆角
|
||||
|
||||
```csharp
|
||||
// ✅ 正确
|
||||
CornerRadius = context.ResolveCornerRadius(PluginCornerRadiusPreset.Component);
|
||||
|
||||
// ❌ 错误 - 硬编码
|
||||
CornerRadius = new CornerRadius(18);
|
||||
```
|
||||
|
||||
### 3. 响应主题变化
|
||||
|
||||
```csharp
|
||||
// ✅ 订阅变化事件
|
||||
Application.Current.ActualThemeVariantChanged += OnThemeChanged;
|
||||
|
||||
// ❌ 只在构造函数中设置一次
|
||||
```
|
||||
|
||||
### 4. 使用语义化颜色
|
||||
|
||||
```csharp
|
||||
// ✅ 根据用途选择颜色
|
||||
var primaryText = isDark ? Colors.White : Colors.Black;
|
||||
var secondaryText = isDark ? Color.Parse("#FFCCCCCC") : Color.Parse("#FF666666");
|
||||
|
||||
// ❌ 避免随意使用颜色
|
||||
var textColor = Color.Parse("#FF123456");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### 问题 1:圆角不生效
|
||||
|
||||
**原因:** 在 XAML 中使用 `{DynamicResource}`
|
||||
|
||||
**解决:** 在代码中设置圆角(见上文)
|
||||
|
||||
### 问题 2:主题切换后颜色不对
|
||||
|
||||
**原因:** 没有订阅主题变化事件
|
||||
|
||||
**解决:** 添加 `ActualThemeVariantChanged` 事件处理
|
||||
|
||||
### 问题 3:组件与内置组件风格不一致
|
||||
|
||||
**排查:**
|
||||
1. 检查圆角是否使用 `PluginCornerRadiusPreset.Component`
|
||||
2. 检查背景是否透明
|
||||
3. 检查是否使用了 FluentAvalonia 控件
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [CORNER_RADIUS_SPEC.md](../../docs/CORNER_RADIUS_SPEC.md)
|
||||
- [VISUAL_SPEC.md](../../docs/VISUAL_SPEC.md)
|
||||
- [FluentAvalonia 文档](https://github.com/amwx/FluentAvalonia)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
学习插件间通信:
|
||||
|
||||
👉 **[05-插件间通信](05-插件间通信.md)** - 与其他插件协作
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
375
docs/Plugins develop/02-核心概念与原理/05-插件间通信.md
Normal file
375
docs/Plugins develop/02-核心概念与原理/05-插件间通信.md
Normal file
@@ -0,0 +1,375 @@
|
||||
# 05-插件间通信
|
||||
|
||||
插件之间可以通过消息总线和服务导出进行通信,实现功能协作和数据共享。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 通信方式概述
|
||||
|
||||
| 方式 | 适用场景 | 方向 |
|
||||
|-----|---------|------|
|
||||
| **消息总线** | 事件通知、广播 | 多对多 |
|
||||
| **服务导出** | 功能共享、API 暴露 | 一对多 |
|
||||
| **共享契约** | 数据交换 | 双向 |
|
||||
|
||||
---
|
||||
|
||||
## 📢 消息总线
|
||||
|
||||
使用 `IPluginMessageBus` 发布和订阅消息。
|
||||
|
||||
### 发布消息
|
||||
|
||||
```csharp
|
||||
public class WeatherService
|
||||
{
|
||||
private readonly IPluginMessageBus _messageBus;
|
||||
|
||||
public WeatherService(IPluginMessageBus messageBus)
|
||||
{
|
||||
_messageBus = messageBus;
|
||||
}
|
||||
|
||||
public async Task UpdateWeatherAsync()
|
||||
{
|
||||
var weather = await FetchWeatherAsync();
|
||||
|
||||
// 发布天气更新消息
|
||||
_messageBus.Publish(new WeatherUpdatedMessage
|
||||
{
|
||||
City = weather.City,
|
||||
Temperature = weather.Temperature,
|
||||
Condition = weather.Condition
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 定义消息
|
||||
public class WeatherUpdatedMessage
|
||||
{
|
||||
public string City { get; set; } = "";
|
||||
public double Temperature { get; set; }
|
||||
public string Condition { get; set; } = "";
|
||||
}
|
||||
```
|
||||
|
||||
### 订阅消息
|
||||
|
||||
```csharp
|
||||
public class AnotherPluginService
|
||||
{
|
||||
public AnotherPluginService(IPluginMessageBus messageBus)
|
||||
{
|
||||
// 订阅天气更新消息
|
||||
messageBus.Subscribe<WeatherUpdatedMessage>(OnWeatherUpdated);
|
||||
}
|
||||
|
||||
private void OnWeatherUpdated(WeatherUpdatedMessage message)
|
||||
{
|
||||
Console.WriteLine($"收到天气更新: {message.City} {message.Temperature}°C");
|
||||
|
||||
// 根据天气更新自己的状态
|
||||
UpdateDisplay(message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 取消订阅
|
||||
|
||||
```csharp
|
||||
public class MyService : IDisposable
|
||||
{
|
||||
private readonly IPluginMessageBus _messageBus;
|
||||
private readonly Guid _subscriptionId;
|
||||
|
||||
public MyService(IPluginMessageBus messageBus)
|
||||
{
|
||||
_messageBus = messageBus;
|
||||
_subscriptionId = messageBus.Subscribe<WeatherUpdatedMessage>(OnWeatherUpdated);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// 取消订阅,避免内存泄漏
|
||||
_messageBus.Unsubscribe<WeatherUpdatedMessage>(_subscriptionId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔌 服务导出
|
||||
|
||||
插件可以将服务导出,供其他插件使用。
|
||||
|
||||
### 导出服务
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 注册服务
|
||||
services.AddSingleton<IWeatherService, WeatherService>();
|
||||
|
||||
// 导出服务供其他插件使用
|
||||
services.AddPluginServiceExport<IWeatherService>(
|
||||
serviceKey: "MyPlugin.WeatherService",
|
||||
description: "提供天气查询服务");
|
||||
}
|
||||
|
||||
// 定义服务接口
|
||||
public interface IWeatherService
|
||||
{
|
||||
Task<WeatherInfo> GetCurrentWeatherAsync(string city);
|
||||
Task<List<WeatherForecast>> GetForecastAsync(string city, int days);
|
||||
}
|
||||
|
||||
// 实现服务
|
||||
public class WeatherService : IWeatherService
|
||||
{
|
||||
public async Task<WeatherInfo> GetCurrentWeatherAsync(string city)
|
||||
{
|
||||
// 实现天气查询
|
||||
}
|
||||
|
||||
public async Task<List<WeatherForecast>> GetForecastAsync(string city, int days)
|
||||
{
|
||||
// 实现天气预报
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用其他插件的服务
|
||||
|
||||
```csharp
|
||||
public class MyWidget : Border
|
||||
{
|
||||
public MyWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 获取其他插件导出的服务
|
||||
var weatherService = context.ServiceProvider
|
||||
.GetExportedService<IWeatherService>("MyPlugin.WeatherService");
|
||||
|
||||
if (weatherService != null)
|
||||
{
|
||||
// 使用服务
|
||||
LoadWeatherAsync(weatherService);
|
||||
}
|
||||
}
|
||||
|
||||
private async void LoadWeatherAsync(IWeatherService weatherService)
|
||||
{
|
||||
var weather = await weatherService.GetCurrentWeatherAsync("北京");
|
||||
UpdateUI(weather);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 服务导出选项
|
||||
|
||||
```csharp
|
||||
services.AddPluginServiceExport<IWeatherService>(
|
||||
serviceKey: "MyPlugin.WeatherService",
|
||||
description: "提供天气查询服务",
|
||||
version: "1.0.0",
|
||||
isPublic: true); // 是否公开给其他插件
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 共享契约
|
||||
|
||||
通过 `sharedContracts` 在插件间共享类型定义。
|
||||
|
||||
### 定义共享契约
|
||||
|
||||
```csharp
|
||||
// 在共享类库项目中定义
|
||||
namespace MyPlugin.Shared;
|
||||
|
||||
public interface IWeatherData
|
||||
{
|
||||
string City { get; }
|
||||
double Temperature { get; }
|
||||
string Condition { get; }
|
||||
}
|
||||
|
||||
public class WeatherData : IWeatherData
|
||||
{
|
||||
public string City { get; set; } = "";
|
||||
public double Temperature { get; set; }
|
||||
public string Condition { get; set; } = "";
|
||||
}
|
||||
```
|
||||
|
||||
### 在 plugin.json 中声明
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "com.example.weather",
|
||||
"name": "天气插件",
|
||||
"sharedContracts": [
|
||||
"MyPlugin.Shared.IWeatherData",
|
||||
"MyPlugin.Shared.WeatherData"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 使用共享类型
|
||||
|
||||
```csharp
|
||||
// 插件 A 发布数据
|
||||
public class WeatherService
|
||||
{
|
||||
public WeatherData GetWeather()
|
||||
{
|
||||
return new WeatherData
|
||||
{
|
||||
City = "北京",
|
||||
Temperature = 25.5,
|
||||
Condition = "晴"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 插件 B 接收数据
|
||||
public class ConsumerService
|
||||
{
|
||||
public void ProcessWeather(IWeatherData weather)
|
||||
{
|
||||
Console.WriteLine($"{weather.City}: {weather.Temperature}°C");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔒 安全考虑
|
||||
|
||||
### 服务导出安全
|
||||
|
||||
```csharp
|
||||
// 只导出必要的接口,不暴露实现细节
|
||||
public interface IPublicApi
|
||||
{
|
||||
Task<Data> GetDataAsync();
|
||||
}
|
||||
|
||||
internal class InternalService : IPublicApi
|
||||
{
|
||||
// 内部实现细节不暴露
|
||||
private readonly SecretKey _key;
|
||||
|
||||
public async Task<Data> GetDataAsync()
|
||||
{
|
||||
// 实现
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 消息验证
|
||||
|
||||
```csharp
|
||||
private void OnMessageReceived(MyMessage message)
|
||||
{
|
||||
// 验证消息来源
|
||||
if (message.SenderId != "TrustedPlugin")
|
||||
{
|
||||
return; // 忽略不信任来源的消息
|
||||
}
|
||||
|
||||
// 处理消息
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 使用接口定义服务契约
|
||||
|
||||
```csharp
|
||||
// ✅ 好的做法 - 定义接口
|
||||
public interface IWeatherService { }
|
||||
|
||||
// ❌ 避免 - 直接导出实现类
|
||||
services.AddPluginServiceExport<WeatherService>(...);
|
||||
```
|
||||
|
||||
### 2. 处理服务不可用情况
|
||||
|
||||
```csharp
|
||||
// ✅ 优雅处理服务缺失
|
||||
var service = context.ServiceProvider
|
||||
.GetExportedService<IWeatherService>("key");
|
||||
|
||||
if (service == null)
|
||||
{
|
||||
// 显示提示或降级处理
|
||||
ShowServiceUnavailableMessage();
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 及时取消消息订阅
|
||||
|
||||
```csharp
|
||||
// ✅ 在 Dispose 中取消订阅
|
||||
public void Dispose()
|
||||
{
|
||||
_messageBus.Unsubscribe<MyMessage>(_subscriptionId);
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 版本兼容性
|
||||
|
||||
```csharp
|
||||
// 在服务导出中包含版本信息
|
||||
services.AddPluginServiceExport<IWeatherService>(
|
||||
serviceKey: "MyPlugin.WeatherService",
|
||||
version: "2.0.0", // 语义化版本
|
||||
description: "天气服务 v2");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### 问题 1:消息收不到
|
||||
|
||||
**排查:**
|
||||
1. 确认消息类型完全一致(包括命名空间)
|
||||
2. 检查订阅是否在消息发布之前
|
||||
3. 确认没有取消订阅
|
||||
|
||||
### 问题 2:服务找不到
|
||||
|
||||
**排查:**
|
||||
1. 确认服务已导出(`AddPluginServiceExport`)
|
||||
2. 检查 `serviceKey` 是否正确
|
||||
3. 确认依赖的插件已安装并启用
|
||||
|
||||
### 问题 3:类型转换错误
|
||||
|
||||
**原因:** 共享契约类型不匹配
|
||||
|
||||
**解决:** 确保所有插件使用相同版本的共享契约程序集
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [IPluginMessageBus 源码](../../LanMountainDesktop.PluginSdk/IPluginMessageBus.cs)
|
||||
- [IPluginExportRegistry 源码](../../LanMountainDesktop.PluginSdk/IPluginExportRegistry.cs)
|
||||
- [Shared.Contracts](../../LanMountainDesktop.Shared.Contracts/)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
查看实战案例:
|
||||
|
||||
👉 **[01-开发天气组件](../04-实战案例/01-开发天气组件.md)** - 完整插件开发流程
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
307
docs/Plugins develop/03-API实践指南/01-PluginBase详解.md
Normal file
307
docs/Plugins develop/03-API实践指南/01-PluginBase详解.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# 01-PluginBase详解
|
||||
|
||||
`PluginBase` 是插件的基类,提供基础功能和生命周期管理。本文详细讲解其用法和扩展点。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 PluginBase 概述
|
||||
|
||||
```csharp
|
||||
public abstract class PluginBase : IPlugin
|
||||
{
|
||||
// 日志记录器
|
||||
protected ILogger? Logger { get; }
|
||||
|
||||
// 初始化方法(必须实现)
|
||||
public abstract void Initialize(HostBuilderContext context, IServiceCollection services);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 基本用法
|
||||
|
||||
### 最小实现
|
||||
|
||||
```csharp
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace MyPlugin;
|
||||
|
||||
[PluginEntrance]
|
||||
public sealed class Plugin : PluginBase
|
||||
{
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 插件初始化逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Initialize 方法详解
|
||||
|
||||
### 方法签名
|
||||
|
||||
```csharp
|
||||
public abstract void Initialize(
|
||||
HostBuilderContext context, // 宿主构建上下文
|
||||
IServiceCollection services // 服务注册集合
|
||||
);
|
||||
```
|
||||
|
||||
### 参数说明
|
||||
|
||||
| 参数 | 类型 | 用途 |
|
||||
|-----|------|------|
|
||||
| `context` | `HostBuilderContext` | 访问宿主配置、环境信息 |
|
||||
| `services` | `IServiceCollection` | 注册服务、组件、设置页面 |
|
||||
|
||||
### context 使用示例
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 访问配置
|
||||
var configValue = context.Configuration["MySetting"];
|
||||
|
||||
// 判断运行环境
|
||||
var isDevelopment = context.HostingEnvironment.IsDevelopment();
|
||||
|
||||
// 获取应用名称
|
||||
var appName = context.HostingEnvironment.ApplicationName;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 日志记录
|
||||
|
||||
### 使用 Logger 属性
|
||||
|
||||
```csharp
|
||||
[PluginEntrance]
|
||||
public sealed class Plugin : PluginBase
|
||||
{
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 记录日志
|
||||
Logger?.LogInformation("插件初始化开始");
|
||||
|
||||
try
|
||||
{
|
||||
// 初始化逻辑
|
||||
services.AddSingleton<IMyService, MyService>();
|
||||
|
||||
Logger?.LogInformation("插件初始化完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger?.LogError(ex, "插件初始化失败");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 日志级别
|
||||
|
||||
```csharp
|
||||
Logger?.LogTrace("详细跟踪信息");
|
||||
Logger?.LogDebug("调试信息");
|
||||
Logger?.LogInformation("一般信息");
|
||||
Logger?.LogWarning("警告信息");
|
||||
Logger?.LogError("错误信息");
|
||||
Logger?.LogCritical("严重错误");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔌 服务注册
|
||||
|
||||
### 注册单例服务
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 单例 - 整个应用生命周期只有一个实例
|
||||
services.AddSingleton<IWeatherService, WeatherService>();
|
||||
}
|
||||
```
|
||||
|
||||
### 注册作用域服务
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 作用域 - 每个作用域一个实例
|
||||
services.AddScoped<IDataContext, DataContext>();
|
||||
}
|
||||
```
|
||||
|
||||
### 注册瞬态服务
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 瞬态 - 每次请求都创建新实例
|
||||
services.AddTransient<IValidator, Validator>();
|
||||
}
|
||||
```
|
||||
|
||||
### 带配置的服务注册
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IWeatherService>(provider =>
|
||||
{
|
||||
var httpClient = provider.GetRequiredService<HttpClient>();
|
||||
var logger = provider.GetRequiredService<ILogger<WeatherService>>();
|
||||
var apiKey = context.Configuration["WeatherApiKey"];
|
||||
|
||||
return new WeatherService(httpClient, logger, apiKey);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧩 完整示例
|
||||
|
||||
```csharp
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WeatherPlugin;
|
||||
|
||||
[PluginEntrance]
|
||||
public sealed class Plugin : PluginBase
|
||||
{
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
Logger?.LogInformation("天气插件初始化开始");
|
||||
|
||||
try
|
||||
{
|
||||
// 1. 注册 HTTP 客户端
|
||||
services.AddHttpClient("weather", client =>
|
||||
{
|
||||
client.BaseAddress = new Uri("https://api.weather.com/");
|
||||
client.Timeout = TimeSpan.FromSeconds(30);
|
||||
});
|
||||
|
||||
// 2. 注册服务
|
||||
services.AddSingleton<IWeatherService, WeatherService>();
|
||||
services.AddSingleton<ILocationService, LocationService>();
|
||||
|
||||
// 3. 注册桌面组件
|
||||
services.AddPluginDesktopComponent<WeatherWidget>(
|
||||
new PluginDesktopComponentOptions
|
||||
{
|
||||
ComponentId = "WeatherPlugin.Widget",
|
||||
DisplayName = "天气",
|
||||
IconKey = "Weather",
|
||||
Category = "信息",
|
||||
MinWidthCells = 4,
|
||||
MinHeightCells = 3
|
||||
});
|
||||
|
||||
// 4. 注册设置页面
|
||||
services.AddPluginSettingsSection(
|
||||
"weather-settings",
|
||||
"天气设置",
|
||||
section => section
|
||||
.AddText("api_key", "API密钥", isPassword: true)
|
||||
.AddText("default_city", "默认城市", defaultValue: "北京")
|
||||
.AddToggle("auto_refresh", "自动刷新", defaultValue: true)
|
||||
.AddNumber("refresh_interval", "刷新间隔(分钟)",
|
||||
defaultValue: 30, minimum: 5, maximum: 120),
|
||||
iconKey: "Settings");
|
||||
|
||||
Logger?.LogInformation("天气插件初始化完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger?.LogError(ex, "天气插件初始化失败");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 使用 try-catch 包装初始化逻辑
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 初始化逻辑
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger?.LogError(ex, "初始化失败");
|
||||
throw; // 重新抛出,让宿主知道初始化失败
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 按依赖顺序注册服务
|
||||
|
||||
```csharp
|
||||
// ✅ 先注册被依赖的服务
|
||||
services.AddSingleton<IDataService, DataService>();
|
||||
|
||||
// 再注册依赖它们的服务
|
||||
services.AddSingleton<IWeatherService, WeatherService>(); // 依赖 IDataService
|
||||
|
||||
// 最后注册组件
|
||||
services.AddPluginDesktopComponent<WeatherWidget>(options);
|
||||
```
|
||||
|
||||
### 3. 记录初始化过程
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
Logger?.LogInformation("开始初始化...");
|
||||
|
||||
Logger?.LogDebug("注册服务...");
|
||||
services.AddSingleton<IMyService, MyService>();
|
||||
|
||||
Logger?.LogDebug("注册组件...");
|
||||
services.AddPluginDesktopComponent<MyWidget>(options);
|
||||
|
||||
Logger?.LogInformation("初始化完成");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [PluginBase 源码](../../LanMountainDesktop.PluginSdk/PluginBase.cs)
|
||||
- [IPlugin 接口](../../LanMountainDesktop.PluginSdk/IPlugin.cs)
|
||||
- [Microsoft.Extensions.DependencyInjection 文档](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
学习组件注册 API:
|
||||
|
||||
👉 **[02-组件注册与配置](02-组件注册与配置.md)**
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
182
docs/Plugins develop/03-API实践指南/02-组件注册与配置.md
Normal file
182
docs/Plugins develop/03-API实践指南/02-组件注册与配置.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# 02-组件注册与配置
|
||||
|
||||
`AddPluginDesktopComponent` 是注册桌面组件的核心 API。本文详细讲解其用法和配置选项。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 API 概览
|
||||
|
||||
```csharp
|
||||
public static IServiceCollection AddPluginDesktopComponent<TComponent>(
|
||||
this IServiceCollection services,
|
||||
PluginDesktopComponentOptions options)
|
||||
where TComponent : class, IControl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 基本用法
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
services.AddPluginDesktopComponent<MyWidget>(
|
||||
new PluginDesktopComponentOptions
|
||||
{
|
||||
ComponentId = "MyPlugin.MyWidget",
|
||||
DisplayName = "我的组件",
|
||||
IconKey = "Home",
|
||||
Category = "工具",
|
||||
MinWidthCells = 4,
|
||||
MinHeightCells = 3
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 PluginDesktopComponentOptions
|
||||
|
||||
### 完整属性列表
|
||||
|
||||
| 属性 | 类型 | 必需 | 说明 |
|
||||
|-----|------|------|------|
|
||||
| `ComponentId` | `string` | ✅ | 唯一标识符 |
|
||||
| `DisplayName` | `string` | ✅ | 显示名称 |
|
||||
| `IconKey` | `string` | ✅ | 图标键名 |
|
||||
| `Category` | `string` | ✅ | 分类 |
|
||||
| `MinWidthCells` | `int` | ✅ | 最小宽度(格) |
|
||||
| `MinHeightCells` | `int` | ✅ | 最小高度(格) |
|
||||
| `CornerRadiusPreset` | `PluginCornerRadiusPreset` | ❌ | 圆角预设 |
|
||||
| `ResizeMode` | `PluginDesktopComponentResizeMode` | ❌ | 调整大小模式 |
|
||||
|
||||
### ComponentId
|
||||
|
||||
```csharp
|
||||
ComponentId = "MyPlugin.WeatherWidget"
|
||||
```
|
||||
|
||||
- 必须唯一
|
||||
- 建议使用 `插件ID.组件名` 格式
|
||||
- 一经发布不可更改
|
||||
|
||||
### DisplayName
|
||||
|
||||
```csharp
|
||||
DisplayName = "天气"
|
||||
```
|
||||
|
||||
- 显示在组件库中
|
||||
- 支持本地化(通过资源文件)
|
||||
|
||||
### IconKey
|
||||
|
||||
```csharp
|
||||
IconKey = "Weather"
|
||||
```
|
||||
|
||||
使用 [Fluent UI System Icons](https://github.com/microsoft/fluentui-system-icons) 的图标名。
|
||||
|
||||
### Category
|
||||
|
||||
```csharp
|
||||
Category = "工具"
|
||||
```
|
||||
|
||||
常用分类:
|
||||
- `工具` - 实用工具
|
||||
- `信息` - 信息展示
|
||||
- `娱乐` - 娱乐相关
|
||||
- `系统` - 系统监控
|
||||
|
||||
### MinWidthCells / MinHeightCells
|
||||
|
||||
```csharp
|
||||
MinWidthCells = 4, // 4格宽,约240像素
|
||||
MinHeightCells = 3 // 3格高,约180像素
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 圆角配置
|
||||
|
||||
```csharp
|
||||
services.AddPluginDesktopComponent<MyWidget>(
|
||||
new PluginDesktopComponentOptions
|
||||
{
|
||||
ComponentId = "MyPlugin.Widget",
|
||||
DisplayName = "我的组件",
|
||||
IconKey = "Home",
|
||||
Category = "工具",
|
||||
MinWidthCells = 4,
|
||||
MinHeightCells = 3,
|
||||
CornerRadiusPreset = PluginCornerRadiusPreset.Component
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📐 调整大小模式
|
||||
|
||||
```csharp
|
||||
services.AddPluginDesktopComponent<MyWidget>(
|
||||
new PluginDesktopComponentOptions
|
||||
{
|
||||
// ...
|
||||
ResizeMode = PluginDesktopComponentResizeMode.Free
|
||||
});
|
||||
```
|
||||
|
||||
| 模式 | 说明 |
|
||||
|-----|------|
|
||||
| `Free` | 自由调整大小 |
|
||||
| `Fixed` | 固定大小 |
|
||||
| `AspectRatio` | 保持宽高比 |
|
||||
|
||||
---
|
||||
|
||||
## 🧩 完整示例
|
||||
|
||||
```csharp
|
||||
public override void Initialize(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
// 天气组件
|
||||
services.AddPluginDesktopComponent<WeatherWidget>(
|
||||
new PluginDesktopComponentOptions
|
||||
{
|
||||
ComponentId = "WeatherPlugin.Widget",
|
||||
DisplayName = "天气",
|
||||
IconKey = "Weather",
|
||||
Category = "信息",
|
||||
MinWidthCells = 4,
|
||||
MinHeightCells = 3,
|
||||
CornerRadiusPreset = PluginCornerRadiusPreset.Component,
|
||||
ResizeMode = PluginDesktopComponentResizeMode.Free
|
||||
});
|
||||
|
||||
// 时钟组件
|
||||
services.AddPluginDesktopComponent<ClockWidget>(
|
||||
new PluginDesktopComponentOptions
|
||||
{
|
||||
ComponentId = "ClockPlugin.Widget",
|
||||
DisplayName = "时钟",
|
||||
IconKey = "Clock",
|
||||
Category = "工具",
|
||||
MinWidthCells = 4,
|
||||
MinHeightCells = 4,
|
||||
CornerRadiusPreset = PluginCornerRadiusPreset.Component,
|
||||
ResizeMode = PluginDesktopComponentResizeMode.AspectRatio
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [PluginDesktopComponentOptions 源码](../../LanMountainDesktop.PluginSdk/PluginDesktopComponentOptions.cs)
|
||||
- [02-桌面组件系统](../02-核心概念与原理/02-桌面组件系统.md)
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
147
docs/Plugins develop/03-API实践指南/03-设置API详解.md
Normal file
147
docs/Plugins develop/03-API实践指南/03-设置API详解.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# 03-设置API详解
|
||||
|
||||
设置 API 允许插件添加配置页面和持久化用户设置。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 API 概览
|
||||
|
||||
### 声明式设置
|
||||
|
||||
```csharp
|
||||
services.AddPluginSettingsSection(
|
||||
string sectionId,
|
||||
string displayName,
|
||||
Action<PluginSettingsSectionBuilder> configure,
|
||||
string iconKey);
|
||||
```
|
||||
|
||||
### 自定义设置页
|
||||
|
||||
```csharp
|
||||
services.AddPluginSettingsSection<TPage>(
|
||||
string sectionId,
|
||||
string displayName,
|
||||
string iconKey)
|
||||
where TPage : SettingsPageBase;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 声明式设置详解
|
||||
|
||||
### 基本用法
|
||||
|
||||
```csharp
|
||||
services.AddPluginSettingsSection(
|
||||
"myplugin-settings",
|
||||
"我的设置",
|
||||
section => section
|
||||
.AddToggle("enabled", "启用", defaultValue: true)
|
||||
.AddText("name", "名称", defaultValue: ""),
|
||||
iconKey: "Settings");
|
||||
```
|
||||
|
||||
### 设置类型
|
||||
|
||||
#### Toggle(开关)
|
||||
|
||||
```csharp
|
||||
.AddToggle(
|
||||
key: "auto_update",
|
||||
displayName: "自动更新",
|
||||
defaultValue: true,
|
||||
description: "启动时检查更新")
|
||||
```
|
||||
|
||||
#### Text(文本)
|
||||
|
||||
```csharp
|
||||
.AddText(
|
||||
key: "api_key",
|
||||
displayName: "API密钥",
|
||||
defaultValue: "",
|
||||
placeholder: "请输入",
|
||||
isPassword: false)
|
||||
```
|
||||
|
||||
#### Number(数值)
|
||||
|
||||
```csharp
|
||||
.AddNumber(
|
||||
key: "interval",
|
||||
displayName: "刷新间隔",
|
||||
defaultValue: 60,
|
||||
minimum: 10,
|
||||
maximum: 3600,
|
||||
increment: 10)
|
||||
```
|
||||
|
||||
#### Select(选择)
|
||||
|
||||
```csharp
|
||||
.AddSelect(
|
||||
key: "theme",
|
||||
displayName: "主题",
|
||||
choices: new[]
|
||||
{
|
||||
new SettingsOptionChoice("light", "浅色"),
|
||||
new SettingsOptionChoice("dark", "深色")
|
||||
},
|
||||
defaultValue: "light")
|
||||
```
|
||||
|
||||
#### Path(路径)
|
||||
|
||||
```csharp
|
||||
.AddPath(
|
||||
key: "save_path",
|
||||
displayName: "保存路径",
|
||||
defaultValue: "",
|
||||
pathType: SettingsPathType.Folder,
|
||||
dialogTitle: "选择文件夹")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 读取和保存设置
|
||||
|
||||
### 使用 IPluginSettingsService
|
||||
|
||||
```csharp
|
||||
public class MyService
|
||||
{
|
||||
private readonly IPluginSettingsService _settings;
|
||||
|
||||
public MyService(IPluginSettingsService settings)
|
||||
{
|
||||
_settings = settings;
|
||||
|
||||
// 读取
|
||||
var value = _settings.GetValue<string>("key", "default");
|
||||
|
||||
// 保存
|
||||
_settings.SetValue("key", "new value");
|
||||
|
||||
// 监听变化
|
||||
_settings.SettingsChanged += (s, e) =>
|
||||
{
|
||||
if (e.Key == "key")
|
||||
{
|
||||
HandleChange(e.NewValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [IPluginSettingsService 源码](../../LanMountainDesktop.PluginSdk/IPluginSettingsService.cs)
|
||||
- [03-设置系统集成](../02-核心概念与原理/03-设置系统集成.md)
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
106
docs/Plugins develop/03-API实践指南/04-外观API详解.md
Normal file
106
docs/Plugins develop/03-API实践指南/04-外观API详解.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# 04-外观API详解
|
||||
|
||||
外观 API 提供圆角、主题等视觉相关的功能。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 IPluginAppearanceContext
|
||||
|
||||
```csharp
|
||||
public interface IPluginAppearanceContext
|
||||
{
|
||||
// 获取圆角值
|
||||
CornerRadius ResolveCornerRadius(PluginCornerRadiusPreset preset);
|
||||
|
||||
// 获取带限制的圆角值
|
||||
CornerRadius ResolveCornerRadius(
|
||||
PluginCornerRadiusPreset preset,
|
||||
CornerRadius? minimum,
|
||||
CornerRadius? maximum);
|
||||
|
||||
// 获取缩放后的圆角值
|
||||
CornerRadius ResolveScaledCornerRadius(
|
||||
double baseRadius,
|
||||
double? minimum,
|
||||
double? maximum);
|
||||
|
||||
// 外观变化事件
|
||||
event EventHandler? AppearanceChanged;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📐 圆角 API
|
||||
|
||||
### 获取圆角值
|
||||
|
||||
```csharp
|
||||
public MyWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
// 使用预设
|
||||
CornerRadius = context.Appearance.ResolveCornerRadius(
|
||||
PluginCornerRadiusPreset.Component);
|
||||
}
|
||||
```
|
||||
|
||||
### 带限制的圆角
|
||||
|
||||
```csharp
|
||||
var radius = context.Appearance.ResolveCornerRadius(
|
||||
PluginCornerRadiusPreset.Component,
|
||||
minimum: new CornerRadius(8),
|
||||
maximum: new CornerRadius(24));
|
||||
```
|
||||
|
||||
### 缩放圆角
|
||||
|
||||
```csharp
|
||||
var radius = context.Appearance.ResolveScaledCornerRadius(
|
||||
baseRadius: 16,
|
||||
minimum: 8,
|
||||
maximum: 32);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 圆角预设
|
||||
|
||||
| 预设 | 值 | 用途 |
|
||||
|-----|---|------|
|
||||
| Micro | 6px | 微小元素 |
|
||||
| Xs | 12px | 小元素 |
|
||||
| Sm | 14px | 小卡片 |
|
||||
| Md | 20px | 普通按钮 |
|
||||
| Lg | 28px | 大面板 |
|
||||
| Xl | 32px | 强调容器 |
|
||||
| Island | 36px | 大型容器 |
|
||||
| Component | 18px | 桌面组件 |
|
||||
| Default | 自适应 | 自动计算 |
|
||||
|
||||
---
|
||||
|
||||
## 🔄 响应外观变化
|
||||
|
||||
```csharp
|
||||
public MyWidget(PluginDesktopComponentContext context)
|
||||
{
|
||||
context.Appearance.AppearanceChanged += (_, _) =>
|
||||
{
|
||||
// 重新应用圆角
|
||||
CornerRadius = context.Appearance.ResolveCornerRadius(
|
||||
PluginCornerRadiusPreset.Component);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [IPluginAppearanceContext 源码](../../LanMountainDesktop.PluginSdk/IPluginAppearanceContext.cs)
|
||||
- [04-外观与主题系统](../02-核心概念与原理/04-外观与主题系统.md)
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
73
docs/Plugins develop/03-API实践指南/05-本地化支持.md
Normal file
73
docs/Plugins develop/03-API实践指南/05-本地化支持.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# 05-本地化支持
|
||||
|
||||
本地化 API 支持多语言资源管理。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 资源文件
|
||||
|
||||
### 文件位置
|
||||
|
||||
```
|
||||
Localization/
|
||||
├── zh-CN.json # 简体中文
|
||||
├── en-US.json # 英文
|
||||
├── ja-JP.json # 日文
|
||||
└── ko-KR.json # 韩文
|
||||
```
|
||||
|
||||
### 资源格式
|
||||
|
||||
```json
|
||||
{
|
||||
"PluginName": "我的插件",
|
||||
"Settings": {
|
||||
"Title": "设置",
|
||||
"Save": "保存"
|
||||
},
|
||||
"Messages": {
|
||||
"Hello": "你好,{0}!",
|
||||
"Error": "错误:{0}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 使用本地化
|
||||
|
||||
### 注入 IStringLocalizer
|
||||
|
||||
```csharp
|
||||
public class MyService
|
||||
{
|
||||
private readonly IStringLocalizer<MyService> _localizer;
|
||||
|
||||
public MyService(IStringLocalizer<MyService> localizer)
|
||||
{
|
||||
_localizer = localizer;
|
||||
}
|
||||
|
||||
public void DoWork()
|
||||
{
|
||||
// 简单字符串
|
||||
var name = _localizer["PluginName"];
|
||||
|
||||
// 带参数
|
||||
var message = _localizer["Messages.Hello", "用户"];
|
||||
|
||||
// 嵌套键
|
||||
var title = _localizer["Settings.Title"];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [Microsoft.Extensions.Localization 文档](https://docs.microsoft.com/dotnet/api/microsoft.extensions.localization)
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
215
docs/Plugins develop/06-CI-CD与自动化/01-GitHub Actions入门.md
Normal file
215
docs/Plugins develop/06-CI-CD与自动化/01-GitHub Actions入门.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# 01-GitHub Actions入门
|
||||
|
||||
GitHub Actions 是自动化构建、测试和发布插件的强大工具。本文介绍如何为插件项目配置 CI/CD 流程。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 什么是 GitHub Actions
|
||||
|
||||
GitHub Actions 是 GitHub 提供的持续集成/持续部署(CI/CD)服务,可以:
|
||||
|
||||
- ✅ 自动构建插件
|
||||
- ✅ 运行单元测试
|
||||
- ✅ 打包 .laapp 文件
|
||||
- ✅ 自动发布到 GitHub Releases
|
||||
|
||||
---
|
||||
|
||||
## 📁 工作流文件位置
|
||||
|
||||
```
|
||||
.github/workflows/
|
||||
├── build.yml # 构建工作流
|
||||
├── release.yml # 发布工作流
|
||||
└── code-quality.yml # 代码质量检查
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 基础工作流示例
|
||||
|
||||
### 最简单的构建工作流
|
||||
|
||||
```yaml
|
||||
# .github/workflows/build.yml
|
||||
name: Build Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
# 1. 检出代码
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# 2. 设置 .NET
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '10.0.x'
|
||||
|
||||
# 3. 还原依赖
|
||||
- name: Restore
|
||||
run: dotnet restore
|
||||
|
||||
# 4. 构建
|
||||
- name: Build
|
||||
run: dotnet build --configuration Release --no-restore
|
||||
|
||||
# 5. 上传构建产物
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: plugin-package
|
||||
path: bin/Release/net10.0/*.laapp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 工作流详解
|
||||
|
||||
### 触发条件(on)
|
||||
|
||||
```yaml
|
||||
on:
|
||||
# 推送到指定分支时触发
|
||||
push:
|
||||
branches: [main, master, develop]
|
||||
|
||||
# 创建 Pull Request 时触发
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
|
||||
# 手动触发
|
||||
workflow_dispatch:
|
||||
|
||||
# 定时触发(每天凌晨2点)
|
||||
schedule:
|
||||
- cron: '0 2 * * *'
|
||||
|
||||
# 创建标签时触发(用于发布)
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
```
|
||||
|
||||
### 运行环境(runs-on)
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest # Windows 环境
|
||||
# 或
|
||||
runs-on: ubuntu-latest # Linux 环境
|
||||
# 或
|
||||
runs-on: macos-latest # macOS 环境
|
||||
```
|
||||
|
||||
### 矩阵构建(多平台)
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, ubuntu-latest, macos-latest]
|
||||
dotnet: ['10.0.x']
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ matrix.dotnet }}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 常用 Actions
|
||||
|
||||
### 检出代码
|
||||
|
||||
```yaml
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # 获取完整历史(用于生成版本号)
|
||||
```
|
||||
|
||||
### 设置 .NET
|
||||
|
||||
```yaml
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '10.0.x'
|
||||
```
|
||||
|
||||
### 上传产物
|
||||
|
||||
```yaml
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: plugin-package
|
||||
path: bin/Release/net10.0/*.laapp
|
||||
retention-days: 30 # 保留30天
|
||||
```
|
||||
|
||||
### 下载产物
|
||||
|
||||
```yaml
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: plugin-package
|
||||
path: ./artifacts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 缓存依赖
|
||||
|
||||
```yaml
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-nuget-
|
||||
```
|
||||
|
||||
### 2. 使用语义化版本
|
||||
|
||||
```yaml
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
VERSION=$(echo ${GITHUB_REF#refs/tags/} | sed 's/^v//')
|
||||
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
|
||||
```
|
||||
|
||||
### 3. 条件执行
|
||||
|
||||
```yaml
|
||||
- name: Deploy
|
||||
if: github.ref == 'refs/heads/main' # 只在 main 分支执行
|
||||
run: echo "Deploying..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
学习自动打包配置:
|
||||
|
||||
👉 **[02-配置自动构建](02-配置自动构建.md)**
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
130
docs/Plugins develop/06-CI-CD与自动化/02-配置自动构建.md
Normal file
130
docs/Plugins develop/06-CI-CD与自动化/02-配置自动构建.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# 02-配置自动构建
|
||||
|
||||
配置 GitHub Actions 自动构建插件项目。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 完整构建工作流
|
||||
|
||||
```yaml
|
||||
# .github/workflows/build.yml
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.gitignore'
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.x'
|
||||
CONFIGURATION: 'Release'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Cache NuGet
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-nuget-
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet build --configuration ${{ env.CONFIGURATION }} --no-restore
|
||||
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: plugin-${{ github.run_number }}
|
||||
path: bin/${{ env.CONFIGURATION }}/net10.0/*.laapp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 关键配置说明
|
||||
|
||||
### 路径过滤
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md' # 忽略文档修改
|
||||
- '.gitignore' # 忽略 gitignore 修改
|
||||
- 'docs/**' # 忽略 docs 文件夹
|
||||
```
|
||||
|
||||
### 环境变量
|
||||
|
||||
```yaml
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.x'
|
||||
CONFIGURATION: 'Release'
|
||||
PLUGIN_NAME: 'MyPlugin'
|
||||
```
|
||||
|
||||
### 构建步骤
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
# 1. 检出
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# 2. 设置 .NET
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
# 3. 缓存
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: nuget-${{ hashFiles('**/*.csproj') }}
|
||||
|
||||
# 4. 还原
|
||||
- run: dotnet restore
|
||||
|
||||
# 5. 构建
|
||||
- run: dotnet build -c ${{ env.CONFIGURATION }} --no-restore
|
||||
|
||||
# 6. 测试
|
||||
- run: dotnet test --no-build
|
||||
|
||||
# 7. 上传
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: plugin
|
||||
path: bin/Release/net10.0/*.laapp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
- [GitHub Actions 文档](https://docs.github.com/actions)
|
||||
- [.NET CI/CD 指南](https://docs.microsoft.com/dotnet/devops/github-actions-overview)
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
156
docs/Plugins develop/06-CI-CD与自动化/03-自动打包与发布.md
Normal file
156
docs/Plugins develop/06-CI-CD与自动化/03-自动打包与发布.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# 03-自动打包与发布
|
||||
|
||||
配置 GitHub Actions 自动打包 .laapp 并发布到 GitHub Releases。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 发布工作流
|
||||
|
||||
```yaml
|
||||
# .github/workflows/release.yml
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.x'
|
||||
|
||||
jobs:
|
||||
build-and-release:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
$version = $env:GITHUB_REF -replace 'refs/tags/v', ''
|
||||
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet build --configuration Release --no-restore
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
$version = "${{ steps.version.outputs.VERSION }}"
|
||||
Rename-Item -Path "bin/Release/net10.0/MyPlugin.laapp" -NewName "MyPlugin-$version.laapp"
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: bin/Release/net10.0/*.laapp
|
||||
generate_release_notes: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 发布流程
|
||||
|
||||
### 1. 创建标签
|
||||
|
||||
```bash
|
||||
# 创建版本标签
|
||||
git tag -a v1.0.0 -m "Release version 1.0.0"
|
||||
|
||||
# 推送标签到 GitHub
|
||||
git push origin v1.0.0
|
||||
```
|
||||
|
||||
### 2. 自动触发
|
||||
|
||||
推送标签后,GitHub Actions 会自动:
|
||||
1. 检出代码
|
||||
2. 构建项目
|
||||
3. 打包 .laapp
|
||||
4. 创建 Release
|
||||
5. 上传产物
|
||||
|
||||
### 3. 查看 Release
|
||||
|
||||
在 GitHub 仓库页面 → Releases 查看自动创建的发布。
|
||||
|
||||
---
|
||||
|
||||
## 🔧 高级配置
|
||||
|
||||
### 预发布版本
|
||||
|
||||
```yaml
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: bin/Release/net10.0/*.laapp
|
||||
prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}
|
||||
```
|
||||
|
||||
### 生成变更日志
|
||||
|
||||
```yaml
|
||||
- name: Generate Changelog
|
||||
id: changelog
|
||||
uses: mikepenz/release-changelog-builder-action@v4
|
||||
with:
|
||||
configuration: .github/changelog-config.json
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body: ${{ steps.changelog.outputs.changelog }}
|
||||
files: bin/Release/net10.0/*.laapp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 版本号管理
|
||||
|
||||
```yaml
|
||||
- name: Update Version
|
||||
run: |
|
||||
$version = "${{ github.ref_name }}" -replace '^v', ''
|
||||
$json = Get-Content plugin.json | ConvertFrom-Json
|
||||
$json.version = $version
|
||||
$json | ConvertTo-Json | Set-Content plugin.json
|
||||
```
|
||||
|
||||
### 多文件发布
|
||||
|
||||
```yaml
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
bin/Release/net10.0/*.laapp
|
||||
README.md
|
||||
LICENSE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
学习多平台构建:
|
||||
|
||||
👉 **[04-多平台构建策略](04-多平台构建策略.md)**
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
92
docs/Plugins develop/07-发布与运营/01-插件打包规范.md
Normal file
92
docs/Plugins develop/07-发布与运营/01-插件打包规范.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# 01-插件打包规范
|
||||
|
||||
了解 .laapp 包的规范和结构,确保插件能正确安装和运行。
|
||||
|
||||
---
|
||||
|
||||
## 📦 .laapp 文件格式
|
||||
|
||||
`.laapp` 是阑山桌面的插件包格式,本质上是一个 **ZIP 压缩包**。
|
||||
|
||||
### 文件结构
|
||||
|
||||
```
|
||||
MyPlugin.laapp
|
||||
├── plugin.json # 插件清单(必需)
|
||||
├── MyPlugin.dll # 主程序集(必需)
|
||||
├── Localization/ # 本地化文件夹
|
||||
│ ├── zh-CN.json
|
||||
│ └── en-US.json
|
||||
└── *.dll # 依赖项
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 plugin.json 规范
|
||||
|
||||
### 必需字段
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "com.example.myplugin",
|
||||
"name": "我的插件",
|
||||
"description": "插件描述",
|
||||
"author": "作者名",
|
||||
"version": "1.0.0",
|
||||
"apiVersion": "4.0.1",
|
||||
"entranceAssembly": "MyPlugin.dll",
|
||||
"sharedContracts": []
|
||||
}
|
||||
```
|
||||
|
||||
### 字段验证规则
|
||||
|
||||
| 字段 | 规则 |
|
||||
|-----|------|
|
||||
| `id` | 小写字母、数字、点号,反向域名格式 |
|
||||
| `version` | 语义化版本(x.y.z) |
|
||||
| `apiVersion` | 必须与 SDK 版本兼容 |
|
||||
| `entranceAssembly` | 必须与 DLL 文件名一致 |
|
||||
|
||||
---
|
||||
|
||||
## 🔨 构建配置
|
||||
|
||||
### .csproj 关键配置
|
||||
|
||||
```xml
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- 确保资源文件复制到输出目录 -->
|
||||
<ItemGroup>
|
||||
<None Update="plugin.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Localization\**\*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 打包检查清单
|
||||
|
||||
- [ ] `plugin.json` 格式正确
|
||||
- [ ] `id` 全局唯一
|
||||
- [ ] `apiVersion` 与 SDK 版本匹配
|
||||
- [ ] DLL 文件名与 `entranceAssembly` 一致
|
||||
- [ ] 所有依赖项已包含
|
||||
- [ ] 本地化文件完整
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
75
docs/Plugins develop/07-发布与运营/02-版本管理策略.md
Normal file
75
docs/Plugins develop/07-发布与运营/02-版本管理策略.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# 02-版本管理策略
|
||||
|
||||
合理的版本管理是插件维护的基础。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 语义化版本(SemVer)
|
||||
|
||||
版本格式:`主版本.次版本.修订号`
|
||||
|
||||
| 版本变化 | 说明 | 示例 |
|
||||
|---------|------|------|
|
||||
| 主版本(Major) | 破坏性变更 | 1.0.0 → 2.0.0 |
|
||||
| 次版本(Minor) | 新功能,向后兼容 | 1.0.0 → 1.1.0 |
|
||||
| 修订号(Patch) | Bug 修复 | 1.0.0 → 1.0.1 |
|
||||
|
||||
---
|
||||
|
||||
## 📋 版本示例
|
||||
|
||||
| 版本 | 含义 |
|
||||
|-----|------|
|
||||
| `1.0.0` | 首个正式版 |
|
||||
| `1.1.0` | 新增功能 |
|
||||
| `1.1.1` | 修复 Bug |
|
||||
| `2.0.0-beta` | 2.0 测试版 |
|
||||
| `2.0.0-rc1` | 2.0 候选版 |
|
||||
|
||||
---
|
||||
|
||||
## 🔄 版本更新流程
|
||||
|
||||
### 1. 更新版本号
|
||||
|
||||
```json
|
||||
// plugin.json
|
||||
{
|
||||
"version": "1.1.0"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 更新 CHANGELOG.md
|
||||
|
||||
```markdown
|
||||
## [1.1.0] - 2024-04-13
|
||||
|
||||
### 新增
|
||||
- 添加天气预警功能
|
||||
- 支持多城市管理
|
||||
|
||||
### 修复
|
||||
- 修复定位失败问题
|
||||
```
|
||||
|
||||
### 3. 创建 Git 标签
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "Release v1.1.0"
|
||||
git tag -a v1.1.0 -m "Release version 1.1.0"
|
||||
git push origin main --tags
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
- 使用 GitHub Releases 管理版本
|
||||
- 每个版本都写更新日志
|
||||
- 测试版使用 `-beta`、`-alpha` 后缀
|
||||
- 保持向后兼容,避免频繁主版本升级
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
71
docs/Plugins develop/07-发布与运营/03-发布到插件市场.md
Normal file
71
docs/Plugins develop/07-发布与运营/03-发布到插件市场.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# 03-发布到插件市场
|
||||
|
||||
将插件发布到阑山桌面插件市场,让更多用户使用。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 发布流程
|
||||
|
||||
### 1. 准备材料
|
||||
|
||||
- 插件包(.laapp)
|
||||
- 插件图标(256x256 PNG)
|
||||
- 截图(至少 1 张)
|
||||
- 详细描述
|
||||
- 更新日志
|
||||
|
||||
### 2. 提交审核
|
||||
|
||||
1. 访问阑山桌面开发者门户
|
||||
2. 登录开发者账号
|
||||
3. 点击「提交新插件」
|
||||
4. 填写插件信息
|
||||
5. 上传 .laapp 文件
|
||||
6. 提交审核
|
||||
|
||||
### 3. 审核标准
|
||||
|
||||
| 检查项 | 要求 |
|
||||
|-------|------|
|
||||
| 功能完整性 | 插件能正常运行 |
|
||||
| 安全性 | 无恶意代码 |
|
||||
| 用户体验 | 界面美观,操作流畅 |
|
||||
| 文档完整 | 有基本使用说明 |
|
||||
|
||||
---
|
||||
|
||||
## 📋 元数据要求
|
||||
|
||||
### 必需信息
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "com.example.plugin",
|
||||
"name": "插件名称",
|
||||
"description": "简短描述(50字以内)",
|
||||
"author": "作者名",
|
||||
"version": "1.0.0",
|
||||
"tags": ["工具", "天气"],
|
||||
"website": "https://github.com/..."
|
||||
}
|
||||
```
|
||||
|
||||
### 图标规范
|
||||
|
||||
- 格式:PNG
|
||||
- 尺寸:256x256 像素
|
||||
- 背景:透明
|
||||
- 风格:与阑山桌面一致
|
||||
|
||||
---
|
||||
|
||||
## 🚀 发布后
|
||||
|
||||
- 关注用户反馈
|
||||
- 及时修复 Bug
|
||||
- 定期更新功能
|
||||
- 维护更新日志
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
82
docs/Plugins develop/07-发布与运营/04-更新与维护.md
Normal file
82
docs/Plugins develop/07-发布与运营/04-更新与维护.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# 04-更新与维护
|
||||
|
||||
插件发布后的持续维护和更新策略。
|
||||
|
||||
---
|
||||
|
||||
## 🔄 更新策略
|
||||
|
||||
### 热修复(Hotfix)
|
||||
|
||||
发现严重 Bug 时立即发布:
|
||||
|
||||
```bash
|
||||
# 1. 修复 Bug
|
||||
git checkout -b hotfix/critical-bug
|
||||
# ... 修复代码 ...
|
||||
|
||||
# 2. 更新版本号(修订号+1)
|
||||
# plugin.json: "version": "1.0.1"
|
||||
|
||||
# 3. 提交并发布
|
||||
git commit -m "Fix critical bug"
|
||||
git tag -a v1.0.1 -m "Hotfix v1.0.1"
|
||||
git push origin main --tags
|
||||
```
|
||||
|
||||
### 功能更新
|
||||
|
||||
```bash
|
||||
# 1. 开发新功能
|
||||
git checkout -b feature/new-feature
|
||||
|
||||
# 2. 完成功能后合并到 main
|
||||
|
||||
# 3. 更新版本号(次版本+1)
|
||||
# plugin.json: "version": "1.1.0"
|
||||
|
||||
# 4. 发布
|
||||
git tag -a v1.1.0 -m "Release v1.1.0"
|
||||
git push origin main --tags
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 维护清单
|
||||
|
||||
### 日常维护
|
||||
|
||||
- [ ] 监控用户反馈
|
||||
- [ ] 查看崩溃报告
|
||||
- [ ] 回复用户问题
|
||||
- [ ] 更新依赖包
|
||||
|
||||
### 定期维护
|
||||
|
||||
- [ ] 每季度检查 SDK 更新
|
||||
- [ ] 每年评估功能需求
|
||||
- [ ] 定期更新文档
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Bug 处理流程
|
||||
|
||||
1. **收集信息** - 复现步骤、环境信息
|
||||
2. **定位问题** - 本地调试复现
|
||||
3. **修复验证** - 修复后充分测试
|
||||
4. **发布更新** - 按热修复流程发布
|
||||
5. **通知用户** - 在 Release 中说明
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
- 保持向后兼容
|
||||
- 及时响应用户反馈
|
||||
- 定期发布小更新
|
||||
- 维护清晰的更新日志
|
||||
- 废弃功能提前通知
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026年4月*
|
||||
Reference in New Issue
Block a user