/** * filekorolheader: 员工管理页面 - 企业员工账户管理页面 * 功能:员工列表查询、添加编辑、状态管理、角色分配 * 路径:/central-config/user/employee * 规范:遵循crop-x/docs/开发项目规范.md,使用事件驱动模式,SearchFormPagination重构 */ 'use client'; import { useState, useEffect, useCallback, useRef } from 'react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Plus, Eye, Edit, Trash2, Power, PowerOff } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; import { EmployeeManagementHeader } from './components/EmployeeManagementHeader'; import { EmployeeManagementStatsCards } from './components/EmployeeManagementStatsCards'; import { EmployeeFormDialog } from './components/EmployeeFormDialog'; import { EmployeeDetailDialog } from './components/EmployeeDetailDialog'; import { SearchFormPagination, type SearchFieldConfig, type TableColumnConfig } from '@/components/common/searchFormPagination'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Employee, Role, EmployeeFormData } from './types'; import { fetchEmployees, transformEmployeesList, createEmployee, updateEmployee, activateUser, deactivateUser, deleteUser, CreateEmployeeRequest, UpdateEmployeeRequest, PaginationState, EmployeesQueryParams } from './components/employeeApi'; import { fetchRoles, transformRolesList } from '../role/components/roleApi'; export default function EmployeeManagementPage() { const [employees, setEmployees] = useState([]); const [roles, setRoles] = useState([]); const [loading, setLoading] = useState(false); const [creating, setCreating] = useState(false); const [updating, setUpdating] = useState(false); const [toggling, setToggling] = useState(null); // 记录正在操作的用户ID const isFirstLoad = useRef(true); // 确认对话框状态 const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); const [userToDelete, setUserToDelete] = useState(null); const [deactivateConfirmOpen, setDeactivateConfirmOpen] = useState(false); const [userToDeactivate, setUserToDeactivate] = useState(null); const [pagination, setPagination] = useState({ page: 1, size: 10, total: 0, totalPages: 0, hasNext: false, hasPrev: false, }); const [searchFilters, setSearchFilters] = useState>({ search: '', status: 'all' }); const [showForm, setShowForm] = useState(false); const [showDetailDialog, setShowDetailDialog] = useState(false); const [formKey, setFormKey] = useState(0); // 添加key来强制重新渲染表单 const [editingEmployee, setEditingEmployee] = useState(null); const [selectedEmployee, setSelectedEmployee] = useState(null); const [formData, setFormData] = useState({ enterpriseId: 'ent-2', enterpriseName: '丰收现代农业集团', status: 'active', auditStatus: 'pending', roleIds: [], idCard: '', address: '', }); // 搜索字段配置 const searchFields: SearchFieldConfig[] = [ { key: 'search', label: '搜索', type: 'text', placeholder: '搜索用户名、姓名、手机号...', }, { key: 'status', label: '账户状态', type: 'select', defaultValue: 'all', options: [ { value: 'all', label: '全部状态' }, { value: 'active', label: '正常' }, { value: 'frozen', label: '停用' }, ], }, ]; // 表格列配置 const columns: TableColumnConfig[] = [ { key: 'username', label: '用户名', render: (value: string) => (
{value}
), }, { key: 'fullName', label: '姓名', render: (value: string) => (
{value || '-'}
), }, { key: 'phone', label: '手机号', render: (value: string) => (
{value || '-'}
), }, { key: 'roles', label: '角色', render: (value: string[]) => (
{value && value.length > 0 ? ( value.map((role, index) => ( {role} )) ) : ( - )}
), }, { key: 'department', label: '部门', render: (value: string) => (
{value || '-'}
), }, { key: 'isActive', label: '状态', render: (value: boolean) => ( {value ? '正常' : '停用'} ), }, { key: 'createdAt', label: '创建时间', render: (value: string) => (
{value ? new Date(value).toLocaleDateString('zh-CN') : '-'}
), }, { key: 'actions', label: '操作', render: (_: any, row: Employee) => (
), }, ]; // 加载角色数据 - 事件驱动 const loadRoles = useCallback(async () => { try { const response = await fetchRoles({ page: 1, size: 100, sort_order: 'desc' }); const transformedRoles = transformRolesList(response.data); setRoles(transformedRoles); } catch (error) { console.error('Failed to load roles:', error); setRoles([]); } }, []); // 加载员工数据 - 事件驱动,移除依赖项 const loadEmployees = useCallback(async (params?: { filters?: Record; pagination?: { page: number; size: number }; resetPage?: boolean; }) => { try { setLoading(true); const queryParams: EmployeesQueryParams = { page: params?.resetPage ? 1 : (params?.pagination?.page || pagination.page), size: params?.pagination?.size || pagination.size, sort_order: 'desc' }; // 搜索关键词 const searchKeyword = params?.filters?.search ?? searchFilters.search; if (searchKeyword) { queryParams.search = searchKeyword; } const response = await fetchEmployees(queryParams); const transformedEmployees = transformEmployeesList(response.data); // 状态筛选(客户端过滤) const statusFilter = params?.filters?.status ?? searchFilters.status; const filteredEmployees = statusFilter === 'all' ? transformedEmployees : transformedEmployees.filter(emp => { const status = emp.isActive ? 'active' : 'frozen'; return status === statusFilter; }); setEmployees(filteredEmployees); setPagination({ page: response.page, size: response.size, total: response.total, totalPages: response.total_pages, hasNext: response.has_next, hasPrev: response.has_prev, }); } catch (error) { console.error('Failed to load employees:', error); toast.error('加载员工数据失败'); setEmployees([]); } finally { setLoading(false); } }, [pagination.page, pagination.size, searchFilters]); // 初始化数据 - 只在组件挂载时执行一次 useEffect(() => { if (isFirstLoad.current) { isFirstLoad.current = false; loadRoles(); loadEmployees({ resetPage: true }); } }, [loadRoles, loadEmployees]); // 事件处理器 - 事件驱动模式 const handleSearch = useCallback((filters: Record) => { setSearchFilters(filters); loadEmployees({ filters, pagination: { page: 1, size: pagination.size } }); }, [loadEmployees, pagination.size]); const handlePageChange = useCallback((page: number) => { setPagination(prev => ({ ...prev, page })); loadEmployees({ filters: searchFilters, pagination: { page, size: pagination.size } }); }, [loadEmployees, searchFilters, pagination.size]); const handleSizeChange = useCallback((size: number) => { setPagination(prev => ({ ...prev, size, page: 1 })); loadEmployees({ filters: searchFilters, pagination: { page: 1, size } }); }, [loadEmployees, searchFilters]); const handleAddEmployee = () => { setEditingEmployee(null); clearForm(); setFormKey(prev => prev + 1); // 增加key强制重新渲染 setShowForm(true); }; const clearForm = () => { // 直接设置空表单,无需setTimeout const emptyForm = { enterpriseId: 'ent-2', enterpriseName: '丰收现代农业集团', status: 'active' as const, auditStatus: 'pending' as const, roleIds: [], username: '', name: '', phone: '', email: '', department: '', position: '', }; setFormData(emptyForm); }; const handleEdit = (employee: Employee) => { setEditingEmployee(employee); setFormData(employee); setShowForm(true); }; const handleSave = async () => { if (!formData.username || !formData.name || !formData.phone) { toast.error('请填写必填项'); return; } // 验证角色选择 if (!formData.roleIds || formData.roleIds.length === 0) { toast.error('请至少选择一个角色'); return; } // 根据角色ID设置角色名称 const selectedRoles = roles.filter(r => formData.roleIds?.includes(r.id)); const roleNames = selectedRoles.map(r => r.name); if (editingEmployee) { // 更新 - 调用API setUpdating(true); try { // 构建API请求参数 const updateRequest: UpdateEmployeeRequest = { email: formData.email || '', username: formData.username, full_name: formData.name, phone: formData.phone, password: '', // 编辑时不传密码 tenant_id: formData.enterpriseId, scope: 'tenant', department_id: formData.departmentId || '', is_superuser: formData.isSuperuser || false, }; // 调用API更新用户 const updatedEmployee = await updateEmployee(editingEmployee.id, updateRequest); // 更新本地列表中的员工数据 const updated = employees.map(emp => emp.id === editingEmployee.id ? { ...emp, ...updatedEmployee, roles: roleNames, updatedAt: new Date().toISOString(), } : emp ); setEmployees(updated); toast.success('员工信息更新成功'); setShowForm(false); clearForm(); // 立即刷新员工列表数据,无需延迟 loadEmployees({ resetPage: true }); } catch (error) { console.error('更新员工失败:', error); // 处理错误,显示具体的错误消息 const errorMessage = error instanceof Error ? error.message : '员工信息更新失败'; toast.error(errorMessage); } finally { setUpdating(false); } } else { // 新增 - 调用API setCreating(true); try { // 构建API请求参数 const createRequest: CreateEmployeeRequest = { email: formData.email || '', // 没有邮箱就传空字符串 username: formData.username, full_name: formData.name, phone: formData.phone, password: '', // 传递空字符串给后端 tenant_id: formData.enterpriseId, scope: 'tenant', department_id: formData.departmentId || '', is_superuser: formData.isSuperuser || false, }; // 调用API创建用户 const newEmployee = await createEmployee(createRequest); // 将新员工添加到列表 const updated = [newEmployee, ...employees]; setEmployees(updated); toast.success('员工添加成功'); setShowForm(false); clearForm(); // 立即刷新员工列表数据,无需延迟 loadEmployees({ resetPage: true }); } catch (error) { console.error('创建员工失败:', error); // 处理错误,显示具体的错误消息 const errorMessage = error instanceof Error ? error.message : '员工添加失败'; toast.error(errorMessage); } finally { setCreating(false); } } }; const handleDelete = (userId: string) => { // 防止重复操作 if (toggling) { toast.warning('操作进行中,请稍候...'); return; } // 查找要删除的员工信息 const employeeToDelete = employees.find(emp => emp.id === userId); if (!employeeToDelete) { toast.error('未找到要删除的用户'); return; } // 设置要删除的用户并显示确认对话框 setUserToDelete(employeeToDelete); setDeleteConfirmOpen(true); }; const executeDelete = async (userId: string) => { setToggling(userId); try { // 调用API删除用户 await deleteUser(userId); // 成功后从本地列表中移除 const updated = employees.filter(emp => emp.id !== userId); setEmployees(updated); toast.success('用户删除成功'); // 立即刷新列表确保数据同步 loadEmployees({ resetPage: true }); // 关闭确认对话框 setDeleteConfirmOpen(false); setUserToDelete(null); } catch (error) { console.error('删除用户失败:', error); // 处理错误,显示具体的错误消息 const errorMessage = error instanceof Error ? error.message : '删除失败,请稍后重试'; toast.error(errorMessage); // 失败时不关闭确认对话框,用户可以重试 } finally { setToggling(null); } }; const handleToggleStatus = (employee: Employee) => { if (toggling) { toast.warning('操作进行中,请稍候...'); return; } if (employee.isActive) { // 当前是激活状态,进行停用操作,需要二次确认 setUserToDeactivate(employee); setDeactivateConfirmOpen(true); } else { // 当前是停用状态,进行激活操作(不需要确认) executeToggleStatus(employee); } }; const executeToggleStatus = async (employee: Employee) => { setToggling(employee.id); try { if (employee.isActive) { // 当前是激活状态,进行停用操作 await deactivateUser(employee.id); toast.success('账户已停用'); } else { // 当前是停用状态,进行激活操作 await activateUser(employee.id); toast.success('账户已激活'); } // 成功后立即刷新列表 loadEmployees({ resetPage: true }); // 关闭停用确认对话框 if (deactivateConfirmOpen) { setDeactivateConfirmOpen(false); setUserToDeactivate(null); } } catch (error) { console.error('切换用户状态失败:', error); // 处理错误,显示具体的错误消息 const errorMessage = error instanceof Error ? error.message : '操作失败,请稍后重试'; toast.error(errorMessage); // 失败时不关闭确认对话框,用户可以重试 } finally { setToggling(null); } }; const handleViewDetail = (employee: Employee) => { setSelectedEmployee(employee); setShowDetailDialog(true); }; const handleAudit = (employee: Employee, action: 'approve' | 'reject') => { if (action === 'approve') { const updated = employees.map(emp => emp.id === employee.id ? { ...emp, auditStatus: 'approved' as const, auditTime: new Date().toISOString(), auditor: '当前用户', updatedAt: new Date().toISOString(), } : emp ); setEmployees(updated); toast.success('审核通过'); } else { const reason = prompt('请输入驳回原因:'); if (reason) { const updated = employees.map(emp => emp.id === employee.id ? { ...emp, auditStatus: 'rejected' as const, auditReason: reason, auditTime: new Date().toISOString(), auditor: '当前用户', updatedAt: new Date().toISOString(), } : emp ); setEmployees(updated); toast.success('已驳回'); } } }; return (
{/* 统计卡片 - 保留原有功能 */} {/* 搜索、表格和分页 - 使用重构后的组件 */} 添加员工 } searchFields={searchFields} columns={columns} data={employees} loading={loading} error={null} pagination={pagination} onPageChange={handlePageChange} onSizeChange={handleSizeChange} onSearch={handleSearch} emptyIcon={
} emptyText="暂无员工数据" showSizeSelector={true} showPageInfo={true} sizeOptions={[10, 20, 50, 100]} /> {/* 添加/编辑表单 - 保留原有功能 */} {/* 详情对话框 - 保留原有功能 */} {/* 删除确认对话框 - 保留原有功能 */} 确认删除用户 确定要删除用户 " {userToDelete?.displayName || userToDelete?.fullName || userToDelete?.username || ''} " 吗?

删除后该用户将无法恢复,所有相关数据将被清除。
取消 { if (userToDelete) { executeDelete(userToDelete.id); } }} disabled={toggling !== null} className="bg-red-600 hover:bg-red-700" > {toggling ? '删除中...' : '确认删除'}
{/* 停用确认对话框 - 保留原有功能 */} 确认停用用户 确定要停用用户 " {userToDeactivate?.displayName || userToDeactivate?.fullName || userToDeactivate?.username || ''} " 吗?

停用后,该用户将无法登录系统。
取消 { if (userToDeactivate) { executeToggleStatus(userToDeactivate); } }} disabled={toggling !== null} className="bg-orange-600 hover:bg-orange-700" > {toggling ? '停用中...' : '确认停用'}
); }