Files
sincos.cc-website/website/static/admin.html
T
2026-08-13 21:24:16 +08:00

234 lines
9.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>管理后台 - ResLib License</title>
<link rel="stylesheet" href="/static/css/style.css">
<link rel="icon" href="/static/img/logo.svg" type="image/svg+xml">
<style>
.filters { display: flex; gap: 8px; flex-wrap: wrap; margin: 18px 0; }
.filter-btn {
padding: 6px 16px; border-radius: 20px; border: 1px solid var(--line);
background: #fff; color: var(--ink-light); font-size: 0.85rem; cursor: pointer; font-family: inherit;
}
.filter-btn.active { background: var(--blue); color: #fff; border-color: var(--blue); }
.empty { text-align: center; color: var(--ink-light); padding: 40px 0; }
.modal-mask {
position: fixed; inset: 0; background: rgba(15, 30, 50, 0.5);
display: none; align-items: center; justify-content: center; z-index: 200; padding: 20px;
}
.modal-mask.show { display: flex; }
.modal {
background: #fff; border-radius: 10px; width: 100%; max-width: 560px;
max-height: 90vh; overflow: auto; padding: 26px;
}
.modal h3 { color: var(--blue-dark); margin-bottom: 14px; }
.kv { display: grid; grid-template-columns: 110px 1fr; gap: 8px 12px; font-size: 0.92rem; margin-bottom: 16px; }
.kv dt { color: var(--ink-light); }
.kv dd { word-break: break-all; }
.lic-preview {
background: #0d2b4a; color: #7cc0ff; font-family: Consolas, monospace;
border-radius: 6px; padding: 12px 14px; font-size: 0.85rem; margin: 12px 0; letter-spacing: 0.05em;
}
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 18px; flex-wrap: wrap; }
.deny-reason { width: 100%; margin-top: 10px; display: none; }
.deny-reason.show { display: block; }
</style>
</head>
<body>
<header class="site-header">
<div class="container header-inner">
<a class="brand" href="/admin">
<img src="/static/img/logo.svg" alt="ResLib">
<span class="brand-name">ResLib License 管理后台</span>
</a>
<div>
<a href="/reslib/" class="btn btn-ghost btn-sm" style="margin-right:8px;">前台</a>
<a href="/admin/logout" class="btn btn-danger btn-sm">退出</a>
</div>
</div>
</header>
<main class="section" style="padding-top: 30px;">
<div class="container">
<div class="filters" id="filters">
<button class="filter-btn active" data-f="all">全部</button>
<button class="filter-btn" data-f="pending">待审核</button>
<button class="filter-btn" data-f="approved">已批准</button>
<button class="filter-btn" data-f="denied">已拒绝</button>
<button id="refresh" class="filter-btn" style="margin-left:auto; border-color: var(--blue); color: var(--blue);">刷新</button>
</div>
<div class="table-wrap">
<table class="admin" id="table">
<thead>
<tr>
<th>申请编号</th><th>提交时间</th><th>公司</th><th>联系人</th><th>邮箱</th>
<th>MAC</th><th>状态</th><th>操作</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
</div>
<div class="empty" id="empty" style="display:none;">暂无申请记录</div>
</div>
</main>
<div class="modal-mask" id="mask">
<div class="modal">
<h3 id="m-title">申请详情</h3>
<dl class="kv" id="m-body"></dl>
<div id="m-lic"></div>
<div class="deny-reason" id="deny-box">
<label style="font-size:0.85rem; font-weight:600;">拒绝原因(可选):</label>
<input type="text" id="deny-reason-input" style="width:100%; margin-top:6px; padding:9px 12px; border:1px solid #cfdcea; border-radius:6px;">
</div>
<div class="modal-actions">
<button class="btn btn-ghost btn-sm" id="m-close">关闭</button>
<button class="btn btn-danger btn-sm" id="m-deny" style="display:none;">拒绝</button>
<button class="btn btn-primary btn-sm" id="m-approve" style="display:none;">批准并生成 License</button>
</div>
</div>
</div>
<script>
(function () {
var tbody = document.getElementById("tbody");
var empty = document.getElementById("empty");
var filter = "all";
var items = [];
var current = null;
function badge(s) {
if (s === "approved") return '<span class="badge badge-approved">已批准</span>';
if (s === "denied") return '<span class="badge badge-denied">已拒绝</span>';
return '<span class="badge badge-pending">待审核</span>';
}
async function load() {
try {
var resp = await fetch("/api/admin/list");
if (resp.status === 401) { window.location.href = "/admin/login"; return; }
items = await resp.json();
render();
} catch (e) {
alert("加载失败:" + e.message);
}
}
function render() {
var list = filter === "all" ? items : items.filter(function (i) { return i.status === filter; });
tbody.innerHTML = "";
empty.style.display = list.length ? "none" : "block";
list.forEach(function (i) {
var tr = document.createElement("tr");
tr.innerHTML =
'<td class="mono">' + i.id + "</td>" +
"<td>" + i.created + "</td>" +
"<td>" + esc(i.company) + "</td>" +
"<td>" + esc(i.contact) + "</td>" +
"<td>" + esc(i.email) + "</td>" +
'<td class="mono">' + esc(i.mac) + "</td>" +
"<td>" + badge(i.status) + "</td>" +
'<td class="actions"><button class="btn btn-ghost btn-sm" data-view="' + i.id + '">详情</button></td>';
tbody.appendChild(tr);
});
}
function esc(s) {
return String(s == null ? "" : s).replace(/[&<>"']/g, function (c) {
return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c];
});
}
function showDetail(id) {
current = items.find(function (i) { return i.id === id; });
if (!current) return;
document.getElementById("m-body").innerHTML =
"<dt>申请编号</dt><dd class='mono'>" + current.id + "</dd>" +
"<dt>提交时间</dt><dd>" + current.created + "</dd>" +
"<dt>状态</dt><dd>" + badge(current.status) + "</dd>" +
"<dt>MAC 地址</dt><dd class='mono'>" + esc(current.mac) + "</dd>" +
"<dt>公司</dt><dd>" + esc(current.company) + "</dd>" +
"<dt>联系人</dt><dd>" + esc(current.contact) + "</dd>" +
"<dt>邮箱</dt><dd>" + esc(current.email) + "</dd>" +
"<dt>Creo 版本</dt><dd>" + esc(current.creo) + "</dd>" +
"<dt>授权套数</dt><dd>" + esc(current.seats) + "</dd>" +
"<dt>备注</dt><dd>" + esc(current.notes) || "(无)" + "</dd>" +
(current.reason ? "<dt>拒绝原因</dt><dd>" + esc(current.reason) + "</dd>" : "");
var licBox = document.getElementById("m-lic");
licBox.innerHTML = "";
if (current.status === "approved") {
licBox.innerHTML =
"<b style='font-size:0.88rem;'>已生成授权文件:</b>" +
"<div class='lic-preview' id='lic-content'></div>" +
"<a class='btn btn-primary btn-sm' href='/api/license/" + encodeURIComponent(current.id) + "' download>下载 res_center.lic</a>";
fetch("/api/license/" + encodeURIComponent(current.id)).then(function (r) { return r.text(); }).then(function (t) {
var el = document.getElementById("lic-content");
if (el) el.textContent = t.trim();
}).catch(function () {});
}
document.getElementById("m-approve").style.display =
current.status === "pending" ? "" : "none";
document.getElementById("m-deny").style.display =
current.status === "pending" ? "" : "none";
document.getElementById("deny-box").classList.remove("show");
document.getElementById("deny-reason-input").value = "";
document.getElementById("mask").classList.add("show");
}
document.getElementById("filters").addEventListener("click", function (e) {
var b = e.target.closest(".filter-btn");
if (!b || b.id === "refresh") return;
document.querySelectorAll(".filter-btn").forEach(function (x) { x.classList.remove("active"); });
b.classList.add("active");
filter = b.dataset.f;
render();
});
document.getElementById("refresh").addEventListener("click", load);
tbody.addEventListener("click", function (e) {
var b = e.target.closest("[data-view]");
if (b) showDetail(b.dataset.view);
});
document.getElementById("m-close").addEventListener("click", function () {
document.getElementById("mask").classList.remove("show");
});
document.getElementById("mask").addEventListener("click", function (e) {
if (e.target === this) this.classList.remove("show");
});
document.getElementById("m-approve").addEventListener("click", async function () {
if (!confirm("确认批准该申请并生成 License?")) return;
var resp = await fetch("/api/admin/approve/" + encodeURIComponent(current.id), { method: "POST" });
if (resp.ok) { closeAndReload(); } else { alert("操作失败"); }
});
document.getElementById("m-deny").addEventListener("click", async function () {
if (!confirm("确认拒绝该申请?")) return;
var reason = document.getElementById("deny-reason-input").value;
var resp = await fetch("/api/admin/deny/" + encodeURIComponent(current.id), {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: "reason=" + encodeURIComponent(reason)
});
if (resp.ok) { closeAndReload(); } else { alert("操作失败"); }
});
function closeAndReload() {
document.getElementById("mask").classList.remove("show");
load();
}
load();
})();
</script>
</body>
</html>