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.
285 lines
12 KiB
285 lines
12 KiB
<!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" />
|
|
</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">评审员管理</h5>
|
|
</div>
|
|
|
|
<div class="reviewer-manage-shell">
|
|
<aside class="reviewer-track-panel">
|
|
<div class="reviewer-track-panel-title">赛道配置</div>
|
|
<div class="reviewer-track-tabs" id="reviewerTrackTabs"></div>
|
|
</aside>
|
|
|
|
<section class="reviewer-editor-panel">
|
|
<div class="reviewer-editor-head">
|
|
<div>
|
|
<h6 class="reviewer-track-title" id="currentTrackTitle">芯片与组件</h6>
|
|
<p class="reviewer-track-meta mb-0" id="currentTrackMeta">已配置 0 名评审员</p>
|
|
</div>
|
|
<div class="reviewer-editor-actions">
|
|
<button type="button" class="btn btn-sm btn-outline-primary" id="addReviewerBtn">新增评审员</button>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm align-middle mb-0 reviewer-manage-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 18%">姓名</th>
|
|
<th style="width: 22%">电话</th>
|
|
<th style="width: 24%">账户</th>
|
|
<th style="width: 24%">密码</th>
|
|
<th class="text-end" style="width: 12%">操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="reviewerTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="reviewerEditModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="reviewerEditTitle">新增评审员</h5>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="reviewerEditForm" class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label reviewer-edit-label">姓名 <span class="text-danger">*</span></label>
|
|
<input class="form-control" id="reviewerNameInput" required />
|
|
<div class="invalid-feedback">请填写姓名</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label reviewer-edit-label">电话</label>
|
|
<input class="form-control" id="reviewerPhoneInput" />
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label reviewer-edit-label">账户 <span class="text-danger">*</span></label>
|
|
<input class="form-control" id="reviewerAccountInput" required />
|
|
<div class="invalid-feedback">请填写账户</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label reviewer-edit-label">密码 <span class="text-danger">*</span></label>
|
|
<input class="form-control" id="reviewerPasswordInput" required />
|
|
<div class="invalid-feedback">请填写密码</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">取消</button>
|
|
<button type="button" class="btn btn-primary" id="reviewerEditConfirmBtn">确定</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="reviewerNoticeModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered notice-modal-dialog">
|
|
<div class="modal-content notice-modal-content notice-success">
|
|
<div class="modal-header border-0 pb-0">
|
|
<button type="button" class="btn-close ms-auto" data-bs-dismiss="modal" aria-label="关闭"></button>
|
|
</div>
|
|
<div class="modal-body notice-modal-body">
|
|
<div class="notice-icon" aria-hidden="true">
|
|
<svg viewBox="0 0 24 24" width="36" height="36" fill="none" aria-hidden="true"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.8"/><path d="M7 12.2l3.1 3.1L17.2 8.8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
</div>
|
|
<h5 class="notice-title">操作成功</h5>
|
|
<p class="notice-text">评审员信息已更新(原型演示)。</p>
|
|
</div>
|
|
<div class="modal-footer notice-modal-footer border-0">
|
|
<button type="button" class="btn btn-primary notice-ok-btn" data-bs-dismiss="modal">确定</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="reviewerDeleteModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">确认删除</h5>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p class="mb-0" id="reviewerDeleteText">确定删除该评审员吗?</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">取消</button>
|
|
<button type="button" class="btn btn-primary" id="reviewerDeleteConfirmBtn">确定删除</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="../assets/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
const tracks = [
|
|
"芯片与组件",
|
|
"制造与封装",
|
|
"材料与设备",
|
|
"场景与应用"
|
|
];
|
|
const reviewersByTrack = {
|
|
"芯片与组件": [
|
|
{ name: "李明", phone: "13810001001", account: "review_xf_01", password: "XF2026@01" },
|
|
{ name: "周倩", phone: "13810001002", account: "review_xf_02", password: "XF2026@02" }
|
|
],
|
|
"制造与封装": [
|
|
{ name: "陈立", phone: "13810002001", account: "review_jk_01", password: "JK2026@01" },
|
|
{ name: "何婧", phone: "13810002002", account: "review_jk_02", password: "JK2026@02" }
|
|
],
|
|
"材料与设备": [
|
|
{ name: "王越", phone: "13810003001", account: "review_wl_01", password: "WL2026@01" },
|
|
{ name: "赵琳", phone: "13810003002", account: "review_wl_02", password: "WL2026@02" }
|
|
],
|
|
"场景与应用": [
|
|
{ name: "孙航", phone: "13810004001", account: "review_ai_01", password: "AI2026@01" },
|
|
{ name: "顾言", phone: "13810004002", account: "review_ai_02", password: "AI2026@02" }
|
|
]
|
|
};
|
|
|
|
let currentTrack = tracks[0];
|
|
let editingIndex = null;
|
|
let pendingDeleteIndex = null;
|
|
const trackTabs = document.getElementById("reviewerTrackTabs");
|
|
const tableBody = document.getElementById("reviewerTableBody");
|
|
const currentTrackTitle = document.getElementById("currentTrackTitle");
|
|
const currentTrackMeta = document.getElementById("currentTrackMeta");
|
|
const editModalEl = document.getElementById("reviewerEditModal");
|
|
const editModal = new bootstrap.Modal(editModalEl);
|
|
const editForm = document.getElementById("reviewerEditForm");
|
|
const editTitle = document.getElementById("reviewerEditTitle");
|
|
const reviewerNameInput = document.getElementById("reviewerNameInput");
|
|
const reviewerPhoneInput = document.getElementById("reviewerPhoneInput");
|
|
const reviewerAccountInput = document.getElementById("reviewerAccountInput");
|
|
const reviewerPasswordInput = document.getElementById("reviewerPasswordInput");
|
|
const noticeModal = new bootstrap.Modal(document.getElementById("reviewerNoticeModal"));
|
|
const deleteModal = new bootstrap.Modal(document.getElementById("reviewerDeleteModal"));
|
|
const reviewerDeleteText = document.getElementById("reviewerDeleteText");
|
|
|
|
function escapeHtml(text) {
|
|
return String(text).replace(/[&<>"']/g, (char) => ({
|
|
"&": "&",
|
|
"<": "<",
|
|
">": ">",
|
|
'"': """,
|
|
"'": "'"
|
|
})[char]);
|
|
}
|
|
|
|
function renderReviewerRow(reviewer, index) {
|
|
return (
|
|
'<tr data-index="' + index + '">' +
|
|
'<td>' + escapeHtml(reviewer.name) + '</td>' +
|
|
'<td>' + escapeHtml(reviewer.phone) + '</td>' +
|
|
'<td>' + escapeHtml(reviewer.account) + '</td>' +
|
|
'<td>' + escapeHtml(reviewer.password) + '</td>' +
|
|
'<td class="text-end reviewer-row-actions">' +
|
|
'<button type="button" class="btn btn-sm btn-outline-primary reviewer-edit-btn">修改</button>' +
|
|
'<button type="button" class="btn btn-sm btn-light border reviewer-remove-btn">删除</button>' +
|
|
'</td>' +
|
|
'</tr>'
|
|
);
|
|
}
|
|
|
|
function renderTrackTabs() {
|
|
trackTabs.innerHTML = tracks.map((track) => (
|
|
'<button type="button" class="reviewer-track-tab' + (track === currentTrack ? " active" : "") + '" data-track="' + escapeHtml(track) + '">' +
|
|
'<span class="reviewer-track-tab-name">' + escapeHtml(track) + '</span>' +
|
|
'<span class="reviewer-track-tab-count">' + reviewersByTrack[track].length + ' 人</span>' +
|
|
'</button>'
|
|
)).join("");
|
|
}
|
|
|
|
function renderPage() {
|
|
renderTrackTabs();
|
|
currentTrackTitle.textContent = currentTrack;
|
|
currentTrackMeta.textContent = "已配置 " + reviewersByTrack[currentTrack].length + " 名评审员";
|
|
tableBody.innerHTML = reviewersByTrack[currentTrack].map(renderReviewerRow).join("");
|
|
}
|
|
|
|
function openReviewerModal(index = null) {
|
|
editingIndex = index;
|
|
const reviewer = index === null
|
|
? { name: "", phone: "", account: "", password: "" }
|
|
: reviewersByTrack[currentTrack][index];
|
|
editTitle.textContent = index === null ? "新增评审员" : "修改评审员";
|
|
reviewerNameInput.value = reviewer.name || "";
|
|
reviewerPhoneInput.value = reviewer.phone || "";
|
|
reviewerAccountInput.value = reviewer.account || "";
|
|
reviewerPasswordInput.value = reviewer.password || "";
|
|
editForm.classList.remove("was-validated");
|
|
editModal.show();
|
|
}
|
|
|
|
trackTabs.addEventListener("click", (ev) => {
|
|
const tab = ev.target.closest(".reviewer-track-tab");
|
|
if (!tab) return;
|
|
currentTrack = tab.getAttribute("data-track");
|
|
renderPage();
|
|
});
|
|
|
|
document.getElementById("addReviewerBtn").addEventListener("click", () => {
|
|
openReviewerModal(null);
|
|
});
|
|
|
|
tableBody.addEventListener("click", (ev) => {
|
|
const editBtn = ev.target.closest(".reviewer-edit-btn");
|
|
if (editBtn) {
|
|
const row = editBtn.closest("tr");
|
|
openReviewerModal(Number(row.getAttribute("data-index")));
|
|
return;
|
|
}
|
|
const removeBtn = ev.target.closest(".reviewer-remove-btn");
|
|
if (removeBtn) {
|
|
const row = removeBtn.closest("tr");
|
|
const index = Number(row.getAttribute("data-index"));
|
|
const reviewer = reviewersByTrack[currentTrack][index];
|
|
pendingDeleteIndex = index;
|
|
reviewerDeleteText.textContent = "确定删除“" + reviewer.name + "”吗?";
|
|
deleteModal.show();
|
|
}
|
|
});
|
|
|
|
document.getElementById("reviewerDeleteConfirmBtn").addEventListener("click", () => {
|
|
if (pendingDeleteIndex === null) return;
|
|
reviewersByTrack[currentTrack].splice(pendingDeleteIndex, 1);
|
|
pendingDeleteIndex = null;
|
|
renderPage();
|
|
deleteModal.hide();
|
|
});
|
|
|
|
document.getElementById("reviewerEditConfirmBtn").addEventListener("click", () => {
|
|
editForm.classList.add("was-validated");
|
|
if (!editForm.checkValidity()) return;
|
|
const reviewer = {
|
|
name: reviewerNameInput.value.trim(),
|
|
phone: reviewerPhoneInput.value.trim(),
|
|
account: reviewerAccountInput.value.trim(),
|
|
password: reviewerPasswordInput.value.trim()
|
|
};
|
|
if (editingIndex === null) {
|
|
reviewersByTrack[currentTrack].push(reviewer);
|
|
} else {
|
|
reviewersByTrack[currentTrack][editingIndex] = reviewer;
|
|
}
|
|
renderPage();
|
|
editModal.hide();
|
|
noticeModal.show();
|
|
});
|
|
|
|
renderPage();
|
|
</script>
|
|
</body>
|
|
</html>
|