This commit is contained in:
186
accounts/templates/accounts/registration_code_requests.html
Normal file
186
accounts/templates/accounts/registration_code_requests.html
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<!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,'<').replace(/>/g,'>');
|
||||||
|
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>
|
||||||
Reference in New Issue
Block a user