Files
ClassHelper/packages/desktop-client/scripts/dist-win.mjs
T
LoveChengke 3a9f8812fc feat: 客户端灵动岛 + 上课时段紧急通知二次确认,并修复打包/校验脚本在 Electron 宿主下的多个缺陷
## 客户端灵动岛(动态岛)

- 新增独立透明置顶窗口:桌面中上方常驻、不占任务栏、不抢焦点(268x44 胶囊 / 404x316 详情 / 424x344 紧急)
- 三种形态与动画:隐藏 ↔ 胶囊 ↔ 详情,尺寸用 easeOutCubic 260ms 逐帧缓动、透明度单独淡入淡出
  (尺寸与淡入使用各自动画令牌,避免互相打断导致窗口卡在胶囊尺寸)
- 行为:普通通知变「新消息 · 共 N 条」胶囊,点击展开详情;上课时段自动隐藏并进入待发队列,
  下课后自动弹出;紧急通知无论是否上课立即展开(红色呼吸光晕 + 紧急角标,无需点击)
- 上课状态来自 GET /api/schedules/current(10s 心跳 + 下课时长定时器),
  新增 src/main/island.ts(控制器/IPC)、src/preload/island.ts、src/island/IslandApp.vue、
  src/renderer/island/bridge.ts、island.html 与多页 Vite 构建

## 上课时段发布紧急通知的二次确认

- 服务端:priority=URGENT 且当前处于上课时间段且未带 confirmDuringClass 时返回
  409 URGENT_DURING_CLASS(携带 current/next/week/serverTime),绕过界面调接口同样被拦截
- 新增 GET /api/schedules/current(inClass/current/next,at 参数用于诊断与测试)
- Web 管理端:全屏警告遮罩(UrgentClassWarning.vue),显示正在上的课时段与待发布标题,
  确认按钮带 3 秒倒计时(圆环进度,倒计时结束前禁用),取消即不发;并发命中 409 同样兜底弹窗

## 打包与验证脚本修复(Electron 宿主环境)

- scripts/lib/node-runtime.mjs:定位真正的 node.exe。此前经 pnpm 运行时 process.execPath 指向
  Electron,dist:server 会把 Electron 复制成安装包内的 node.exe,dist:win 会让 electron-builder 报
  Unknown argument: .../cli.js(改为显式使用真实 Node)
- scripts/lib/electron-env.mjs:启动 Electron 前清理继承的 ELECTRON_RUN_AS_NODE(否则 Electron 退化为
  纯 Node,require('electron') 直接 MODULE_NOT_FOUND),并从 path.txt 定位 electron.exe
  (process.versions.electron 存在时 import 'electron' 返回的是 API 对象而非路径)
- scripts/dist-server.mjs:新增构建产物新鲜度校验(曾出现 server 已重建但打进安装包的 shared/dist 是旧的,
  安装后服务启动即报 does not provide an export named MAX_TERM_WEEK)
- scripts/ui-smoke:新增 live-probe.mjs 真实制造/清理上课时段,Electron 驱动真实点击断言全屏警告文案、
  按钮禁用与 3 秒倒计时时长(实测 2966ms),测试后自动删除探针课表与测试通知

## 验证结果

- pnpm verify:e2e 62/62(新增上课时段拦截、二次确认后发布、普通通知不受限等 8 项)
- pnpm verify:web 8/8(新增上课时段紧急通知全屏警告 + 倒计时)
- pnpm verify:desktop 18/18(新增灵动岛窗口/上课隐藏/下课弹出/紧急立即展开/胶囊点击展开 + 截图)
- 打包后 EXE 冒烟 15/15(packaged: true,退出码 0)
- 服务端安装程序升级安装:.env 与数据库保持不变,/api/schedules/current 正常
- pnpm lint / format:check / typecheck 全部通过
2026-09-12 08:41:08 +08:00

72 lines
3.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Windows 打包入口(等价于 electron-builder --win,但做了三件工程化处理):
*
* 1) 复用本地已解压的 Electron:node_modules/electron/dist 存在时通过
* --config.electronDist 直接使用,避免再下载 150MB+ 发行包(受网络影响最大的步骤)。
* 2) 默认使用国内镜像(可用环境变量覆盖):
* ELECTRON_MIRROR Electron 发行包
* ELECTRON_BUILDER_BINARIES_MIRROR nsis / winCodeSign 等构建工具
* 3) 缓存固定到仓库内的 .cache 目录,便于清理与迁移,不污染用户全局目录。
*
* 用法:
* node scripts/dist-win.mjs # nsis 安装包 + portable 单文件
* node scripts/dist-win.mjs --dir # 仅生成免安装目录 release/win-unpacked
*/
import { spawn } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { resolveNodeRuntime } from '../../../scripts/lib/node-runtime.mjs';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const repoRoot = path.resolve(root, '..', '..');
const args = process.argv.slice(2);
const dirOnly = args.includes('--dir');
const passthrough = args.filter((item) => item !== '--dir');
const env = {
...process.env,
ELECTRON_MIRROR: process.env.ELECTRON_MIRROR ?? 'https://cdn.npmmirror.com/binaries/electron/',
ELECTRON_BUILDER_BINARIES_MIRROR:
process.env.ELECTRON_BUILDER_BINARIES_MIRROR ??
'https://npmmirror.com/mirrors/electron-builder-binaries/',
ELECTRON_BUILDER_CACHE:
process.env.ELECTRON_BUILDER_CACHE ?? path.join(repoRoot, '.cache', 'electron-builder'),
ELECTRON_CACHE: process.env.ELECTRON_CACHE ?? path.join(repoRoot, '.cache', 'electron'),
};
const localElectronDist = path.join(root, 'node_modules', 'electron', 'dist');
const overrides = [];
if (fs.existsSync(path.join(localElectronDist, 'electron.exe'))) {
overrides.push(`--config.electronDist=${localElectronDist}`);
console.log('[dist] 复用本地已解压的 Electron:', localElectronDist);
} else {
console.log('[dist] 未找到本地 Electron,将由 electron-builder 下载(镜像:' + env.ELECTRON_MIRROR + ')');
}
const cli = path.join(root, 'node_modules', 'electron-builder', 'out', 'cli', 'cli.js');
if (!fs.existsSync(cli)) {
console.error('[dist] 未找到 electron-builder,请先执行 pnpm install');
process.exit(2);
}
const cliArgs = [cli, '--win', ...(dirOnly ? ['--dir'] : []), ...overrides, ...passthrough];
console.log('[dist] electron-builder', cliArgs.slice(1).join(' '));
// 必须用真正的 node 执行 electron-builder CLI:
// 若宿主终端是 Electron(本机 pnpm 即如此),process.execPath 会指向 Electron,
// 直接 spawn 会让 Electron 把 cli.js 当成应用入口,多出一个位置参数,
// electron-builder 报 `Unknown argument: .../cli.js`。
const runtime = resolveNodeRuntime();
console.log(`[dist] Node 运行时:${runtime.path}(${runtime.version})`);
const child = spawn(runtime.path, cliArgs, { cwd: root, stdio: 'inherit', env });
child.on('exit', (code) => {
if (code === 0) {
const output = path.join(root, 'release');
console.log('\n[dist] 打包完成,产物目录:', output);
}
process.exit(code ?? 1);
});