# u-desk 插件化架构设计方案 > 状态:调研完成,待实施 > 日期:2026-04-11 ## 一、现状痛点 | 痛点 | 现状 | 影响 | |------|------|------| | **app.go God Object** | 958 行,67 个方法全在一个 struct 上 | 难以维护,新功能必须改核心文件 | | **App.vue 硬编码映射** | `getComponent()` 是 3 个 key 的字面量对象 | 新 Tab 必须改源码 | | **文件预览 if/else 链** | `FileEditorPanel.vue` 有 ~12 层 v-if/v-else-if | 新增文件类型需改 5+ 处 | | **大体积功能嵌入** | draw.io 等 ~12-15MB 功能无法按需加载 | 安装包膨胀 | ## 二、架构总览 ``` +================================================================+ | u-desk Application | +================================================================= | Core (Go) Plugin Manager | | - App facade - Registry / Lifecycle / Loader | | - ConfigStore (SQLite) - TabRegistry | | - EventBus (Wails Events) - PreviewRegistry | +----------+----------+---------+-----------+----------+ | | | | | | | +-------+ +------+ +-----+ +--------+ +------+ +---+ | |builtin: |builtin: |builtin | builtin: | future | future | | file-sys| db-cli | md-edit| drawio | JS/WASM| Go .so | +---------+---------+--------+-----------+--------+------------+ | +==============================================================+| | Frontend (Vue 3) || | PluginRegistry (TS) || | - TabProviders → 替代 App.vue 硬编码映射 || | - FilePreviewHandlers → 替代 FileEditorPanel if/else 链 || | - ComponentLoader → defineAsyncComponent 懒加载 || +==============================================================+ ``` ## 三、核心接口定义 ### 3.1 后端插件接口(Go) ```go // 文件路径: internal/plugin/plugin.go // PluginID 插件唯一标识 type PluginID string // PluginMetadata 插件元数据 type PluginMetadata struct { ID PluginID `json:"id"` Name string `json:"name"` Version string `json:"version"` Description string `json:"description"` Author string `json:"author"` TabKey string `json:"tab_key,omitempty"` // 提供的 Tab key FileExtensions []string `json:"file_extensions,omitempty"` // 处理的文件扩展名 } // PluginCapability 插件能力标志 type PluginCapability int const ( CapabilityTabProvider PluginCapability = 1 << iota // 提供 Tab 页面 CapabilityFilePreview // 文件预览 ) // Plugin 核心插件接口 type Plugin interface { Meta() PluginMetadata Capabilities() PluginCapability Init(ctx context.Context, core CoreServices) error Start() error Stop() error } // TabProvider Tab 提供者接口(可选) type TabProvider interface { TabDefinition() TabDef TabComponentPath() string } // FilePreviewHandler 文件预览处理接口(可选) type FilePreviewHandler interface { CanPreview(filename string, mimeType string) bool PreviewInfo(filename string) PreviewInfo } // PreviewInfo 预览元信息(发送给前端) type PreviewInfo struct { Type string `json:"type"` // "drawio", "image", "pdf" Title string `json:"title"` Icon string `json:"icon,omitempty"` NeedsContainer bool `json:"needs_container,omitempty"` ContainerConfig map[string]string `json:"container_config,omitempty"` SupportsEdit bool `json:"supports_edit"` PreloadHint string `json:"preload_hint,omitempty"` } // TabDef Tab 定义 type TabDef struct { Key string `json:"key"` Title string `json:"title"` Icon string `json:"icon,omitempty"` Enabled bool `json:"enabled"` Order int `json:"order"` } ``` ### 3.2 前端插件接口(TypeScript) ```typescript // 文件路径: frontend/src/plugin/types.ts /** 插件能力标志 */ export enum PluginCapability { None = 0, TabProvider = 1 << 0, // 提供 Tab FilePreview = 1 << 1, // 文件预览 Settings = 1 << 2, // 设置页 } /** Tab 插件定义 */ export interface TabPluginDefinition { key: string title: string icon?: string componentLoader: () => Promise // 异步组件加载器 defaultVisible?: boolean order?: number } /** 文件预览处理器定义 */ export interface FilePreviewHandlerDefinition { id: string name: string icon: string priority: number // 越大越优先 canHandle: (filename: string) => boolean getComponent?: () => Promise getRenderConfig?: (filePath: string) => RenderConfig supportsEdit?: boolean } /** 渲染配置 */ export interface RenderConfig { type: 'iframe' | 'html' | 'custom' src?: string htmlContent?: string props?: Record } ``` ## 四、插件管理器设计 ### 4.1 后端 PluginManager ```go // internal/plugin/manager.go type Manager struct { mu sync.RWMutex plugins map[PluginID]Plugin core CoreServices tabReg *TabRegistry previewReg *PreviewRegistry ctx context.Context } func NewManager(core CoreServices) *Manager func (m *Manager) Register(p Plugin) error // 注册插件 func (m *Manager) InitAll(ctx context.Context) error // 初始化所有插件 func (m *Manager) StartByTabKey(tabKey string) error // 按 Tab 懒启动 func (m *Manager) GetPluginInfos() []PluginMetadata // 获取插件列表 func (m *Manager) ResolvePreview(filename string) (*PreviewInfo, error) func (m *Manager) Shutdown() error ``` ### 4.2 前端注册中心 ```typescript // frontend/src/plugin/registry.ts import { reactive } from 'vue' const state = reactive({ tabPlugins: new Map(), previewHandlers: [] as FilePreviewHandlerDefinition[], }) export function registerTabPlugin(def: TabPluginDefinition): void export function getTabComponent(key: string): (() => Promise) | null export function getAllTabDefinitions(): TabPluginDefinition[] export function registerPreviewHandler(handler: FilePreviewHandlerDefinition): void export function resolvePreviewHandler(filename: string): FilePreviewHandlerDefinition | null ``` ## 五、关键改造点 ### 5.1 FileEditorPanel.vue 重构(最大收益) **改造前**:12 层 v-if/v-else-if 链(image/video/audio/pdf/html/md/excel/word/csv/text/binary) **改造后**: ```vue