292 lines
12 KiB
HTML
292 lines
12 KiB
HTML
<!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="/static/css/ui-components.css?v=3">
|
||
<link rel="stylesheet" href="/static/css/admin.css">
|
||
</head>
|
||
|
||
<body class="admin-layout">
|
||
<header class="admin-header">
|
||
<div class="header-container">
|
||
<a href="/admin/dashboard" class="brand">
|
||
<span style="font-size: 1.5rem;">⚡</span> JieXi Admin
|
||
</a>
|
||
<nav class="nav-links">
|
||
<a href="/admin/dashboard" class="nav-item">仪表板</a>
|
||
<a href="/admin/users" class="nav-item">用户管理</a>
|
||
<a href="/admin/apis" class="nav-item">接口管理</a>
|
||
<a href="/admin/config" class="nav-item">系统配置</a>
|
||
<a href="/admin/logs" class="nav-item">日志审计</a>
|
||
</nav>
|
||
<div class="user-actions">
|
||
<a href="/admin/profile" class="ui-btn ui-btn-secondary ui-btn-sm">账号设置</a>
|
||
<button class="ui-btn ui-btn-secondary ui-btn-sm" onclick="logout()">退出登录</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="main-container">
|
||
<div class="page-header">
|
||
<h1 class="page-title">账号管理</h1>
|
||
</div>
|
||
|
||
<div class="stats-grid" style="grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));">
|
||
<!-- Basic Info Card -->
|
||
<div class="ui-card">
|
||
<h3 class="text-lg font-bold mb-4">基本信息</h3>
|
||
<div class="form-group">
|
||
<label>用户名</label>
|
||
<div class="text-lg font-medium" id="username">-</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>邮箱</label>
|
||
<div class="text-lg" id="email">-</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>2FA状态</label>
|
||
<div id="2faStatus">-</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>最后登录</label>
|
||
<div class="text-sm text-muted">
|
||
<span id="lastLoginAt">-</span> (IP: <span id="lastLoginIp">-</span>)
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Change Email Card -->
|
||
<div class="ui-card">
|
||
<h3 class="text-lg font-bold mb-4">修改邮箱</h3>
|
||
<form id="emailForm">
|
||
<div class="form-group">
|
||
<label>新邮箱地址</label>
|
||
<input type="email" id="newEmail" class="ui-input" required placeholder="admin@example.com">
|
||
</div>
|
||
<button type="submit" class="ui-btn ui-btn-primary w-full">保存邮箱</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- Change Password Card -->
|
||
<div class="ui-card">
|
||
<h3 class="text-lg font-bold mb-4">修改密码</h3>
|
||
<form id="passwordForm">
|
||
<div class="form-group">
|
||
<label>原密码</label>
|
||
<input type="password" id="oldPassword" class="ui-input" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>新密码</label>
|
||
<input type="password" id="newPassword" class="ui-input" required minlength="8">
|
||
<small class="text-muted text-sm">密码长度至少8位</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>确认新密码</label>
|
||
<input type="password" id="confirmPassword" class="ui-input" required minlength="8">
|
||
</div>
|
||
<button type="submit" class="ui-btn ui-btn-primary w-full">修改密码</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- 2FA Card -->
|
||
<div class="ui-card">
|
||
<h3 class="text-lg font-bold mb-4">两步验证 (2FA)</h3>
|
||
<p class="text-sm text-muted mb-4">启用2FA可以提高账号安全性,需要使用Google Authenticator等应用扫描二维码。</p>
|
||
|
||
<div id="2faButtons">
|
||
<button class="ui-btn ui-btn-success w-full" onclick="enable2FA()">启用2FA</button>
|
||
</div>
|
||
|
||
<div id="2faSetup" style="display: none;">
|
||
<div style="text-align: center; margin-bottom: 1.5rem;">
|
||
<img id="qrCode" style="max-width: 200px; border-radius: 8px;">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>验证码</label>
|
||
<input type="text" id="2faCode" class="ui-input" placeholder="请输入6位验证码" maxlength="6">
|
||
</div>
|
||
<div class="flex gap-2">
|
||
<button class="ui-btn ui-btn-success flex-1" onclick="verify2FA()">验证并启用</button>
|
||
<button class="ui-btn ui-btn-secondary flex-1" onclick="cancel2FA()">取消</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<script src="/static/js/ui-components.js"></script>
|
||
<script>
|
||
async function loadProfile() {
|
||
try {
|
||
const response = await fetch('/admin/api/profile');
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
const data = result.data;
|
||
document.getElementById('username').textContent = data.username;
|
||
document.getElementById('email').textContent = data.email || '未设置';
|
||
document.getElementById('newEmail').value = data.email || '';
|
||
|
||
const is2faEnabled = data.is_2fa_enabled;
|
||
document.getElementById('2faStatus').innerHTML = is2faEnabled
|
||
? '<span class="badge badge-success">已启用</span>'
|
||
: '<span class="badge badge-neutral">未启用</span>';
|
||
|
||
document.getElementById('lastLoginIp').textContent = data.last_login_ip || '-';
|
||
document.getElementById('lastLoginAt').textContent = data.last_login_at
|
||
? new Date(data.last_login_at).toLocaleString('zh-CN')
|
||
: '-';
|
||
|
||
document.getElementById('2faButtons').innerHTML = is2faEnabled
|
||
? '<button class="ui-btn ui-btn-danger w-full" onclick="disable2FA()">禁用2FA</button>'
|
||
: '<button class="ui-btn ui-btn-success w-full" onclick="enable2FA()">启用2FA</button>';
|
||
}
|
||
} catch (error) {
|
||
UI.notify('加载失败: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
document.getElementById('emailForm').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
const email = document.getElementById('newEmail').value;
|
||
|
||
try {
|
||
const response = await fetch('/admin/api/profile/email', {
|
||
method: 'PUT',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ email })
|
||
});
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
UI.notify('邮箱修改成功', 'success');
|
||
loadProfile();
|
||
} else {
|
||
UI.notify(result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
UI.notify('修改失败: ' + error.message, 'error');
|
||
}
|
||
});
|
||
|
||
document.getElementById('passwordForm').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
const oldPassword = document.getElementById('oldPassword').value;
|
||
const newPassword = document.getElementById('newPassword').value;
|
||
const confirmPassword = document.getElementById('confirmPassword').value;
|
||
|
||
if (newPassword !== confirmPassword) {
|
||
UI.notify('两次输入的密码不一致', 'error');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch('/admin/api/profile/password', {
|
||
method: 'PUT',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ old_password: oldPassword, new_password: newPassword })
|
||
});
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
UI.notify('密码修改成功', 'success');
|
||
document.getElementById('passwordForm').reset();
|
||
} else {
|
||
UI.notify(result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
UI.notify('修改失败: ' + error.message, 'error');
|
||
}
|
||
});
|
||
|
||
async function enable2FA() {
|
||
try {
|
||
const response = await fetch('/admin/api/2fa/enable', { method: 'POST' });
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
document.getElementById('qrCode').src = result.qr_code;
|
||
document.getElementById('2faSetup').style.display = 'block';
|
||
document.getElementById('2faButtons').style.display = 'none';
|
||
} else {
|
||
UI.notify(result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
UI.notify('操作失败: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
async function verify2FA() {
|
||
const code = document.getElementById('2faCode').value;
|
||
if (!code || code.length !== 6) {
|
||
UI.notify('请输入6位验证码', 'error');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch('/admin/api/2fa/verify', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ code })
|
||
});
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
UI.notify('2FA已启用', 'success');
|
||
cancel2FA();
|
||
loadProfile();
|
||
} else {
|
||
UI.notify(result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
UI.notify('操作失败: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
function cancel2FA() {
|
||
document.getElementById('2faSetup').style.display = 'none';
|
||
document.getElementById('2faButtons').style.display = 'block';
|
||
document.getElementById('2faCode').value = '';
|
||
}
|
||
|
||
async function disable2FA() {
|
||
UI.prompt('禁用2FA', '请输入2FA验证码', '', async (code) => {
|
||
if (!code) return;
|
||
|
||
try {
|
||
const response = await fetch('/admin/api/2fa/disable', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ code })
|
||
});
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
UI.notify('2FA已禁用', 'success');
|
||
loadProfile();
|
||
} else {
|
||
UI.notify(result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
UI.notify('操作失败: ' + error.message, 'error');
|
||
}
|
||
});
|
||
}
|
||
|
||
async function logout() {
|
||
try {
|
||
await fetch('/admin/logout', { method: 'POST' });
|
||
window.location.href = '/admin/login';
|
||
} catch (error) {
|
||
UI.notify('退出失败', 'error');
|
||
}
|
||
}
|
||
|
||
loadProfile();
|
||
</script>
|
||
</body>
|
||
|
||
</html> |