Files
Achievement_Inputing/accounts/templates/accounts/registration_code_requests.html
DSQ 5e38ebf856
All checks were successful
CI / docker-ci (push) Successful in 32s
[0.2.7.5][ci]
2026-03-18 21:56:39 +08:00

187 lines
8.2 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">
<title>注册码申请管理</title>
<style>
body { margin: 0; font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; background: #f5f6fa; }
.sidebar { position: fixed; top: 0; left: 0; width: 180px; height: 100vh; background: #1e1e2e; color: white; padding: 20px; box-shadow: 2px 0 5px rgba(0,0,0,0.1); z-index: 1000; display: flex; flex-direction: column; align-items: center; }
.sidebar h3 { margin-top: 0; font-size: 18px; color: #add8e6; text-align: center; margin-bottom: 20px; }
.navigation-links { width: 100%; margin-top: 60px; }
.sidebar a { display: block; color: #8be9fd; text-decoration: none; margin: 10px 0; font-size: 16px; padding: 15px; border-radius: 4px; transition: all 0.2s ease; }
.sidebar a:hover { color: #ff79c6; background-color: rgba(139, 233, 253, 0.2); }
.main-content { margin-left: 220px; padding: 40px; }
.card { background: #fff; border-radius: 14px; box-shadow: 0 10px 24px rgba(31,35,40,0.08); padding: 24px; }
.header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.btn { padding: 8px 12px; border: none; border-radius: 10px; cursor: pointer; }
.btn-primary { background: #4f46e5; color: #fff; }
.btn-secondary { background: #64748b; color: #fff; }
.btn-danger { background: #ff4d4f; color: #fff; }
.muted { color: #6b7280; font-size: 12px; }
table { width: 100%; border-collapse: collapse; margin-top: 12px; }
th, td { text-align: left; border-bottom: 1px solid #e5e7eb; padding: 10px 8px; vertical-align: top; font-size: 13px; }
tr:hover { background: #f8fafc; }
.tag { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 12px; background: #eef2ff; color: #3730a3; }
.tag.pending { background: #fff7ed; color: #9a3412; }
.tag.approved { background: #dcfce7; color: #166534; }
.tag.rejected { background: #fee2e2; color: #991b1b; }
</style>
{% csrf_token %}
</head>
<body>
<div class="sidebar">
<h3>你好,{{ username|default:"管理员" }}</h3>
<div class="navigation-links">
<a href="{% url 'main:home' %}">返回主页</a>
<a id="logoutBtn" style="cursor:pointer;">退出登录</a>
<div id="logoutMsg" class="muted" style="margin-top:6px;"></div>
{% csrf_token %}
</div>
</div>
<div class="main-content">
<div class="card">
<div class="header">
<h2 style="margin:0;">注册码申请管理</h2>
<div style="display:flex; gap:10px; align-items:center;">
<select id="statusFilter" style="padding:8px 10px; border:1px solid #d1d5db; border-radius:10px;">
<option value="pending">待审核</option>
<option value="">全部</option>
<option value="approved">已同意</option>
<option value="rejected">已拒绝</option>
</select>
<button id="refreshBtn" class="btn btn-secondary" type="button">刷新</button>
</div>
</div>
<div class="muted">同意后,用户会获得“注册码管理”入口,且仅能使用自己新增的 key。</div>
<table>
<thead>
<tr>
<th style="width:120px;">用户</th>
<th>申请理由</th>
<th style="width:170px;">时间</th>
<th style="width:110px;">状态</th>
<th style="width:220px;">操作</th>
</tr>
</thead>
<tbody id="reqBody"></tbody>
</table>
<div id="pageMsg" class="muted" style="margin-top:12px;"></div>
</div>
</div>
<script>
function getCookie(name){const v=`; ${document.cookie}`;const p=v.split(`; ${name}=`);if(p.length===2) return p.pop().split(';').shift();}
document.getElementById('logoutBtn').addEventListener('click', async () => {
const msg = document.getElementById('logoutMsg');
msg.textContent = '';
const csrftoken = getCookie('csrftoken');
try {
const resp = await fetch('/accounts/logout/', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrftoken || '' },
body: JSON.stringify({})
});
const data = await resp.json();
if (data.ok) window.location.href = data.redirect_url;
} catch (e) { msg.textContent = '登出失败'; }
});
function fmtTime(t){
try{
const d = new Date(t);
if(String(d) !== 'Invalid Date'){
const pad = n=> String(n).padStart(2,'0');
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
}catch(e){}
return t || '';
}
function renderStatus(s){
const v = String(s || 'pending');
const cls = (v === 'approved' || v === 'rejected') ? v : 'pending';
const text = v === 'approved' ? '已同意' : (v === 'rejected' ? '已拒绝' : '待审核');
return `<span class="tag ${cls}">${text}</span>`;
}
async function loadRequests(){
const status = document.getElementById('statusFilter').value;
const msg = document.getElementById('pageMsg');
msg.textContent = '加载中...';
const url = status ? `/accounts/registration-code/requests/list/?status=${encodeURIComponent(status)}` : '/accounts/registration-code/requests/list/';
try{
const resp = await fetch(url, { credentials: 'same-origin' });
const data = await resp.json();
if(!(resp.ok && data && data.ok)){
msg.textContent = (data && data.message) ? data.message : '加载失败';
return;
}
const body = document.getElementById('reqBody');
body.innerHTML = '';
const rows = data.data || [];
if(!rows.length){
msg.textContent = '暂无数据';
return;
}
msg.textContent = '';
rows.forEach(r=>{
const tr = document.createElement('tr');
const uname = (r.username || '') + (r.user_id !== undefined ? `${r.user_id}` : '');
const reason = String(r.reason || '').replace(/</g,'&lt;').replace(/>/g,'&gt;');
const created = fmtTime(r.created_at);
const statusHtml = renderStatus(r.status);
const id = r.request_id || r._id || '';
const ops = (String(r.status || 'pending') === 'pending')
? `<button class="btn btn-primary" data-act="approve" data-id="${id}">同意</button>
<button class="btn btn-danger" data-act="reject" data-id="${id}">拒绝</button>`
: `<button class="btn btn-secondary" data-act="view" data-id="${id}">查看</button>`;
tr.innerHTML = `<td>${uname}</td><td style="white-space:pre-wrap;">${reason}</td><td>${created}</td><td>${statusHtml}</td><td>${ops}</td>`;
body.appendChild(tr);
});
}catch(e){
msg.textContent = '加载失败';
}
}
async function decide(id, action){
const csrftoken = getCookie('csrftoken');
const note = '';
const resp = await fetch('/accounts/registration-code/requests/decide/', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrftoken || '' },
body: JSON.stringify({ request_id: id, action, note })
});
const data = await resp.json();
if(!(resp.ok && data && data.ok)){
alert((data && data.message) ? data.message : '操作失败');
return;
}
loadRequests();
}
document.getElementById('refreshBtn').addEventListener('click', loadRequests);
document.getElementById('statusFilter').addEventListener('change', loadRequests);
document.addEventListener('click', (e)=>{
const t = e.target;
if(!(t && t.dataset && t.dataset.id && t.dataset.act)) return;
const id = t.dataset.id;
const act = t.dataset.act;
if(act === 'approve'){
if(confirm('确定同意该申请吗?')) decide(id, 'approve');
}else if(act === 'reject'){
if(confirm('确定拒绝该申请吗?')) decide(id, 'reject');
}else if(act === 'view'){
return;
}
});
loadRequests();
</script>
</body>
</html>