diff --git a/crop-x/src/app/(app)/central-config/user/employee/components/EmployeeList.tsx b/crop-x/src/app/(app)/central-config/user/employee/components/EmployeeList.tsx deleted file mode 100644 index d8a090d..0000000 --- a/crop-x/src/app/(app)/central-config/user/employee/components/EmployeeList.tsx +++ /dev/null @@ -1,305 +0,0 @@ -'use client'; - -import React from 'react'; -import { Card } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; -import { - Pagination, - PaginationContent, - PaginationItem, - PaginationLink, - PaginationNext, - PaginationPrevious, - PaginationEllipsis -} from '@/components/ui/pagination'; -import { Eye, Edit, Trash2, UserX, UserCheck, CheckCircle, XCircle, Loader2 } from 'lucide-react'; -import { Employee, UserStatus } from '../types'; -import { PaginationState } from './employeeApi'; - -interface EmployeeListProps { - employees: Employee[]; - loading?: boolean; - pagination?: PaginationState; - onPageChange?: (page: number) => void; - onPageSizeChange?: (size: number) => void; - onViewDetail: (employee: Employee) => void; - onEdit: (employee: Employee) => void; - onToggleStatus: (employee: Employee) => void; - onDelete: (id: string) => void; - onAudit?: (employee: Employee, action: 'approve' | 'reject') => void; - togglingId?: string | null; -} - -export function EmployeeList({ - employees, - loading = false, - pagination, - onPageChange, - onPageSizeChange, - onViewDetail, - onEdit, - onToggleStatus, - onDelete, - onAudit, - togglingId -}: EmployeeListProps) { - const getStatusBadge = (isActive: boolean, status?: UserStatus) => { - // 优先使用isActive字段(来自API),其次使用status字段(兼容旧数据) - const finalStatus = isActive !== undefined ? (isActive ? 'active' : 'frozen') : status; - - switch (finalStatus) { - case 'active': - return 正常; - case 'frozen': - return 已冻结; - case 'inactive': - return 停用; - default: - return {finalStatus}; - } - }; - - const getAuditStatusBadge = (auditStatus?: string) => { - switch (auditStatus) { - case 'pending': - return 待审核; - case 'approved': - return 审核通过; - case 'rejected': - return 已驳回; - default: - return 未知; - } - }; - - return ( - - - - - - 姓名 - 用户名 - 电话 - 部门 - 角色 - 账号状态 - 审核状态 - 操作 - - - - {loading && employees.length === 0 ? ( - - -
- - 加载中... -
-
-
- ) : employees.length === 0 ? ( - - - 暂无数据 - - - ) : ( - employees.map((employee) => ( - - {employee.displayName || employee.name || employee.username} - {employee.username} - {employee.phone || '-'} - {employee.departmentName || employee.department || '-'} - - {employee.roles && employee.roles.length > 0 - ? employee.roles.join(', ') - : '-'} - - {getStatusBadge(employee.isActive, employee.status)} - {getAuditStatusBadge(employee.auditStatus)} - -
- {employee.auditStatus === 'pending' && onAudit && ( - <> - - - - )} - - - - - -

查看详情

-
-
- - - - - -

编辑员工

-
-
- - - - - -

{(employee.isActive || employee.status === 'active') ? '停用员工' : '激活员工'}

-
-
- - - - - -

删除员工

-
-
-
-
-
- )) - )} -
-
- - {/* 分页组件 */} - {pagination && ( -
-
- 显示第 {((pagination.page - 1) * pagination.size) + 1} - {Math.min(pagination.page * pagination.size, pagination.total)} 条,共 {pagination.total} 条 -
-
-
- 每页显示 - - -
- - - - - pagination.hasPrev && onPageChange?.(pagination.page - 1)} - /> - - - {/* 生成页码 */} - {Array.from({ length: Math.min(pagination.totalPages, 5) }, (_, i) => { - let pageNum; - if (pagination.totalPages <= 5) { - pageNum = i + 1; - } else if (pagination.page <= 3) { - pageNum = i + 1; - } else if (pagination.page >= pagination.totalPages - 2) { - pageNum = pagination.totalPages - 4 + i; - } else { - pageNum = pagination.page - 2 + i; - } - - return ( - - onPageChange?.(pageNum)} - > - {pageNum} - - - ); - })} - - {pagination.totalPages > 5 && pagination.page < pagination.totalPages - 2 && ( - - - - )} - - - pagination.hasNext && onPageChange?.(pagination.page + 1)} - /> - - - -
-
- )} -
-
- ); -} \ No newline at end of file diff --git a/crop-x/src/app/(app)/central-config/user/employee/components/EmployeeManagementFilters.tsx b/crop-x/src/app/(app)/central-config/user/employee/components/EmployeeManagementFilters.tsx deleted file mode 100644 index 8e428dd..0000000 --- a/crop-x/src/app/(app)/central-config/user/employee/components/EmployeeManagementFilters.tsx +++ /dev/null @@ -1,64 +0,0 @@ -'use client'; - -import React from 'react'; -import { Card } from '@/components/ui/card'; -import { Input } from '@/components/ui/input'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; -import { Search } from 'lucide-react'; -import { EmployeeFilters } from '../types'; - -interface EmployeeManagementFiltersProps { - filters: EmployeeFilters; - onFiltersChange?: (filters: EmployeeFilters) => void; - onSearchChange?: (searchKeyword: string) => void; - onStatusFilterChange?: (statusFilter: string) => void; -} - -export function EmployeeManagementFilters({ - filters, - onFiltersChange, - onSearchChange, - onStatusFilterChange -}: EmployeeManagementFiltersProps) { - const updateFilter = (key: keyof EmployeeFilters, value: string) => { - // 优先使用新的回调函数 - if (key === 'searchKeyword' && onSearchChange) { - onSearchChange(value); - } else if (key === 'statusFilter' && onStatusFilterChange) { - onStatusFilterChange(value); - } else if (onFiltersChange) { - onFiltersChange({ - ...filters, - [key]: value - }); - } - }; - - return ( - -
-
-
- - updateFilter('searchKeyword', e.target.value)} - className="pl-10" - /> -
-
- -
-
- ); -} \ No newline at end of file diff --git a/crop-x/src/app/(app)/central-config/user/employee/page.tsx b/crop-x/src/app/(app)/central-config/user/employee/page.tsx index b786199..fc73ea1 100644 --- a/crop-x/src/app/(app)/central-config/user/employee/page.tsx +++ b/crop-x/src/app/(app)/central-config/user/employee/page.tsx @@ -1,14 +1,22 @@ +/** + * filekorolheader: 员工管理页面 - 企业员工账户管理页面 + * 功能:员工列表查询、添加编辑、状态管理、角色分配 + * 路径:/central-config/user/employee + * 规范:遵循crop-x/docs/开发项目规范.md,使用事件驱动模式,SearchFormPagination重构 + */ 'use client'; -import { useState, useEffect } from 'react'; +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 { EmployeeManagementFilters } from './components/EmployeeManagementFilters'; -import { EmployeeList } from './components/EmployeeList'; import { EmployeeFormDialog } from './components/EmployeeFormDialog'; import { EmployeeDetailDialog } from './components/EmployeeDetailDialog'; +import { SearchFormPagination, type SearchFieldConfig, type TableColumnConfig } from '@/components/common/searchFormPagination'; import { AlertDialog, AlertDialogAction, @@ -18,9 +26,8 @@ import { AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, - AlertDialogTrigger, } from '@/components/ui/alert-dialog'; -import { Employee, Role, EmployeeFilters, EmployeeFormData } from './types'; +import { Employee, Role, EmployeeFormData } from './types'; import { fetchEmployees, transformEmployeesList, @@ -46,6 +53,7 @@ export default function EmployeeManagementPage() { 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); @@ -60,9 +68,9 @@ export default function EmployeeManagementPage() { hasNext: false, hasPrev: false, }); - const [filters, setFilters] = useState({ - searchKeyword: '', - statusFilter: 'all' + const [searchFilters, setSearchFilters] = useState>({ + search: '', + status: 'all' }); const [showForm, setShowForm] = useState(false); const [showDetailDialog, setShowDetailDialog] = useState(false); @@ -79,60 +87,198 @@ export default function EmployeeManagementPage() { address: '', }); - useEffect(() => { - loadEmployees(); - loadRoles(); - }, [pagination.page, pagination.size,filters.searchKeyword, filters.statusFilter]); + // 搜索字段配置 + 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 loadRoles = async () => { + // 表格列配置 + 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 { - // 调用角色API获取角色数据 const response = await fetchRoles({ page: 1, - size: 100, // 获取所有角色 + size: 100, sort_order: 'desc' }); - - // 转换数据格式 const transformedRoles = transformRolesList(response.data); setRoles(transformedRoles); } catch (error) { console.error('Failed to load roles:', error); - // API失败时设置为空数组 setRoles([]); } - }; + }, []); - const loadEmployees = async () => { - setLoading(true); + // 加载员工数据 - 事件驱动,移除依赖项 + const loadEmployees = useCallback(async (params?: { + filters?: Record; + pagination?: { page: number; size: number }; + resetPage?: boolean; + }) => { try { + setLoading(true); const queryParams: EmployeesQueryParams = { - page: pagination.page, - size: pagination.size, + page: params?.resetPage ? 1 : (params?.pagination?.page || pagination.page), + size: params?.pagination?.size || pagination.size, sort_order: 'desc' }; - // 如果有搜索关键词,添加到查询参数 - if (filters.searchKeyword) { - queryParams.search = filters.searchKeyword; - } - - // 如果有状态筛选,添加到查询参数 - if (filters.statusFilter !== 'all') { - // 注意:API可能不支持直接的状态筛选,这里暂时在客户端过滤 + // 搜索关键词 + const searchKeyword = params?.filters?.search ?? searchFilters.search; + if (searchKeyword) { + queryParams.search = searchKeyword; } const response = await fetchEmployees(queryParams); - - // 转换数据格式 const transformedEmployees = transformEmployeesList(response.data); - // 应用状态筛选(如果API不支持) - const filteredEmployees = filters.statusFilter === 'all' + // 状态筛选(客户端过滤) + const statusFilter = params?.filters?.status ?? searchFilters.status; + const filteredEmployees = statusFilter === 'all' ? transformedEmployees : transformedEmployees.filter(emp => { const status = emp.isActive ? 'active' : 'frozen'; - return status === filters.statusFilter; + return status === statusFilter; }); setEmployees(filteredEmployees); @@ -147,35 +293,45 @@ export default function EmployeeManagementPage() { } catch (error) { console.error('Failed to load employees:', error); toast.error('加载员工数据失败'); - // 如果API失败,使用localStorage中的数据 setEmployees([]); } finally { setLoading(false); } - }; + }, [pagination.page, pagination.size, searchFilters]); - // 搜索处理函数 - const handleSearch = (searchKeyword: string) => { - setFilters(prev => ({ ...prev, searchKeyword })); - // 重置到第一页 - setPagination(prev => ({ ...prev, page: 1 })); - }; + // 初始化数据 - 只在组件挂载时执行一次 + useEffect(() => { + if (isFirstLoad.current) { + isFirstLoad.current = false; + loadRoles(); + loadEmployees({ resetPage: true }); + } + }, [loadRoles, loadEmployees]); - // 状态筛选处理函数 - const handleStatusFilter = (statusFilter: string) => { - setFilters(prev => ({ ...prev, statusFilter })); - // 重置到第一页 - setPagination(prev => ({ ...prev, page: 1 })); - }; + // 事件处理器 - 事件驱动模式 + const handleSearch = useCallback((filters: Record) => { + setSearchFilters(filters); + loadEmployees({ + filters, + pagination: { page: 1, size: pagination.size } + }); + }, [loadEmployees, pagination.size]); - // 分页处理函数 - const handlePageChange = (page: number) => { + const handlePageChange = useCallback((page: number) => { setPagination(prev => ({ ...prev, page })); - }; + loadEmployees({ + filters: searchFilters, + pagination: { page, size: pagination.size } + }); + }, [loadEmployees, searchFilters, pagination.size]); - const handlePageSizeChange = (size: number) => { + const handleSizeChange = useCallback((size: number) => { setPagination(prev => ({ ...prev, size, page: 1 })); - }; + loadEmployees({ + filters: searchFilters, + pagination: { page: 1, size } + }); + }, [loadEmployees, searchFilters]); const handleAddEmployee = () => { setEditingEmployee(null); @@ -185,7 +341,7 @@ export default function EmployeeManagementPage() { }; const clearForm = () => { - // 先设置一个空的表单对象 + // 直接设置空表单,无需setTimeout const emptyForm = { enterpriseId: 'ent-2', enterpriseName: '丰收现代农业集团', @@ -199,14 +355,7 @@ export default function EmployeeManagementPage() { department: '', position: '', }; - - // 强制清空表单 setFormData(emptyForm); - - // 使用setTimeout确保状态更新完成 - setTimeout(() => { - setFormData({...emptyForm}); - }, 0); }; const handleEdit = (employee: Employee) => { @@ -268,8 +417,8 @@ export default function EmployeeManagementPage() { setShowForm(false); clearForm(); - // 刷新员工列表数据 - await loadEmployees(); + // 立即刷新员工列表数据,无需延迟 + loadEmployees({ resetPage: true }); } catch (error) { console.error('更新员工失败:', error); @@ -307,8 +456,8 @@ export default function EmployeeManagementPage() { setShowForm(false); clearForm(); - // 刷新员工列表数据 - await loadEmployees(); + // 立即刷新员工列表数据,无需延迟 + loadEmployees({ resetPage: true }); } catch (error) { console.error('创建员工失败:', error); @@ -353,8 +502,8 @@ export default function EmployeeManagementPage() { toast.success('用户删除成功'); - // 刷新列表确保数据同步 - await loadEmployees(); + // 立即刷新列表确保数据同步 + loadEmployees({ resetPage: true }); // 关闭确认对话框 setDeleteConfirmOpen(false); @@ -402,8 +551,8 @@ export default function EmployeeManagementPage() { toast.success('账户已激活'); } - // 成功后刷新列表 - await loadEmployees(); + // 成功后立即刷新列表 + loadEmployees({ resetPage: true }); // 关闭停用确认对话框 if (deactivateConfirmOpen) { @@ -471,32 +620,35 @@ export default function EmployeeManagementPage() { onAddEmployee={handleAddEmployee} /> - {/* 统计卡片 */} + {/* 统计卡片 - 保留原有功能 */} - {/* 搜索和筛选 */} - - - {/* 员工列表 */} - + + 添加员工 + + } + searchFields={searchFields} + columns={columns} + data={employees} loading={loading} + error={null} pagination={pagination} onPageChange={handlePageChange} - onPageSizeChange={handlePageSizeChange} - onViewDetail={handleViewDetail} - onEdit={handleEdit} - onToggleStatus={handleToggleStatus} - onDelete={handleDelete} - onAudit={handleAudit} - togglingId={toggling} + onSizeChange={handleSizeChange} + onSearch={handleSearch} + emptyIcon={
} + emptyText="暂无员工数据" + showSizeSelector={true} + showPageInfo={true} + sizeOptions={[10, 20, 50, 100]} /> - {/* 添加/编辑表单 */} + {/* 添加/编辑表单 - 保留原有功能 */} - {/* 详情对话框 */} + {/* 详情对话框 - 保留原有功能 */} - {/* 删除确认对话框 */} + {/* 删除确认对话框 - 保留原有功能 */} @@ -552,7 +704,7 @@ export default function EmployeeManagementPage() { - {/* 停用确认对话框 */} + {/* 停用确认对话框 - 保留原有功能 */} diff --git a/crop-x/src/app/(app)/central-config/user/role/components/RoleList.tsx b/crop-x/src/app/(app)/central-config/user/role/components/RoleList.tsx deleted file mode 100644 index b13bd62..0000000 --- a/crop-x/src/app/(app)/central-config/user/role/components/RoleList.tsx +++ /dev/null @@ -1,146 +0,0 @@ -'use client'; - -import React from 'react'; -import { Card } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; -import { DataPagination } from '@/components/ui/data-pagination'; -import { Eye, Edit, Trash2, Shield } from 'lucide-react'; -import { Role, RoleType } from '../types'; -import { PaginationState } from './roleApi'; - -interface RoleListProps { - roles: Role[]; - loading?: boolean; - pagination?: PaginationState; - onPageChange?: (page: number) => void; - onPageSizeChange?: (size: number) => void; - onViewDetail: (role: Role) => void; - onEdit: (role: Role) => void; - onDelete: (id: string) => void; -} - -export function RoleList({ - roles, - loading = false, - pagination, - onPageChange, - onPageSizeChange, - onViewDetail, - onEdit, - onDelete -}: RoleListProps) { - const getRoleTypeBadge = (type: RoleType) => { - return type === 'system' ? ( - 系统角色 - ) : ( - 自定义 - ); - }; - - const getStatusBadge = (status: string) => { - return status === 'active' ? ( - 启用 - ) : ( - 停用 - ); - }; - - return ( - - - - - 角色名称 - 角色代码 - 角色描述 - 类型 - 状态 - 创建时间 - 操作 - - - - {loading ? ( - - - 加载中... - - - ) : roles.length === 0 ? ( - - - 暂无数据 - - - ) : ( - roles.map((role) => ( - - -
- - {role.name} -
-
- {role.code} - - {role.description} - - {getRoleTypeBadge(role.type)} - {getStatusBadge(role.status)} - - {new Date(role.createdAt).toLocaleDateString('zh-CN')} - - -
- - - {role.type === 'custom' && ( - - )} -
-
-
- )) - )} -
-
- - {/* 分页控制 */} - {!loading && pagination && pagination.total > 0 && ( - onPageChange?.(page)} - onPageSizeChange={(size) => onPageSizeChange?.(size)} - canPreviousPage={pagination.hasPrev} - canNextPage={pagination.hasNext} - pageSizeOptions={[10, 20, 50, 100]} - /> - )} -
- ); -} \ No newline at end of file diff --git a/crop-x/src/app/(app)/central-config/user/role/components/RoleSearch.tsx b/crop-x/src/app/(app)/central-config/user/role/components/RoleSearch.tsx deleted file mode 100644 index cdf4b35..0000000 --- a/crop-x/src/app/(app)/central-config/user/role/components/RoleSearch.tsx +++ /dev/null @@ -1,27 +0,0 @@ -'use client'; - -import React from 'react'; -import { Card } from '@/components/ui/card'; -import { Input } from '@/components/ui/input'; -import { Search } from 'lucide-react'; - -interface RoleSearchProps { - searchKeyword: string; - onSearchChange: (keyword: string) => void; -} - -export function RoleSearch({ searchKeyword, onSearchChange }: RoleSearchProps) { - return ( - -
- - onSearchChange(e.target.value)} - className="pl-10" - /> -
-
- ); -} \ No newline at end of file diff --git a/crop-x/src/app/(app)/central-config/user/role/page.tsx b/crop-x/src/app/(app)/central-config/user/role/page.tsx index b230908..cff2e0f 100644 --- a/crop-x/src/app/(app)/central-config/user/role/page.tsx +++ b/crop-x/src/app/(app)/central-config/user/role/page.tsx @@ -2,13 +2,14 @@ * filekorolheader: 角色管理页面 - 系统角色访问控制管理 * 功能:角色列表管理、API数据加载、分页查询、角色搜索、详情查看 * 路径:/central-config/user/role - * 规范:遵循crop-x/docs/开发项目规范.md,使用API调用,shadcn语义化样式,支持翻页 + * 规范:遵循crop-x/docs/开发项目规范.md,使用SearchFormPagination公共组件,shadcn语义化样式,支持翻页 */ 'use client'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, useCallback, useMemo } from 'react'; import { toast } from 'sonner'; +import { Button } from '@/components/ui/button'; import { AlertDialog, AlertDialogAction, @@ -22,12 +23,12 @@ import { import { RoleManagementHeader } from './components/RoleManagementHeader'; import { RoleManagementStatsCards } from './components/RoleManagementStatsCards'; -import { RoleSearch } from './components/RoleSearch'; -import { RoleList } from './components/RoleList'; import { RoleFormDialog } from './components/RoleFormDialog'; import { RoleDetailDialog } from './components/RoleDetailDialog'; import { RoleManagementInstructions } from './components/RoleManagementInstructions'; -import { Role, RoleFormData, RoleFilters } from './types'; +import { SearchFormPagination, SearchFieldConfig, TableColumnConfig } from '@/components/common/searchFormPagination'; +import { Role, RoleFormData } from './types'; +import { Eye, Edit, Trash2, Shield } from 'lucide-react'; import { fetchRoles, createRole, @@ -51,9 +52,116 @@ export default function RoleManagementPage() { hasNext: false, hasPrev: false, }); - const [filters, setFilters] = useState({ - searchKeyword: '' - }); + const [searchFilters, setSearchFilters] = useState>({}); + + // 搜索字段配置 + const searchFields: SearchFieldConfig[] = useMemo(() => [ + { + key: 'search', + label: '搜索', + type: 'text', + placeholder: '搜索角色名称、代码、描述...', + }, + ], []); + + // 表格列配置 + const columns: TableColumnConfig[] = useMemo(() => [ + { + key: 'name', + label: '角色名称', + sortable: true, + render: (value: string, role: Role) => ( +
+ + {value} +
+ ), + }, + { + key: 'code', + label: '角色代码', + sortable: true, + render: (value: string) => ( + {value} + ), + }, + { + key: 'description', + label: '角色描述', + render: (value: string) => ( + {value || '-'} + ), + }, + { + key: 'type', + label: '类型', + sortable: true, + render: (value: string) => ( +
+ {value === 'system' ? '系统角色' : '自定义'} +
+ ), + }, + { + key: 'status', + label: '状态', + sortable: true, + render: (value: string) => ( +
+ {value === 'active' ? '启用' : '停用'} +
+ ), + }, + { + key: 'createdAt', + label: '创建时间', + sortable: true, + render: (value: string) => ( + + {new Date(value).toLocaleDateString('zh-CN')} + + ), + }, + { + key: 'actions', + label: '操作', + render: (_, role: Role) => ( +
+ + + {role.type === 'custom' && ( + + )} +
+ ), + }, + ], []); const [showForm, setShowForm] = useState(false); const [showDetailDialog, setShowDetailDialog] = useState(false); const [showDeleteDialog, setShowDeleteDialog] = useState(false); @@ -69,11 +177,8 @@ export default function RoleManagementPage() { permissionIds: [], }); - useEffect(() => { - loadRoles(); - }, [pagination.page, pagination.size, filters.searchKeyword]); - - const loadRoles = async () => { + // 加载角色数据 + const loadRoles = useCallback(async () => { setLoading(true); try { const queryParams: RolesQueryParams = { @@ -82,6 +187,11 @@ export default function RoleManagementPage() { sort_order: 'desc' }; + // 添加搜索条件 + if (searchFilters.search) { + queryParams.search = searchFilters.search; + } + const response = await fetchRoles(queryParams); // 转换数据格式 @@ -99,67 +209,37 @@ export default function RoleManagementPage() { } catch (error) { console.error('Failed to load roles:', error); toast.error('加载角色数据失败'); - - // API失败时设置为空数组 setRoles([]); } finally { setLoading(false); } - }; + }, [pagination.page, pagination.size, searchFilters]); - // 搜索处理函数 - const handleSearch = (searchKeyword: string) => { - setFilters(prev => ({ ...prev, searchKeyword })); - // 重置到第一页 + // 加载数据 + useEffect(() => { + loadRoles(); + }, [loadRoles]); + + // 搜索处理 + const handleSearch = useCallback((filters: Record) => { + setSearchFilters(filters); setPagination(prev => ({ ...prev, page: 1 })); - }; + }, []); - // 分页处理函数 - const handlePageChange = (page: number) => { - // 确保页码不超出范围 - const filteredCount = roles.filter(role => { - const matchKeyword = !filters.searchKeyword || - role.name.includes(filters.searchKeyword) || - role.code.includes(filters.searchKeyword) || - (role.description && role.description.includes(filters.searchKeyword)); - return matchKeyword; - }).length; + // 分页处理 + const handlePageChange = useCallback((page: number) => { + if (page < 1) { + page = 1; + } else if (page > pagination.totalPages && pagination.totalPages > 0) { + page = pagination.totalPages; + } + setPagination(prev => ({ ...prev, page })); + }, [pagination.totalPages]); - const maxPage = Math.ceil(filteredCount / pagination.size); - const validPage = Math.min(Math.max(1, page), maxPage || 1); - - setPagination(prev => ({ ...prev, page: validPage })); - }; - - const handlePageSizeChange = (size: number) => { - // 当分页大小改变时,重置到第一页 + // 每页条数变化处理 + const handleSizeChange = useCallback((size: number) => { setPagination(prev => ({ ...prev, size, page: 1 })); - }; - - // 过滤角色数据(客户端过滤,用于搜索) - const filteredRoles = roles.filter(role => { - const matchKeyword = !filters.searchKeyword || - role.name.includes(filters.searchKeyword) || - role.code.includes(filters.searchKeyword) || - (role.description && role.description.includes(filters.searchKeyword)); - return matchKeyword; - }); - - // 客户端分页逻辑 - const totalFilteredItems = filteredRoles.length; - const totalPages = Math.ceil(totalFilteredItems / pagination.size); - const startIndex = (pagination.page - 1) * pagination.size; - const endIndex = Math.min(startIndex + pagination.size, totalFilteredItems); - const paginatedRoles = totalFilteredItems > 0 ? filteredRoles.slice(startIndex, endIndex) : []; - - // 计算分页状态 - const clientPagination = { - ...pagination, - total: totalFilteredItems, - totalPages, - hasNext: pagination.page < totalPages, - hasPrev: pagination.page > 1, - }; + }, []); const handleAddRole = () => { setEditingRole(null); @@ -472,22 +552,25 @@ export default function RoleManagementPage() { {/* 统计卡片 */} - {/* 搜索 */} - - - {/* 角色列表 */} - + 新建角色 + + } + searchFields={searchFields} + columns={columns} + data={roles} loading={loading} - pagination={clientPagination} + error={null} + pagination={pagination} onPageChange={handlePageChange} - onPageSizeChange={handlePageSizeChange} - onViewDetail={handleViewDetail} - onEdit={handleEdit} - onDelete={handleDelete} + onSizeChange={handleSizeChange} + onSearch={handleSearch} + emptyText="暂无角色数据" + sizeOptions={[10, 20, 50, 100]} /> {/* 添加/编辑表单 */}