'use client'; import * as React from 'react'; import { useRouter } from 'next/navigation'; import { Avatar, AvatarFallback, AvatarImage } from '@nice/ui/components/avatar'; import { Badge } from '@nice/ui/components/badge'; import { Button } from '@nice/ui/components/button'; import { Checkbox } from '@nice/ui/components/checkbox'; import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@nice/ui/components/dropdown-menu'; import { Input } from '@nice/ui/components/input'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@nice/ui/components/table'; import { ColumnDef, ColumnFiltersState, flexRender, getCoreRowModel, getSortedRowModel, SortingState, useReactTable, VisibilityState, } from '@tanstack/react-table'; import { IconChevronLeft, IconChevronRight, IconChevronsLeft, IconChevronsRight, IconDots, IconEdit, IconEye, IconSearch, IconTrash, } from '@tabler/icons-react'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@nice/ui/components/select'; import { Label } from '@nice/ui/components/label'; import { useUser } from '@fenghuo/client'; import { toast } from '@nice/ui/components/sonner'; import { Organization } from '@fenghuo/db'; import { UserWithRelations } from '@fenghuo/common/user'; // 分页信息类型 export interface PaginationInfo { totalPages: number; totalCount: number; hasNextPage: boolean; hasPreviousPage: boolean; } // 角色颜色映射 const roleColors: Record = { admin: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300', user: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300', editor: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300', viewer: 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300', manager: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300', }; // 创建列定义的工厂函数 const createColumns = (router: ReturnType): ColumnDef[] => [ { id: 'select', header: ({ table }) => ( table.toggleAllPageRowsSelected(!!value)} aria-label="全选" className="h-4 w-4" /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} aria-label="选择行" className="h-4 w-4" /> ), enableSorting: false, enableHiding: false, }, { accessorKey: 'username', header: '用户名', cell: ({ row }) => (
{row.original.username.charAt(0)}
{row.original.username}
), }, { accessorKey: 'organization', header: '部门', cell: ({ row }) => { console.log(row) return {row.original.organization.name}; }, }, { accessorKey: 'role', header: '角色', cell: ({ row }) => { const user = row.original; // 获取所有角色信息 const userRoles = user.roles || []; // 如果没有角色信息,显示默认信息 if (!userRoles || userRoles.length === 0) { return ( 无角色 ); } // 显示所有角色标签 return (
{userRoles.map((roleInfo, index: number) => { const roleName = roleInfo.name; const roleSlug = roleInfo.slug || roleName.toLowerCase(); return ( {roleName} ); })}
); }, }, { id: 'actions', header: '操作', cell: ({ row }) => { const user = row.original; const handleViewUser = () => { router.push(`/dashboard/user/${user.id}`); }; const handleEditUser = () => { router.push(`/dashboard/user/${user.id}`); }; const userMutations = useUser(); const handleDeleteUser = (userIds: string[]) => { userMutations.softDeleteByIds.mutate( { ids: userIds, }, { onSuccess: () => { toast.success('删除成功'); }, onError: (error) => { toast.error(`删除失败: ${error.message}`); }, }, ); }; return ( 查看详情 编辑用户 handleDeleteUser([user.id])} className="text-red-600 focus:text-red-600 cursor-pointer" > 删除用户 ); }, enableSorting: false, enableHiding: false, }, ]; interface UsersDataTableProps { data: UserWithRelations[]; organizations?: Organization[]; // 新增:部门数据 onAddUser?: () => void; // 新增:服务端分页和筛选相关的 props pagination: { page: number; pageSize: number; }; paginationInfo: PaginationInfo; onPaginationChange: (pagination: { page: number; pageSize: number }) => void; searchQuery: string; onSearchChange: (query: string) => void; selectedRole: string; onRoleChange: (role: string) => void; selectedOrganizationId: string; onOrganizationChange: (organizationId: string) => void; isLoading?: boolean; availableRoles: Array<{ value: string; label: string }>; } export function UsersDataTable({ data, pagination, paginationInfo, onPaginationChange, searchQuery, onSearchChange, selectedRole, onRoleChange, isLoading = false, availableRoles, }: UsersDataTableProps) { const router = useRouter(); const columns = React.useMemo(() => createColumns(router), [router]); const [sorting, setSorting] = React.useState([]); const [columnVisibility, setColumnVisibility] = React.useState({}); const [rowSelection, setRowSelection] = React.useState({}); // 使用 React Table,但移除客户端分页逻辑 const table = useReactTable({ data, columns, onSortingChange: setSorting, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), onColumnVisibilityChange: setColumnVisibility, onRowSelectionChange: setRowSelection, // 移除分页相关的配置,使用服务端分页 manualPagination: true, pageCount: paginationInfo.totalPages, // 添加 getRowId 函数,确保使用正确的 ID getRowId: (row) => row.id, state: { sorting, columnVisibility, rowSelection, pagination: { pageIndex: pagination.page - 1, // React Table 使用 0-based index pageSize: pagination.pageSize, }, }, }); // 处理搜索输入的防抖 const [searchInputValue, setSearchInputValue] = React.useState(searchQuery); React.useEffect(() => { setSearchInputValue(searchQuery); }, [searchQuery]); React.useEffect(() => { const timer = setTimeout(() => { if (searchInputValue !== searchQuery) { onSearchChange(searchInputValue); } }, 300); // 300ms 防抖 return () => clearTimeout(timer); }, [searchInputValue, searchQuery, onSearchChange]); // 当数据变化时清理选择状态(分页、搜索、筛选等) React.useEffect(() => { setRowSelection({}); }, [pagination.page, searchQuery, selectedRole]); return (
{/* 工具栏 */}
{/* 搜索框 */}
setSearchInputValue(event.target.value)} className="pl-8 max-w-sm h-8 text-sm" />
{/* 角色筛选 */}
{/* 表格 */}
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} ))} ))} {isLoading ? (
加载中...
) : data?.length ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} )) ) : ( 暂无数据 )}
{/* 分页控制 */}
已选择 {table.getFilteredSelectedRowModel().rows.length} 行,共 {paginationInfo.totalCount} 条记录
第 {pagination.page} 页,共 {paginationInfo.totalPages} 页
); }