feat: 初始化船长·HeiXiu 桌面应用项目(错误是svg的问题)

Electron + React 19 + TypeScript + Ant Design 6 + Tailwind CSS 4

  【核心架构】
  - Electron 主进程(窗口管理、平台检测、图标适配)
  - Vite 构建链(双产物 dist/ + dist-electron/)
  - 共享层 shared/(类型、常量、工具函数)
  - IPC 通信框架 + 安全守卫

  【主题系统】
  - 蓝紫渐变主题(亮色/暗色双模式)
  - Ant Design ConfigProvider 适配(lightAlgorithm / darkAlgorithm)
  - Tailwind CSS 4 @theme 指令 + CSS 变量
  - 设计令牌三处同步(tokens.ts / globals.css / antd-theme.ts)

  【导航栏】
  - 自定义 H5 导航栏替代系统菜单
  - 个人版/企业版双布局
  - 未登录拦截 + 事件总线驱动登录弹窗

  【登录注册】
  - Modal 弹层(Portal)+ Tabs 切换
  - 登录/注册表单动态字段渲染
  - 记住密码 / 自动登录 / 用户协议

  【更新系统】
  - 自定义 API 版本检查(替代 electron-updater generic provider)
  - 手动下载 + SHA512 校验 + spawn NSIS 安装
  - 渲染进程更新状态机 Hook(useUpdater)
  - 检查更新按钮 + 下载进度展示

  【构建工具链】
  - build.mjs 构建总管(--win/--mac/--linux --personal/--enterprise --build/--clean/--sign/--upload/--publish)
  - scripts/ 自动化脚本(clean / generate-update-info / upload-oss / publish-release)
  - electron-builder NSIS 安装器(可选路径、多版本打包)
  - update-info.json 自动生成(fileSize/sha512/changelog/releaseDate)

  【代码质量】
  - ESLint + TypeScript strict + Prettier
  - husky + lint-staged(提交前自动检查)
  - commitlint(规范提交信息)
  - Playwright E2E + Vitest 单元测试框架
  - rollup-plugin-visualizer 打包体积分析

  【文档】
  - README.md(快速开始 + 目录结构 + 命令速查)
  - UpdateA.md(发布手册:API/签名/发版流程/数据库)
  - CHANGELOG.md(更新日志)
  - .env.example(构建环境变量模板)
This commit is contained in:
2026-06-03 15:00:51 +08:00
commit 767bb8b297
95 changed files with 20814 additions and 0 deletions

136
src/hooks/use-updater.ts Normal file
View File

@@ -0,0 +1,136 @@
// ============================================================
// useUpdater — 渲染进程中的更新状态管理
//
// 用法:
// const { status, progress, checkForUpdates, installUpdate } = useUpdater();
//
// 状态机idle → checking → (no-update | available → downloading → downloaded → idle)
// ============================================================
import { useState, useEffect, useCallback, useRef } from 'react';
import { safeIpcInvoke, safeIpcOn, safeIpcOff, safeIpcSend } from '@/utils/ipc';
// ---------- 类型 ----------
export type UpdaterStatus =
| 'idle' // 空闲
| 'checking' // 正在检查
| 'no-update' // 已是最新
| 'available' // 发现新版本,准备下载
| 'downloading' // 下载中
| 'downloaded' // 下载完成,待安装
| 'error'; // 出错
export interface UpdateInfo {
version: string;
releaseDate?: string;
releaseNotes?: string;
forceUpdate?: boolean;
}
export interface UpdateProgress {
percent: number;
bytesPerSecond?: number;
transferred?: number;
total?: number;
}
export interface UpdateError {
message: string;
}
// ---------- IPC 通道名(与主进程 updater.ts 保持一致)----------
const CH = {
UPDATE_AVAILABLE: 'update-available',
UPDATE_PROGRESS: 'update-progress',
UPDATE_DOWNLOADED: 'update-downloaded',
UPDATE_ERROR: 'update-error',
UPDATE_NOT_AVAILABLE: 'update-not-available',
INSTALL_UPDATE: 'install-update',
CHECK_FOR_UPDATE: 'check-for-update',
} as const;
// ---------- Hook ----------
export function useUpdater() {
const [status, setStatus] = useState<UpdaterStatus>('idle');
const [info, setInfo] = useState<UpdateInfo | null>(null);
const [progress, setProgress] = useState<UpdateProgress>({ percent: 0 });
const [error, setError] = useState<UpdateError | null>(null);
// 防止 StrictMode 双重监听
const registered = useRef(false);
useEffect(() => {
if (registered.current) return;
registered.current = true;
safeIpcOn(CH.UPDATE_AVAILABLE, (_event: unknown, ...args: unknown[]) => {
setInfo(args[0] as UpdateInfo);
setStatus('available');
});
safeIpcOn(CH.UPDATE_PROGRESS, (_event: unknown, ...args: unknown[]) => {
setStatus('downloading');
setProgress(args[0] as UpdateProgress);
});
safeIpcOn(CH.UPDATE_DOWNLOADED, () => {
setStatus('downloaded');
});
safeIpcOn(CH.UPDATE_ERROR, (_event: unknown, ...args: unknown[]) => {
setError(args[0] as UpdateError);
setStatus('error');
});
safeIpcOn(CH.UPDATE_NOT_AVAILABLE, () => {
setStatus('no-update');
});
return () => {
safeIpcOff(CH.UPDATE_AVAILABLE, () => {});
safeIpcOff(CH.UPDATE_PROGRESS, () => {});
safeIpcOff(CH.UPDATE_DOWNLOADED, () => {});
safeIpcOff(CH.UPDATE_ERROR, () => {});
safeIpcOff(CH.UPDATE_NOT_AVAILABLE, () => {});
};
}, []);
/** 手动检查更新 */
const checkForUpdates = useCallback(async () => {
setStatus('checking');
setError(null);
try {
await safeIpcInvoke(CH.CHECK_FOR_UPDATE);
} catch (err) {
setStatus('error');
setError({ message: (err as Error).message || '检查更新失败' });
}
}, []);
/** 确认安装更新(退出 → 安装 → 重启) */
const installUpdate = useCallback(() => {
safeIpcSend(CH.INSTALL_UPDATE);
}, []);
/** 重置状态 */
const reset = useCallback(() => {
setStatus('idle');
setInfo(null);
setProgress({ percent: 0 });
setError(null);
}, []);
return {
status,
info,
progress,
error,
checkForUpdates,
installUpdate,
reset,
} as const;
}