导出-每次考试/多考试对比: - 合格次数阈值等标记项抽为常显区块,三类成绩表共用 - 附缺考名单扩展到多考试导出(取所选考试全都没交的人) - 块表每场固定2列(成绩+合并空位),修复漏推空串导致的整行左移/串列 - 列宽按实际内容估算,合并区空位只给最小宽度 - 合格次数始终显示实际次数(含0),仅 ≥ 阈值标绿底 - 打印标题改为「成绩明细」,缺考名单分页单独成页 - 打印样式加 print-color-adjust:exact,绿底/红字可正常印出 数据大屏: - ?class= 班级大屏免登录公开访问,接口侧放行并按班级裁剪后回传 - 单班大屏移除退出按钮,仅首页保留 - 修复红绿判定真bug:membersTable 误用 window.DASH(DASH为let声明取不到) 改为 scoreGrade() 直接读自由变量,按后台合格率正确判红/黄/绿 测试/样例/文档同步更新,全量回归通过。
665 lines
31 KiB
PHP
665 lines
31 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
require __DIR__ . '/bootstrap.php';
|
||
|
||
if (!ny_is_auth()) {
|
||
header('Location: login.php');
|
||
exit;
|
||
}
|
||
|
||
$cfg = ny_load_config();
|
||
$rosterCount = Roster::count();
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title><?= htmlspecialchars(NY_APP_NAME) ?></title>
|
||
<link rel="stylesheet" href="assets/style.css">
|
||
</head>
|
||
<body>
|
||
|
||
<header class="topbar">
|
||
<div class="brand">
|
||
<h1 class="brand-title">NY问卷考试系统数据平台</h1>
|
||
</div>
|
||
<div class="top-right">
|
||
<div id="conn" class="conn conn-off"><i></i><span>未连接</span></div>
|
||
<a class="btn" id="btnDashboard" href="dashboard.php" target="_blank" rel="noopener" title="在新标签页打开数据看板">📊 数据看板</a>
|
||
<a class="btn" id="btnDraw" href="draw.php" target="_blank" rel="noopener" title="在新标签页打开随机抽取">🎲 随机抽取</a>
|
||
<button class="btn" id="btnFullscreen" title="切换全屏">⛶ 全屏</button>
|
||
<button class="btn" id="btnRoster">名单管理 <b id="rosterBadge"><?= (int)$rosterCount ?></b></button>
|
||
<button class="btn btn-primary" id="btnSettings">设置</button>
|
||
<button class="btn" id="btnLogout" title="退出登录">退出</button>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="wrap">
|
||
|
||
<section class="toolbar">
|
||
<div class="field grow">
|
||
<label>问卷 / 考试</label>
|
||
<select id="projectSel"><option value="">加载中…</option></select>
|
||
</div>
|
||
<div class="field">
|
||
<label>姓名字段</label>
|
||
<select id="nameFieldSel"><option value="">自动识别</option></select>
|
||
</div>
|
||
<div class="field">
|
||
<label>班级</label>
|
||
<select id="clsSel"><option value="">全部班级</option></select>
|
||
</div>
|
||
<div class="field">
|
||
<label>组别</label>
|
||
<select id="grpSel"><option value="">全部组别</option></select>
|
||
</div>
|
||
<div class="field">
|
||
<label>自动刷新</label>
|
||
<div class="inline">
|
||
<label class="switch"><input type="checkbox" id="autoRefresh" checked><span></span></label>
|
||
<select id="intervalSel">
|
||
<option value="5">5 秒</option>
|
||
<option value="10">10 秒</option>
|
||
<option value="15" selected>15 秒</option>
|
||
<option value="30">30 秒</option>
|
||
<option value="60">60 秒</option>
|
||
<option value="120">2 分钟</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="field btns">
|
||
<button class="btn btn-primary" id="btnRefresh">立即刷新</button>
|
||
<button class="btn btn-primary" id="btnProjCfg" title="为当前选中的试卷单独设置满分 / 合格率 / 免考名单">试卷设置</button>
|
||
</div>
|
||
<div class="field grow-2">
|
||
<label>搜索</label>
|
||
<input type="text" id="kw" placeholder="输入学号或姓名筛选">
|
||
</div>
|
||
</section>
|
||
|
||
<section class="subbar">
|
||
<span class="subbar-label">导出</span>
|
||
<span class="hint" id="exportBarHint">成绩明细 / 每次考试 / 缺考名单 / 多考试对比,都在「导出」里配置</span>
|
||
<span class="spacer"></span>
|
||
<button class="btn btn-primary" id="btnExportOpen" title="导出与打印:选好导出内容、范围与标记方式,再导出 Excel 或直接打印">导出</button>
|
||
</section>
|
||
|
||
<section class="cards" id="statCards">
|
||
<div class="card accent-blue"><span>名单总人数</span><b id="sTotal">-</b></div>
|
||
<div class="card ok accent-green"><span>已提交</span><b id="sSubmit">-</b></div>
|
||
<div class="card warn accent-orange"><span>缺考</span><b id="sAbsent">-</b></div>
|
||
<div class="card accent-purple"><span>提交率</span><b id="sRate">-</b></div>
|
||
<div class="card accent-teal"><span>平均分</span><b id="sAvg">-</b></div>
|
||
<div class="card accent-green"><span>最高分</span><b id="sMax">-</b></div>
|
||
<div class="card accent-red"><span>最低分</span><b id="sMin">-</b></div>
|
||
<div class="card accent-indigo"><span>合格率<span id="sPassLabel">(≥60%满分)</span></span><b id="sPass">-</b></div>
|
||
<div class="card accent-gray"><span>平均用时</span><b id="sDur">-</b></div>
|
||
</section>
|
||
|
||
<div class="layout" id="dataLayout" data-layout="default">
|
||
<section class="panel" id="tablePanel">
|
||
<div class="panel-head">
|
||
<h2>成绩明细 <small id="tableHint"></small></h2>
|
||
<div class="updated">
|
||
<button class="btn sm" id="btnClearHl" hidden title="清除新数据的绿色高亮标记(每 30 秒自动清除一次)">清除高亮 <b id="hlBadge">0</b></button>
|
||
<button class="btn sm" id="btnPrintTable" title="打印成绩明细(默认按成绩降序,可选分组对比)">打印</button>
|
||
最后更新:<span id="lastUpdate">—</span><i id="liveDot" class="dot"></i>
|
||
</div>
|
||
</div>
|
||
<div class="table-scroll">
|
||
<table class="grid" id="dataTable">
|
||
<thead>
|
||
<tr>
|
||
<th class="sortable" data-k="sid" style="width:16%">学号</th>
|
||
<th class="sortable" data-k="name" style="width:12%">姓名</th>
|
||
<th class="sortable" data-k="cls" style="width:10%">班级</th>
|
||
<th class="sortable" data-k="grp" style="width:7%">组别</th>
|
||
<th class="sortable" data-k="score" style="width:10%">成绩</th>
|
||
<th class="sortable" data-k="attempts" style="width:11%" title="该生提交过的次数,点击表头可排序">考试次数</th>
|
||
<th class="sortable" data-k="submit_ts" style="width:22%" title="最近一次提交时间">提交时间</th>
|
||
<th class="sortable" data-k="duration" style="width:12%">考试时长</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="tbody"><tr><td colspan="8" class="empty">请选择问卷 / 考试</td></tr></tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel" id="absentPanel">
|
||
<div class="panel-head">
|
||
<h2>缺考名单 <small id="absentHint"></small></h2>
|
||
<span class="spacer"></span>
|
||
<button class="btn sm" id="btnCopySids" title="复制全部缺考学号(逗号分隔)">复制学号</button>
|
||
<button class="btn sm" id="btnCopyNames" title="复制全部缺考姓名">复制姓名</button>
|
||
<button class="btn sm" id="btnPrintAbsent" title="打印缺考名单">打印</button>
|
||
</div>
|
||
<div class="table-scroll short">
|
||
<table class="grid compact" id="absentTable">
|
||
<thead><tr><th style="width:25%">学号</th><th style="width:30%">姓名</th><th style="width:25%">班级</th><th style="width:20%">组别</th></tr></thead>
|
||
<tbody id="absentBody"><tr><td colspan="4" class="empty">—</td></tr></tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel" id="unmatchedPanel">
|
||
<div class="panel-head"><h2>未匹配答卷 <small id="unmatchedHint">姓名不在名单中</small></h2></div>
|
||
<div class="table-scroll short">
|
||
<table class="grid compact">
|
||
<thead><tr><th style="width:25%">姓名</th><th style="width:18%" data-k="score">成绩</th><th style="width:35%" data-k="submit_ts">提交时间</th><th style="width:22%">操作</th></tr></thead>
|
||
<tbody id="unmatchedBody"><tr><td colspan="4" class="empty">—</td></tr></tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- ================= 设置 ================= -->
|
||
<div class="modal" id="settingsModal">
|
||
<div class="modal-box">
|
||
<div class="modal-head"><h3>系统设置</h3><button class="x" data-close>×</button></div>
|
||
<div class="modal-body">
|
||
<div class="form-grid">
|
||
<div class="fi full">
|
||
<label>布局</label>
|
||
<div class="fi-inline">
|
||
<label class="chk"><input type="radio" name="cfgLayout" value="default" id="layoutDef"> 默认(缺考+未匹配同行)</label>
|
||
<label class="chk"><input type="radio" name="cfgLayout" value="side" id="layoutSide"> 成绩明细与缺考名单同行</label>
|
||
</div>
|
||
<p class="hint">主界面三个面板(成绩明细 / 缺考名单 / 未匹配答卷)的排版方式</p>
|
||
</div>
|
||
<div class="fi">
|
||
<label>默认刷新间隔(秒)</label>
|
||
<input type="number" id="cfgInterval" min="3" max="600">
|
||
</div>
|
||
<div class="fi">
|
||
<label>请求超时(秒)</label>
|
||
<input type="number" id="cfgTimeout" min="3" max="120">
|
||
</div>
|
||
<div class="fi">
|
||
<label>每页拉取条数</label>
|
||
<input type="number" id="cfgPageSize" min="20" max="200">
|
||
<p class="hint">实际生效上限 200,超出会自动夹到 200(防止单页内存耗尽)</p>
|
||
</div>
|
||
<div class="fi">
|
||
<label>全局默认满分</label>
|
||
<input type="number" id="cfgFull" min="1" max="1000">
|
||
<p class="hint" id="cfgFullHint">全局默认;每份试卷可在主页面「试卷设置」中单独覆盖</p>
|
||
</div>
|
||
<div class="fi">
|
||
<label>全局默认合格率(%)</label>
|
||
<input type="number" id="cfgPassRate" min="1" max="100" step="1">
|
||
<p class="hint">达到满分的该百分比即算合格;每份试卷可单独覆盖</p>
|
||
</div>
|
||
<div class="fi">
|
||
<label>历史成绩展示方式</label>
|
||
<select id="cfgHistoryDisplay">
|
||
<option value="click">点击成员弹窗查看(默认)</option>
|
||
<option value="inline">成绩明细内全部展开</option>
|
||
</select>
|
||
<p class="hint">「全部展开」时每次考试单独一行,直接显示在成绩明细中</p>
|
||
</div>
|
||
<div class="fi">
|
||
<label>成绩导出排序</label>
|
||
<select id="cfgExportSort">
|
||
<option value="time">按提交时间(默认)</option>
|
||
<option value="sid">按学号(同学号按提交时间)</option>
|
||
<option value="score_desc">按成绩降序</option>
|
||
<option value="score_asc">按成绩升序</option>
|
||
</select>
|
||
<p class="hint">导出包含每次考试记录,始终带「考试次数」列</p>
|
||
</div>
|
||
<div class="fi full">
|
||
<label>姓名题关键词</label>
|
||
<input type="text" id="cfgKeywords">
|
||
<p class="hint">英文逗号分隔,用于自动识别"姓名"题目</p>
|
||
</div>
|
||
<div class="fi full checks">
|
||
<label><input type="checkbox" id="cfgDedupe"> 同一学号只保留最新一份答卷</label>
|
||
<label><input type="checkbox" id="cfgTemp"> 包含"暂存"未提交的答卷</label>
|
||
<label><input type="checkbox" id="cfgSsl"> 校验 HTTPS 证书</label>
|
||
</div>
|
||
<p class="hint" style="margin-top:-4px">
|
||
<b>「只保留最新一份」说明</b>:勾选后主明细表的「成绩」列取该生最新一次提交的分数,
|
||
取消勾选则取最高分。<b>无论是否勾选,「考试次数」与「历史成绩」始终展示全部提交记录</b>。
|
||
</p>
|
||
|
||
<div class="fi full" style="margin-top:6px">
|
||
<div style="display:flex;align-items:center;gap:8px">
|
||
<label style="margin:0">🔌 卷王系统连接</label>
|
||
<span class="hint" id="cfgConnInfo" style="font-size:12px"></span>
|
||
</div>
|
||
</div>
|
||
<div class="fi full">
|
||
<label>系统地址</label>
|
||
<input type="text" id="cfgBase" placeholder="https://exam.nxxy335.top" spellcheck="false">
|
||
<p class="hint">卷王后台地址,结尾不要带 <code>/</code>;修改后保存会自动重连</p>
|
||
</div>
|
||
<div class="fi">
|
||
<label>接口前缀</label>
|
||
<input type="text" id="cfgPrefix" placeholder="/api" spellcheck="false">
|
||
<p class="hint">社区版 <code>/api</code>;企业版可能是 <code>/admin-api</code></p>
|
||
</div>
|
||
<div class="fi">
|
||
<label>登录账号</label>
|
||
<input type="text" id="cfgUser" autocomplete="username" placeholder="登录卷王的账号">
|
||
</div>
|
||
<div class="fi">
|
||
<label>登录密码</label>
|
||
<input type="password" id="cfgPass" autocomplete="new-password" placeholder="已保存 · 留空表示不修改">
|
||
<p class="hint">为安全,已保存的密码不回显;如需修改请直接输入新密码后保存</p>
|
||
</div>
|
||
</div>
|
||
<div id="pingResult" class="ping-result hidden"></div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<span class="hint" style="align-self:center">修改地址 / 前缀 / 账号 / 密码后保存会自动重新连接</span>
|
||
<span class="spacer"></span>
|
||
<button class="btn" id="btnTestConn">测试连接</button>
|
||
<button class="btn" data-close>取消</button>
|
||
<button class="btn btn-primary" id="btnSaveCfg">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ================= 名单管理 ================= -->
|
||
<div class="modal" id="rosterModal">
|
||
<div class="modal-box wide">
|
||
<div class="modal-head"><h3>学号名单管理</h3><button class="x" data-close>×</button></div>
|
||
<div class="modal-body">
|
||
<div class="upload-zone" id="dropZone">
|
||
<input type="file" id="rosterFile" accept=".xlsx,.xlsm,.csv,.txt" hidden>
|
||
<div class="uz-inner">
|
||
<strong>点击或拖拽文件到此上传</strong>
|
||
<span>支持 .xlsx / .csv,需包含「学号」「姓名」两列(「班级」「组别」可选,组别如 A/B)</span>
|
||
</div>
|
||
</div>
|
||
<div class="roster-bar">
|
||
<button class="btn sm" id="btnTpl" title="下载名单导入模板(CSV,含「登陆账号」列)">⬇ 下载模板</button>
|
||
<div class="inline">
|
||
<label class="chk"><input type="radio" name="impMode" value="replace"> 覆盖</label>
|
||
<label class="chk"><input type="radio" name="impMode" value="append" checked> 追加</label>
|
||
</div>
|
||
<input type="text" id="rosterSearch" placeholder="搜索学号 / 姓名">
|
||
<span class="spacer"></span>
|
||
<button class="btn sm" id="btnAddRow">+ 新增</button>
|
||
<button class="btn sm danger" id="btnDelSel" hidden>删除选中</button>
|
||
<button class="btn sm danger" id="btnClearRoster">清空</button>
|
||
<button class="btn sm btn-primary" id="btnSaveRoster">保存名单</button>
|
||
</div>
|
||
<div class="table-scroll mid">
|
||
<table class="grid compact" id="rosterTable">
|
||
<thead><tr><th style="width:6%"><input type="checkbox" id="rosterCheckAll" title="全选"></th><th style="width:26%">学号</th><th style="width:22%">姓名</th><th style="width:26%">班级</th><th style="width:20%">组别</th></tr></thead>
|
||
<tbody id="rosterBody"></tbody>
|
||
</table>
|
||
</div>
|
||
<p class="hint" id="rosterMeta">共 0 人</p>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<span class="spacer"></span>
|
||
<button class="btn" data-close>关闭</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 多次刷题明细弹窗 -->
|
||
<div class="modal" id="attemptsModal">
|
||
<div class="modal-box">
|
||
<div class="modal-head"><h3 id="attTitle">提交明细</h3><button class="x" data-close>×</button></div>
|
||
<div class="modal-body" id="attBody"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 新增学生弹窗 -->
|
||
<div class="modal" id="addStudentModal">
|
||
<div class="modal-box">
|
||
<div class="modal-head"><h3>新增学生</h3><button class="x" data-close>×</button></div>
|
||
<div class="modal-body">
|
||
<div class="fi">
|
||
<label>学号 <b class="req">*</b></label>
|
||
<input type="text" id="addSid" placeholder="必填">
|
||
</div>
|
||
<div class="fi">
|
||
<label>姓名 <b class="req">*</b></label>
|
||
<input type="text" id="addName" placeholder="必填">
|
||
</div>
|
||
<div class="fi">
|
||
<label>班级</label>
|
||
<input type="text" id="addCls" placeholder="可选">
|
||
</div>
|
||
<div class="fi">
|
||
<label>组别</label>
|
||
<input type="text" id="addGrp" placeholder="如 A / B,可选">
|
||
</div>
|
||
<div class="fi" hidden>
|
||
<label>登陆账号</label>
|
||
<input type="text" id="addLogin" placeholder="可选,用于抽取页登录">
|
||
</div>
|
||
<p class="hint" id="addStudentHint"></p>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<span class="spacer"></span>
|
||
<button class="btn" data-close>取消</button>
|
||
<button class="btn btn-primary" id="btnAddStudentOk">添加</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 未匹配答卷映射到名单 -->
|
||
<div class="modal" id="mapUnmatchedModal">
|
||
<div class="modal-box">
|
||
<div class="modal-head"><h3>加入名单</h3><button class="x" data-close>×</button></div>
|
||
<div class="modal-body">
|
||
<p class="hint" style="margin:0 0 12px">答卷姓名:<b id="mapUmName"></b>(不在名单中)</p>
|
||
<div class="fi">
|
||
<label>学号 <b class="req">*</b></label>
|
||
<input type="text" id="mapUmSid" placeholder="输入该生的学号">
|
||
</div>
|
||
<div class="fi">
|
||
<label>班级</label>
|
||
<input type="text" id="mapUmCls" placeholder="可选">
|
||
</div>
|
||
<div class="fi">
|
||
<label>组别</label>
|
||
<input type="text" id="mapUmGrp" placeholder="可选">
|
||
</div>
|
||
<p class="hint" id="mapUmHint"></p>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<span class="spacer"></span>
|
||
<button class="btn" data-close>取消</button>
|
||
<button class="btn btn-primary" id="btnMapUmOk">加入名单</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 成绩明细打印设置 -->
|
||
<div class="modal" id="printTableModal">
|
||
<div class="modal-box">
|
||
<div class="modal-head"><h3>打印成绩明细</h3><button class="x" data-close>×</button></div>
|
||
<div class="modal-body">
|
||
<div class="fi">
|
||
<label>打印标题</label>
|
||
<input type="text" id="ptTitle" placeholder="默认:试卷名 + 成绩明细">
|
||
</div>
|
||
<div class="fi">
|
||
<label>排序方式</label>
|
||
<select id="ptSort">
|
||
<option value="score_desc" selected>按成绩降序(默认)</option>
|
||
<option value="score_asc">按成绩升序</option>
|
||
<option value="sid">按学号</option>
|
||
<option value="time">按提交时间</option>
|
||
</select>
|
||
</div>
|
||
<div class="fi">
|
||
<label>分组对比</label>
|
||
<select id="ptGroup">
|
||
<option value="none" selected>不分组(连续打印)</option>
|
||
<option value="cls">按班级分组</option>
|
||
<option value="grp">按组别分组</option>
|
||
</select>
|
||
<p class="hint">分组后每个班/组单独一段,段首显示小计(人数/平均/最高/最低/合格率)</p>
|
||
</div>
|
||
<div class="fi">
|
||
<label>纸张方向</label>
|
||
<select id="ptOrient">
|
||
<option value="portrait" selected>纵向(A4,列少时清晰)</option>
|
||
<option value="landscape">横向(A4,列多时不易折行)</option>
|
||
</select>
|
||
</div>
|
||
<div class="fi">
|
||
<label>打印范围</label>
|
||
<select id="ptScope">
|
||
<option value="latest" selected>每人一行(汇总)</option>
|
||
<option value="all">每次考试一行(含历史提交)</option>
|
||
</select>
|
||
<p class="hint">「每次考试一行」会把每位学生的每次提交各打一行,完整留存历史。</p>
|
||
</div>
|
||
<div class="fi">
|
||
<label>每人成绩口径</label>
|
||
<select id="ptRowScore" title="每人一行时的代表成绩;选「每次考试一行」时本项不生效">
|
||
<option value="latest" selected>最新成绩(最近一次提交)</option>
|
||
<option value="best">最高成绩(该生最高分)</option>
|
||
</select>
|
||
<p class="hint">与「导出 → 成绩明细」的「每人成绩」一致,默认最新成绩。</p>
|
||
</div>
|
||
<div class="fi checks">
|
||
<label><input type="checkbox" id="ptRank" checked> 显示名次(全表连续编号)</label>
|
||
<label><input type="checkbox" id="ptPass"> 显示「是否合格」列(默认不显示)</label>
|
||
<label><input type="checkbox" id="ptAttempts" checked> 显示考试次数</label>
|
||
<label><input type="checkbox" id="ptAbsent" title="成绩表后另起一页附缺考名单,含备注列供手写"> 附缺考名单(另起一页)</label>
|
||
</div>
|
||
<p class="hint">打印内容<b>不受页面当前筛选外的影响</b>,始终按当前班级/组别筛选结果输出;表头会在每页自动重复,末尾附签字栏。</p>
|
||
<p class="hint">要打印<b>多场考试对比</b>或<b>「每次考试」表</b>(最高分标红、合格次数达标绿底)?用工具栏的 <b>导出</b> 弹窗,选「多考试对比」或「每次考试」后点「打印」。</p>
|
||
<p class="hint" id="ptHint"></p>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<span class="spacer"></span>
|
||
<button class="btn" data-close>取消</button>
|
||
<button class="btn btn-primary" id="btnPtPrint">打印</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 试卷独立设置(满分 / 合格率 / 免考名单)——仅对当前选中试卷生效 -->
|
||
<div class="modal" id="projCfgModal">
|
||
<div class="modal-box">
|
||
<div class="modal-head"><h3>试卷设置 <small id="projCfgSub"></small></h3><button class="x" data-close>×</button></div>
|
||
<div class="modal-body">
|
||
<p class="hint" style="margin:0 0 12px">以下设置<b>仅对当前选中的这份试卷生效</b>,各试卷之间互不影响。留空表示跟随全局默认。</p>
|
||
<div class="fi">
|
||
<label>本卷满分</label>
|
||
<input type="number" id="pcFull" min="1" max="1000" placeholder="留空=用全局默认">
|
||
<p class="hint" id="pcFullHint"></p>
|
||
</div>
|
||
<div class="fi">
|
||
<label>本卷合格率(%)</label>
|
||
<input type="number" id="pcPassRate" min="1" max="100" step="1" placeholder="留空=用全局默认">
|
||
<p class="hint" id="pcPassHint"></p>
|
||
</div>
|
||
<div class="fi">
|
||
<label>免考学生(学号)</label>
|
||
<textarea id="pcExempt" rows="4" placeholder="每行一个学号,也可用逗号分隔"></textarea>
|
||
<p class="hint">免考学生不计入「名单总人数」与「缺考名单」</p>
|
||
</div>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="btn danger" id="btnPcReset">清除本卷设置</button>
|
||
<span class="spacer"></span>
|
||
<button class="btn" data-close>取消</button>
|
||
<button class="btn btn-primary" id="btnPcSave">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ================= 统一导出 / 打印 =================
|
||
所有导出(成绩明细 / 每次考试 / 缺考名单 / 多考试对比)与打印都在这一个弹窗里配置:
|
||
先选「导出什么」,再按需展开「范围」「列与标记」「打印」三个区块。 -->
|
||
<div class="modal" id="exportModal">
|
||
<div class="modal-box wide">
|
||
<div class="modal-head"><h3>导出 / 打印</h3><button class="x" data-close>×</button></div>
|
||
<div class="modal-body">
|
||
|
||
<!-- 1. 导出内容 -->
|
||
<div class="ex-sec">
|
||
<div class="ex-sec-title">导出内容</div>
|
||
<div class="ex-types">
|
||
<label class="ex-type" data-for="detail">
|
||
<input type="radio" name="exType" value="detail" checked>
|
||
<span class="ex-type-t">
|
||
<b>成绩明细</b>
|
||
<small>每人一行:学号 / 姓名 / 班级 / 组别 / 成绩 / 次数 / 时间 / 时长</small>
|
||
</span>
|
||
</label>
|
||
<label class="ex-type" data-for="attempts">
|
||
<input type="radio" name="exType" value="attempts">
|
||
<span class="ex-type-t">
|
||
<b>每次考试</b>
|
||
<small>按截图版式:一行一次考试,最高分标红、合格次数标绿</small>
|
||
</span>
|
||
</label>
|
||
<label class="ex-type" data-for="absent">
|
||
<input type="radio" name="exType" value="absent">
|
||
<span class="ex-type-t">
|
||
<b>缺考名单</b>
|
||
<small>学号 / 姓名 / 班级 / 组别(跟随下方筛选范围)</small>
|
||
</span>
|
||
</label>
|
||
<label class="ex-type" data-for="multi">
|
||
<input type="radio" name="exType" value="multi">
|
||
<span class="ex-type-t">
|
||
<b>多考试对比</b>
|
||
<small>勾选多场考试,横向对比:每场一列,最高分标红、合格次数标绿</small>
|
||
</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 2. 统计范围(班级 / 组别) -->
|
||
<div class="ex-sec" id="exScopeSec">
|
||
<div class="ex-sec-title">统计范围 <small id="exScopeSum"></small></div>
|
||
<div class="ex-row">
|
||
<label class="ex-field">班级
|
||
<select id="exClsSel" multiple size="4" title="按住 Ctrl / ⌘ 可多选;不选 = 全部班级"></select>
|
||
</label>
|
||
<label class="ex-field">组别
|
||
<select id="exGrpSel" multiple size="4" title="按住 Ctrl / ⌘ 可多选;不选 = 全部组别"></select>
|
||
</label>
|
||
<div class="ex-field ex-field-tip">
|
||
<span class="hint" id="exScopeHint">不选 = 全部(Ctrl / ⌘ 多选)</span>
|
||
<span class="hint">选多个班级 / 组别时,导出会自动补出对应列并按相同值合并单元格。</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 3. 明细列与标记(成绩明细 / 每次考试 用) -->
|
||
<div class="ex-sec" id="exDetailSec">
|
||
<div class="ex-sec-title">成绩口径与列</div>
|
||
<div class="ex-row">
|
||
<label class="ex-field">每人成绩
|
||
<select id="exRowScore" title="每人一行时的代表成绩:最新 = 最近一次真实提交;最高 = 该生最高分">
|
||
<option value="latest" selected>最新成绩</option>
|
||
<option value="best">最高成绩</option>
|
||
</select>
|
||
</label>
|
||
<label class="ex-field">排序
|
||
<select id="exSort">
|
||
<option value="time">按提交时间</option>
|
||
<option value="sid">按学号</option>
|
||
<option value="score_desc">按成绩降序</option>
|
||
<option value="score_asc">按成绩升序</option>
|
||
</select>
|
||
</label>
|
||
<label class="ex-field">出表方式
|
||
<select id="exDetailMode">
|
||
<option value="latest" selected>每人一行(汇总)</option>
|
||
<option value="all">每次考试一行(明细)</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
<div class="ex-checks" id="exDetailChecks">
|
||
<label><input type="checkbox" id="exSplit"> 按班级分工作表</label>
|
||
</div>
|
||
<p class="hint" id="exDetailHint">每人一行时可用「每人成绩」切换最新 / 最高;「每次考试一行」会保留每位学生的历史提交。</p>
|
||
</div>
|
||
|
||
<!-- 3.5 附加工作表(成绩明细 / 每次考试 / 多考试对比 共用) -->
|
||
<div class="ex-sec" id="exExtraSec">
|
||
<div class="ex-sec-title">附加内容</div>
|
||
<div class="ex-checks">
|
||
<label><input type="checkbox" id="exWithAbsent" checked> 附缺考名单(名单里有、所选考试全都没交的人)</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 4. 标记与合格次数(成绩明细 / 每次考试 / 多考试对比 共用) -->
|
||
<div class="ex-sec" id="exMarkSec">
|
||
<div class="ex-sec-title">标记与合格次数</div>
|
||
<div class="ex-row">
|
||
<label class="ex-field">合格次数阈值
|
||
<input type="number" id="exPassTimes" min="1" max="20" step="1" value="1"
|
||
title="同一场考试里「达到合格线」的次数 ≥ 此值才标绿(默认 1 次)">
|
||
</label>
|
||
<div class="ex-field ex-field-tip">
|
||
<span class="hint">合格线 = 该场考试满分 × 合格率(在「试卷设置」里按卷配置,缺省用全局)</span>
|
||
<span class="hint">例:阈值填 2,则该场考到合格线 ≥ 2 次才把「合格次数」标绿。</span>
|
||
</div>
|
||
</div>
|
||
<div class="ex-checks">
|
||
<label><input type="checkbox" id="exMarkBest" checked> 最高分标红</label>
|
||
<label><input type="checkbox" id="exPassRow" checked> 输出「合格次数」行(达标绿底)</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 5. 多考试内容 -->
|
||
<div class="ex-sec" id="exMultiSec" hidden>
|
||
<div class="ex-sec-title">多考试对比</div>
|
||
<div class="ex-row">
|
||
<label class="ex-field">表格形态
|
||
<select id="exMultiMode">
|
||
<option value="block" selected>两行表头(每次考试 + 合格次数)</option>
|
||
<option value="summary">成绩对比(每人一行)</option>
|
||
<option value="both">两者都出</option>
|
||
</select>
|
||
</label>
|
||
<label class="ex-field">对比成绩
|
||
<select id="exMultiScore">
|
||
<option value="latest" selected>最新成绩</option>
|
||
<option value="highest">最高成绩</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
<div class="ex-pick">
|
||
<div class="ex-pick-head">
|
||
<span>勾选要对比的考试 <small id="exPickCount"></small></span>
|
||
<span class="spacer"></span>
|
||
<button class="btn sm" id="btnExPickAll" type="button">全选</button>
|
||
<button class="btn sm" id="btnExPickNone" type="button">清空</button>
|
||
</div>
|
||
<div class="table-scroll mid">
|
||
<table class="grid compact" id="exExamTable">
|
||
<thead><tr><th style="width:8%"><input type="checkbox" id="exCheckAll" title="全选"></th><th style="width:8%">顺序</th><th>考试名称</th></tr></thead>
|
||
<tbody id="exExamBody"><tr><td colspan="3" class="empty">(暂无可导出的考试,请先在右上角刷新问卷列表)</td></tr></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 6. 打印设置 -->
|
||
<div class="ex-sec" id="exPrintSec" hidden>
|
||
<div class="ex-sec-title">打印设置</div>
|
||
<div class="ex-row">
|
||
<label class="ex-field">纸张方向
|
||
<select id="exOrient" title="多考试打印默认横向(列多,横向不易折行)">
|
||
<option value="landscape" selected>横向(A4)</option>
|
||
<option value="portrait">纵向(A4)</option>
|
||
</select>
|
||
</label>
|
||
<label class="ex-field ex-field-grow">打印标题
|
||
<input type="text" id="exPrintTitle" placeholder="留空 = 用考试名自动生成">
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="hint" id="exSummaryLine" style="margin-top:4px"></p>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<span class="spacer"></span>
|
||
<button class="btn" data-close>取消</button>
|
||
<button class="btn" id="btnExPrint">打印</button>
|
||
<button class="btn btn-primary" id="btnExExport">导出 Excel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<script>
|
||
window.NY = {
|
||
appName: <?= json_encode(NY_APP_NAME, JSON_UNESCAPED_UNICODE) ?>,
|
||
version: <?= json_encode(NY_VERSION, JSON_UNESCAPED_UNICODE) ?>,
|
||
interval: <?= (int)$cfg['refresh_interval'] ?>
|
||
};
|
||
</script>
|
||
<script src="assets/app.js"></script>
|
||
</body>
|
||
</html>
|