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/ 同步更新。
This commit is contained in:
1 parent
9c6d0bd3b3
commit
fc96d17660
42 files changed
+2451
-95
No files matched your search
@@ -1,16 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useScoreboard } from '../useScoreboard'
|
||||
import { isWindowed } from '../windowed'
|
||||
|
||||
const { booted, status, source, version, authRequired } = useScoreboard()
|
||||
const { booted, status, source, authRequired } = useScoreboard()
|
||||
|
||||
// connecting 是启动瞬间的正常状态,闪「连接中断」会误报;只有真正断开才提示。
|
||||
const offline = computed(() => booted.value && status.value === 'offline')
|
||||
const mock = computed(() => source.value === 'mock')
|
||||
// 数据版本号(change_log 版本)是给现场核对「这块屏有没有跟上服务端」用的调试信息,
|
||||
// 与「实例 · 屏」同规矩:只在窗口化调试时显示,kiosk 全屏不出现。
|
||||
const showVersion = computed(() => isWindowed() && version.value !== null)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -18,7 +14,6 @@ const showVersion = computed(() => isWindowed() && version.value !== null)
|
||||
<span v-if="mock" class="badge mock">示例数据</span>
|
||||
<span v-if="authRequired" class="badge auth">登录已失效 · 请重新登录</span>
|
||||
<span v-if="offline" class="badge offline">⚠ 连接中断 · 数据可能不是最新</span>
|
||||
<span v-if="showVersion" class="badge version">v{{ version }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -54,8 +49,4 @@ const showVersion = computed(() => isWindowed() && version.value !== null)
|
||||
border-color: rgba(255, 160, 64, 0.55);
|
||||
color: #ffd0a0;
|
||||
}
|
||||
.version {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
color: var(--text-dim);
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onUnmounted, ref } from 'vue'
|
||||
import { formatClock } from '@/shared'
|
||||
import { useShellFullscreen } from '../shell'
|
||||
import { useScoreboard } from '../useScoreboard'
|
||||
import { isWindowed } from '../windowed'
|
||||
import OfflineBadge from './OfflineBadge.vue'
|
||||
@@ -21,13 +22,8 @@ const host = computed(() => settings.value?.event.host?.trim() || '主办方待
|
||||
const logo = computed(() => display.value?.logo?.trim() || '')
|
||||
const clock = computed(() => formatClock(now.value))
|
||||
// 单屏窗口化时用来分辨「这块屏是哪个实例」;现场展示(kiosk,或窗口化后按 F11 全屏)都不该出现。
|
||||
// 监听在 setup 阶段就挂上,onUnmounted 里摘掉,不引入额外 import。
|
||||
const fullscreen = ref(document.fullscreenElement !== null)
|
||||
function onFullscreenChange(): void {
|
||||
fullscreen.value = document.fullscreenElement !== null
|
||||
}
|
||||
document.addEventListener('fullscreenchange', onFullscreenChange)
|
||||
onUnmounted(() => document.removeEventListener('fullscreenchange', onFullscreenChange))
|
||||
// 窗口级全屏只有 Console 的 preload 看得到,合成判断与清理都在 shell.ts 里。
|
||||
const fullscreen = useShellFullscreen()
|
||||
|
||||
// 实例/屏号都缺失时返回空串,模板的 v-if 顺带挡掉空元素。
|
||||
const identity = computed(() => {
|
||||
|
||||
Reference in new issue
Block a user