import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { Table, Button, Modal, Form, Input, Switch, Space, Tag, Popconfirm, message, Card, Typography, Badge, InputNumber, Row, Col, Pagination, Dropdown, } from 'antd'; import { PlusOutlined, EditOutlined, DeleteOutlined, KeyOutlined, StopOutlined, CheckCircleOutlined, ArrowLeftOutlined, TeamOutlined, UserOutlined, SearchOutlined, MoreOutlined, } from '@ant-design/icons'; import { adminApi } from '../services/api'; import type { User } from '../types'; import UserMenu from '../components/UserMenu'; const { Title, Text } = Typography; interface UserWithStatus extends User { is_active?: boolean; } export default function UserManagement() { const navigate = useNavigate(); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(false); const [modalVisible, setModalVisible] = useState(false); const [editModalVisible, setEditModalVisible] = useState(false); const [resetPasswordModalVisible, setResetPasswordModalVisible] = useState(false); const [currentUser, setCurrentUser] = useState(null); const [newPassword, setNewPassword] = useState(''); const [pageSize, setPageSize] = useState(20); const [currentPage, setCurrentPage] = useState(1); const [searchText, setSearchText] = useState(''); const [form] = Form.useForm(); const [editForm] = Form.useForm(); const [modal, contextHolder] = Modal.useModal(); // 过滤用户列表 const filteredUsers = users.filter(user => { if (!searchText) return true; const searchLower = searchText.toLowerCase(); return ( user.username?.toLowerCase().includes(searchLower) || user.display_name?.toLowerCase().includes(searchLower) || user.user_id?.toLowerCase().includes(searchLower) ); }); // 加载用户列表 const loadUsers = async () => { setLoading(true); try { const res = await adminApi.getUsers(); setUsers(res.users); } catch (error) { console.error('加载用户列表失败:', error); message.error('加载用户列表失败'); } finally { setLoading(false); } }; useEffect(() => { loadUsers(); }, []); // 添加用户 interface CreateUserValues { username: string; display_name: string; password?: string; avatar_url?: string; trust_level?: number; is_admin?: boolean; } const handleCreate = async (values: CreateUserValues) => { try { const res = await adminApi.createUser(values); message.success('用户创建成功'); // 如果有默认密码,显示给管理员 if (res.default_password) { modal.info({ title: '用户创建成功', content: (

用户名:{values.username}

初始密码:{res.default_password}

⚠️ 请复制密码并告知用户,此密码仅显示一次!

), width: 500, centered: true, }); } setModalVisible(false); form.resetFields(); loadUsers(); } catch (error) { console.error('创建用户失败:', error); message.error('创建用户失败'); } }; // 编辑用户 const handleEdit = (user: UserWithStatus) => { setCurrentUser(user); editForm.setFieldsValue({ display_name: user.display_name, avatar_url: user.avatar_url, trust_level: user.trust_level, is_admin: user.is_admin, }); setEditModalVisible(true); }; interface UpdateUserValues { display_name: string; avatar_url?: string; trust_level?: number; is_admin?: boolean; } const handleUpdate = async (values: UpdateUserValues) => { if (!currentUser) return; try { await adminApi.updateUser(currentUser.user_id, values); message.success('用户信息更新成功'); setEditModalVisible(false); editForm.resetFields(); loadUsers(); } catch (error) { console.error('更新用户失败:', error); message.error('更新用户失败'); } }; // 切换用户状态 const handleToggleStatus = async (user: UserWithStatus) => { const isActive = user.is_active !== false; const action = isActive ? '禁用' : '启用'; try { await adminApi.toggleUserStatus(user.user_id, !isActive); message.success(`用户已${action}`); loadUsers(); } catch (error) { console.error(`${action}用户失败:`, error); message.error(`${action}用户失败`); } }; // 重置密码 const handleResetPassword = (user: UserWithStatus) => { setCurrentUser(user); setNewPassword(''); setResetPasswordModalVisible(true); }; const handleResetPasswordConfirm = async () => { if (!currentUser) return; try { const res = await adminApi.resetPassword( currentUser.user_id, newPassword || undefined ); modal.info({ title: '密码重置成功', content: (

用户:{currentUser.username}

新密码:{res.new_password}

