Files
JieXi/templates/admin_profile.html
2025-11-30 19:49:25 +08:00

293 lines
12 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>账号管理 - 管理后台</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/redeem-codes" 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>