- 任务右键菜单:实现打开输出目录(inputs/outputs/.cache 结构)、
复制资源链接、重新编辑、再次生成、拉取结果、重新下载、收藏、删除记录
- 主进程下载:通过 Electron net 模块绕过 CORS,支持 CDN 媒体文件下载
- sharp 缩略图:替换 nativeImage,支持 4K/8K+ 大尺寸素材的流式处理
- IPC 精简:40+ 冗余通道 → 15 个分组通道,移除未使用的 preload 桥接方法
- 任务存储:新增收藏字段、schema 迁移、task-store 持久化
- 清理废弃模块:canvas-ipc / updater / request / pricing
refactor: IPC/Preload/Services 模块化拆分 — 单一职责 + 向后兼容
主要变更:
- shared/constants/ipc-channels.ts → ipc/ 模块 (base/storage/canvas/updater)
- electron/preload.ts → preload/ 模块 (base/safe-storage/file-storage/canvas)
- electron/main/ipc/canvas-ipc.ts → canvas/ 模块 (types/walk/sidecar/mime/thumbnail)
- electron/main/ipc/storage-ipc.ts → 提取 utils/ (path/download/thumbnail)
- electron/main/updater.ts → updater/ 模块 (provider/platform-updaters)
- src/services/request.ts → request/ 模块 (token/interceptors/methods)
- src/shared/utils/pricing.ts → pricing/ 模块 (types/helpers/format/calculate/fields)
- src/modules/home/center/views/ModelInputForm.tsx → 提取 utils + DependentFormItem
架构优化:
- 所有拆分均保持向后兼容(旧导入路径仍可用)
- TypeScript 编译通过 ✓
- 每个模块单一职责,平均 ~130 行
433 lines
28 KiB
Markdown
433 lines
28 KiB
Markdown
# 项目概览
|
||
|
||
- 本项目使用 Vite + React 19 + TypeScript + Electron 重构现有 Python Qt
|
||
桌面应用,打造跨平台(Windows/macOS)的多窗口桌面程序。
|
||
- 核心界面为单页应用(SPA),同时支持通过 Electron 打开独立窗口以承载子功能。
|
||
|
||
# 核心需求
|
||
|
||
- 完整还原原有 Qt 应用的业务逻辑与视觉体验,并对交互进行现代化改造。
|
||
|
||
- 必须支持 多窗口(主窗口 + 可动态创建的辅助窗口),窗口间需通过事件/消息进行低耦合通信。
|
||
|
||
- 适配 Windows 与 macOS,处理好快捷键、托盘、菜单、窗口边框等平台差异。
|
||
|
||
- 采用 离线优先 架构,保障本地数据处理性能,合理使用本地存储(如 electron-store 或 SQLite)。
|
||
|
||
- 所有对系统底层能力的调用必须通过 Electron 主进程,严禁在渲染进程直接使用 Node.js API(除非通过 preload 安全暴露)。
|
||
|
||
# 技术架构
|
||
|
||
## 整体分层
|
||
|
||
- 主进程(Main Process):Electron 主进程,管理窗口生命周期、系统菜单、托盘、IPC 事件调度、本地文件/数据库操作。
|
||
|
||
- 预加载脚本(Preload):桥接渲染进程与主进程,使用 contextBridge 安全暴露有限 API。
|
||
|
||
- 渲染进程(Renderer):基于 React 19 的 SPA(主窗口)与独立窗口的 React 入口,负责 UI 展示与用户交互。
|
||
|
||
- 共享层(Shared):进程间共享的 TypeScript 类型、常量、工具函数、事件名称枚举。
|
||
|
||
## 前端架构原则
|
||
|
||
- 组件化:以函数组件 + Hooks 为唯一组件形式,UI 拆分为展示组件与容器组件。
|
||
|
||
- 单向数据流:全局状态使用 Context + Provider 模式(AppContext / ThemeContext / SettingsContext),
|
||
遵循 Provider → Context → Comsumer 单向流动。组件内部状态使用 useState/useReducer,不污染全局 Context。
|
||
|
||
- 事件驱动(EDA):
|
||
1. 主进程与渲染进程通信基于 Electron IPC(ipcMain/ipcRenderer),定义明确的事件通道与载荷类型。
|
||
|
||
2. 渲染进程内跨组件通信优先使用 Context;需要解耦的场景使用自定义事件总线(src/shared/utils/bus.ts),
|
||
所有 emit 调用自动记录到日志文件用于审计追踪。
|
||
|
||
3. 自定义错误体系(RequestError / RefreshError)通过事件总线解耦:请求拦截器抛出自定义错误 →
|
||
全局捕获 → emit 事件 → UI 层响应。
|
||
|
||
4. 窗口间通信统一通过主进程转发(或使用 MessagePort 等 Electron 支持的机制),保持窗口独立性。
|
||
|
||
- 路由与导航:主窗口为单页应用,不使用 React Router。所有页面切换通过事件总线 + PageDispatcher 实现:
|
||
- 叠加层(设置、公告、素材库等)→ Modal/Drawer + 事件总线驱动,主页面不卸载
|
||
- NavBar 导航项点击 → `emit(OPEN_PAGE, { target, modalType })` → PageDispatcher 按 `PAGE_MAP` 注册表渲染对应容器
|
||
- 每个页面路径在 `PageDispatcher.PAGE_MAP` 中注册一行(target → { component, modalType, width, height })
|
||
- 需跨页保持的状态放 Provider 或 localStorage
|
||
|
||
- 辅助窗口(设置、预览、导入导出等)以 Modal/Drawer 叠加在主窗口之上,不与主窗口共享 DOM,但保持在同一个 React 组件树中以确保状态一致性。
|
||
|
||
# 目录结构
|
||
|
||
> 2026-06-29 重构:业务模块从 `pages/` + `services/modules/` 分散模式统一为 `modules/` 下的
|
||
> VCHSM 五层架构(Views → Controllers → Hooks → Settings → Model)。
|
||
|
||
```md
|
||
├── electron/ # Electron 主进程 + 预加载
|
||
│ ├── main.ts # 入口编排:窗口创建 / IPC 注册 / 生命周期
|
||
│ ├── preload.ts # contextBridge:向渲染进程暴露安全 API(薄入口)
|
||
│ ├── electron-env.d.ts # 类型声明(APP_ROOT、Window 扩展)
|
||
│ └── main/ # 主进程核心模块
|
||
│ │ ├── load-env.ts # .env.{mode} + .env.local 加载器
|
||
│ │ ├── logger.ts # 日志核心:JSON Lines / 每日轮转 / 批量冲刷
|
||
│ │ ├── net-request.ts # Electron net.request 的 axios 风格封装
|
||
│ │ ├── updater/ # 自动更新模块
|
||
│ │ │ ├── provider.ts # 自定义 Provider + 设备指纹 + 构建配置
|
||
│ │ │ ├── platform-updaters.ts # 平台特定 Updater(Windows/macOS/Linux)
|
||
│ │ │ └── index.ts # 初始化 + IPC 注册
|
||
│ │ ├── menu/index.ts # 系统菜单(macOS Edit / Win 无菜单)
|
||
│ │ ├── ipc/ # IPC handler 集合
|
||
│ │ │ ├── log-ipc.ts # 渲染进程日志 → 主进程日志桥接
|
||
│ │ │ ├── safe-storage-ipc.ts # safeStorage 加密/解密 IPC handler
|
||
│ │ │ ├── storage-ipc.ts # 沙箱文件存储 IPC handler(heixiu-data/)
|
||
│ │ │ ├── utils/ # 存储通用工具
|
||
│ │ │ │ ├── path.ts # 路径安全解析(resolveSafe / ensureDir)
|
||
│ │ │ │ ├── download.ts # Electron net 文件下载(绕过 CORS)
|
||
│ │ │ │ └── thumbnail.ts # sharp 缩略图生成(内存优化)
|
||
│ │ │ └── canvas/ # 画布文件操作模块
|
||
│ │ │ ├── types.ts # 类型定义(StageType / ScanEntry / ThumbnailResult)
|
||
│ │ │ ├── walk.ts # 递归目录遍历
|
||
│ │ │ ├── sidecar.ts # Sidecar JSON 元数据读写
|
||
│ │ │ ├── mime.ts # MIME 类型推断
|
||
│ │ │ ├── thumbnail.ts # 画布缩略图生成(缓存优先)
|
||
│ │ │ ├── scan-roots.ts # 扫描根目录计算 + 阶段过滤
|
||
│ │ │ └── index.ts # IPC handler 注册
|
||
│ │ └── utils/ # 平台判断 / 图标路径
|
||
│ └── preload/ # 预加载脚本(contextBridge 桥接层)
|
||
│ ├── base.ts # ipcRenderer 基础桥接(on/off/send/invoke)
|
||
│ ├── safe-storage.ts # safeStorage 加密/解密 API
|
||
│ ├── file-storage.ts # 文件存储 API(getDataDir / readFile / writeFile)
|
||
│ ├── canvas.ts # 画布 API(openWindow / scanProject / readSidecar)
|
||
│ ├── app-runtime.ts # 运行时 API(setWindowEdition)
|
||
│ ├── index.ts # 汇总导出(exposeBaseIpc / exposeSafeStorage / ...)
|
||
│ ├── device.ts # 硬件指纹采集
|
||
│ └── device_service.ts # 设备信息缓存层
|
||
│
|
||
├── src/ # 渲染进程(React 19 + Vite + TypeScript)
|
||
│ │
|
||
│ ├── app/ # ═══ 应用壳 ═══
|
||
│ │ ├── App.tsx # 根组件:叠加层编排(登录/设置/页面调度)
|
||
│ │ ├── main.tsx # React 挂载 + Provider 嵌套 + 全局错误捕获
|
||
│ │ ├── providers/ # 全局 Context Provider
|
||
│ │ │ ├── AppProvider.tsx # 认证状态(login/logout/Token 刷新)
|
||
│ │ │ ├── ThemeProvider.tsx # 亮/暗主题切换 + antd ConfigProvider
|
||
│ │ │ └── SettingsProvider.tsx # 本地持久化设置(存储路径等)
|
||
│ │ └── layout/ # 布局框架
|
||
│ │ ├── Layout.tsx # 全高 flex 列布局壳(NavBar + 内容区)
|
||
│ │ └── PageDispatcher.tsx # 通用页面调度器(Modal/Drawer/FloatingPanel)
|
||
│ │
|
||
│ ├── modules/ # ═══ 业务模块(VCHSM 五层架构)═══
|
||
│ │ │ # V=views C=controllers H=hooks S=settings M=model
|
||
│ │ ├── navbar/ V+C+S+M # 导航栏 UI 壳
|
||
│ │ │ ├── views/ # NavBar.tsx / NavItem.tsx
|
||
│ │ │ ├── settings/ # nav-config.ts(左右导航项注册表)
|
||
│ │ │ └── model/ # 导航类型定义
|
||
│ │ ├── announcement/ V+C # 公告(Badge 角标 + 15 分钟轮询缓存)
|
||
│ │ │ ├── views/ # AnnouncementDrawer.tsx
|
||
│ │ │ └── controllers/ # announcement-api.ts
|
||
│ │ ├── auth/ V+C+H+S+M # 认证模块(14 文件)
|
||
│ │ │ ├── views/ # AuthPage.tsx + components/(LoginForm/RegisterForm/改密/忘记密码)
|
||
│ │ │ ├── controllers/ # auth-api.ts / token-manager.ts / auth-persistence.ts
|
||
│ │ │ ├── hooks/ # use-auth-state.ts / use-sms-cooldown.ts
|
||
│ │ │ ├── settings/ # login-fields.ts / register-fields.ts / validators.ts
|
||
│ │ │ └── model/ # 类型定义
|
||
│ │ ├── home/ V+C+H+S+M # 主工作区(三栏布局 → VCHSM 嵌套)
|
||
│ │ │ ├── views/
|
||
│ │ │ │ ├── HomePage.tsx / HomeContent.tsx
|
||
│ │ │ │ ├── left/ # 左侧栏 — 输入控件(LeftPanel + controls/)
|
||
│ │ │ │ ├── center/ # 中央栏 — 预览区(CenterPanel)
|
||
│ │ │ │ └── right/ # 右侧栏 — 模型/任务/输出/Banner
|
||
│ │ │ ├── controllers/ # task-api.ts / model-api.ts / banner-api.ts / menus/
|
||
│ │ │ ├── hooks/ # useModelUI.ts
|
||
│ │ │ ├── stores/ # HomeContext.tsx(页面级状态)
|
||
│ │ │ └── model/ # 类型定义
|
||
│ │ ├── account/ V+C # 账户与支付(6 子功能独立子目录)
|
||
│ │ │ ├── views/ # info/ vip/ recharge/ billing/ orders/ wechat-work/
|
||
│ │ │ └── controllers/ # vip-api.ts / payment-api.ts / billing-api.ts
|
||
│ │ ├── canvas/ V+C+H+M+S # 无限画布 + 项目管理
|
||
│ │ │ ├── views/ # CanvasApp.tsx / CanvasPage.tsx
|
||
│ │ │ │ ├── core/ # CanvasViewport / LODImage / MediaCard / MediaPreview
|
||
│ │ │ │ ├── navigation/ # CanvasSidebar / ProjectSelector / EpisodeSelector / ShotList
|
||
│ │ │ │ └── toolbar/ # CanvasToolbar / CanvasFilterBar / CategoryTabs / StageTabs
|
||
│ │ │ ├── controllers/ # menus/(右键菜单注册)
|
||
│ │ │ ├── hooks/ # useCanvasInteraction / useDirectoryScan / useSidecarSync
|
||
│ │ │ ├── stores/ # useCanvasStore.ts(Zustand store)
|
||
│ │ │ └── utils/ # scanner.ts / layout.ts / LocalFileDataSource.ts
|
||
│ │ └── settings/ V+C # 应用设置
|
||
│ │ ├── views/ # SettingsPage.tsx + sections/(6 个设置分区)
|
||
│ │ └── controllers/ # settings-actions.ts
|
||
│ │
|
||
│ └── shared/ # ═══ 跨模块共享 ═══
|
||
│ ├── components/ # 通用 UI(FloatingPanel / FormField / LegalTextModal / ContextMenu)
|
||
│ ├── hooks/ # 全局 Hooks(useAnnouncements / useApi / useAsync / useUpdater)
|
||
│ ├── services/ # HTTP 客户端 + API 模块
|
||
│ │ ├── request/ # Axios 请求封装
|
||
│ │ │ ├── token.ts # Token 管理(加密存储 + 内存缓存)
|
||
│ │ │ ├── interceptors.ts # 请求/响应拦截器(401 刷新 + 错误处理)
|
||
│ │ │ ├── methods.ts # 请求方法(get/post/put/del/upload/download)
|
||
│ │ │ └── index.ts # 主入口 + 导出
|
||
│ │ └── modules/ # API 模块(auth / home / account / announcement)
|
||
│ ├── utils/ # 工具函数
|
||
│ │ ├── bus.ts # 事件总线(emit / on / off / EVENTS)
|
||
│ │ ├── pricing/ # 定价计算模块
|
||
│ │ │ ├── types.ts # 类型定义(ModelPricingConfig / ModelDefinition)
|
||
│ │ │ ├── helpers.ts # 内部工具(asFloat / matchDimensions)
|
||
│ │ │ ├── format.ts # 金额格式化(formatYuan / formatCent)
|
||
│ │ │ ├── calculate.ts # 计价逻辑(6 种定价模式)
|
||
│ │ │ ├── fields.ts # 依赖字段收集
|
||
│ │ │ └── index.ts # 主入口 + 导出
|
||
│ │ ├── enum-resolver.ts # 枚举值解析
|
||
│ │ ├── display.ts # 显示工具
|
||
│ │ └── safe-storage.ts # 安全存储工具
|
||
│ ├── infrastructure/storage/ # 本地加密数据库(sql.js + safeStorage)
|
||
│ │ ├── core/ # db-core.ts / db-encrypt.ts / schema.ts
|
||
│ │ ├── stores/ # task-store / order-store / media-store / settings-store
|
||
│ │ └── services/ # media-loader / sync-manager / data-sync / user-scope
|
||
│ └── theme/ # 主题系统(tokens / antd-theme / CSS 过渡)
|
||
│
|
||
├── shared/ # 主进程 & 渲染进程共享(根级 @shared 别名)
|
||
│ ├── constants/ # 应用常量
|
||
│ │ ├── app.ts # 应用名称 / 窗口标题
|
||
│ │ ├── version.ts # 版本号
|
||
│ │ ├── keyboard.ts # 快捷键映射
|
||
│ │ └── ipc/ # IPC 通道常量(模块化)
|
||
│ │ ├── base.ts # 基础通道(窗口/日志/主题/配置)
|
||
│ │ ├── storage.ts # 存储通道(文件/加密/下载/缩略图)
|
||
│ │ ├── canvas.ts # 画布通道(扫描/sidecar/拖拽)
|
||
│ │ ├── updater.ts # 更新通道(检查/下载/安装)
|
||
│ │ └── index.ts # 汇总导出 + 兼容性 BIDIRECTIONAL
|
||
│ ├── types/ # index.ts(Platform/Theme/Edition)/ logging.ts
|
||
│ └── utils/ # sanitize.ts(日志脱敏)+ index.ts(通用工具)
|
||
│
|
||
├── scripts/ # 构建与发布脚本
|
||
├── vite.config.ts # Vite 构建配置
|
||
├── package.json # 含 electron-builder 打包配置
|
||
└── pnpm-workspace.yaml
|
||
```
|
||
|
||
### VCHSM 五层架构说明
|
||
|
||
| 层 | 目录 | 职责 | 示例 |
|
||
|----|------|------|------|
|
||
| **V** views | `views/` | 页面组件,纯 JSX 结构 | `NavBar.tsx`, `VipPage.tsx`, `CanvasViewport.tsx` |
|
||
| **C** controllers | `controllers/` | 交互逻辑 + API 请求 + 菜单注册 | `auth-api.ts`, `task-api.ts`, `menus/` |
|
||
| **H** hooks | `hooks/` | 组件间复用逻辑(状态/副作用/数据流) | `use-auth-state.ts`, `useCanvasInteraction.ts` |
|
||
| **S** settings | `settings/` | 配置常量、字段映射、验证规则、注册表 | `nav-config.ts`, `login-fields.ts` |
|
||
| **M** model | `model/` | 纯 TS 类型/接口/枚举(编译时) | `types.ts` |
|
||
|
||
模块按需建层:navbar 只用 V+S+M,auth 用 V+C+H+S+M,canvas 额外有 stores/ + utils/。
|
||
|
||
# 多窗口与平台判定
|
||
|
||
- 窗口管理由 `electron/main.ts` 统一负责:`createMainWindow()` 创建主窗口,`BrowserWindow` 通过 `contextIsolation: true` + `nodeIntegration: false` 运行沙箱化渲染进程。辅助窗口通过 `RENDERER_TO_MAIN.OPEN_SUB_WINDOW` IPC 通道按需创建。
|
||
|
||
- 平台判定双端各有一份:`electron/main/utils/platform.ts`(主进程,直接使用 `process.platform`)和 `src/shared/utils/env.ts`(渲染进程,优先 `process.platform`,降级 `navigator.userAgent`)。
|
||
|
||
- 打包配置 (`package.json` build 字段) 须针对平台设置不同的图标、签名策略、安装包格式。
|
||
|
||
# 代码风格
|
||
|
||
## TypeScript
|
||
|
||
- 所有语句必须以分号 ; 结尾,缺失分号将被视为错误。
|
||
|
||
- 启用 strict 模式,禁止隐式 any,优先使用 interface 定义对象类型,复杂类型可适度使用 type。
|
||
|
||
- 文件名使用小写短横线连接(kebab-case),如 use-window-state.ts;组件文件名与组件名保持一致(PascalCase),如
|
||
ThemeAdapter.tsx。
|
||
|
||
- 导入顺序:第三方库 → 项目内共享模块 → 相对路径模块,中间用空行分隔。
|
||
|
||
## React
|
||
|
||
- 只允许函数组件与 Hooks,禁止使用类组件。
|
||
|
||
- 每个组件必须有明确的 Props 类型,使用 React.FC<Props> 或直接标注函数参数类型(推荐直接标注)。
|
||
|
||
- 事件处理函数命名以 handle 开头(如 handleClick),自定义 Hook 以 use 开头。
|
||
|
||
- 避免直接操作 DOM,除非封装在自定义 Hook 中(如焦点管理)。
|
||
|
||
- 使用 React.memo、useMemo、useCallback 优化性能,但不要过早优化。
|
||
|
||
## 状态管理
|
||
|
||
- 使用 React Context + Provider 模式管理全局状态,按领域拆分:AppContext(平台/登录)、ThemeContext(主题)、
|
||
SettingsContext(设置项)。各 Context 定义与 Provider 合并在 `app/providers/` 目录下,避免引入 Redux 等重量级方案。
|
||
|
||
- Provider 在 main.tsx 中按层级嵌套挂载:
|
||
AppProvider → ThemeProvider → SettingsProvider → AntdApp → App
|
||
|
||
- 各模块通过自定义 Hook 消费(useAppContext / useTheme / useSettings),Hook 内部做 null 检查并抛出明确错误信息。
|
||
|
||
- 页面级状态(如首页的 selectedModel / selectedTask / 分页)放在 HomeContext 中,由 HomeContent 提供,遵循与全局 Provider 相同的 Context + Provider + Hook 模式。
|
||
|
||
## 日志系统
|
||
|
||
- 双端日志:主进程直接写文件(electron/main/logger.ts),渲染进程通过 IPC send 上报(src/shared/utils/bus.ts)。
|
||
两端暴露一致的 API:`logger.debug / info / warn / error(category, message, error?, context?)`。
|
||
|
||
- 文件存储:JSON Lines 格式(每行一个 JSON),路径 `{userData}/logs/app-YYYY-MM-DD.log`,
|
||
每日轮转 + 7 天自动清理。
|
||
|
||
- 事件总线联动:event-bus.ts 暴露 `setEventLogListener` 钩子,logger 注册后所有 emit 自动记录 DEBUG 日志。
|
||
|
||
- 错误日志:request.ts 拦截器根据错误类型自动分级记录(AUTH_EXPIRED → debug,5xx → error,其余 → warn)。
|
||
|
||
- 401 判断策略:HTTP 401 拦截处双重校验——同时检查 HTTP 状态码和响应体中的业务码 `data.code`。
|
||
仅 `status === 401 && data?.code === 401` 才走 token 刷新流程;其他业务码(如密码错误)穿透到正常错误处理,
|
||
透传后端的 `data.message`。这确保登录接口返回的"账号或密码错误"不被 RefreshError("登录已过期") 覆盖。
|
||
|
||
- 脱敏:shared/utils/sanitize.ts 在日志写入前自动处理 ID(哈希)、邮箱/手机/名称/Token 等字段。
|
||
|
||
# 安全与认证
|
||
|
||
## Token 加密存储
|
||
|
||
- `access_token` 和 `refresh_token` 通过 Electron `safeStorage` API 加密存储,不落明文:
|
||
- macOS → Keychain
|
||
- Windows → DPAPI
|
||
- Linux → libsecret
|
||
- 实现文件:
|
||
- `electron/main/safe-storage-ipc.ts` — 主进程加密/解密 IPC 处理(`BIDIRECTIONAL.SAFESTORAGE_ENCRYPT / DECRYPT`)
|
||
- `src/shared/utils/safe-storage.ts` — 渲染进程桥接层(`initSafeStore / getSafeStore / setSafeStore / clearSafeStore`)
|
||
- `src/shared/services/http.ts` — token 存取通过 `getSafeStore / setSafeStore`,无需感知加密细节
|
||
- `src/modules/auth/controllers/token-manager.ts` — refresh_token 同理通过 safeStorage 存取
|
||
|
||
## 双 Token 机制
|
||
|
||
- `access_token`(短效)+ `refresh_token`(长效),登录成功同时下发
|
||
- 请求拦截器自动附加 `Authorization: Bearer <access_token>`
|
||
- 401 响应 → 双重校验(HTTP `status` + 业务 `data.code`)→ 仅 `code === 401` 时触发刷新
|
||
- 刷新期间并发请求排队(`failedQueue`),刷新完成后批量重试
|
||
- refresh 接口自身 401 直接拒绝(防死循环),login 接口 401 穿透透传后端 `message`
|
||
- 启动时 `refresh_token` 静默续期(自动登录),失败仅清除标记不影响用户名回填
|
||
- 登录/退出通过事件总线解耦(`LOGIN_SUCCESS` / `LOGOUT` / `AUTH_REQUIRED`)
|
||
|
||
## 日志脱敏
|
||
|
||
- `shared/utils/sanitize.ts` 在日志写入前自动脱敏处理
|
||
- 覆盖字段:用户 ID(SHA256 哈希)、邮箱(`***@domain`)、手机号(掩码)、用户名称、Token(截断)
|
||
- 主进程 `electron/main/logger.ts` 和渲染进程 `src/shared/utils/bus.ts` 均写入前调用 `sanitizeLogEntry()`
|
||
|
||
# 样式与主题
|
||
|
||
- 优先使用 Tailwind 工具类完成布局与常规样式。自定义设计令牌在 `src/shared/theme/tokens.css` 的 `@theme` 块中定义(Tailwind v4 语法),自动生成 `bg-primary-500`、`text-secondary` 等工具类。
|
||
|
||
- Ant Design 组件样式通过 ConfigProvider 的 theme 属性覆盖,确保与 Tailwind 主题令牌同步。
|
||
`ConfigProvider` 同时配置 `locale={zhCN}` 实现全局中文(分页器"条/页"等无需逐个组件设置)。
|
||
在 src/shared/theme/ 中创建 antd-theme.ts,导出主题配置对象,引用 tokens.ts 中的设计变量。
|
||
组件级 token(如 Pagination.itemActiveColor / itemActiveBg)在 sharedComponents 中统一管理,
|
||
亮色/暗色主题共享,避免 CSS !important hack。
|
||
|
||
- 禁止在组件中硬编码颜色、字号等设计令牌,一律使用主题变量或 Tailwind 类名。
|
||
|
||
- 全局样式文件 globals.css 只引入 Tailwind 指令和必要的重置样式,不堆积组件样式。
|
||
|
||
# 事件与 IPC
|
||
|
||
- 所有 IPC 通道名称定义在 shared/constants/ipc-channels.ts 中,分为三类:
|
||
MAIN_TO_RENDERER / RENDERER_TO_MAIN / BIDIRECTIONAL。
|
||
|
||
- 渲染进程通过 src/shared/utils/bus.ts 中的安全守卫访问 IPC(hasIpc 检查 → safeIpcOn / send / invoke),
|
||
非 Electron 环境下静默跳过,保证浏览器预览不崩溃。
|
||
|
||
- 主进程事件处理函数需做好错误捕获与日志记录,渲染进程调用 IPC 时使用 try-catch 并处理超时。
|
||
|
||
- 窗口间通信走主进程转发:发送窗口 safeIpcSend,主进程接收后定位目标窗口并 webContents.send。
|
||
|
||
# 开发与构建命令
|
||
|
||
- 开发模式:npm run dev 或 yarn dev(同时启动 Vite 与 Electron,支持热重载)。
|
||
|
||
- 构建:npm run build 编译渲染进程 → 打包 Electron 应用。
|
||
|
||
- 代码检查:npm run lint(ESLint + Prettier,规则已配置为强制分号、单引号等)。
|
||
|
||
- 类型检查:npm run typecheck(tsc --noEmit)。
|
||
|
||
# 自动更新系统
|
||
|
||
## 架构
|
||
|
||
```
|
||
客户端 (Electron 主进程)
|
||
checkForUpdates()
|
||
├─ ① HeiXiuProvider.getLatestVersion() → 调用自有版本检查 API
|
||
│ └─ hasUpdate=false → 直接 IPC 通知 UI(不经过 electron-updater)
|
||
└─ ② hasUpdate=true → electron-updater.checkForUpdates()
|
||
├─ HeiXiuProvider.getLatestVersion() → TTL 缓存命中,不重复请求
|
||
├─ emit 'update-available'(含 releaseNotes)→ UI 展示版本信息
|
||
└─ 用户点击"下载更新" → downloadUpdate()
|
||
├─ 差分下载(对比 .blockmap,仅下载变更区块)
|
||
├─ SHA512 校验
|
||
└─ 下载完成 → 用户点击"安装更新" → NSIS/DMG/AppImage 执行
|
||
```
|
||
|
||
| 模块 | 位置 | 职责 |
|
||
|-------------------|-----------------------------------------------|------------------------------------------|
|
||
| `HeiXiuProvider` | `electron/main/updater.ts` | 自定义 Provider,调用自有 API,无更新返回 APP_VERSION |
|
||
| `checkForUpdates` | `electron/main/updater.ts` | 预检版本 → 无更新直发 IPC / 有更新交 electron-updater |
|
||
| `downloadUpdate` | `electron/main/updater.ts` | 用户确认后触发下载(从 checkForUpdates 分离) |
|
||
| `netRequest` | `electron/main/net-request.ts` | Electron net.request 的 axios 风格封装 |
|
||
| `useUpdater` | `src/shared/hooks/use-updater.ts` | 渲染进程更新状态 Hook(单例 IPC 监听) |
|
||
| `UpdateSetting` | `src/modules/settings/views/sections/UpdateSetting.tsx` | 设置页更新区块(含"查看详情"弹窗) |
|
||
| `server.py` | `test-server/server.py` | 本地测试服务器,扫描 release/ 目录,语义版本排序 |
|
||
|
||
## 增量更新(差分下载)
|
||
|
||
- `package.json` 配置 `"differentialPackage": true`,electron-builder 打包时自动生成 `.exe.blockmap` 文件
|
||
- 自定义 Provider 模式:`HeiXiuNsisUpdater extends NsisUpdater` / `HeiXiuMacUpdater extends MacUpdater` /
|
||
`HeiXiuAppImageUpdater extends AppImageUpdater`
|
||
- 三平台 Updater 均覆盖 `getUpdateInfoAndProvider()` 注入 `HeiXiuProvider`(自定义 API 版本检查),差分下载由
|
||
electron-updater 父类原生处理
|
||
- blockmap 文件流程:
|
||
- `scripts/generate-update-info.mjs` — 扫描 blockmap 文件,写 `blockmap.url` + `blockmap.size` 到 `update-info.json`
|
||
- `scripts/upload-oss.mjs` — 安装包 + blockmap 文件一并上传 OSS(支持 Node.js 直传和 ossutil 两种模式)
|
||
- `scripts/publish-release.mjs` — 通过 `--blockmap-url` 参数将 blockmap 信息发布到后端 API
|
||
- 效果:日常更新仅下载变更区块,预计节省 60%~90% 流量
|
||
|
||
## 环境变量加载机制(主进程)
|
||
|
||
加载顺序(后者覆盖前者,OS 环境变量始终优先):
|
||
|
||
```
|
||
① OS 环境变量(set KEY=VALUE && .\app.exe) ← 最高优先级
|
||
② .env.local(不进 git,个人覆盖用)
|
||
③ .env.{mode}(dev→.env.development,build→.env.production)
|
||
④ updater.ts 硬编码默认值(https://www.heixiu.com)
|
||
```
|
||
|
||
| 文件 | 何时生效 | 进 git | 用途 |
|
||
|--------------------|---------------------|:-----:|--------------------|
|
||
| `.env.development` | `npm run dev` | ✅ | 本地开发(API → 测试服务器) |
|
||
| `.env.production` | `npm run build:xxx` | ❌ | 构建时参考(API → 生产服务器) |
|
||
| `.env.local` | 始终(后加载) | ❌ | 个人特殊配置(打包后连本地测试) |
|
||
|
||
> `.env.production` 和 `.env.local` 不打包进 ASAR。打包后无这些文件时自动使用代码默认值。
|
||
|
||
## 测试工作流
|
||
|
||
```powershell
|
||
# 1. 构建两个版本
|
||
npm run build:win # → release/0.0.3/
|
||
# 改 package.json version = "0.0.4"
|
||
npm run build:win # → release/0.0.4/
|
||
|
||
# 2. 启动测试服务器
|
||
cd test-server && python server.py --release-dir ../release --port 8000
|
||
|
||
# 3. 启动旧版本客户端
|
||
set UPDATE_API_URL=http://127.0.0.1:8000 && .\release\0.0.3\win-unpacked\船长·HeiXiu.exe
|
||
# → 5 秒后自动检测到 0.0.4 → 差分下载 → 提示安装
|
||
```
|
||
|
||
相关文档:[UpdateA.md](UpdateA.md)、[test-server/README.md](test-server/README.md)
|
||
|
||
# 与其他 AI 协作的补充说明
|
||
|
||
- 所有生成的代码片段必须放在完整的文件上下文中,并注明所属文件路径。
|
||
|
||
- 若涉及跨平台逻辑,必须同时给出 Windows 与 macOS 的兼容实现或判断分支。
|
||
|
||
- 新增窗口时,需同步说明窗口入口、预加载脚本、主进程创建逻辑及 IPC 通道注册。
|
||
|
||
- 修改主题令牌时,需同步更新 tokens.ts、tokens.css 及 antd-theme.ts,确保三处一致。
|