161 lines
7.2 KiB
HTML
161 lines
7.2 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>用户管理 — Mi Diario</title>
|
|
<link rel="stylesheet" href="style.css">
|
|
<style>
|
|
body { display: block; max-width: 960px; margin: 0 auto; padding: 1rem; }
|
|
h1 { font-size: 1.4rem; margin: 0.75rem 0; }
|
|
.admin-card {
|
|
background: var(--panel-bg); border: 1px solid var(--border);
|
|
border-radius: var(--radius); padding: 1.25rem; margin-bottom: 1rem;
|
|
}
|
|
.admin-card h2 { font-size: 1rem; margin-bottom: 0.75rem; }
|
|
.admin-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 0.5rem; }
|
|
.admin-form label { font-size: 0.8rem; color: var(--text-muted); display: flex; flex-direction: column; gap: 0.2rem; }
|
|
.admin-form input, .admin-form select {
|
|
border: 1px solid var(--border); border-radius: 6px; padding: 0.45rem 0.6rem;
|
|
background: var(--bg); color: var(--text); font-size: 0.9rem;
|
|
}
|
|
.admin-form button {
|
|
background: var(--accent); color: #fff; border: none; border-radius: 6px;
|
|
padding: 0.5rem 1rem; cursor: pointer; align-self: end;
|
|
}
|
|
table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
|
|
th, td { border: 1px solid var(--border); padding: 0.5rem 0.6rem; text-align: left; }
|
|
th { background: var(--bg); }
|
|
.user-actions { display: flex; gap: 0.35rem; flex-wrap: wrap; max-width: 240px; }
|
|
.user-actions button {
|
|
border: 1px solid var(--border); background: none; border-radius: 4px;
|
|
padding: 0.3rem 0.5rem; cursor: pointer; color: var(--text-muted);
|
|
}
|
|
.user-actions button:hover { border-color: var(--accent); color: var(--accent); }
|
|
.user-actions .danger { color: var(--danger); border-color: var(--danger); }
|
|
.status-active { color: var(--success); }
|
|
.status-disabled { color: var(--danger); }
|
|
#admin-msg { min-height: 1.5em; font-size: 0.85rem; color: var(--text-muted); }
|
|
#back-home { display: inline-block; margin-bottom: 0.75rem; color: var(--accent); text-decoration: none; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<a id="back-home" href="index.html">← 返回日记</a>
|
|
<h1>用户管理</h1>
|
|
<div id="admin-msg"></div>
|
|
|
|
<div class="admin-card">
|
|
<h2>添加用户</h2>
|
|
<div class="admin-form">
|
|
<label>用户名<input id="new-username" type="text"></label>
|
|
<label>显示名<input id="new-display" type="text"></label>
|
|
<label>密码<input id="new-password" type="password"></label>
|
|
<label>管理员<select id="new-admin"><option value="0">否</option><option value="1">是</option></select></label>
|
|
<label>启用<select id="new-active"><option value="1">是</option><option value="0">否</option></select></label>
|
|
<button id="btn-add-user">添加</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="admin-card">
|
|
<h2>用户列表</h2>
|
|
<table>
|
|
<thead><tr><th>ID</th><th>用户名</th><th>显示名</th><th>角色</th><th>状态</th><th>创建时间</th><th>操作</th></tr></thead>
|
|
<tbody id="user-table-body"></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<script src="api.js"></script>
|
|
<script>
|
|
var adminMsg = document.getElementById('admin-msg');
|
|
function esc(s) {
|
|
var div = document.createElement('div');
|
|
div.textContent = s == null ? '' : String(s);
|
|
return div.innerHTML;
|
|
}
|
|
|
|
function loadUsers() {
|
|
apiAdminUsers().then(function(users) {
|
|
document.getElementById('user-table-body').innerHTML = users.map(function(u) {
|
|
var statusClass = u.is_active ? 'status-active' : 'status-disabled';
|
|
var statusText = u.is_active ? '启用' : '禁用';
|
|
var roleText = u.is_admin ? '管理员' : '普通用户';
|
|
return '<tr>' +
|
|
'<td>' + u.id + '</td>' +
|
|
'<td>' + esc(u.username) + '</td>' +
|
|
'<td>' + esc(u.display_name) + '</td>' +
|
|
'<td>' + roleText + '</td>' +
|
|
'<td class="' + statusClass + '">' + statusText + '</td>' +
|
|
'<td>' + esc(u.created_at) + '</td>' +
|
|
'<td><div class="user-actions">' +
|
|
'<button data-id="' + u.id + '" data-active="' + (u.is_active ? '0' : '1') + '" class="toggle-user">' + (u.is_active ? '禁用' : '启用') + '</button>' +
|
|
'<button data-id="' + u.id + '" class="reset-password">重置密码</button>' +
|
|
'<button data-id="' + u.id + '" class="danger delete-user">删除</button>' +
|
|
'</div></td>' +
|
|
'</tr>';
|
|
}).join('') || '<tr><td colspan="7">暂无用户</td></tr>';
|
|
}).catch(function(err) {
|
|
adminMsg.textContent = err.message;
|
|
});
|
|
}
|
|
|
|
document.getElementById('btn-add-user').addEventListener('click', function() {
|
|
var username = document.getElementById('new-username').value.trim();
|
|
var display = document.getElementById('new-display').value.trim();
|
|
var password = document.getElementById('new-password').value;
|
|
if (!username || !password) { adminMsg.textContent = '用户名和密码不能为空'; return; }
|
|
apiAdminCreateUser({
|
|
username: username,
|
|
displayName: display || username,
|
|
password: password,
|
|
isAdmin: document.getElementById('new-admin').value === '1',
|
|
isActive: document.getElementById('new-active').value === '1'
|
|
}).then(function() {
|
|
adminMsg.textContent = '用户已添加';
|
|
document.getElementById('new-username').value = '';
|
|
document.getElementById('new-display').value = '';
|
|
document.getElementById('new-password').value = '';
|
|
loadUsers();
|
|
}).catch(function(err) { adminMsg.textContent = err.message; });
|
|
});
|
|
|
|
document.getElementById('user-table-body').addEventListener('click', function(e) {
|
|
var btn = e.target.closest('button');
|
|
if (!btn) return;
|
|
var id = btn.dataset.id;
|
|
if (btn.classList.contains('toggle-user')) {
|
|
apiAdminSetStatus(id, btn.dataset.active === '1').then(function() {
|
|
loadUsers();
|
|
}).catch(function(err) { adminMsg.textContent = err.message; });
|
|
} else if (btn.classList.contains('delete-user')) {
|
|
if (!confirm('确定删除该用户及其全部日记、收藏数据吗?')) return;
|
|
apiAdminDeleteUser(id).then(function() {
|
|
adminMsg.textContent = '用户已删除';
|
|
loadUsers();
|
|
}).catch(function(err) { adminMsg.textContent = err.message; });
|
|
} else if (btn.classList.contains('reset-password')) {
|
|
var newPassword = prompt('请输入该用户的新密码(至少 6 位):');
|
|
if (newPassword == null) return;
|
|
if (newPassword.length < 6) {
|
|
adminMsg.textContent = '密码至少 6 位';
|
|
return;
|
|
}
|
|
apiAdminResetPassword(id, newPassword).then(function() {
|
|
adminMsg.textContent = '密码已重置';
|
|
}).catch(function(err) { adminMsg.textContent = err.message; });
|
|
}
|
|
});
|
|
|
|
apiGetMe().then(function(me) {
|
|
if (!me.isAdmin) {
|
|
location.href = 'index.html';
|
|
return;
|
|
}
|
|
loadUsers();
|
|
}).catch(function(err) {
|
|
if (err && err.status === 401) location.href = 'login.html';
|
|
else adminMsg.textContent = err.message;
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|