You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

755 lines
34 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>“光联万物 创芯未来”首届沪苏协同新兴产业科创大赛报名系统</title>
<link rel="stylesheet" href="../assets/bootstrap.min.css" />
<link rel="stylesheet" href="../assets/prototype.css" />
<script src="../assets/prototype-scale.js?v=20260819-hero"></script>
</head>
<body class="prototype-page admin-desktop-page">
<div class="container-fluid p-3 p-md-4">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
<h5 class="mb-0 section-title" id="adminListTitle">项目管理</h5>
<div class="d-flex gap-2">
<button type="button" class="btn btn-primary btn-sm" id="adminListExportBtn" data-bs-toggle="modal" data-bs-target="#exportModal">导出</button>
</div>
</div>
<div class="filter-panel mb-3" id="adminListFilterPanel">
<div class="row g-2">
<div class="col-md-3">
<input type="search" class="form-control" id="filterKeyword" placeholder="项目名称/姓名/院校/所在地" autocomplete="off" />
</div>
<div class="col-md-3">
<div class="track-custom-select w-100">
<select id="filterTrack" class="visually-hidden" tabindex="-1" aria-hidden="true">
<option value="">全部赛道</option>
<option value="芯片与组件">芯片与组件</option>
<option value="制造与封装">制造与封装</option>
<option value="材料与设备">材料与设备</option>
<option value="场景与应用">场景与应用</option>
</select>
<div class="dropdown w-100">
<button type="button" class="form-select track-custom-toggle w-100 text-start" id="filterTrackDropdownBtn" data-bs-toggle="dropdown" data-bs-display="static" aria-label="筛选赛道" aria-expanded="false">
<span class="d-block text-truncate"><span id="filterTrackMainText" class="text-secondary">全部赛道</span><span id="filterTrackNoteText" class="track-custom-note"></span></span>
</button>
<ul class="dropdown-menu w-100 border shadow-sm rounded-2 py-0 my-1 track-custom-menu">
<li><button type="button" class="dropdown-item track-pick border-0 w-100 text-start rounded-0" data-value=""><span class="text-secondary">全部赛道</span></button></li>
<li><button type="button" class="dropdown-item track-pick border-0 w-100 text-start rounded-0" data-value="芯片与组件"><span class="text-body">芯片与组件</span></button></li>
<li><button type="button" class="dropdown-item track-pick border-0 w-100 text-start rounded-0" data-value="制造与封装"><span class="text-body">制造与封装</span></button></li>
<li><button type="button" class="dropdown-item track-pick border-0 w-100 text-start rounded-0" data-value="材料与设备"><span class="text-body">材料与设备</span></button></li>
<li><button type="button" class="dropdown-item track-pick border-0 w-100 text-start rounded-0" data-value="场景与应用"><span class="text-body">场景与应用</span></button></li>
</ul>
</div>
</div>
</div>
<div class="col-md-2">
<select class="form-select" id="filterStatus">
<option value="">全部状态</option>
<option value="通过">通过</option>
<option value="未通过">未通过</option>
</select>
</div>
<div class="col-12 col-md-auto d-flex gap-2 align-items-end flex-shrink-0">
<button type="button" class="btn btn-primary px-3" id="filterQueryBtn">查询</button>
<button type="button" class="btn btn-light border px-3" id="filterResetBtn">重置</button>
</div>
</div>
<p class="small text-secondary mb-0 mt-2" id="filterResultHint" aria-live="polite"></p>
</div>
<div class="d-flex align-items-center gap-2 mb-2" id="adminBatchToolbar">
<button type="button" class="btn btn-sm btn-light border" id="selectAllBtn">全选</button>
<button type="button" class="btn btn-sm btn-light border" id="invertSelectBtn">反选</button>
<button type="button" class="btn btn-sm btn-light border" id="clearSelectBtn">取消选择</button>
</div>
<div class="table-responsive table-panel">
<table class="table table-hover align-middle mb-0 admin-list-table">
<thead>
<tr>
<th class="text-center" style="width: 2.5rem">
<input class="form-check-input" type="checkbox" id="checkAllRows" aria-label="全选当前列表" />
</th>
<th class="text-nowrap" style="width: 3.5rem">序号</th>
<th>项目编号</th>
<th>项目名称</th>
<th>姓名</th>
<th>项目所在地</th>
<th class="d-none reviewer-only-col" id="adminListSchoolHead">毕业院校</th>
<th class="d-none reviewer-only-col" id="adminListDegreeHead">最高学历</th>
<th>赛道</th>
<th id="adminListStatusHead">总分</th>
<th id="adminListAvgHead">平均分</th>
<th class="admin-manage-status-col" id="adminListManageStatusHead">状态</th>
<th class="d-none reviewer-only-col" id="adminListSubmitTimeHead">提交时间</th>
<th class="admin-actions-head">操作</th>
</tr>
</thead>
<tbody id="adminListBody">
<tr data-list-row data-review-scores="" data-review-progress="1/4" data-review-result="">
<td class="text-center"><input class="form-check-input row-check" type="checkbox" aria-label="选择项目 P2026-0001" /></td>
<td class="text-secondary text-nowrap">1</td>
<td>P2026-0001</td>
<td>高速硅光芯片研发平台</td>
<td>张晓华</td>
<td>中国 / 江苏省 / 南京市</td>
<td class="d-none reviewer-only-col">南京大学</td>
<td class="d-none reviewer-only-col">硕士</td>
<td>场景与应用</td>
<td class="admin-total-pending-text">待评审</td>
<td class="admin-avg-cell">-</td>
<td class="admin-manage-status-col admin-status-cell">-</td>
<td class="d-none reviewer-only-col">2026-04-27 10:25</td>
<td class="admin-actions-cell">
<a class="btn btn-sm btn-outline-primary action-view-link" href="./admin-detail.html?state=pending&amp;id=P2026-0001">查看</a>
</td>
</tr>
<tr data-list-row data-review-scores="99,100,98,97" data-review-progress="4/4" data-review-result="通过">
<td class="text-center"><input class="form-check-input row-check" type="checkbox" aria-label="选择项目 P2026-0002" /></td>
<td class="text-secondary text-nowrap">2</td>
<td>P2026-0002</td>
<td>高密度光模块封装方案</td>
<td>顾苏明</td>
<td>中国 / 浙江省 / 杭州市</td>
<td class="d-none reviewer-only-col">浙江大学</td>
<td class="d-none reviewer-only-col">本科</td>
<td>材料与设备</td>
<td>99</td>
<td class="admin-avg-cell">99.00</td>
<td class="admin-manage-status-col admin-status-cell">通过</td>
<td class="d-none reviewer-only-col">2026-04-26 16:08</td>
<td class="admin-actions-cell">
<a class="btn btn-sm btn-outline-primary action-view-link" href="./admin-detail.html?state=pass&amp;id=P2026-0002">查看</a>
</td>
</tr>
<tr data-list-row data-review-scores="80,78,82,79" data-review-progress="4/4" data-review-result="未通过">
<td class="text-center"><input class="form-check-input row-check" type="checkbox" aria-label="选择项目 P2026-0003" /></td>
<td class="text-secondary text-nowrap">3</td>
<td>P2026-0003</td>
<td>智能光纤传感系统</td>
<td>陈静</td>
<td>海外 / 新加坡</td>
<td class="d-none reviewer-only-col">新加坡国立大学</td>
<td class="d-none reviewer-only-col">本科</td>
<td>制造与封装</td>
<td>80</td>
<td class="admin-avg-cell">80.00</td>
<td class="admin-manage-status-col admin-status-cell">未通过</td>
<td class="d-none reviewer-only-col">2026-04-25 09:40</td>
<td class="admin-actions-cell">
<a class="btn btn-sm btn-outline-primary action-view-link" href="./admin-detail.html?state=reject&amp;id=P2026-0003">查看</a>
</td>
</tr>
<tr id="filterEmptyRow" class="d-none">
<td colspan="14" class="text-center text-secondary py-4">暂无符合条件的数据</td>
</tr>
</tbody>
</table>
</div>
<div id="adminBatchActionBar" class="d-flex justify-content-start gap-2 mt-2">
<button type="button" class="btn btn-sm btn-outline-primary" id="batchPassBtn" disabled>通过</button>
<button type="button" class="btn btn-sm btn-outline-primary" id="batchFailBtn" disabled>未通过</button>
</div>
</div>
<div class="modal fade" id="exportModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-export-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">批量导出报名材料</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p class="mb-2">共 800 份报名数据</p>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="includeAttach" checked />
<label class="form-check-label text-nowrap" for="includeAttach">包含附件打包(ZIP)</label>
</div>
<p class="small fw-semibold text-secondary mb-2 text-nowrap">导出清单</p>
<div class="table-responsive border rounded export-preview-scroll">
<table class="table table-sm table-striped mb-0 align-middle small export-preview-table">
<thead class="table-light sticky-top">
<tr>
<th class="text-nowrap">序号</th>
<th class="text-nowrap">项目编号</th>
<th class="text-nowrap">参赛人姓名</th>
<th>毕业院校</th>
<th class="text-nowrap">最高学历</th>
<th class="text-nowrap">注册邮箱</th>
<th class="text-nowrap">联系电话</th>
<th>企业名称</th>
<th>项目名称</th>
<th>细分赛道</th>
<th>项目所在地</th>
<th class="text-nowrap">状态</th>
<th class="text-nowrap">提交时间</th>
<th>导出项</th>
<th class="text-nowrap">估大小</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>P2026-0001</td>
<td>张晓华</td>
<td>南京大学</td>
<td>硕士</td>
<td>zhangxh@example.com</td>
<td>13800138000</td>
<td>江苏某科技有限公司</td>
<td>高速硅光芯片研发平台</td>
<td>场景与应用</td>
<td>中国 / 江苏省 / 南京市</td>
<td>未通过</td>
<td>2026-04-27 10:25</td>
<td>报名表.pdf + 商业计划书.pdf</td>
<td>4.2 MB</td>
</tr>
<tr>
<td>2</td>
<td>P2026-0002</td>
<td>顾苏明</td>
<td>浙江大学</td>
<td>本科</td>
<td>gusm@example.com</td>
<td>13900139000</td>
<td>杭州文旅创新有限公司</td>
<td>高密度光模块封装方案</td>
<td>材料与设备</td>
<td>中国 / 浙江省 / 杭州市</td>
<td>通过</td>
<td>2026-04-26 16:08</td>
<td>报名表.pdf + 商业计划书.pdf</td>
<td>3.8 MB</td>
</tr>
<tr>
<td>3</td>
<td>P2026-0003</td>
<td>陈静</td>
<td>新加坡国立大学</td>
<td>本科</td>
<td>chenj@example.com</td>
<td>13700137000</td>
<td>-</td>
<td>智能光纤传感系统</td>
<td>制造与封装</td>
<td>海外 / 新加坡</td>
<td>未通过</td>
<td>2026-04-25 09:40</td>
<td>报名表.pdf + 商业计划书.pdf</td>
<td>2.1 MB</td>
</tr>
<tr>
<td>4</td>
<td>P2026-0004</td>
<td>赵磊</td>
<td>四川大学</td>
<td>本科</td>
<td>zhaol@example.com</td>
<td>13600136000</td>
<td>-</td>
<td>数字文旅导览</td>
<td>材料与设备</td>
<td>中国 / 四川省 / 成都市</td>
<td>未通过</td>
<td>2026-04-24 14:20</td>
<td>报名表.pdf + 商业计划书.pptx</td>
<td>5.6 MB</td>
</tr>
<tr>
<td>5</td>
<td>P2026-0005</td>
<td>周敏</td>
<td>中山大学</td>
<td>博士</td>
<td>zhoum@example.com</td>
<td>13500135000</td>
<td>银发健康科技有限公司</td>
<td>银发助餐平台</td>
<td>制造与封装</td>
<td>中国 / 广东省 / 广州市</td>
<td>通过</td>
<td>2026-04-23 11:05</td>
<td>报名表.pdf + 商业计划书.pdf</td>
<td>1.9 MB</td>
</tr>
<tr>
<td>6</td>
<td>P2026-0006</td>
<td>吴凯</td>
<td>厦门大学</td>
<td>本科</td>
<td>wukai@example.com</td>
<td>13400134000</td>
<td>厦门绿色材料有限公司</td>
<td>绿色包装新材料</td>
<td>芯片与组件</td>
<td>中国 / 福建省 / 厦门市</td>
<td>未通过</td>
<td>2026-04-22 09:30</td>
<td>报名表.pdf + 商业计划书.docx</td>
<td>3.2 MB</td>
</tr>
<tr>
<td>7</td>
<td>P2026-0007</td>
<td>孙悦</td>
<td>东南大学</td>
<td>硕士</td>
<td>suny@example.com</td>
<td>13300133000</td>
<td>-</td>
<td>社区健康驿站</td>
<td>制造与封装</td>
<td>中国 / 江苏省 / 南京市</td>
<td>通过</td>
<td>2026-04-21 16:45</td>
<td>报名表.pdf + 商业计划书.pdf</td>
<td>2.7 MB</td>
</tr>
<tr>
<td>8</td>
<td>P2026-0008</td>
<td>马超</td>
<td>郑州大学</td>
<td>大专</td>
<td>machao@example.com</td>
<td>13200132000</td>
<td>-</td>
<td>夜经济市集运营</td>
<td>芯片与组件</td>
<td>中国 / 河南省 / 郑州市</td>
<td>未通过</td>
<td>2026-04-20 10:10</td>
<td>报名表.pdf + 商业计划书.pdf</td>
<td>4.0 MB</td>
</tr>
<tr class="text-secondary">
<td>…</td>
<td colspan="12" class="text-center">其余 792 条在正式导出包中按相同规则生成</td>
<td>—</td>
<td>—</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-light" data-bs-dismiss="modal">取消</button>
<button class="btn btn-primary" id="startExport">开始导出</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="batchPassModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">审核意见</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="关闭"></button>
</div>
<div class="modal-body">
<textarea id="batchPassReason" class="form-control" rows="6">同意参赛。</textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-success" id="batchPassConfirmBtn">确认通过</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="batchFailModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">审核意见</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="关闭"></button>
</div>
<div class="modal-body">
<textarea id="batchFailReason" class="form-control" rows="8"></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-warning text-dark" id="batchFailConfirmBtn">确认未通过</button>
</div>
</div>
</div>
</div>
<script src="../assets/bootstrap.bundle.min.js"></script>
<script>
(function () {
var params = new URLSearchParams(location.search);
var role = params.get("role") || "";
if (role === "reviewer") {
var reviewerTrack = params.get("track") || "";
document.body.classList.add("reviewer-score-2colors");
var t = document.getElementById("adminListTitle");
if (t) t.textContent = reviewerTrack ? ("项目评审(" + reviewerTrack + ")") : "项目评审";
var sh = document.getElementById("adminListStatusHead");
if (sh) sh.textContent = "评分";
document.querySelectorAll(".reviewer-only-col").forEach(function (el) {
el.classList.remove("d-none");
});
document.querySelectorAll(".admin-manage-status-col").forEach(function (el) {
el.classList.add("d-none");
});
var ah = document.getElementById("adminListAvgHead");
if (ah) ah.classList.add("d-none");
var fp = document.getElementById("adminListFilterPanel");
if (fp) fp.classList.add("d-none");
var bt = document.getElementById("adminBatchToolbar");
if (bt) bt.classList.add("d-none");
var bab = document.getElementById("adminBatchActionBar");
if (bab) bab.classList.add("d-none");
var eb = document.getElementById("adminListExportBtn");
if (eb) eb.classList.add("d-none");
var checkAll = document.getElementById("checkAllRows");
if (checkAll && checkAll.closest("th")) {
checkAll.closest("th").classList.add("d-none");
}
document.querySelectorAll("#adminListBody tr[data-list-row]").forEach(function (tr) {
var firstCell = tr.querySelector("td");
if (firstCell) firstCell.classList.add("d-none");
tr.querySelectorAll(".admin-avg-cell").forEach(function (td) {
td.classList.add("d-none");
});
tr.querySelectorAll(".action-pass-btn,.action-fail-btn").forEach(function (btn) {
btn.classList.add("d-none");
});
});
document.querySelectorAll('#adminListBody tr[data-list-row] .action-view-link').forEach(function (a) {
var href = a.getAttribute("href") || "";
if (!href) return;
var tr = a.closest("tr");
var pill = tr ? tr.querySelector(".status-pill") : null;
var cells = tr ? tr.querySelectorAll("td") : [];
var scoreText = pill ? (pill.textContent || "").trim() : "";
var rawReviewerScores = tr ? (tr.getAttribute("data-review-scores") || "").trim() : "";
if (rawReviewerScores) {
var list = rawReviewerScores.split(",").map(function (x) { return (x || "").trim(); }).filter(Boolean);
if (list.length) scoreText = list[list.length - 1];
}
if (!scoreText && cells.length > 9) {
scoreText = (cells[9].textContent || "").trim();
}
var scoreMatch = scoreText.match(/^(\d+(?:\.\d+)?)(?:分)?$/);
var joiner = href.indexOf("?") >= 0 ? "&" : "?";
if (href.indexOf("role=reviewer") === -1) {
href = href + joiner + "role=reviewer";
}
if (scoreMatch && href.indexOf("score=") === -1) {
href = href + "&score=" + encodeURIComponent(scoreMatch[1]);
}
a.setAttribute("href", href);
});
var visibleIndex = 1;
document.querySelectorAll('#adminListBody tr[data-list-row]').forEach(function (tr) {
var cells = tr.querySelectorAll("td");
var rowTrack = cells.length > 8 ? (cells[8].textContent || "").trim() : "";
var matched = !reviewerTrack || rowTrack === reviewerTrack;
tr.classList.toggle("d-none", !matched);
if (matched && cells.length > 1) {
cells[1].textContent = String(visibleIndex++);
}
});
var firstVisibleReviewerRow = document.querySelector('#adminListBody tr[data-list-row]:not(.d-none)');
if (firstVisibleReviewerRow) {
var firstActionLink = firstVisibleReviewerRow.querySelector(".action-view-link");
if (firstActionLink) firstActionLink.textContent = "评审";
}
}
})();
(function () {
const currentRole = new URLSearchParams(location.search).get("role") || "";
const keywordEl = document.getElementById("filterKeyword");
const trackEl = document.getElementById("filterTrack");
const filterTrackBtn = document.getElementById("filterTrackDropdownBtn");
const filterTrackMain = document.getElementById("filterTrackMainText");
const filterTrackNote = document.getElementById("filterTrackNoteText");
const filterTrackNotes = {};
function syncFilterTrackDisplay() {
if (!trackEl || !filterTrackBtn || !filterTrackMain || !filterTrackNote) return;
const v = trackEl.value;
if (!v) {
filterTrackMain.textContent = "全部赛道";
filterTrackMain.className = "text-secondary";
filterTrackNote.textContent = "";
} else {
filterTrackMain.textContent = v;
filterTrackMain.className = "text-body";
filterTrackNote.textContent = filterTrackNotes[v] || "";
}
}
document.querySelectorAll(".filter-panel .track-pick").forEach((pick) => {
pick.addEventListener("click", () => {
trackEl.value = pick.getAttribute("data-value") || "";
trackEl.dispatchEvent(new Event("change", { bubbles: true }));
syncFilterTrackDisplay();
const inst = bootstrap.Dropdown.getInstance(filterTrackBtn);
if (inst) inst.hide();
});
});
const statusEl = document.getElementById("filterStatus");
const hintEl = document.getElementById("filterResultHint");
const emptyRow = document.getElementById("filterEmptyRow");
const rows = () => Array.from(document.querySelectorAll("#adminListBody tr[data-list-row]"));
const adminBatchActionBar = document.getElementById("adminBatchActionBar");
const batchPassBtn = document.getElementById("batchPassBtn");
const batchFailBtn = document.getElementById("batchFailBtn");
const batchPassModal = new bootstrap.Modal(document.getElementById("batchPassModal"));
const batchFailModal = new bootstrap.Modal(document.getElementById("batchFailModal"));
const batchPassConfirmBtn = document.getElementById("batchPassConfirmBtn");
const batchFailConfirmBtn = document.getElementById("batchFailConfirmBtn");
const batchPassReason = document.getElementById("batchPassReason");
const batchFailReason = document.getElementById("batchFailReason");
const batchFailDefaultCopy =
"参赛者,您好!\n\n" +
"非常感谢您主动报名本次大赛,认真提交相关申报材料。\n" +
"经组委会严格初审综合评定后,遗憾通知您,本次未能进入下一阶段。\n" +
"感谢您的积极参与和付出,望再接再厉,未来再创佳绩!";
function syncAdminScoreColumns() {
if (currentRole === "reviewer") return;
rows().forEach((tr) => {
const cells = tr.querySelectorAll("td");
if (cells.length < 14) return;
const totalCell = cells[9];
const avgCell = cells[10];
const statusCell = cells[11];
const rawScores = (tr.getAttribute("data-review-scores") || "").trim();
const reviewProgress = (tr.getAttribute("data-review-progress") || "").trim();
const reviewResult = (tr.getAttribute("data-review-result") || "").trim();
const progressMatch = reviewProgress.match(/^(\d+)\s*\/\s*(\d+)$/);
const reviewedCount = progressMatch ? Number(progressMatch[1]) : 0;
const reviewerCount = progressMatch ? Number(progressMatch[2]) : 0;
const allReviewed = reviewerCount > 0 && reviewedCount >= reviewerCount;
const scoreList = rawScores
? rawScores
.split(",")
.map((x) => Number((x || "").trim()))
.filter((x) => Number.isFinite(x))
: [];
if (!allReviewed || !scoreList.length) {
statusCell.textContent = "-";
totalCell.textContent = "待评审";
totalCell.classList.add("admin-total-pending-text");
avgCell.textContent = "-";
return;
}
const total = scoreList.reduce((sum, n) => sum + n, 0);
const avg = total / scoreList.length;
statusCell.textContent = reviewResult || "-";
totalCell.classList.remove("admin-total-pending-text");
totalCell.textContent = total.toFixed(2);
avgCell.textContent = avg.toFixed(2);
});
}
function syncManageStatusStyles() {
if (currentRole === "reviewer") return;
rows().forEach((tr) => {
const statusCell = tr.querySelector(".admin-status-cell");
if (!statusCell) return;
const text = (statusCell.textContent || "").trim();
if (text === "-") {
statusCell.textContent = "-";
return;
}
let cls = "admin-manage-status-progress";
if (text === "通过") cls = "admin-manage-status-pass";
if (text === "未通过") cls = "admin-manage-status-fail";
statusCell.innerHTML =
'<span class="admin-manage-status-tag ' + cls + '">' + text + "</span>";
});
}
function syncAuditActionLabels() {
rows().forEach((tr) => {
const pill = tr.querySelector(".status-pill");
const link = tr.querySelector(".action-view-link");
if (!pill || !link) return;
if (currentRole === "reviewer") {
link.textContent = pill.textContent.trim() === "待评审" ? "评审" : "查看";
} else {
link.textContent = "查看";
}
});
}
function upsertQueryParam(href, key, value) {
const parts = (href || "").split("?");
const base = parts[0] || "";
const qs = new URLSearchParams(parts[1] || "");
qs.set(key, value);
return base + "?" + qs.toString();
}
function syncManageDetailLinkTotals() {
if (currentRole === "reviewer") return;
rows().forEach((tr) => {
const link = tr.querySelector(".action-view-link");
const cells = tr.querySelectorAll("td");
if (!link || cells.length < 10) return;
const totalText = (cells[9].textContent || "").trim();
const href = link.getAttribute("href") || "";
link.setAttribute("href", upsertQueryParam(href, "total", totalText));
});
}
function rowMatches(tr) {
const cells = tr.querySelectorAll("td");
if (cells.length < 14) return false;
const project = cells[3].textContent.trim();
const name = cells[4].textContent.trim();
const location = cells[5].textContent.trim();
const school = cells[6].textContent.trim();
const track = cells[8].textContent.trim();
const statusPill = tr.querySelector(".status-pill");
const status = statusPill ? statusPill.textContent.trim() : cells[11].textContent.trim();
const kw = keywordEl.value.trim().toLowerCase();
if (kw) {
const hay = (project + name + location + school).toLowerCase();
if (!hay.includes(kw)) return false;
}
const trackVal = trackEl.value.trim();
if (trackVal && track !== trackVal) return false;
const statusVal = statusEl.value.trim();
if (statusVal && status !== statusVal) return false;
return true;
}
const checkAllRowsEl = document.getElementById("checkAllRows");
const rowCheckEls = () => Array.from(document.querySelectorAll("#adminListBody .row-check"));
function setCheckAllState() {
if (!checkAllRowsEl) return;
const visibleChecks = rowCheckEls().filter((x) => !x.closest("tr").classList.contains("d-none"));
const checkedCount = visibleChecks.filter((x) => x.checked).length;
checkAllRowsEl.checked = visibleChecks.length > 0 && checkedCount === visibleChecks.length;
checkAllRowsEl.indeterminate = checkedCount > 0 && checkedCount < visibleChecks.length;
if (currentRole !== "reviewer") {
if (batchPassBtn) batchPassBtn.disabled = checkedCount === 0;
if (batchFailBtn) batchFailBtn.disabled = checkedCount === 0;
}
}
if (checkAllRowsEl) {
checkAllRowsEl.addEventListener("change", function () {
const checked = this.checked;
rowCheckEls().forEach((x) => {
if (!x.closest("tr").classList.contains("d-none")) x.checked = checked;
});
setCheckAllState();
});
}
rowCheckEls().forEach((x) => x.addEventListener("change", setCheckAllState));
document.getElementById("selectAllBtn").addEventListener("click", function () {
rowCheckEls().forEach((x) => {
if (!x.closest("tr").classList.contains("d-none")) x.checked = true;
});
setCheckAllState();
});
document.getElementById("invertSelectBtn").addEventListener("click", function () {
rowCheckEls().forEach((x) => {
if (!x.closest("tr").classList.contains("d-none")) x.checked = !x.checked;
});
setCheckAllState();
});
document.getElementById("clearSelectBtn").addEventListener("click", function () {
rowCheckEls().forEach((x) => (x.checked = false));
setCheckAllState();
});
if (batchPassBtn) {
batchPassBtn.addEventListener("click", function () {
if (batchPassReason) batchPassReason.value = "同意参赛。";
batchPassModal.show();
});
}
if (batchFailBtn) {
batchFailBtn.addEventListener("click", function () {
if (batchFailReason) batchFailReason.value = batchFailDefaultCopy;
batchFailModal.show();
});
}
if (batchPassConfirmBtn) {
batchPassConfirmBtn.addEventListener("click", function () {
batchPassModal.hide();
});
}
if (batchFailConfirmBtn) {
batchFailConfirmBtn.addEventListener("click", function () {
batchFailModal.hide();
});
}
function applyFilter() {
let n = 0;
rows().forEach((tr) => {
const ok = rowMatches(tr);
tr.classList.toggle("d-none", !ok);
if (ok) n += 1;
});
emptyRow.classList.toggle("d-none", n !== 0);
setCheckAllState();
hintEl.textContent =
n === 0
? "符合当前筛选条件:0 条"
: "符合当前筛选条件:" + n + " 条";
}
function resetFilter() {
keywordEl.value = "";
trackEl.value = "";
statusEl.value = "";
syncFilterTrackDisplay();
rows().forEach((tr) => tr.classList.remove("d-none"));
emptyRow.classList.add("d-none");
rowCheckEls().forEach((x) => (x.checked = false));
setCheckAllState();
hintEl.textContent = "";
}
document.getElementById("filterQueryBtn").addEventListener("click", applyFilter);
document.getElementById("filterResetBtn").addEventListener("click", resetFilter);
keywordEl.addEventListener("keydown", function (e) {
if (e.key === "Enter") {
e.preventDefault();
applyFilter();
}
});
syncAdminScoreColumns();
syncManageStatusStyles();
syncManageDetailLinkTotals();
resetFilter();
syncAuditActionLabels();
})();
document.getElementById("startExport").addEventListener("click", function () {
this.textContent = "导出中...";
this.disabled = true;
setTimeout(() => {
this.textContent = "导出完成(演示)";
}, 1200);
});
</script>
</body>
</html>