|
|
<!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&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&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&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>
|