feat(export/dashboard): 导出增强与大屏公开化

导出-每次考试/多考试对比:
- 合格次数阈值等标记项抽为常显区块,三类成绩表共用
- 附缺考名单扩展到多考试导出(取所选考试全都没交的人)
- 块表每场固定2列(成绩+合并空位),修复漏推空串导致的整行左移/串列
- 列宽按实际内容估算,合并区空位只给最小宽度
- 合格次数始终显示实际次数(含0),仅 ≥ 阈值标绿底
- 打印标题改为「成绩明细」,缺考名单分页单独成页
- 打印样式加 print-color-adjust:exact,绿底/红字可正常印出

数据大屏:
- ?class= 班级大屏免登录公开访问,接口侧放行并按班级裁剪后回传
- 单班大屏移除退出按钮,仅首页保留
- 修复红绿判定真bug:membersTable 误用 window.DASH(DASH为let声明取不到)
  改为 scoreGrade() 直接读自由变量,按后台合格率正确判红/黄/绿

测试/样例/文档同步更新,全量回归通过。
This commit is contained in:
2026-09-27 22:04:17 +08:00
parent 0ba9c7c33c
commit d05fd4a9b5
34 changed files with 4290 additions and 305 deletions
+56
View File
@@ -0,0 +1,56 @@
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
<title>数据大屏首页(预览)</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../assets/style.css">
<style>
/* 预览页专属:补上 hero 里样式类所需的小件(原页面由 dashboard.php 内联给出) */
.brand-text{display:flex;flex-direction:column;gap:2px}
.dot{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--border-2);vertical-align:1px}
.dot.on{background:var(--ok)}
.dot.stale{background:var(--danger)}
.spacer{flex:1}
.preview-tip{margin:12px 0 0;padding:8px 12px;border:1px dashed var(--border);border-radius:var(--radius);
font-size:12px;color:var(--text-3);background:var(--panel-weak,#f7f8fc)}
</style></head>
<body class="dash dash-home">
<div class="wrap">
<div class="dash-hero">
<div class="brand"><h1 class="brand-title">NY问卷考试系统数据平台</h1></div>
<div class="dash-controls">
<select id="projectSel"><option value="p1">高一数学月考(31 份)</option></select>
<span id="classSelWrap"><select id="classSel" disabled><option value="">请先选问卷</option></select></span>
<label class="switch"><input type="checkbox" id="autoRefresh" checked><span></span></label>
<select id="intervalSel"><option value="15" selected>15 秒</option></select>
<button class="btn" id="btnRefresh">刷新</button>
<button class="btn" id="btnFullscreen">⛶ 全屏</button>
<a class="btn back" id="linkIndex" href="index.php">← 主系统</a>
<a class="btn back" id="linkLogout" href="logout.php">退出</a>
</div>
</div>
<div style="margin-top:14px;font-size:12px;color:var(--text-3);display:flex;align-items:center;gap:6px;flex-wrap:wrap;">
<span id="heroSub">共 4 个班级 · 点击卡片进入该班大屏</span>
<span class="spacer"></span>
最后更新:<span id="lastUpdate">2026-09-27 20:57:25</span><i id="liveDot" class="dot on"></i>
</div>
<div class="preview-tip">静态预览:这张页面由生产环境 dashboard.php 的 <code>clsCardHtml()</code> 渲染,
数据取自 mock 卷王。真实站点上点卡片会整页跳转到 <code>dashboard.php?pid=…&amp;class=…</code>,
进入该班独立大屏(那里不再有班级选择、返回主系统与退出,浏览器后退键也被锁住)。</div>
<div id="board">
<section class="cards" id="boardSummary">
<div class="card accent-blue"><span>总人数</span><b>0</b></div>
<div class="card ok accent-green"><span>已提交</span><b>0</b></div>
<div class="card warn accent-orange"><span>缺考</span><b>0</b></div>
<div class="card accent-teal"><span>平均分</span><b>-</b></div>
<div class="card accent-indigo"><span>合格率</span><b>-</b></div>
</section>
<div class="sec-title" id="boardTitle">班级入口 <span class="tag-gray">点击卡片进入该班独立大屏</span></div>
<div id="boardBody"><div class="cls-grid"><div class="cls-card" data-cls="高一(1)班" role="button" tabindex="0" title="点击进入「高一(1)班」的独立大屏"><div class="cls-head"><span class="cls-name">高一(1)班</span><span class="cls-go">进入 ›</span></div><div class="cls-kpis"><div class="cls-kpi "><span>总人数</span><b>2</b></div><div class="cls-kpi k-ok"><span>已提交</span><b>2</b></div><div class="cls-kpi "><span>缺考</span><b>0</b></div><div class="cls-kpi "><span>平均分</span><b>81</b></div><div class="cls-kpi k-ok"><span>合格率</span><b>100%</b></div></div><div class="cls-bar"><i style="width:100%;background:var(--ok)"></i></div><div class="cls-sub"><span>提交率 100%</span></div></div><div class="cls-card" data-cls="高一(2)班" role="button" tabindex="0" title="点击进入「高一(2)班」的独立大屏"><div class="cls-head"><span class="cls-name">高一(2)班</span><span class="cls-go">进入 ›</span></div><div class="cls-kpis"><div class="cls-kpi "><span>总人数</span><b>1</b></div><div class="cls-kpi k-ok"><span>已提交</span><b>1</b></div><div class="cls-kpi "><span>缺考</span><b>0</b></div><div class="cls-kpi "><span>平均分</span><b>93</b></div><div class="cls-kpi k-ok"><span>合格率</span><b>100%</b></div></div><div class="cls-bar"><i style="width:100%;background:var(--ok)"></i></div><div class="cls-sub"><span>提交率 100%</span></div></div><div class="cls-card" data-cls="高一(3)班" role="button" tabindex="0" title="点击进入「高一(3)班」的独立大屏"><div class="cls-head"><span class="cls-name">高一(3)班</span><span class="cls-go">进入 ›</span></div><div class="cls-kpis"><div class="cls-kpi "><span>总人数</span><b>1</b></div><div class="cls-kpi k-ok"><span>已提交</span><b>1</b></div><div class="cls-kpi "><span>缺考</span><b>0</b></div><div class="cls-kpi "><span>平均分</span><b>90</b></div><div class="cls-kpi k-ok"><span>合格率</span><b>100%</b></div></div><div class="cls-bar"><i style="width:100%;background:var(--ok)"></i></div><div class="cls-sub"><span>提交率 100%</span></div></div><div class="cls-card" data-cls="高一(4)班" role="button" tabindex="0" title="点击进入「高一(4)班」的独立大屏"><div class="cls-head"><span class="cls-name">高一(4)班</span><span class="cls-go">进入 ›</span></div><div class="cls-kpis"><div class="cls-kpi "><span>总人数</span><b>1</b></div><div class="cls-kpi k-ok"><span>已提交</span><b>1</b></div><div class="cls-kpi "><span>缺考</span><b>0</b></div><div class="cls-kpi "><span>平均分</span><b>76</b></div><div class="cls-kpi k-ok"><span>合格率</span><b>100%</b></div></div><div class="cls-bar"><i style="width:100%;background:var(--ok)"></i></div><div class="cls-sub"><span>提交率 100%</span></div></div></div></div>
</div>
</div>
<script>/* 预览页:把"全屏/刷新"按钮做成 plainly 不可点,仅示意版式 */
document.addEventListener('DOMContentLoaded', function () {
["#btnFullscreen", "#btnRefresh", "#projectSel", "#classSelWrap"].forEach(function (s) {
var el = document.querySelector(s); if (el) el.setAttribute("disabled", "disabled");
});
});</script>
</body></html>
Binary file not shown.
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
<title>多考试导出 · 每次考试(两行表头)(预览)</title><style>body{font-family:"Microsoft YaHei","PingFang SC",sans-serif;color:#222;background:#f7f7f7;margin:0;padding:24px}.page{background:#fff;border:1px solid #e3e3e3;border-radius:6px;padding:20px 22px;margin:0 auto 20px;max-width:1100px}.page h2{font-size:16px;margin:0 0 4px}.page .sub{font-size:12px;color:#777;margin:0 0 14px}table.bt{border-collapse:collapse;border:1px solid #999;width:100%;background:#fff;font-size:11px}table.bt td,table.bt th{border:1px solid #999;text-align:center;padding:3px 5px}table.bt th,table.bt thead td{background:#eaeaea;font-weight:700}table.bt thead tr:first-child td{background:#eaeaea}table.bt td.absent{color:#888}table.bt td.max{color:#d0021b;font-weight:700}table.bt td.pass{background:#63c384;font-weight:700;-webkit-print-color-adjust:exact;print-color-adjust:exact}html,body,.pass,.absent,.max{-webkit-print-color-adjust:exact;print-color-adjust:exact}.legend{margin:10px 0 0;font-size:12px;color:#555}.legend i{display:inline-block;width:11px;height:11px;vertical-align:-1px;margin:0 4px 0 12px}.legend .r{color:#d0021b;font-weight:700}.legend .g{background:#63c384;font-weight:700}.sign{margin-top:16px;font-size:12px}</style></head><body>
<div class="page"><h2>多考试导出 · 每次考试(两行表头)</h2><p class="sub">合格线 110 分,达 1 次标绿</p>
<table class="bt"><thead><tr><td rowspan="2">学号</td><td rowspan="2">姓名</td><td rowspan="2">第几次</td><td colspan="2">第一次月考·数学</td><td colspan="2">第一次月考·英语</td></tr><tr><td>成绩</td><td></td><td>成绩</td><td></td></tr></thead><tbody><tr><td rowspan="5">356001</td><td rowspan="5">张三</td><td rowspan="4">1</td><td>112</td><td></td><td class="max">112</td><td></td></tr><tr><td>100</td><td></td><td>99</td><td></td></tr><tr><td class="max">136</td><td></td><td></td><td></td></tr><tr><td>127</td><td></td><td></td><td></td></tr><tr><td>合格次数</td><td class="pass">3</td><td></td><td class="pass">1</td><td></td></tr><tr><td rowspan="4">356002</td><td rowspan="4">李四</td><td rowspan="3">1</td><td></td><td></td><td>130</td><td></td></tr><tr><td></td><td></td><td>129</td><td></td></tr><tr><td></td><td></td><td class="max">133</td><td></td></tr><tr><td>合格次数</td><td>0</td><td></td><td class="pass">3</td><td></td></tr><tr><td rowspan="2">356003</td><td rowspan="2">王五</td><td>1</td><td class="max">95</td><td></td><td colspan="2" class="absent">未考</td></tr><tr><td>合格次数</td><td>0</td><td></td><td></td><td></td></tr></tbody></table>
<p class="legend">标记说明:<span class="r">最高分</span> 标红、<i class="r">红字</i>;<span class="g">合格次数</span> 始终显示(含 0 次),达 1 次标绿、<i class="g">绿底</i>;未达标显示次数、不标绿。
<p class="sign">教师签字:______________  教研组长签字:______________  日期:____________</p>
</div></body></html>
+7
View File
@@ -0,0 +1,7 @@
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
<title>单场导出 · 每次考试(每人一行的宽表)(预览)</title><style>body{font-family:"Microsoft YaHei","PingFang SC",sans-serif;color:#222;background:#f7f7f7;margin:0;padding:24px}.page{background:#fff;border:1px solid #e3e3e3;border-radius:6px;padding:20px 22px;margin:0 auto 20px;max-width:1100px}.page h2{font-size:16px;margin:0 0 4px}.page .sub{font-size:12px;color:#777;margin:0 0 14px}table.bt{border-collapse:collapse;border:1px solid #999;width:100%;background:#fff;font-size:11px}table.bt td,table.bt th{border:1px solid #999;text-align:center;padding:3px 5px}table.bt th,table.bt thead td{background:#eaeaea;font-weight:700}table.bt thead tr:first-child td{background:#eaeaea}table.bt td.absent{color:#888}table.bt td.max{color:#d0021b;font-weight:700}table.bt td.pass{background:#63c384;font-weight:700;-webkit-print-color-adjust:exact;print-color-adjust:exact}html,body,.pass,.absent,.max{-webkit-print-color-adjust:exact;print-color-adjust:exact}.legend{margin:10px 0 0;font-size:12px;color:#555}.legend i{display:inline-block;width:11px;height:11px;vertical-align:-1px;margin:0 4px 0 12px}.legend .r{color:#d0021b;font-weight:700}.legend .g{background:#63c384;font-weight:700}.sign{margin-top:16px;font-size:12px}</style></head><body>
<div class="page"><h2>单场导出 · 每次考试(每人一行的宽表)</h2><p class="sub">第一次月考·数学 · 合格线 110 分,达 1 次标绿</p>
<table class="bt"><thead><tr><td>学号</td><td>姓名</td><td>第1次</td><td>第2次</td><td>第3次</td><td>第4次</td><td>合格次数</td></tr></thead><tbody><tr><td>356001</td><td>张三</td><td>112</td><td>100</td><td class="max">136</td><td>127</td><td class="pass">3</td></tr><tr><td>356002</td><td>李四</td><td></td><td></td><td></td><td></td><td>0</td></tr><tr><td>356003</td><td>王五</td><td class="max">95</td><td></td><td></td><td></td><td>0</td></tr></tbody></table>
<p class="legend">标记说明:<span class="r">最高分</span> 标红、<i class="r">红字</i>;<span class="g">合格次数</span> 始终显示(含 0 次),达 1 次标绿、<i class="g">绿底</i>;未达标显示次数、不标绿。
<p class="sign">教师签字:______________  教研组长签字:______________  日期:____________</p>
</div></body></html>
+287
View File
@@ -0,0 +1,287 @@
{
"block": {
"title": "多考试导出 · 每次考试(两行表头)",
"sheet": {
"rows": [
[
"学号",
"姓名",
"第几次",
"第一次月考·数学",
"",
"第一次月考·英语",
""
],
[
"",
"",
"",
"成绩",
"",
"成绩",
""
],
[
"356001",
"张三",
"1",
"112",
"",
"112",
""
],
[
"",
"",
"2",
"100",
"",
"99",
""
],
[
"",
"",
"3",
"136",
"",
"",
""
],
[
"",
"",
"4",
"127",
"",
"",
""
],
[
"",
"",
"合格次数",
"3",
"",
"1",
""
],
[
"356002",
"李四",
"1",
"",
"",
"130",
""
],
[
"",
"",
"2",
"",
"",
"129",
""
],
[
"",
"",
"3",
"",
"",
"133",
""
],
[
"",
"",
"合格次数",
"0",
"",
"3",
""
],
[
"356003",
"王五",
"1",
"95",
"",
"未考",
""
],
[
"",
"",
"合格次数",
"0",
"",
"",
""
]
],
"merges": [
[
1,
1,
2,
1
],
[
1,
2,
2,
2
],
[
1,
3,
2,
3
],
[
1,
4,
1,
5
],
[
1,
6,
1,
7
],
[
3,
1,
7,
1
],
[
3,
2,
7,
2
],
[
3,
3,
6,
3
],
[
8,
1,
11,
1
],
[
8,
2,
11,
2
],
[
8,
3,
10,
3
],
[
12,
6,
12,
7
],
[
12,
1,
13,
1
],
[
12,
2,
13,
2
],
[
12,
3,
12,
3
]
],
"header_rows": 2,
"cell_styles": {
"3,6": "red",
"5,4": "red",
"7,4": "green",
"7,6": "green",
"10,6": "red",
"11,6": "green",
"12,4": "red"
}
},
"meta": {
"pass_line": 110,
"pass_times": 1
}
},
"wide": {
"title": "单场导出 · 每次考试(每人一行的宽表)",
"sheet": {
"rows": [
[
"学号",
"姓名",
"第1次",
"第2次",
"第3次",
"第4次",
"合格次数"
],
[
"356001",
"张三",
"112",
"100",
"136",
"127",
"3"
],
[
"356002",
"李四",
"",
"",
"",
"",
"0"
],
[
"356003",
"王五",
"95",
"",
"",
"",
"0"
]
],
"merges": [],
"header_rows": 1,
"cell_styles": {
"2,5": "red",
"2,7": "green",
"4,3": "red"
}
},
"meta": {
"pass_line": 110,
"pass_times": 1,
"exam_name": "第一次月考·数学"
}
}
}