Files
ny-exam-platform/draw.php
T
nxxy335 25a2e8a42c 文档部署到 /ny-exam-platform 子路径 + 移除快照/趋势/异常 + 打印与 UI 全面完善
文档站(#88)
- base 默认改为 /ny-exam-platform/,可用 DOCS_BASE=/ 覆盖本地预览
- 新增 DOCS_OUT 环境变量,避免构建时清空输出目录失败导致产物半截
- 新增 tools/docs-preview.js:按子路径重写路由的本地预览服务
- 删除 docs/guide/snapshot.md,同步清理全部相关文档

移除存快照 / 趋势 / 异常(#89)
- 后端:删除 api.php 的 snapshot_save/list/delete/trend 四个 action 与
  $WRITE_ACTIONS 中的同名条目;删除 lib/Snapshot.php 与自动加载注册;
  DataService 移除 detectAnomalies() 及其调用
- 前端:移除三个按钮与三个弹窗、相关 JS 处理与 state 字段
- 测试:移除对应用例,补充 5 条名单导入列识别断言(17+9+18+32+29+11 全过)

打印完善(#90)
- 改用隐藏 iframe 输出,规避弹窗拦截;@page A4 支持纵向/横向
- 表头每页重复、行内不跨页断开、全表连续名次、可选「是否合格」列
- 分组小计(人数/平均/最高/最低/合格人数与合格率)、可附缺考名单另起一页
- 末尾签字栏;缺考名单打印改为同一套输出

UI / 交互 / Bug 修复(#91)
- dashboard.php:补「← 主系统 / 退出」入口(此前全屏后无法离开);
  修复成员下钻 onclick 里 JSON.stringify 双引号提前闭合属性导致点击报错,
  改为 data-sid 事件委托;补 Esc 关弹窗;后台标签页暂停轮询、切回自动补拉;
  刷新中断原因写入 title;数值字段统一兜底,避免 NaN / undefined%
- draw.php / draw.js:结果表新增「登陆账号」列(导出前可核对,缺失显示 学号 ⚠);
  弹窗只关最上层且支持 Esc;「全部」改为跟随候选池,手工改人数即解除;
  候选池不足时给出明确提示;自定义比例框隐藏时清空,避免旧权重被误读
- app.js / dashboard.php:成绩着色改为按「占本卷满分的百分比」判断,
  修复满分不是 100 时整列误标红
- style.css:数值列右对齐;新增 subbar、draw-stage-sub;
  看板顶栏窄屏下拉与按钮换行,消除横向滚动
2026-09-11 16:23:08 +08:00

170 lines
7.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* NY 问卷考试系统数据平台 - 随机抽取
* 从名单 / 已提交人员中随机抽取(支持按人数加权/均分/自定义比例/不分组、排除名单、历史记录),
* 导出为卷王白名单(两列:姓名 / 登陆账号)
*/
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
if (!ny_is_auth()) {
header('Location: login.php');
exit;
}
$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">
<a class="btn" href="index.php#roster" title="前往主系统管理学号名单">名单管理 <b id="rosterBadge"><?= (int)$rosterCount ?></b></a>
<button class="btn" id="btnClearLog" title="清空全部历史记录(重置结果不影响历史记录)">清空历史</button>
<a class="btn" href="index.php">← 返回主系统</a>
<a class="btn" href="logout.php" title="退出登录">退出</a>
</div>
</header>
<main class="wrap">
<section class="toolbar">
<div class="field">
<label>抽取来源</label>
<select id="sourceSel">
<option value="roster">学号名单(全部)</option>
<option value="project">某试卷已提交人员</option>
</select>
</div>
<div class="field grow hidden" id="projectField">
<label>问卷 / 考试</label>
<select id="projectSel"><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">
<input type="number" id="numInput" min="1" step="1" value="1">
<button class="btn sm" data-num="1">1</button>
<button class="btn sm" data-num="5">5</button>
<button class="btn sm" data-num="10">10</button>
<button class="btn sm" data-num="all">全部</button>
</div>
</div>
<div class="field btns">
<label class="chk" title="决定有分组时如何分配名额"><span style="color:var(--text-2);font-size:12px">分组比例</span>
<select id="balanceMode" style="height:30px;border-radius:8px;border:1px solid var(--border);padding:0 8px;background:#fff;margin-left:4px">
<option value="weight">按人数加权(默认)</option>
<option value="even">各组均分(5:5)</option>
<option value="custom">自定义比例</option>
<option value="off">不分组(整池随机)</option>
</select>
</label>
<label class="chk" title="再次抽取时不与本次会话已抽中的人重复;「重置结果」后清零"><input type="checkbox" id="excludePicked" checked> 排除已抽中</label>
<button class="btn" id="btnExclude" title="勾选不参与抽取的人">🚫 排除管理</button>
<button class="btn btn-primary" id="btnDraw">🎲 开始抽取</button>
<button class="btn" id="btnReset" title="清空本次结果与累计已抽取(历史记录保留)">重置结果</button>
</div>
<div class="field full hidden" id="ratioBox" style="grid-column:1/-1"></div>
</section>
<section class="cards" id="statCards">
<div class="card accent-blue"><span>候选人数</span><b id="dPool">-</b></div>
<div class="card accent-green"><span>本次抽取</span><b id="dPicked">-</b></div>
<div class="card warn accent-orange"><span>累计已抽取</span><b id="dHistory">-</b></div>
<div class="card accent-red"><span>已排除</span><b id="dExcluded">-</b></div>
<div class="card accent-gray"><span>抽取来源</span><b id="dSource">学号名单</b></div>
</section>
<section class="panel">
<div class="panel-head">
<h2>抽取结果 <small id="drawHint"></small></h2>
<div class="actions">
<button class="btn" id="btnLog" title="查看历史抽取批次(重置结果后仍保留)">📜 历史 <b id="logBadge">0</b></button>
<button class="btn" id="btnCopy" title="复制姓名 / 登陆账号到剪贴板(可直接粘贴到微信 / Excel)">📋 复制名单</button>
<button class="btn" id="btnDlImport" title="导出为「白名单导入模板.xlsx」">⬇ 白名单导入模板</button>
<button class="btn btn-primary" id="btnDlList" title="导出为「白名单列表.xlsx」">⬇ 白名单列表</button>
</div>
</div>
<div class="draw-stage" id="drawStage">点击「🎲 开始抽取」</div>
<div class="table-scroll">
<table class="grid">
<thead>
<tr>
<th style="width:64px">序号</th>
<th style="width:22%">学号</th>
<th style="width:18%">姓名</th>
<th>班级</th>
<th>组别</th>
<th style="width:22%">登陆账号</th>
<th style="width:88px">操作</th>
</tr>
</thead>
<tbody id="drawBody"><tr><td colspan="7" class="empty">尚未抽取</td></tr></tbody>
</table>
</div>
<p class="hint">两个下载与「📋 复制名单」均为两列「<b>姓名</b> / <b>登陆账号</b>」,登陆账号取名单中导入的「登陆账号」列。<b>表中「登陆账号」列显示为空即表示名单里没有填,导出时会自动回退用学号</b>,请先到主系统「名单管理」补全,否则学生无法登录考试。</p>
</section>
</main>
<!-- ================= 排除管理 ================= -->
<div class="modal" id="exclModal">
<div class="modal-box wide">
<div class="modal-head"><h3>抽取排除管理</h3><button class="x" data-close>&times;</button></div>
<div class="modal-body">
<div class="roster-bar">
<input type="text" id="exclSearch" placeholder="搜索学号 / 姓名 / 班级">
<span class="spacer"></span>
<span class="hint" id="exclCount">已排除 0 人</span>
<button class="btn sm danger" id="btnClearExcl">清空排除</button>
</div>
<div class="table-scroll mid">
<table class="grid compact">
<thead><tr><th style="width:64px">排除</th><th style="width:24%">学号</th><th style="width:22%">姓名</th><th>班级</th><th>组别</th></tr></thead>
<tbody id="exclBody"><tr><td colspan="5" class="empty">加载中…</td></tr></tbody>
</table>
</div>
<p class="hint">勾选后这些人不再参与抽取(勾选即时生效);仅影响随机抽取,不影响其他功能。</p>
</div>
<div class="modal-foot"><span class="spacer"></span><button class="btn btn-primary" data-close>完成</button></div>
</div>
</div>
<!-- ================= 历史记录 ================= -->
<div class="modal" id="logModal">
<div class="modal-box wide">
<div class="modal-head"><h3>抽取历史记录</h3><button class="x" data-close>&times;</button></div>
<div class="modal-body">
<div id="logList" class="log-list"><p class="empty" style="text-align:center;padding:30px 0">暂无历史记录</p></div>
<p class="hint">每次抽取自动记一笔;「重置结果」不会清除历史记录,可随时恢复某一批次重新下载。</p>
</div>
<div class="modal-foot">
<span class="spacer"></span>
<button class="btn btn-primary" data-close>关闭</button>
</div>
</div>
</div>
<script src="assets/draw.js"></script>
</body>
</html>