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 文件通过。
This commit is contained in:
1 parent
24fff8dd08
commit
6483acf9ad
50 files changed
+2980
-395
No files matched your search
+119
-114
@@ -46,130 +46,135 @@
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="card">
|
||||
<div class="card-head">
|
||||
<h2>服务端状态</h2>
|
||||
<div class="actions">
|
||||
<button id="btn-start" class="btn primary" type="button">启动服务</button>
|
||||
<button id="btn-stop" class="btn" type="button">停止服务</button>
|
||||
<button id="btn-restart" class="btn" type="button">重启服务</button>
|
||||
<!-- 左栏放状态与配置类卡片,右栏整栏留给运行日志 -->
|
||||
<div class="col-left">
|
||||
<section class="card">
|
||||
<div class="card-head">
|
||||
<h2>服务端状态</h2>
|
||||
<div class="actions">
|
||||
<button id="btn-start" class="btn primary" type="button">启动服务</button>
|
||||
<button id="btn-stop" class="btn" type="button">停止服务</button>
|
||||
<button id="btn-restart" class="btn" type="button">重启服务</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-row">
|
||||
<span id="state-dot" class="dot"></span>
|
||||
<strong id="state-label">已停止</strong>
|
||||
<span id="state-detail" class="muted"></span>
|
||||
</div>
|
||||
<div class="status-row">
|
||||
<span id="state-dot" class="dot"></span>
|
||||
<strong id="state-label">已停止</strong>
|
||||
<span id="state-detail" class="muted"></span>
|
||||
</div>
|
||||
|
||||
<dl class="kv">
|
||||
<div>
|
||||
<dt>服务地址</dt>
|
||||
<dd id="k-url">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>进程 PID</dt>
|
||||
<dd id="k-pid">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>自动重启</dt>
|
||||
<dd id="k-restarts">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>防熄屏</dt>
|
||||
<dd id="k-power">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>开机自启</dt>
|
||||
<dd>
|
||||
<label class="switch">
|
||||
<input id="sw-open-at-login" type="checkbox" />
|
||||
<span id="sw-open-at-login-label" class="muted">—</span>
|
||||
</label>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="wide">
|
||||
<dt>服务端程序</dt>
|
||||
<dd id="k-exe" class="mono">—</dd>
|
||||
</div>
|
||||
<div class="wide">
|
||||
<dt>配置文件</dt>
|
||||
<dd id="k-config" class="mono">—</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<dl class="kv">
|
||||
<div>
|
||||
<dt>服务地址</dt>
|
||||
<dd id="k-url">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>进程 PID</dt>
|
||||
<dd id="k-pid">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>自动重启</dt>
|
||||
<dd id="k-restarts">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>防熄屏</dt>
|
||||
<dd id="k-power">—</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>开机自启</dt>
|
||||
<dd>
|
||||
<label class="switch">
|
||||
<input id="sw-open-at-login" type="checkbox" />
|
||||
<span id="sw-open-at-login-label" class="muted">—</span>
|
||||
</label>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="wide">
|
||||
<dt>服务端程序</dt>
|
||||
<dd id="k-exe" class="mono">—</dd>
|
||||
</div>
|
||||
<div class="wide">
|
||||
<dt>配置文件</dt>
|
||||
<dd id="k-config" class="mono">—</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<p id="config-warning" class="warning" hidden></p>
|
||||
</section>
|
||||
<p id="config-warning" class="warning" hidden></p>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="card-head">
|
||||
<h2>大屏窗口</h2>
|
||||
<div class="actions">
|
||||
<button id="btn-relayout" class="btn" type="button">重新排布</button>
|
||||
<button id="btn-open-admin" class="btn" type="button">打开服务端管理面板</button>
|
||||
<section class="card">
|
||||
<div class="card-head">
|
||||
<h2>大屏窗口</h2>
|
||||
<div class="actions">
|
||||
<button id="btn-relayout" class="btn" type="button">重新排布</button>
|
||||
<button id="btn-open-admin" class="btn" type="button">打开服务端管理面板</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint">
|
||||
单屏时窗口化、按 F11 切换全屏;多屏时除内置屏外(无内置屏时除主屏外)每块屏一个 kiosk 窗口,各锁一页。
|
||||
</p>
|
||||
<ul id="display-list" class="display-list">
|
||||
<li class="muted">尚未打开大屏窗口</li>
|
||||
</ul>
|
||||
</section>
|
||||
<p class="hint">
|
||||
单屏时窗口化、按 F11 切换全屏;多屏时除内置屏外(无内置屏时除主屏外)每块屏一个 kiosk 窗口,各锁一页。
|
||||
</p>
|
||||
<ul id="display-list" class="display-list">
|
||||
<li class="muted">尚未打开大屏窗口</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="card-head">
|
||||
<h2>已连接的大屏实例</h2>
|
||||
<span id="instances-summary" class="muted"></span>
|
||||
</div>
|
||||
<p class="hint">
|
||||
来自服务端 <span class="mono">GET /api/display/instances</span>,每 5 秒刷新一次;
|
||||
沿用服务端管理面板的登录态,需要记录员及以上权限。
|
||||
</p>
|
||||
<ul id="instance-list" class="display-list">
|
||||
<li class="muted">正在读取…</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="card-head">
|
||||
<h2>管理员账号</h2>
|
||||
<div class="actions">
|
||||
<button id="btn-reset-account" class="btn" type="button">重置账号</button>
|
||||
<section class="card">
|
||||
<div class="card-head">
|
||||
<h2>已连接的大屏实例</h2>
|
||||
<span id="instances-summary" class="muted"></span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint">
|
||||
本机直接调用同目录的 <span class="mono">scoreboard.exe passwd</span>,无需登录验证。
|
||||
重置会作废该用户的全部登录会话,已在用的管理面板会被强制退出。
|
||||
</p>
|
||||
<div class="form-row">
|
||||
<label class="field">
|
||||
<span>当前用户名</span>
|
||||
<input id="acct-user" class="input" type="text" value="admin" autocomplete="off" spellcheck="false" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>新用户名(留空 = 不改)</span>
|
||||
<input id="acct-new-user" class="input" type="text" autocomplete="off" spellcheck="false" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>新密码(留空 = 随机生成)</span>
|
||||
<input id="acct-password" class="input" type="password" autocomplete="new-password" spellcheck="false" />
|
||||
</label>
|
||||
</div>
|
||||
<p id="acct-status" class="hint" hidden></p>
|
||||
<pre id="acct-output" class="log account-output" hidden></pre>
|
||||
</section>
|
||||
<p class="hint">
|
||||
来自服务端 <span class="mono">GET /api/display/instances</span>,每 5 秒刷新一次;
|
||||
沿用服务端管理面板的登录态,需要记录员及以上权限。
|
||||
</p>
|
||||
<ul id="instance-list" class="display-list">
|
||||
<li class="muted">正在读取…</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="card log-card">
|
||||
<div class="card-head">
|
||||
<h2>运行日志</h2>
|
||||
<div class="actions">
|
||||
<label class="check"><input id="autoscroll" type="checkbox" checked /> 自动滚动</label>
|
||||
<button id="btn-clear-log" class="btn" type="button">清空</button>
|
||||
<section class="card">
|
||||
<div class="card-head">
|
||||
<h2>管理员账号</h2>
|
||||
<div class="actions">
|
||||
<button id="btn-reset-account" class="btn" type="button">重置账号</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="log" class="log"></pre>
|
||||
</section>
|
||||
<p class="hint">
|
||||
本机直接调用同目录的 <span class="mono">scoreboard.exe passwd</span>,无需登录验证。
|
||||
重置会作废该用户的全部登录会话,已在用的管理面板会被强制退出。
|
||||
</p>
|
||||
<div class="form-row">
|
||||
<label class="field">
|
||||
<span>当前用户名</span>
|
||||
<input id="acct-user" class="input" type="text" value="admin" autocomplete="off" spellcheck="false" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>新用户名(留空 = 不改)</span>
|
||||
<input id="acct-new-user" class="input" type="text" autocomplete="off" spellcheck="false" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>新密码(留空 = 随机生成)</span>
|
||||
<input id="acct-password" class="input" type="password" autocomplete="new-password" spellcheck="false" />
|
||||
</label>
|
||||
</div>
|
||||
<p id="acct-status" class="hint" hidden></p>
|
||||
<pre id="acct-output" class="log account-output" hidden></pre>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-right">
|
||||
<section class="card log-card">
|
||||
<div class="card-head">
|
||||
<h2>运行日志</h2>
|
||||
<div class="actions">
|
||||
<label class="check"><input id="autoscroll" type="checkbox" checked /> 自动滚动</label>
|
||||
<button id="btn-clear-log" class="btn" type="button">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="log" class="log"></pre>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="app.js"></script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
// 服务端管理面板是 http://127.0.0.1 下的 Web 页面,只放行窗口控制这一组能力,
|
||||
// 服务端管理面板是 http://127.0.0.1 下的 Web 页面,只放行窗口与显示器控制这两组能力,
|
||||
// 不把 ipcRenderer 交给页面,避免面板页面拿到主进程的任意通道
|
||||
contextBridge.exposeInMainWorld('desktopShell', {
|
||||
platform: process.platform,
|
||||
@@ -14,5 +14,13 @@ contextBridge.exposeInMainWorld('desktopShell', {
|
||||
const handler = (_event, maximized) => callback(maximized)
|
||||
ipcRenderer.on('window:maximize-changed', handler)
|
||||
return () => ipcRenderer.removeListener('window:maximize-changed', handler)
|
||||
},
|
||||
// 显示器菜单:列屏 / 按屏开窗 / 收窗 / 按设置重建。全屏放行与否由主进程判定,
|
||||
// 这里只透传,页面拿不到 ipcRenderer 本身
|
||||
displays: {
|
||||
list: () => ipcRenderer.invoke('display:list'),
|
||||
open: (displayId, options) => ipcRenderer.invoke('display:open', displayId, options),
|
||||
close: (displayId) => ipcRenderer.invoke('display:close', displayId),
|
||||
relayout: () => ipcRenderer.invoke('display:relayout')
|
||||
}
|
||||
})
|
||||
@@ -151,14 +151,28 @@ main {
|
||||
padding: 16px 20px 20px;
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
/* 五张卡片各占一整行;最后一行(运行日志)吃掉剩余高度 */
|
||||
grid-template-rows: auto auto auto auto minmax(160px, 1fr);
|
||||
/* 左栏稍宽放卡片,右栏整栏给运行日志 */
|
||||
grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
|
||||
/* 滚动交给两栏各自处理,否则整页滚动时日志也跟着跑 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.col-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
min-height: 0;
|
||||
/* 左栏卡片总高超出时左栏自己滚,不影响右栏日志 */
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.col-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
grid-column: span 2;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
@@ -168,6 +182,8 @@ main {
|
||||
.log-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* 撑满右栏整高,日志区随窗口高度伸缩 */
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
@@ -256,11 +272,13 @@ h2 {
|
||||
|
||||
.kv {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
/* 左栏只有半屏宽,4 列会挤成窄条,改 2 列 */
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px 16px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 2 列下 span 2 正好是一整行,长路径仍然通栏 */
|
||||
.kv > div.wide {
|
||||
grid-column: span 2;
|
||||
}
|
||||
@@ -410,7 +428,8 @@ h2 {
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
/* 左栏宽度不定:够宽排 3 个,窄了自动换行成 2 个 / 1 个 */
|
||||
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
|
||||
gap: 10px 16px;
|
||||
}
|
||||
|
||||
@@ -471,3 +490,24 @@ h2 {
|
||||
max-height: 180px;
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
/* 窗口拖窄到放不下两栏时退回单栏,日志仍排在最后并保留最小高度 */
|
||||
@media (max-width: 900px) {
|
||||
/* 这里必须换成 flex:栅格下 auto 行会被压缩,左栏内容会被裁掉而不是撑开滚动 */
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* 单栏时整页一起滚,左栏按内容高、不再自成一个滚动区 */
|
||||
.col-left {
|
||||
flex: none;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.col-right {
|
||||
flex: 1;
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user