⚠️ 请复制密码并告知用户!

), width: 500, centered: true, }); setResetPasswordModalVisible(false); setNewPassword(''); } catch (error) { console.error('重置密码失败:', error); message.error('重置密码失败'); } }; // 删除用户 const handleDelete = async (user: UserWithStatus) => { try { await adminApi.deleteUser(user.user_id); message.success('用户已删除'); loadUsers(); } catch (error) { console.error('删除用户失败:', error); message.error('删除用户失败'); } }; const isMobile = window.innerWidth <= 768; // 表格列定义 const columns = [ { title: '用户名', dataIndex: 'username', key: 'username', width: 150, render: (text: string) => ( {text} ), }, { title: '显示名称', dataIndex: 'display_name', key: 'display_name', width: 150, }, { title: '状态', dataIndex: 'is_active', key: 'is_active', width: 100, render: (isActive: boolean) => ( ), }, { title: '角色', dataIndex: 'is_admin', key: 'is_admin', width: 100, render: (isAdmin: boolean) => ( {isAdmin ? '👑 管理员' : '普通用户'} ), }, { title: '信任等级', dataIndex: 'trust_level', key: 'trust_level', width: 100, render: (level: number) => ( = 5 ? 'green' : 'blue'}> {level === -1 ? '已禁用' : `Level ${level}`} ), }, { title: '创建时间', dataIndex: 'created_at', key: 'created_at', width: 180, render: (date: string) => date ? new Date(date).toLocaleString('zh-CN') : '-', }, { title: '最后登录', dataIndex: 'last_login', key: 'last_login', width: 180, render: (date: string) => date ? new Date(date).toLocaleString('zh-CN') : '从未登录', }, { title: '操作', key: 'action', width: isMobile ? 80 : 300, fixed: 'right' as const, render: (_: unknown, record: UserWithStatus) => { const isActive = record.is_active !== false; // 移动端:使用下拉菜单 if (isMobile) { const menuItems = [ { key: 'edit', label: '编辑用户', icon: , onClick: () => handleEdit(record), }, { key: 'reset', label: '重置密码', icon: , onClick: () => handleResetPassword(record), }, { key: 'toggle', label: isActive ? '禁用用户' : '启用用户', icon: isActive ? : , danger: isActive, onClick: () => { modal.confirm({ title: `确定${isActive ? '禁用' : '启用'}该用户吗?`, onOk: () => handleToggleStatus(record), okText: '确定', cancelText: '取消', }); }, }, ...(!record.is_admin ? [{ key: 'delete', label: '删除用户', icon: , danger: true, onClick: () => { modal.confirm({ title: '确定删除该用户吗?此操作不可恢复!', onOk: () => handleDelete(record), okText: '确定', cancelText: '取消', okButtonProps: { danger: true }, }); }, }] : []), ]; return ( handleToggleStatus(record)} okText="确定" cancelText="取消" > {!record.is_admin && ( handleDelete(record)} okText="确定" cancelText="取消" okButtonProps={{ danger: true }} > )} ); }, }, ]; return (
{contextHolder}
{/* 顶部导航卡片 */} {/* 装饰性背景元素 */}
<TeamOutlined style={{ color: 'rgba(255,255,255,0.9)', marginRight: 12 }} /> 用户管理 管理系统用户和权限 {/* 主内容卡片 */} {/* 搜索栏 */}
} value={searchText} onChange={(e) => { setSearchText(e.target.value); setCurrentPage(1); // 搜索时重置到第一页 }} allowClear style={{ borderRadius: 8, }} />
{/* 表格区域 */}
{/* 固定分页控件 */}
`共 ${total} 个用户${searchText ? ' (已过滤)' : ''}`} pageSizeOptions={[20, 50, 100]} onChange={(page, size) => { setCurrentPage(page); setPageSize(size); }} onShowSizeChange={(_current, size) => { setCurrentPage(1); setPageSize(size); }} />
{/* 添加用户对话框 */} 添加用户} open={modalVisible} onCancel={() => { setModalVisible(false); form.resetFields(); }} onOk={() => form.submit()} width={isMobile ? '90%' : 600} centered okText="创建" cancelText="取消" >
{/* 编辑用户对话框 */} 编辑用户} open={editModalVisible} onCancel={() => { setEditModalVisible(false); editForm.resetFields(); }} onOk={() => editForm.submit()} width={isMobile ? '90%' : 600} centered okText="保存" cancelText="取消" >
{/* 重置密码对话框 */} 重置密码} open={resetPasswordModalVisible} onCancel={() => { setResetPasswordModalVisible(false); setNewPassword(''); }} onOk={handleResetPasswordConfirm} width={isMobile ? '90%' : 500} centered okText="确认重置" cancelText="取消" >
用户:{currentUser?.username}
setNewPassword(e.target.value)} placeholder="留空则使用默认密码" />
); }