feat: 分批发布 + 实时赛况页 + 显式批次层 + 成绩框实时提示 + 窄屏适配 + 版本号单一真相

## 分批发布(现场:100 米跑道只有 8 条,不能等所有人跑完才发)
- 空成绩从"错误"降级为"提示",不再阻塞发布;真会算错(写法无法识别、秒位 ≥ 60、
  手动名次缺名次)仍挡住。发布确认框说明"只发已填的 M 人,其余保持未发布"。
- 服务端核实:publishResults/computeEventScore 本就不会因缺成绩报错
  (collectCandidates 直接跳过没有 results 的报名项),根因在前端防呆。

## 实时赛况(新大屏页 + 定向接口)
- 新增 `GET /api/display/live`(guest 可读):返回「当前项目」判定 + 该项目**全量**已发布成绩
  (不受快照 latestResults 的 20 行截断)。回退链:manual 指定 > 排期命中 > 最近发布。
  设置键 display.live_events / display.live_source。
- 大屏新页 `live`「实时赛况」:1 个项目单栏、2 个自动左右分栏;失败保留上一次画面
  (下一场开始前持续显示上一场);30s 轮询(判定依赖当前时间)+ 快照变更触发。
- 管理端「大屏控制」新增「实时赛况」卡;该卡走 PUT /api/settings(admin-only),
  故对非管理员整组置灰 + 提示条(不新增角色)。

