fix: 大屏背景图被兜底渐变盖住 + 配置/规则也搬到用户数据目录

1) 大屏背景图不生效:App.vue 里 .bg 与 .bg-fallback 都是 absolute inset:0,
   而兜底渐变是不透明的且在图片之后绘制 → 配了背景图也只看得到渐变。
   改成 v-if/v-else 互斥(用无头 Chromium 真渲染对照验证:修复后取到图片像素,
   回退版本只剩渐变)。新增 background-layer.test.ts 锁住层次关系。

2) 规则/config 仍在安装目录,覆盖安装/卸载会连配置一起删:现在三样都落在
   %LOCALAPPDATA%\ScoreboardConsole\(与数据目录同一基座):
   - console\config.json(Console 读写都用这份)
   - config.yaml(Console 用绝对路径 -config 传给服务端与 passwd)
   - rules\(首次从安装目录复制过去,之后只认用户目录那份)
   首次运行复制迁移、已存在不覆盖、不可写则回落安装目录并记日志(不影响启动);
   开发态行为不变。备份脚本与文档同步。
   注意 resolveExecutable 必须显式传部署包根目录,否则会去用户目录找 scoreboard.exe。

自检:服务端 11 包全绿;console 语法 16 文件 + node:test 20/20;
前端 vue-tsc + build、node --test 69/69。
This commit is contained in:
AskaEth committed 2026-09-10 10:06:21 +08:00
1 parent 2d207fd654
commit 407abec3b0
15 files changed
+759 -81

No files matched your search

