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

5 months ago
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
1 month ago
<title>“光联万物 创芯未来”首届沪苏协同新兴产业科创大赛报名系统</title>
5 months ago
<link rel="stylesheet" href="../assets/bootstrap.min.css" />
<link rel="stylesheet" href="../assets/prototype.css" />
1 month ago
<script src="../assets/prototype-scale.js?v=20260819-hero"></script>
5 months ago
</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>
1 month ago
<option value="芯片与组件">芯片与组件</option>
<option value="制造与封装">制造与封装</option>
<option value="材料与设备">材料与设备</option>
<option value="场景与应用">场景与应用</option>
5 months ago
</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>
1 month ago
<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>
5 months ago
</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>
1 month ago
<td>高速硅光芯片研发平台</td>
5 months ago
<td>张晓华</td>
<td>中国 / 江苏省 / 南京市</td>
<td class="d-none reviewer-only-col">南京大学</td>
<td class="d-none reviewer-only-col">硕士</td>
1 month ago
<td>场景与应用</td>
5 months ago
<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>
1 month ago
<td>高密度光模块封装方案</td>
5 months ago
<td>顾苏明</td>
<td>中国 / 浙江省 / 杭州市</td>
<td class="d-none reviewer-only-col">浙江大学</td>
<td class="d-none reviewer-only-col">本科</td>
1 month ago
<td>材料与设备</td>
5 months ago
<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>
1 month ago
<td>智能光纤传感系统</td>
5 months ago
<td>陈静</td>
<td>海外 / 新加坡</td>
<td class="d-none reviewer-only-col">新加坡国立大学</td>
<td class="d-none reviewer-only-col">本科</td>
1 month ago
<td>制造与封装</td>
5 months ago
<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>
1 month ago
<th>细分赛道</th>
5 months ago
<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>
1 month ago
<td>高速硅光芯片研发平台</td>
<td>场景与应用</td>
5 months ago
<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>
1 month ago
<td>高密度光模块封装方案</td>
<td>材料与设备</td>
5 months ago
<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>
1 month ago
<td>智能光纤传感系统</td>
<td>制造与封装</td>
5 months ago
<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>
1 month ago
<td>材料与设备</td>
5 months ago
<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>
1 month ago
<td>制造与封装</td>
5 months ago
<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>
1 month ago
<td>芯片与组件</td>
5 months ago
<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>
1 month ago
<td>制造与封装</td>
5 months ago
<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>
1 month ago
<td>芯片与组件</td>
5 months ago
<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>