import { flexRender, getCoreRowModel, getSortedRowModel, useReactTable, type ColumnDef, type SortingState, } from '@tanstack/react-table' import { ArrowDown, ArrowUp, ChevronsUpDown } from 'lucide-react' import { useMemo, useState } from 'react' import TableScrollRegion from '../../../components/Scrollbar/TableScrollRegion' import { Button } from '../ui/button' interface DataTableProps { columns: Array> data: TData[] getRowId?: (row: TData, index: number) => string getRowClassName?: (row: TData) => string | undefined selection?: { selectedRowIds: Set onToggleAllVisible: (rowIds: string[]) => void onToggleRow: (rowId: string, row: TData) => void getCheckboxLabel?: (row: TData) => string isRowSelectable?: (row: TData) => boolean } loading?: boolean emptyText?: string className?: string footer?: React.ReactNode onRowClick?: (row: TData) => void } export function DataTable({ columns, data, getRowId, getRowClassName, selection, loading = false, emptyText = '暂无数据', className = '', footer, onRowClick, }: DataTableProps) { const [sorting, setSorting] = useState([]) const memoizedColumns = useMemo(() => columns, [columns]) const table = useReactTable({ data, columns: memoizedColumns, state: { sorting }, onSortingChange: setSorting, getRowId, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), }) const visibleSelectableRows = selection ? table.getRowModel().rows.filter((row) => selection.isRowSelectable?.(row.original) ?? true) : [] const visibleSelectableIds = visibleSelectableRows.map((row) => row.id) const allVisibleSelected = visibleSelectableIds.length > 0 && visibleSelectableIds.every((rowId) => selection?.selectedRowIds.has(rowId)) const someVisibleSelected = visibleSelectableIds.some((rowId) => selection?.selectedRowIds.has(rowId)) const columnCount = columns.length + (selection ? 1 : 0) return (
{table.getHeaderGroups().map((headerGroup) => ( {selection ? ( ) : null} {headerGroup.headers.map((header) => { const sorted = header.column.getIsSorted() const stickyEnd = header.column.id === 'actions' || header.column.id === 'action' return ( ) })} ))} {loading ? ( ) : table.getRowModel().rows.length > 0 ? ( table.getRowModel().rows.map((row) => ( onRowClick(row.original) : undefined} data-clickable={onRowClick ? 'true' : undefined} > {selection ? ( ) : null} {row.getVisibleCells().map((cell) => ( ))} )) ) : ( )}
{ if (element) element.indeterminate = someVisibleSelected && !allVisibleSelected }} onChange={() => selection.onToggleAllVisible(visibleSelectableIds)} /> {header.isPlaceholder ? null : ( )}
加载中
event.stopPropagation()} onChange={() => selection.onToggleRow(row.id, row.original)} /> {flexRender(cell.column.columnDef.cell, cell.getContext())}
{emptyText}
{footer ?
{footer}
: null}
) } export function DataTablePager({ page, pageSize, total, onPageChange, }: { page: number pageSize: number total: number onPageChange: (page: number) => void }) { const totalPages = Math.max(1, Math.ceil(total / pageSize)) return (
第 {page} / {totalPages} 页,共 {total.toLocaleString()} 条
) }