import React, { useState, useEffect, useRef } from 'react'; import { Card, Table, Button, Space, Modal, Form, Input, Select, message, Tag, Popconfirm, Avatar, Tooltip, Badge } from 'antd'; import { PlusOutlined, EditOutlined, DeleteOutlined, UserOutlined, ReloadOutlined, LockOutlined, CameraOutlined } from '@ant-design/icons'; import { userAPI, roleAPI } from '../api'; const { Option } = Select; const UserManagement = () => { const [users, setUsers] = useState([]); const [roles, setRoles] = useState([]); const [loading, setLoading] = useState(false); const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); const [modalVisible, setModalVisible] = useState(false); const [passwordModalVisible, setPasswordModalVisible] = useState(false); const [avatarModalVisible, setAvatarModalVisible] = useState(false); const [editingUser, setEditingUser] = useState(null); const [passwordUser, setPasswordUser] = useState(null); const [avatarUser, setAvatarUser] = useState(null); const [uploadLoading, setUploadLoading] = useState(false); const [form] = Form.useForm(); const [passwordForm] = Form.useForm(); const fileInputRef = useRef(null); useEffect(() => { fetchUsers(); fetchRoles(); }, [pagination.current]); const fetchUsers = async () => { setLoading(true); try { const response = await userAPI.list({ page: pagination.current, pageSize: pagination.pageSize }); if (response.success) { setUsers(response.data.users); setPagination(prev => ({ ...prev, total: response.data.total })); } } catch (error) { message.error('获取用户列表失败'); } finally { setLoading(false); } }; const fetchRoles = async () => { try { const response = await roleAPI.all(); if (response.success) { setRoles(response.data); } else { message.error('获取角色列表失败: ' + (response.message || '未知错误')); } } catch (error) { console.error('获取角色列表失败:', error); message.error('获取角色列表失败,请检查网络连接'); } }; const handleAdd = () => { setEditingUser(null); form.resetFields(); setModalVisible(true); }; const handleEdit = (user) => { setEditingUser(user); form.setFieldsValue({ username: user.username, email: user.email, phone: user.phone, realName: user.realName, status: user.status, roleIds: user.roles?.map(r => r.roleId) || [] }); setModalVisible(true); }; const handleResetPassword = (user) => { setPasswordUser(user); passwordForm.resetFields(); setPasswordModalVisible(true); }; const handleAvatarClick = (user) => { setAvatarUser(user); setAvatarModalVisible(true); }; const handleAvatarUpload = async (e) => { const file = e.target.files[0]; if (!file) return; if (!file.type.match(/image\/(jpeg|png|gif|webp)/)) { message.error('只支持 JPG、PNG、GIF 和 WebP 格式的图片'); return; } if (file.size > 5 * 1024 * 1024) { message.error('图片大小不能超过 5MB'); return; } setUploadLoading(true); try { const response = await userAPI.uploadAvatar(avatarUser.userId, file); if (response.success) { message.success('头像上传成功'); fetchUsers(); setAvatarModalVisible(false); } else { message.error(response.message || '上传失败'); } } catch (error) { message.error('上传失败'); } finally { setUploadLoading(false); if (fileInputRef.current) { fileInputRef.current.value = ''; } } }; const handleAvatarDelete = async () => { try { const response = await userAPI.deleteAvatar(avatarUser.userId); if (response.success) { message.success('头像已删除'); fetchUsers(); setAvatarModalVisible(false); } else { message.error(response.message || '删除失败'); } } catch (error) { message.error('删除失败'); } }; const handleDelete = async (userId) => { try { const response = await userAPI.delete(userId); if (response.success) { message.success('删除成功'); fetchUsers(); } else { message.error(response.message || '删除失败'); } } catch (error) { message.error('删除失败'); } }; const handleSubmit = async (values) => { try { let response; if (editingUser) { response = await userAPI.update(editingUser.userId, values); } else { response = await userAPI.create(values); } if (response.success) { message.success(editingUser ? '更新成功' : '创建成功'); setModalVisible(false); fetchUsers(); } else { message.error(response.message || '操作失败'); } } catch (error) { message.error('操作失败'); } }; const handleResetPasswordSubmit = async (values) => { try { const response = await userAPI.resetPassword(passwordUser.userId, values); if (response.success) { message.success('密码重置成功'); setPasswordModalVisible(false); } else { message.error(response.message || '重置失败'); } } catch (error) { message.error('重置失败'); } }; const getStatusColor = (status) => { const colors = { active: 'green', inactive: 'red', locked: 'orange' }; return colors[status] || 'default'; }; const getStatusText = (status) => { const texts = { active: '正常', inactive: '禁用', locked: '锁定' }; return texts[status] || status; }; const getAvatarUrl = (user) => { if (!user?.avatar) return null; return user.avatar; }; const columns = [ { title: '头像', key: 'avatar', width: 80, render: (_, record) => ( } src={getAvatarUrl(record)} style={{ backgroundColor: record.avatar ? 'transparent' : '#1890ff', cursor: 'pointer' }} onClick={() => handleAvatarClick(record)} /> ) }, { title: '用户名', key: 'username', width: 150, render: (_, record) => (
{record.realName || record.username}
@{record.username}
) }, { title: '邮箱', dataIndex: 'email', key: 'email', width: 200, render: (email) => email || '-' }, { title: '手机号', dataIndex: 'phone', key: 'phone', width: 130, render: (phone) => phone || '-' }, { title: '角色', key: 'roles', render: (_, record) => ( {record.roles?.map(role => ( {role.roleName} )) || '-'} ) }, { title: '状态', dataIndex: 'status', key: 'status', render: (status) => ( {getStatusText(status)} ) }, { title: '最后登录', key: 'lastLogin', render: (_, record) => (
{record.lastLoginTime ? new Date(record.lastLoginTime).toLocaleString() : '从未登录'}
{record.lastLoginIp || '-'}
) }, { title: '操作', key: 'action', render: (_, record) => ( `共 ${total} 条记录` }} onChange={(newPagination) => { setPagination(prev => ({ ...prev, ...newPagination })); }} /> setModalVisible(false)} footer={null} width={500} >
{!editingUser && ( )} {editingUser && ( )}
setPasswordModalVisible(false)} footer={null} width={400} >
setAvatarModalVisible(false)} footer={null} width={400} >
} src={getAvatarUrl(avatarUser)} style={{ backgroundColor: avatarUser?.avatar ? 'transparent' : '#1890ff', border: '1px solid #f0f0f0' }} />
{avatarUser?.avatar && ( )}
支持 JPG、PNG、GIF、WebP 格式,大小不超过 5MB
); }; export default UserManagement;