## 显式批次层(组别之下)
- 迁移 0019 `entries.heat`;批次仅是录入/展示分组,**不参与算分**(有"分批与不分批结果
  完全一致"的断言守住)。批量设置接口 + 导入 CSV 可选列 heat + 录入页批次下拉 +
  一键均分(按 N 人切片,已有批次号接着往后排)。

## 成绩输入框实时悬浮提示
- 聚焦格旁的浮动气泡:pointer-events:none、不占布局不改行高、贴底自动上翻;
  合法显示解析结果(1:06 → = 66 秒)、可疑/不合法显示原因并带那一格原文;
  与发布防呆共用同一套纯函数(entryGuards.ts),口径不会分叉。

## 窄屏 / 手机端布局
- 真浏览器(Playwright + chromium)在 1440/1024/768/390 四宽度逐页审计 + 溢出探针,
  四轮迭代修复;桌面端 1440 前后像素差异 < 0.25%(无回归);修掉如「修改密码」输入框
  塌成 22px 这类真问题。

## 版本号单一真相
- 新增仓库根 `VERSION`:构建时注入二进制(-X main.version)并同步 console/package.json
  的版本(产物名跟随)。源码里不再有人工维护的版本号(兜底值改为 `dev`,故意不写数字)。

测试:服务端 13 包全绿;前端 vue-tsc 通过、249 例全过;Console 18 文件。
docs/ 与 devdocs/ 同步(FAQ 到 Q80、新增 devdocs/16 自定义大屏模板设计文档)。
This commit is contained in:
AskaEth committed 2026-09-11 04:05:02 +08:00
1 parent e5e0944d26
commit 08a5740fb2
64 files changed
+4968 -270

No files matched your search

+7 -7
View File
@@ -32,11 +32,11 @@ deploy\build.bat
| 产物 | 类型 | 说明 |
| --- | --- | --- |
| `dist\scoreboard.exe` | 服务端 | 前端已通过内嵌方式打进可执行文件,单独运行也能提供全部页面 |
| `dist\console\Scoreboard Console-0.1.0-x64.exe` | 安装包 | NSIS 安装程序,双击按向导安装 |
| `dist\console\Scoreboard Console-0.1.0-x64.zip` | 免安装包 | 解压即用 |
| `dist\console\Scoreboard Console-0.1.1-x64.exe` | 安装包 | NSIS 安装程序,双击按向导安装 |
| `dist\console\Scoreboard Console-0.1.1-x64.zip` | 免安装包 | 解压即用 |
| `dist\console\win-unpacked\` | 目录 | 未压缩的运行目录,便于现场调试 |
> 文件名中的 `0.1.0` 来自 `console\package.json` 的版本号,会随版本升级变化;以 `dist\console\` 下的实际文件名为准。
> 版本号来自仓库根的 `VERSION` 文件:构建时由 `deploy\build.bat` 注入服务端二进制(`-ldflags -X main.version`,运行期不再读任何文件),并同步到 `console\package.json` 决定安装包/免安装包的文件名。升级版本只改 `VERSION` 一处;以 `dist\console\` 下的实际文件名为准。
>
> 目标机器**不需要** Go / Node / pnpm,也不联网;上述工具只用于构建机。
>
@@ -48,7 +48,7 @@ deploy\build.bat
### 3.1 使用安装包
1. 双击 `Scoreboard Console-0.1.0-x64.exe`。
1. 双击 `Scoreboard Console-0.1.1-x64.exe`。
2. 按向导选择安装目录(可自定义)。
3. 安装完成后,桌面会出现名为**「赛事积分榜」**的快捷方式。
4. 双击「赛事积分榜」启动。
@@ -57,7 +57,7 @@ deploy\build.bat
### 3.2 使用免安装包
1. 把 `Scoreboard Console-0.1.0-x64.zip` 解压到任意目录(建议使用纯英文路径)。
1. 把 `Scoreboard Console-0.1.1-x64.zip` 解压到任意目录(建议使用纯英文路径)。
2. 双击目录里的 `Scoreboard_Console.exe` 启动。
### 3.3 首次启动会自动完成
@@ -295,7 +295,7 @@ scoreboard.exe passwd -config "%LOCALAPPDATA%\ScoreboardConsole\config.yaml" -da
| `server.healthyResetMs` | `60000` | 连续健康多久后清零崩溃计数 |
| `display.selection` | `all-secondary` | 选屏策略,见第 7 节 |
| `display.displays` | `[]` | 仅 `specified` 策略生效,填显示器 id 或 0 起序号 |
| `display.pages` | `["standings","medals","individual","results"]` | 多屏时按屏序轮转锁定的页面 |
| `display.pages` | `["standings","medals","individual","results"]` | 多屏时按屏序轮转锁定的页面;可加入 `live`(实时赛况)等页面值,见 7.4 |
| `display.pageParam` | `page` | 页面 URL 参数名 |
| `display.kiosk` | `true` | 多屏时是否 kiosk 全屏(单屏恒为窗口化) |
| `display.ignoreMouse` | `true` | 多屏窗口是否忽略鼠标 |
@@ -351,7 +351,7 @@ scoreboard.exe passwd -config "%LOCALAPPDATA%\ScoreboardConsole\config.yaml" -da
- 除内置屏外的每块屏各开一个 kiosk 全屏窗口;
- 每块屏**锁定一个页面**,按 `display.pages` 的顺序轮转分配;
- 多屏 kiosk 不响应 F11,也不接收鼠标(`ignoreMouse`),也不显示任何调试标识;
- 可用的页面值:`standings`(总积分榜)、`medals`(团体奖牌榜)、`individual`(个人积分奖牌榜)、`results`(分项成绩)、`announcement`(公告);管理端切页用的是同一批页面的规范名 `team-medals` / `athlete-standings` / `event-results`,两种写法大屏都认。
- 可用的页面值:`standings`(总积分榜)、`medals`(团体奖牌榜)、`individual`(个人积分奖牌榜)、`results`(分项成绩)、`live`(实时赛况)、`announcement`(公告);管理端切页用的是同一批页面的规范名 `team-medals` / `athlete-standings` / `event-results`,两种写法大屏都认。把 `live` 加进 `display.pages`,就能把某块屏固定成「实时赛况」(`live` 也认 `live-results` / `实时赛况` / `实时` / `赛况` 这些别名,与其它页一样)。
### 7.5 在管理端单独控制某块屏