Files
2026-03-16 09:24:05 +08:00

308 lines
14 KiB
HTML
Raw Permalink 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" class="precheck-hide">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>用户管理 - iDing's临时邮箱</title>
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<noscript>
<meta http-equiv="refresh" content="0; url=/templates/loading.html?redirect=%2Fhtml%2Fadmin.html" />
</noscript>
<style>.precheck-hide body{ visibility: hidden !important; }</style>
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="shortcut icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="/css/app.css" />
<link rel="stylesheet" href="/css/admin.css" />
<script src="/js/auth-guard.js" defer></script>
<script>
// 访问管理页先做前端快速鉴权,避免内容闪现;未登录或权限不足均进入 loading 或回首页
(function(){
fetch('/api/session', { headers: { 'Cache-Control': 'no-cache' } })
.then(r => r.ok ? r.json() : Promise.reject('unauth'))
.then(s => {
if (s && (s.role === 'admin' || s.role === 'guest' || s.strictAdmin)){
try{ document.documentElement.classList.remove('precheck-hide'); }catch(_){ }
return;
}
// 已登录但无权限:回首页
location.replace('/');
})
.catch(() => {
// 未登录:进入加载页做鉴权
if (window.AuthGuard) { window.AuthGuard.goLoading('/html/admin.html', '正在校验权限…'); }
else { location.replace('/templates/loading.html?redirect=%2Fhtml%2Fadmin.html&status=' + encodeURIComponent('正在校验权限…')); }
});
})();
</script>
</head>
<body class="admin-page">
<div class="topbar">
<div class="brand">
<span class="brand-icon">🚀</span>
<span>iDing's临时邮箱 - 用户管理</span>
</div>
<div class="nav-actions">
<button id="back" class="btn btn-ghost" title="返回邮箱">
<span>📧</span>
<span>返回邮箱</span>
</button>
<button id="logout" class="btn btn-secondary" title="退出登录">
<span>👋</span>
<span>退出登录</span>
</button>
</div>
</div>
<div id="demo-banner" class="demo-banner" style="display:none">当前为演示管理页:所有改动仅在浏览器会话中模拟</div>
<div class="admin-container">
<div class="admin-left">
<div class="card">
<h2><span class="card-icon">👤</span><span>用户管理</span></h2>
<div class="generate-action">
<button id="u-open" class="btn btn-primary">
<span></span>
<span>创建用户</span>
</button>
</div>
<div class="generate-action generate-action-row">
<button id="unassign-open" class="btn btn-secondary">
<span>📭</span>
<span>取消分配</span>
</button>
<button id="a-open" class="btn btn-primary">
<span>📬</span>
<span>分配邮箱</span>
</button>
</div>
</div>
<div class="card">
<div class="card-header">
<h2><span class="card-icon">📋</span><span>用户列表</span><span id="users-count" class="users-count">0 用户)</span></h2>
<button id="users-refresh" class="btn btn-ghost btn-sm" title="刷新用户列表">
<span>🔄</span>
<span>刷新</span>
</button>
</div>
<div id="users-loading" class="loading-indicator" style="margin: 4px 0 8px 0">
<div class="spinner"></div>
<span>加载中…</span>
</div>
<div class="table-container">
<table class="table">
<thead>
<tr>
<th class="col-id">ID</th>
<th class="col-username">用户名</th>
<th class="col-role">角色</th>
<th class="col-mailbox">邮箱数量</th>
<th class="col-can">发件</th>
<th class="col-created">创建时间</th>
<th class="col-actions">操作</th>
</tr>
</thead>
<tbody id="users-tbody"></tbody>
</table>
</div>
<div class="pagination-container" id="users-pagination" style="display:none">
<div class="pagination-info">
<span id="pagination-text">显示 1-50 条,共 0 条</span>
</div>
<div class="pagination-controls">
<button id="prev-page" class="btn btn-ghost btn-sm" disabled>上一页</button>
<span id="page-info">1 / 1</span>
<button id="next-page" class="btn btn-ghost btn-sm" disabled>下一页</button>
</div>
</div>
</div>
</div>
<div class="admin-right">
<div class="card">
<div class="card-header">
<h2><span class="card-icon">📦</span><span>用户的邮箱列表</span><span id="mailboxes-count" class="mailboxes-count">0 邮箱)</span></h2>
<button id="mailboxes-refresh" class="btn btn-ghost btn-sm" title="刷新邮箱列表" style="display:none">
<span>🔄</span>
<span>刷新</span>
</button>
</div>
<div id="user-mailboxes-loading" class="loading-indicator" style="margin: 4px 0 8px 0; display:none">
<div class="spinner"></div>
<span>加载中…</span>
</div>
<div id="user-mailboxes">
<div class="mailbox-list"></div>
</div>
<div class="pagination-container" id="mailboxes-pagination" style="display:none">
<div class="pagination-info">
<span id="mailboxes-pagination-text">显示 1-20 条,共 0 条</span>
</div>
<div class="pagination-controls">
<button id="mailboxes-prev-page" class="btn btn-ghost btn-sm" disabled>上一页</button>
<span id="mailboxes-page-info">1 / 1</span>
<button id="mailboxes-next-page" class="btn btn-ghost btn-sm" disabled>下一页</button>
</div>
</div>
</div>
</div>
<div id="toast" class="toast"></div>
<!-- 编辑用户 二级页面 -->
<div class="modal" id="edit-modal">
<div class="modal-card">
<div class="modal-header">
<div>
<span class="modal-icon">✏️</span>
<span>编辑用户</span>
<span id="edit-user-display" class="user-chip"></span>
</div>
<button id="edit-close" class="close"></button>
</div>
<div class="modal-body">
<div class="form-grid">
<div>
<label class="config-label">新密码</label>
<input id="edit-pass" class="input" type="password" placeholder="留空则不修改" />
</div>
<div>
<label class="config-label">新用户名</label>
<input id="edit-new-name" class="input" placeholder="留空则不修改" />
</div>
<div>
<label class="config-label">邮箱上限</label>
<input id="edit-limit" class="input" type="number" min="0" />
</div>
</div>
<div class="checks-row">
<label class="toggle"><input id="edit-role-check" type="checkbox" /><span>高级用户</span></label>
<label class="toggle"><input id="edit-send-check" type="checkbox" /><span>允许发件</span></label>
</div>
</div>
<div class="modal-footer" style="display:flex;gap:8px;justify-content:flex-end;padding:12px 16px">
<button id="edit-cancel" class="btn btn-secondary">取消</button>
<button id="edit-save" class="btn btn-primary">保存</button>
<button id="edit-delete" class="btn btn-danger">删除用户</button>
</div>
</div>
</div>
<!-- 自定义确认对话框(删除用户) -->
<div class="modal" id="admin-confirm-modal">
<div class="modal-card confirm-card">
<div class="modal-header confirm-header">
<div>
<span class="modal-icon">🔔</span>
<span>确认操作</span>
</div>
<button id="admin-confirm-close" class="close"></button>
</div>
<div class="modal-body confirm-body">
<div id="admin-confirm-message" class="confirm-message"></div>
<div class="confirm-actions">
<button id="admin-confirm-cancel" class="btn btn-secondary">取消</button>
<button id="admin-confirm-ok" class="btn btn-danger">确定</button>
</div>
</div>
</div>
</div>
<div class="modal" id="u-modal">
<div class="modal-card">
<div class="modal-header">
<div>
<span class="modal-icon">👤</span>
<span>创建用户</span>
</div>
<button id="u-close" class="close"></button>
</div>
<div class="modal-body">
<div class="config-form">
<div class="config-item"><label class="config-label">用户名</label><input id="u-name" class="input" placeholder="username" /></div>
<div class="config-item"><label class="config-label">密码</label><input id="u-pass" class="input" type="password" placeholder="password" /></div>
<div class="config-item"><label class="config-label">角色</label><select id="u-role" class="select"><option value="user">普通用户</option><option value="admin">高级用户</option></select></div>
</div>
</div>
<div class="modal-footer" style="display:flex;gap:8px;justify-content:flex-end;padding:12px 16px">
<button id="u-cancel" class="btn btn-secondary">取消</button>
<button id="u-create" class="btn btn-primary">创建</button>
</div>
</div>
</div>
<div class="modal" id="a-modal">
<div class="modal-card">
<div class="modal-header">
<div>
<span class="modal-icon">📬</span>
<span>分配邮箱</span>
</div>
<button id="a-close" class="close"></button>
</div>
<div class="modal-body">
<div class="config-form">
<div class="config-item"><label class="config-label">用户名</label><input id="a-name" class="input" placeholder="username" /></div>
<div class="config-item">
<label class="config-label">邮箱地址</label>
<textarea id="a-mail" class="input" placeholder="foo@example.com&#10;bar@example.com&#10;baz@example.com" rows="4"></textarea>
<div class="help-text">每行一个邮箱地址,支持批量分配</div>
</div>
</div>
</div>
<div class="modal-footer" style="display:flex;gap:8px;justify-content:flex-end;padding:12px 16px">
<button id="a-cancel" class="btn btn-secondary">取消</button>
<button id="a-assign" class="btn btn-primary">分配</button>
</div>
</div>
</div>
<div class="modal" id="unassign-modal">
<div class="modal-card">
<div class="modal-header">
<div>
<span class="modal-icon">📬</span>
<span>取消分配邮箱</span>
</div>
<button id="unassign-close" class="close"></button>
</div>
<div class="modal-body">
<div class="config-form">
<div class="config-item"><label class="config-label">用户名</label><input id="unassign-name" class="input" placeholder="username" /></div>
<div class="config-item">
<label class="config-label">邮箱地址</label>
<textarea id="unassign-mail" class="input" placeholder="foo@example.com&#10;bar@example.com&#10;baz@example.com" rows="4"></textarea>
<div class="help-text">每行一个邮箱地址,支持批量取消分配</div>
</div>
</div>
</div>
<div class="modal-footer" style="display:flex;gap:8px;justify-content:flex-end;padding:12px 16px">
<button id="unassign-cancel" class="btn btn-secondary">取消</button>
<button id="unassign-submit" class="btn btn-danger">取消分配</button>
</div>
</div>
</div>
</div>
<div id="footer-slot"></div>
<script src="/js/toast-utils.js"></script>
<script type="module" src="/js/admin.js"></script>
<script>
// 动态加载公共 footer 模板,并在宿主页面设置年份,确保执行
(async function(){
try{
const res = await fetch('/templates/footer.html', { cache: 'no-cache' });
const html = await res.text();
const slot = document.getElementById('footer-slot');
if (slot){
slot.outerHTML = html;
// 等 DOM 更新后填充年份
setTimeout(() => {
const y = document.getElementById('footer-year');
if (y) y.textContent = new Date().getFullYear();
}, 0);
}
}catch(_){ }
})();
</script>
</body>
</html>