+42 -15
View File
@@ -16,12 +16,14 @@
```
console/
├── package.json # main 指向 main/index.js;electron-builder 配置
├── config.json # 运行配置(端口 / exe 路径 / 选屏策略)
├── config.json # 运行配置模板(打包后首次启动复制到用户数据目录,改这里的只在开发态生效)
├── main/
│ ├── index.js # 应用入口:窗口 / 托盘 / IPC / 生命周期
│ ├── config.js # 配置加载与合并、exe 路径解析
│ ├── datadir.js # 数据目录解析 + 旧数据一次性迁移
│ ├── datadir.test.js # 数据目录的单元测试(node --test main/datadir.test.js)
│ ├── datadir.test.js # 数据目录的单元测试
│ ├── userpaths.js # 配置/规则目录解析 + 首次复制迁移(config.json / config.yaml / rules)
│ ├── userpaths.test.js # 配置/规则路径的单元测试
│ ├── service.js # 服务端子进程守护:spawn / 日志转发 / 健康检查 / 自动重启
│ ├── instances.js # 已连接大屏实例看板:复用管理面板登录态读 /api/display/instances
│ ├── account.js # 管理员账号重置:参数校验 + 调 scoreboard.exe passwd
@@ -60,16 +62,16 @@ pnpm start # 等价于 electron .
scoreboard-windows-amd64/
├── Scoreboard_Console.exe
├── resources/app.asar # 内含 main/ renderer/ config.json
├── config.json # exe 旁边的可编辑配置
├── config.json # exe 旁边的配置模板(首次启动复制到用户数据目录)
├── scoreboard.exe
├── config.yaml
├── rules/
└── logs/console.log # 运行时生成
├── config.yaml # 服务端配置模板(同上)
├── rules/ # 规则包模板(同上)
└── logs/console.log # 运行时生成(日志仍留在安装目录)
```
数据不在安装目录里,默认落在 `%LOCALAPPDATA%\ScoreboardConsole\data`(见下文「数据目录」)。
数据、配置与规则都不在安装目录里,都在 `%LOCALAPPDATA%\ScoreboardConsole\` 下(见下文两节)。
打包后 `config.json` 读取顺序:`SCOREBOARD_CONSOLE_CONFIG` 环境变量 → exe 同目录的 `config.json`。
打包后 `config.json` 读取顺序:`SCOREBOARD_CONSOLE_CONFIG` 环境变量 → 用户数据目录 `<用户根>\console\config.json`(首次启动从 exe 旁边的模板复制)。开发态(`pnpm start`)三者都不接管,仍用仓库里的 `console/config.json`。
## 数据目录(重要)
@@ -89,6 +91,28 @@ electron-builder 生成的卸载器结尾是 `RMDir /r $INSTDIR`,而**覆盖
改完 `datadir.js` 跑 `node --test main/datadir.test.js`(Node 内置测试运行器,无额外依赖)。
## 配置与规则目录(重要)
同样因为卸载器会删掉整个安装目录,随包的 `config.json`、`config.yaml`、`rules\` 也不放在安装目录里,而是跟数据目录同一基座:
| 内容 | 打包态(`planUserPaths` mode=`user`) | 开发态(mode=`install`) |
| --- | --- | --- |
| Console 配置 | `<用户根>\console\config.json` | 仓库 `console/config.json` |
| 服务端配置 | `<用户根>\config.yaml` | 仓库 `config.yaml` 的位置不变 |
| 规则目录 | `<用户根>\rules\` | 仓库 `rules/` |
(`<用户根>` = `%LOCALAPPDATA%\ScoreboardConsole`,与数据目录同一处。)
- `userpaths.js` 只做三件事:解析这三个落点、首次复制、失败回落;`planUserPaths` 是纯函数(只算路径),`prepareUserPaths` 才碰磁盘,便于单测;
- **首次运行复制**:目标不存在且安装目录里有那份模板 → `fs.copyFileSync` / `fs.cpSync`(规则目录先拷到 `<target>.tmp-<pid>` 再整体 rename,失败不会留下半份)。安装目录那份**留作模板**,不做移动;
- **已存在就绝不覆盖**:用户目录里已经有的那份一律优先,既不覆盖也不删除(用户改过的配置、自己加的规则包都不会被模板顶掉);
- 拉起服务端时 `-config` 的值被换成 `<用户根>\config.yaml` 的绝对路径(`withConfigArg`:参数里有 `-config` 就改值,没有就补一条),`passwd` 用同一份;这样服务端读的配置不再取决于 cwd;
- 任一项目标目录建不出来或不可写时只记日志并**回落到安装目录里那份**(`serverConfig` 回落时返回 `null`,拉起服务端时沿用 `server.args` 原值),绝不抛异常、绝不删用户文件;
- 拿不到 `%LOCALAPPDATA%` 时三项全回落安装目录并记一条警告;
- 日志文件 `logs/console.log` 是个例外:仍然相对**部署包根目录**(打包态 = 安装目录,开发态 = `console/`)解析,不跟着配置搬(易失的排查材料,搬了反而与文档、现场习惯不一致)。
改完 `userpaths.js` 跑 `node --test main/*.test.js`。
## 行为说明
| 场景 | 行为 |
@@ -101,6 +125,7 @@ electron-builder 生成的卸载器结尾是 `RMDir /r $INSTDIR`,而**覆盖
| 服务端就绪 | 首次 `/api/health` 通过后开大屏窗口;等待超过 `display.waitForServerMs` 也先开,避免现场只见桌面 |
| 服务端崩溃 | 按 `restartDelayMs` 递增退避自动重启(`maxRestarts=0` 为无限);持续健康 `healthyResetMs` 后崩溃计数清零;恢复健康后自动重载大屏 |
| 数据目录 | 默认 `%LOCALAPPDATA%\ScoreboardConsole\data`(安装目录之外,覆盖安装/卸载都不删),启动时按 `-data <绝对路径>` 交给服务端;打包态首次启动做一次性迁移(见「数据目录」一节),开发态不接管 |
| 配置与规则 | 同样在 `%LOCALAPPDATA%\ScoreboardConsole\`(`console\config.json`、`config.yaml`、`rules\`),打包态首次从安装目录复制、之后只用用户目录那份;拉起服务端时 `-config` 传绝对路径(见「配置与规则目录」一节) |
| 停止服务 | 先 `POST server.shutdownPath` 并带 `X-Shutdown-Token`(启动时用 `crypto.randomBytes(32)` 生成,经环境变量 `SCOREBOARD_SHUTDOWN_TOKEN` 只交给该子进程)请求服务端优雅关闭;受理后最多等 `shutdownTimeoutMs`,超时或接口不可用(404/连接失败)再强杀 |
| 已连接大屏实例 | 管理窗口打开时每 5 秒读一次服务端 `GET /api/display/instances`(复用管理面板的 `scoreboard_session` Cookie,需记录员及以上),显示实例数、页面名与在线状态;窗口隐藏即停表,未登录/服务未启动时显示占位文案 |
| 关管理窗口 | 只隐藏到托盘,服务端继续运行;退出必须走托盘「退出」 |
@@ -109,15 +134,16 @@ electron-builder 生成的卸载器结尾是 `RMDir /r $INSTDIR`,而**覆盖
| 服务端管理面板 | 用独立无边框 Electron 窗口加载 `http://127.0.0.1:<port>/admin`(`shell-preload.js` 暴露 `window.desktopShell`),不再调系统浏览器;面板页在浏览器里直接打开时标题栏自动不渲染 |
| 显示器菜单 | 面板页的「大屏控制 → 显示器」卡片经 `display:list` / `display:open` / `display:close` / `display:relayout` 指挥本机开窗:按屏创建、聚焦、收窗。**全屏只在「多于一块屏 + 非主屏」时放行**,判定在主进程做,页面传 `fullscreen: true` 也会被降级为窗口化;面板页只拿到这几条白名单通道,拿不到 `ipcRenderer` |
| 管理员账号 | 主进程 `spawn` 同目录 `scoreboard.exe passwd -config <同一份配置> -data <同一个数据目录> -user <当前用户名> -new-username <新用户名> -password <新密码>`(留空 = 不改 / 随机生成)。本机进程、不经 HTTP,因此免登录验证;用户名/密码含空格、制表符或换行会被主进程拒绝,密码不进日志。`passwd` 会作废该用户全部会话,UI 提交前二次确认并提示 |
| 日志 | 内存环形缓冲(`log.maxLines`)+ 文件 `logs/console.log`(超 `maxFileBytes` 轮转一份 `.1`,轮转前会删掉旧 `.1` 以兼容 Windows) |
| 日志 | 内存环形缓冲(`log.maxLines`)+ 文件 `logs/console.log`(超 `maxFileBytes` 轮转一份 `.1`,轮转前会删掉旧 `.1` 以兼容 Windows);文件固定落在部署包根目录(打包态 = 安装目录),不随配置搬到用户数据目录 |
| 服务管理窗口的路径显示 | 「服务端程序」「配置文件」两行分别是解析出的 `scoreboard.exe` 与 Console 自己那份 `config.json` 的绝对路径(打包态后者在用户数据目录里,见上一节) |
## 配置项(`config.json`)
## 配置项(`config.json`;打包态生效的是用户数据目录里那份)
| 键 | 默认值 | 说明 |
| --- | --- | --- |
| `server.exe` | `scoreboard.exe` | 相对路径按上文顺序探测,也可写绝对路径 |
| `server.args` | `["-config","config.yaml"]` | 传给服务端的参数 |
| `server.dataDir` | `""` | 数据目录;空串 = 用默认位置(打包态 `%LOCALAPPDATA%\ScoreboardConsole\data`),填了就按它来(相对路径以安装目录为基准)。Console 会把它作为 `-data <绝对路径>` 追加到服务端参数后 |
| `server.exe` | `scoreboard.exe` | 相对路径会以部署包根目录(打包态 = exe 所在目录,开发态 = `console/`)、当前目录、上级目录、`dist\` 依次探测,也可写绝对路径 |
| `server.args` | `["-config","config.yaml"]` | 传给服务端的参数;其中 `-config` 的值会被换成解析出的绝对路径(`<用户根>\config.yaml`),其余参数(如 `-project`)原样传 |
| `server.dataDir` | `""` | 数据目录;空串 = 用默认位置(打包态 `%LOCALAPPDATA%\ScoreboardConsole\data`),填了就按它来(相对路径以部署包根目录为基准)。Console 会把它作为 `-data <绝对路径>` 追加到服务端参数后 |
| `server.host` / `server.port` | `127.0.0.1` / `19540` | 健康检查、关闭握手与管理面板地址(`0.0.0.0` 会自动换成回环地址探测) |
| `server.autoStart` | `true` | Console 启动后是否立刻拉起服务端 |
| `server.healthIntervalMs` | `5000` | 健康检查间隔 |
@@ -137,7 +163,7 @@ electron-builder 生成的卸载器结尾是 `RMDir /r $INSTDIR`,而**覆盖
| `display.backgroundColor` | `#0b0f1a` | 窗口底色,减少加载白闪 |
| `startup.openAtLogin` | `false` | 默认注册但不启用(任务管理器可见、已禁用);在服务管理窗口「开机自启」开关里控制(开发态不注册) |
| `admin.width` / `admin.height` | `1040` / `760` | 管理窗口初始尺寸(服务端管理面板窗口按 `width` 不小于 1100 取用) |
| `log.maxLines` / `log.file` / `log.maxFileBytes` | `2000` / `logs/console.log` / `4194304` | 日志缓冲与文件 |
| `log.maxLines` / `log.file` / `log.maxFileBytes` | `2000` / `logs/console.log` / `4194304` | 日志缓冲与文件;`log.file` 相对部署包根目录解析(不随配置搬到用户数据目录) |
配置缺项自动用内置默认值,文件不存在或 JSON 语法错误时回落到全默认值,并在管理窗口顶部给出提示。
@@ -154,7 +180,7 @@ pnpm pack # 只出目录,不生成安装包,便于现场调试
```
- 产物:NSIS 安装包 + zip(`Scoreboard Console-0.1.0-x64.*`)。
- `extraFiles` 会把 `../dist/scoreboard.exe`、`../config.yaml`、`config.json`(exe 旁的可编辑配置,缺失时 Console 会回落到内置默认值)与 `../rules` 拷到安装目录,最终形成第 4 节里的部署包形态。
- `extraFiles` 会把 `../dist/scoreboard.exe`、`../config.yaml`、`config.json`、`../rules` 拷到安装目录 —— 这四样里的后三样(`config.json`、`config.yaml`、`rules/`)在打包态只当**模板**:首次启动时复制到用户数据目录,之后 Console 与 `backup.bat` 都只用用户目录那份(见「配置与规则目录」一节)。`config.json` 缺失时 Console 会回落到内置默认值。
- `使用说明.txt` 是可选的:`deploy/build.bat` 在打包前依次找 `docs/使用说明.txt`、`docs/使用手册.md`,命中就暂存到 `console/extra/使用说明.txt`(`extraFiles` 会把该目录内容平铺到部署包根目录),都没有就跳过并在构建日志里注明。
- 图标在 `resources/icon.ico`(`package.json` 的 `win.icon` 指向它)。
- 主程序文件名由 `win.executableName` 决定,现在是 `Scoreboard_Console.exe`;`productName` 仍是 `Scoreboard Console`(显示名、`userData` 目录不变)。
@@ -171,4 +197,5 @@ pnpm pack # 只出目录,不生成安装包,便于现场调试
- 托盘图标在 Windows 任务栏的显示效果(16px 缩放)。
- `scoreboard.exe` 真实拉起、日志转发、健康检查与崩溃重启的端到端链路。
- 数据目录迁移实机路径:老版本安装目录里的 `data\` 是否被整体搬到 `%LOCALAPPDATA%\ScoreboardConsole\data`、跨盘安装(安装目录在 D 盘、`%LOCALAPPDATA%` 在 C 盘)时的失败日志与「旧数据原地保留」表现。
- 配置/规则首次复制实机路径:老版本升级到本版本后,安装目录里的 `config.json` / `config.yaml` / `rules\` 是否被复制到 `%LOCALAPPDATA%\ScoreboardConsole\` 下对应位置、改过配置后覆盖安装是否仍然生效、`%LOCALAPPDATA%` 不可写(或被杀毒软件拦截)时是否只记日志并回落到安装目录里那份。
- electron-builder 打包已通过 `deploy\build.bat` 实跑;NSIS 安装包在真机上的安装 / 卸载流程尚未验证。