"use client" // 标记为客户端组件,可以使用浏览器 API 和交互功能 // 导入 React 库和 Hooks import * as React from "react" // 导入表格相关组件和类型 import { ColumnDef, ColumnFiltersState, ColumnSizingState, flexRender, getCoreRowModel, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, SortingState, useReactTable, VisibilityState, } from "@tanstack/react-table" // 导入图标组件 import { IconChevronDown, IconChevronLeft, IconChevronRight, IconChevronsLeft, IconChevronsRight, IconLayoutColumns, IconPlus, IconTrash, IconSearch, IconLoader2, } from "@tabler/icons-react" // 导入 UI 组件 import { Button } from "@/components/ui/button" import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" // 导入类型定义 import type { Organization, OrganizationDataTableProps } from "@/types/organization.types" export function OrganizationDataTable({ data, columns, onAddNew, onBulkDelete, onSelectionChange, searchPlaceholder = "搜索组织名称...", searchColumn = "name", searchValue, onSearch, isSearching, pagination: externalPagination, setPagination: setExternalPagination, paginationInfo, onPaginationChange, }: OrganizationDataTableProps) { // 表格状态管理 const [rowSelection, setRowSelection] = React.useState({}) const [columnVisibility, setColumnVisibility] = React.useState({}) const [columnFilters, setColumnFilters] = React.useState([]) const [columnSizing, setColumnSizing] = React.useState({}) const [sorting, setSorting] = React.useState([ { id: "createdAt", desc: true, // 降序排列,最新的在前 }, ]) // 使用外部分页状态或内部默认状态 const [internalPagination, setInternalPagination] = React.useState({ pageIndex: 0, pageSize: 10, }) const pagination = externalPagination || internalPagination const setPagination = setExternalPagination || setInternalPagination // 本地搜索输入状态(只在回车或点击按钮时触发搜索) const [localSearchValue, setLocalSearchValue] = React.useState(searchValue ?? "") React.useEffect(() => { setLocalSearchValue(searchValue ?? "") }, [searchValue]) const handleSearchSubmit = () => { if (onSearch) { onSearch(localSearchValue) } else { table.getColumn(searchColumn)?.setFilterValue(localSearchValue) } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { handleSearchSubmit() } } // 创建表格实例 const table = useReactTable({ data, columns, state: { sorting, columnVisibility, rowSelection, columnFilters, pagination, columnSizing, }, enableColumnResizing: true, columnResizeMode: 'onChange', onColumnSizingChange: setColumnSizing, getRowId: (row) => row.id.toString(), enableRowSelection: true, onRowSelectionChange: setRowSelection, onSortingChange: setSorting, onColumnFiltersChange: setColumnFilters, onColumnVisibilityChange: setColumnVisibility, onPaginationChange: (updater) => { const newPagination = typeof updater === 'function' ? updater(pagination) : updater setPagination(newPagination) // 如果有外部分页变化回调,则调用它 if (onPaginationChange) { onPaginationChange(newPagination) } }, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), getSortedRowModel: getSortedRowModel(), getFacetedRowModel: getFacetedRowModel(), getFacetedUniqueValues: getFacetedUniqueValues(), // 如果有外部分页信息,则禁用内部分页,使用手动分页 ...(paginationInfo ? { manualPagination: true, pageCount: paginationInfo.totalPages, } : { getPaginationRowModel: getPaginationRowModel(), }), }) // 监听选中行变化,通知父组件 React.useEffect(() => { if (onSelectionChange) { const selectedRows = table.getFilteredSelectedRowModel().rows.map(row => row.original) onSelectionChange(selectedRows) } }, [rowSelection]) return (
{/* 工具栏 */}
{/* 搜索框 */}
setLocalSearchValue(e.target.value)} onKeyDown={handleKeyDown} className="h-8 max-w-sm" />
{/* 右侧操作按钮 */}
{/* 列显示控制 */} {table .getAllColumns() .filter( (column) => typeof column.accessorFn !== "undefined" && column.getCanHide() ) .map((column) => { return ( column.toggleVisibility(!!value)} > {column.id === "name" && "Organization"} {column.id === "description" && "Description"} {column.id === "targetCount" && "Total Targets"} {column.id === "createdAt" && "Added"} {!["name", "description", "targetCount", "createdAt"].includes(column.id) && column.id} ) })} {/* 批量删除按钮 */} {onBulkDelete && ( )} {/* 添加新组织按钮 */} {onAddNew && ( )}
{/* 表格容器 */}
{/* 表头 */} {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { return ( {header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext() )} {header.column.getCanResize() && (
header.column.resetSize()} className="absolute -right-2.5 top-0 h-full w-5 cursor-col-resize select-none touch-none bg-transparent flex justify-center" >
)} ) })} ))} {/* 表体 */} {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender( cell.column.columnDef.cell, cell.getContext() )} ))} )) ) : ( No results )}
{/* 分页控制 */}
{/* 选中行信息 */}
{table.getFilteredSelectedRowModel().rows.length} of{" "} {table.getFilteredRowModel().rows.length} row(s) selected
{/* 分页控制器 */}
{/* 每页显示数量选择 */}
{/* 页码信息 */}
Page {table.getState().pagination.pageIndex + 1} of{" "} {table.getPageCount()}
{/* 分页按钮 */}
) }