Skip to content

Commit 514f6e6

Browse files
author
CodeJudge
committed
feat: 管理员角色管理
1 parent 3ff7125 commit 514f6e6

2 files changed

Lines changed: 35 additions & 4 deletions

File tree

backend/src/routes/auth.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,12 @@ router.delete('/admin/users/:id', authenticate, (req, res) => {
102102
res.json({ message: '用户已删除' });
103103
});
104104

105+
router.put('/admin/users/:id', authenticate, (req, res) => {
106+
if (req.user.role !== 'admin') return res.status(403).json({ error: '需要管理员权限' });
107+
const { role } = req.body;
108+
if (!['user', 'admin'].includes(role)) return res.status(400).json({ error: '无效角色' });
109+
run('UPDATE users SET role = ? WHERE id = ?', [role, req.params.id]);
110+
res.json({ message: '用户已更新' });
111+
});
112+
105113
module.exports = router;

frontend/src/pages/Admin.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,14 +358,37 @@ export default function Admin() {
358358
<td className="py-2 px-3 text-white">{u.username}</td>
359359
<td className="py-2 px-3 text-dark-300 hidden md:table-cell">{u.email}</td>
360360
<td className="py-2 px-3 text-center">
361-
{u.role === 'admin' ? <Shield className="w-4 h-4 text-primary-400 mx-auto" /> : <span className="text-dark-400 text-xs">user</span>}
361+
{u.role === 'admin' ? (
362+
<Shield className="w-4 h-4 text-primary-400 mx-auto" />
363+
) : (
364+
<span className="text-dark-400 text-xs">user</span>
365+
)}
362366
</td>
363367
<td className="py-2 px-3 text-dark-400 text-center hidden md:table-cell">{u.created_at?.slice(0, 10)}</td>
364368
<td className="py-2 px-3 text-center">
365369
{u.role !== 'admin' && (
366-
<button onClick={() => handleDeleteUser(u.id, u.username)} className="btn-danger text-xs px-2 py-1">
367-
<Trash2 className="w-3.5 h-3.5" />
368-
</button>
370+
<div className="flex items-center justify-center gap-1">
371+
<button
372+
onClick={async () => {
373+
try {
374+
const token = localStorage.getItem('oj_token');
375+
const res = await fetch(`/api/auth/admin/users/${u.id}`, {
376+
method: 'PUT',
377+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
378+
body: JSON.stringify({ role: u.role === 'admin' ? 'user' : 'admin' }),
379+
});
380+
if (res.ok) { toast.success('角色已更新'); fetchData(); }
381+
else toast.error('更新失败');
382+
} catch { toast.error('更新失败'); }
383+
}}
384+
className="text-[10px] px-2 py-0.5 rounded bg-dark-700 text-dark-400 hover:text-white transition-colors"
385+
>
386+
{u.role === 'admin' ? '降为普通' : '设为管理员'}
387+
</button>
388+
<button onClick={() => handleDeleteUser(u.id, u.username)} className="btn-danger text-xs px-2 py-1">
389+
<Trash2 className="w-3.5 h-3.5" />
390+
</button>
391+
</div>
369392
)}
370393
</td>
371394
</tr>

0 commit comments

Comments
 (0)