AskaEth
|
793d2ac064
|
fix(console): 修「全屏时反而画标题栏」——窗口模式必须与 URL 的 windowed 一致
用户实机反馈:单屏窗口化窗口按 F11 全屏后标题栏不消失,退回窗口化反而没标题栏,
表现与预期完全相反。
根因不在标题栏,而在 Console 侧的窗口模式管理:entry 没记录窗口是以什么模式建的,
layout() 每次排布都拿「整机是否单屏」的 single 去覆盖每个已存在窗口的几何与模式
(!single 时会 win.setKiosk(true) + 铺满),而页面 URL 里的 windowed=1 是建窗时定死的、
loadPage 只在页码变化时才重载 —— 于是窗口的真实模式与页面认知脱钩:
窗口化窗口被改成 kiosk 全屏而 URL 仍带 windowed=1(全屏却画标题栏),反向亦然。
修复:建立并保证「窗口几何/模式 ↔ URL 的 windowed 必须永远一致」
- entry 记 { win, page, single, manual, reloadTimer }
- createWindow(..., manual=false);openOn() 传 manual=true(手动开的窗口不被排布改模式)
- layout() 的 existing 分支:target = manual ? entry.single : single,applyGeometry 与
loadPage 都用同一个 target,且 modeChanged || page 变了都要重载 URL
- 删掉 isKioskWindow()(用 isFullScreen() 反推会把「窗口化窗口被 F11 全屏」误报成 kiosk),
换成 modeOf(entry)
- F11 通信时序:enter/leave-full-screen 回调里不再回读 win.isFullScreen()(切换瞬间可能读到
旧值,页面会收到相反状态),改为下发事件本身的确定值;F11 直接下发算出来的 next
回归测试 console/main/displays.test.js(8 例,assertModeConsistent 四处自洽)。
反向对照:修复前的 displays.js 跑同一份测试是 1 pass / 7 fail。
Console 语法检查 18 个文件、node --test 28 例全过。
|
2026-09-11 00:13:09 +08:00 |
|
AskaEth
|
fc96d17660
|
feat: 字体可配并随赛事包打包 + 大屏调试标识收敛 + 报名表 CSV 编码兼容
三块改动,均为现场反馈:
一、字体配置与随包打包(此前「没有入口可以配置字体」,导出包也不带字体)
- 服务端新增 httpapi/fonts.go 与 4 条路由:GET /api/fonts(recorder)、
POST /api/fonts(admin,multipart file)、DELETE /api/fonts/{name}(admin)、
GET /uploads/fonts/{name}(匿名——大屏 @font-face 不带会话)
- 上传限 ttf/otf/woff/woff2、单个 20 MiB;文件名经 safeFontName 收敛且需与
fontContentType 往返一致,挡住路径穿越;重名 409、删除被引用字体 409
- 新增设置键 display.font_title / display.font_body(DTO fontTitle/fontBody,
随快照下发给大屏),写入时校验取值合法
- 导出:接上一直没被赋值的 IncludeFonts/FontsDir;包内落点用 uploads/fonts/
(运行时只读这里,落顶层 fonts/ 等于没人读)
- 大屏按配置动态注册字体并挂 --display-font-title/body,加载失败静默回落默认字体
- 管理端「赛事信息」新增字体区块:标题/正文字体下拉、上传、字体库、实时预览
二、大屏显示收敛
- 数据版本号角标 vN 彻底移除(原先只有窗口化窗口会显示,两处显隐口径不一致)
- 窗口化窗口按 F11 全屏后自绘标题栏不让位:根因是 Console 用 win.setFullScreen()
做窗口级全屏,而页面用 document.fullscreenElement 判断,后者恒为 null;
新增 display-preload.js 下发窗口级全屏状态,页面侧 shell.ts 把两级全屏取或,
无 preload 时(浏览器直连 /display)回落到文档级监听
三、报名表 CSV 编码兼容(用户报「解析中文字符遇到编码问题」)
- 服务端不再把原始字节直接喂给 csv.Reader:先摘 UTF-8 BOM,再认 UTF-16 LE/BE
BOM,其余非合法 UTF-8 按 GB18030 解(GBK 是其子集)
- 修复前症状:GBK 文件(Excel 另存为 CSV 的默认编码)中文变乱码;带 BOM 的文件
(Excel「CSV UTF-8」,也正是我们自己模板的格式)把 BOM 拼进第一列表头,
报「报名表缺少列 event_code」——连「下载模板→填→传回来」都走不通
- golang.org/x/text 由 indirect 提升为直接依赖,未新增外部依赖
测试:服务端 12 包全绿(新增 fonts_test.go / fonts_internal_test.go /
handlers_import_encoding_test.go);前端 vue-tsc 通过、pnpm build 通过、157 例全过;
Console 语法检查 17 个文件通过。docs/ 与 devdocs/ 同步更新。
|
2026-09-10 17:45:11 +08:00 |
|
AskaEth
|
407abec3b0
|
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。
|
2026-09-10 10:06:21 +08:00 |
|
AskaEth
|
005b51b2ec
|
fix: 数据目录移出安装目录,修掉「覆盖安装/卸载即丢数据」
根因:数据目录是 <安装目录>\data(config.yaml 的 dataDir 相对服务端工作目录),
而 electron-builder 的卸载器结尾执行 RMDir /r $INSTDIR,且覆盖安装会先跑旧卸载器
(installSection.nsh:52 的 uninstallOldVersion)→ 赛事库/规则/用户/审计/上传图片
连同安装目录被整个删除,再装新版。卸载一次同样如此。
- 服务端新增 -data <dir>(主命令、backup、passwd 三处),非空时覆盖 config.yaml 的
dataDir;不传时行为完全不变(兼容便携/手工运行)
- Console 把数据指到当前用户目录 %LOCALAPPDATA%\ScoreboardConsole\data(卸载器不会碰
这里;没有用 app.getPath('userData'),因为 %APPDATA%\<AppName> 正是被删的那个)
- 一次性迁移:目标为空且 <安装目录>\data 有数据时整体改名过去;两边都有数据时不动作
只记警告;任何失败都保留原地数据、绝不删除
- passwd 也带 -data(否则重置的是安装目录下那个空库里的账号)
- deploy/backup.bat 指向同一数据目录
- 文档:部署指南新增「数据放在哪里(升级/卸载都不会删)」+ 备份/恢复/迁移路径改写,
上线检查清单与使用手册同步
自检:服务端 12 包全绿;console 语法 14 文件 + datadir 单测 11/11;backup.bat 保持纯 ASCII。
|
2026-09-10 09:17:56 +08:00 |
|
AskaEth
|
6483acf9ad
|
feat: 人员年龄/号码布、图片上传、多显示器菜单、Console 两栏、exe 改名等一批改动
人员与报名:
- 人员唯一标识改为「证件号 或 号码布」二选一(0013/0014 迁移 + 导入匹配/回填/冲突)
- 人员新增「年龄」(0015 迁移):表单/列表/导入模板表头 11 列;冲突比较只在两边
都有值且不同时才算差异,旧数据不误报
- 冲突决策键改为「证件号优先 → 号码布」(前端原先只用 idCard,号码布行会互相覆盖)
- 报名选择器支持无证件号但有号码布的运动员
大屏与 Console:
- 大屏控制新增「显示器」菜单:可给指定显示器重建全屏窗口;单屏/主显示器强制窗口化,
限制在 Console 侧判定,不信任页面传参
- Console 服务管理窗口改左右两栏,运行日志独占右栏满高;窗口默认尺寸 1280×860
- 大屏右上角时钟精确到秒(同时把刷新间隔 15s 改为 1s)
图片与配置:
- 新增图片上传 POST /api/uploads(recorder+,png/jpg/webp/gif,≤5MiB)与匿名静态
访问 GET /uploads/{name}(防目录穿越);文件落赛事项目目录 uploads/,随赛事包
一起导出/导入
- 「赛事信息」新增赛事会标 / 大屏会标 / 大屏背景图三个上传项
- 参赛单位 logo 输入框右侧加上传按钮
关于页与构建:
- 构建时间打通:version.BuildTime + /api/system/about 返回 + build.bat 注入
(用 PowerShell 取时间戳,避免本地化 %DATE% 的非 ASCII 星期)
安装包:
- 主程序改名 Scoreboard_Console.exe(win.executableName;productName/显示名不变)
- 记住上次安装目录:自定义 NSIS 宏优先沿用自存路径(卸载不清除),并在 customInit
里补写回 electron-builder 的键,避免安装模式页把 $INSTDIR 打回默认
自检:服务端 12 包全绿;前端 vue-tsc + build 通过、node --test 58/58;console 12 文件通过。
|
2026-09-10 09:05:06 +08:00 |
|
AskaEth
|
0c9a6b6a27
|
fix(console): 开机自启改为「注册但不启用」,开关切换 enabled
上一版把「关闭」实现成不写登录项。实际在 Windows 上实测 Electron:
setLoginItemSettings({ openAtLogin: true, enabled: false })
→ openAtLogin=true、launchItems[0].enabled=false、executableWillLaunchAtLogin=false
即登录项已注册(任务管理器「启动」里可见、显示为已禁用)但不会随系统拉起。
所以改成始终注册登录项、只用 enabled 控制是否真正启动,语义与用户要求一致:
- applyOpenAtLogin / setOpenAtLogin 都走 loginItemSettings(wanted)
- macOS 无 enabled 参数,仍按注册/注销处理
- 状态快照的 openAtLogin 以系统实际状态为准(打包后读 executableWillLaunchAtLogin)
- 文档同步:默认「已注册但未启用」
|
2026-09-10 00:09:49 +08:00 |
|
AskaEth
|
1b78c38bb8
|
feat(console): 开机自启默认关闭,服务管理窗口加手动开关
- DEFAULTS.startup.openAtLogin 与随包 config.json 默认改为 false;
applyOpenAtLogin 只有显式 true 才注册登录项(缺省/null/false 都不注册)
- config.js 新增 updateConfig():只写回文件里已有的键 + 目标键,不把 DEFAULTS 落盘
- 主进程 setOpenAtLogin():改内存 → 写回 config.json → setLoginItemSettings,
任一失败都回滚,返回 {ok, openAtLogin};状态快照新增 openAtLogin / openAtLoginSupported
- 渲染层:服务端状态卡片新增「开机自启」滑块(纯 CSS,无依赖),
切换即时生效并有本地意图守卫,避免被随后到达的陈旧快照冲掉
- 文档:console/README.md 与 docs/部署指南.md 同步
注意:旧包 config.json 里已显式写 true 的用户升级后仍会自启(不悄悄改既有行为),
在窗口里关一次即可;全新安装默认不注册。
|
2026-09-10 00:06:59 +08:00 |
|
AskaEth
|
13f1f03cd0
|
fix: 修掉十路审查发现的 9 个 P0 与一批 P1(数据正确性 / 越权 / 现场可用性)
P0(9 条全修):
- 多通道项目奖牌重复计数:domain 层按 (EventID,CategoryID,EntryID/Subject) 去重,
通道优先级 athlete→team→id
- 「只排名不计分」项目(scored:false)不再进奖牌榜,REST/WS/导出口径统一
- aggregate 流水 entry_id 恒 NULL 导致重算后分数翻倍:作废范围改按 event+category 定位
- 已发布成绩仍可被「存草稿」覆盖并清空 rank:服务端 409 STATE_INVALID + 前端禁编辑
- voidResult / 规则导入重算后名次不回写:接住 Ranked 调 applyComputedRanks
- 赛事包覆盖导入可能「旧项目已删、新项目没成」:先改名成功再清理,失败回滚
- 导入赛事包可覆盖正在运行的赛事目录:name==当前项目直接 400
- Console spawn 失败后守护器永久卡死:清 child/pid + 调度重启 + stop 超时兜底
- /ws 快照向非 admin 下发 admin-only 配置:按角色裁剪 settings
P1(计分口径 / 契约 / 现场):
- standings.tieBreak 三处接线;profiles[].recordBonusMode 生效;破纪录校验
RecordEligible 与 entry 归属;破纪录登记校验已发布成绩
- export.loadRanked 与主路径对齐;ListBackups reason 解析;CLI backup 前缀修正
(原来只增不减会撑爆磁盘);单位积分明细/导出统一权重折算;停用单位展示
- 规则导入原子化(重算并入同一事务)+ 返回 compatible/affectedEventCount/
recalculatedLedgerRows;登录审计 user_id 修正
- WS 策略性断开用 Close(code,reason);HelloMessage 补 page/instanceId;
实例注册表清理;settings/单位/项目写操作补 change_log 触发大屏刷新
- 大屏:全量快照不再被版本守卫丢弃;个人积分榜刷新;公告返回页/轮播;行数截断;
REST 竞态;1008 停止重连;握手超时;时区补 Z;401 仅 AUTH_REQUIRED 才登出
- 管理端:published 行禁编辑;saveDraft 补 unit(1:06.33 显示);前端预览改用服务端
points;空成绩行不再恒「已修改」;修订详情按 id 拉取;切项目请求序号与 dirty 保护;
发布后刷新项目状态;NAV/路由按角色过滤;recorder 不再打 admin-only 接口
- Console:大屏窗口销毁后重建;大屏 URL 复用 host 归一化;backup.bat 改纯 ASCII
(已在 Meowhome 实跑验证)
自检:服务端 gofmt/build/vet 通过、12 包测试全绿;前端 vue-tsc + build 通过、
node --test 24/24;console check-syntax 12 文件通过。
|
2026-09-09 22:16:58 +08:00 |
|
AskaEth
|
2c9ff65fc4
|
feat: 补齐第一批已知缺口(关闭握手 / 定时备份 / Origin 白名单 / DTO 收敛)
- httpapi:新增 POST /api/shutdown(admin 会话或 X-Shutdown-Token 常量时间比对)
与 GET /api/rules/switches(recorder 可读,只回布尔开关);settings/standings 的
重复 DTO 收敛到 internal/dto;standings.showIndividualMedals 贯通到设置读写与前端
- export:抽出 export.Service,新增自动备份定时任务(backup.intervalMinutes/keep),
自动、手动、发布前三类备份共用同一把互斥锁与滚动清理
- realtime:/ws 去掉 InsecureSkipVerify,改用 OriginPatterns(同源与无 Origin 放行,
跨站需命中 server.allowedOrigins)
- console:停止服务改走 POST /api/shutdown 优雅关闭(5 秒宽限后回退强杀),
服务管理器新增「已连接的大屏实例」卡片
- deploy:build.bat 改用 for /f 读内部署名——set /p 在 chcp 65001 下返回空,
导致最近这次构建的 exe 里根本没注入署名
- docs:备份策略、关闭超时、Origin 配置的说明与实现对齐
|
2026-09-09 21:41:08 +08:00 |
|
AskaEth
|
7f9701c465
|
fix: 审查修复波次(P0x4 + P1x30+)
- domain/rules: 计分与排名解耦、破纪录按组别、tieBreak 生效、调整分不丢、移除 aggregate 假能力
- realtime: 新建 internal/dto,快照三项契约对齐,/ws 只认会话身份
- httpapi: 流水幂等、standings 整表载荷、广播接线、强制改密、XFF 防伪造、records 写入、审计、赛事包内容、发布前备份、result.points、实例列表
- web: FormData 上传、YAML 解析、快照归一化、大屏解析层、bootstrap 降级、访客判定、主题切换
- console: 优雅关闭握手、中文化、免验证改管理员账号、选屏与日志轮转修复
- docs: 用户文档三份 + README
- 主代理: 破纪录加分接线(levels 未加载导致恒 0)
|
2026-09-09 21:06:40 +08:00 |
|
AskaEth
|
b80c64a115
|
fix: 补 .gitattributes(CRLF for .bat) + README 端口 19540 + 提交 console 锁文件
|
2026-09-09 20:57:58 +08:00 |
|
AskaEth
|
5ae55b8057
|
fix(server): 赛事包导出传入项目名,修复 GET /api/export/project.zip 恒 500
|
2026-09-09 19:57:23 +08:00 |
|
AskaEth
|
72462792ec
|
feat(server): 用户管理与变更历史接口;修复 result_revisions.parent_id 从未写入
|
2026-09-09 19:51:50 +08:00 |
|
AskaEth
|
f3813171d3
|
fix(console): 停止服务时同步关闭扩展屏大屏窗口
|
2026-09-09 19:38:09 +08:00 |
|
AskaEth
|
4cbf5a74f0
|
feat(server): 新增修改密码接口 POST /api/auth/password(验原密码、作废会话)
|
2026-09-09 19:26:35 +08:00 |
|
AskaEth
|
e47251658b
|
fix(console): 默认端口改 19540;打包改为手动组装便携版(不依赖 GitHub 下载)
|
2026-09-09 19:15:13 +08:00 |
|
AskaEth
|
7d5d94f815
|
feat: M0-M2 骨架落地(服务端/前端/宿主/数据库/积分引擎)
- server: Go 单二进制骨架,embed 内嵌前端,配置/迁移/HTTP 路由
- store: system.db + event.db 两套迁移,业务表 + 种子数据
- domain: 积分引擎(并列三口径/计分通道/流水/排名/破纪录)+ 68 个测试
- web: Vite + Vue 三入口骨架 + shared 层(类型/API/WebSocket 客户端)
- console: Electron 宿主骨架(选屏/防熄屏/进程守护/托盘)
|
2026-09-09 18:36:40 +08:00 |
|