/** * 通用历史记录表格组件 * * 可用于任何类型的历史数据展示: * - 翻译历史 * - 对话历史 * - 任务历史 * - 操作日志 * - 订单记录 * - 等等... */ import React from 'react'; import { Loader2 } from 'lucide-react'; import Pagination from './Pagination'; // ==================== 类型定义 ==================== /** * 历史记录项 - 只要求唯一标识 */ export interface HistoryItem { id: string | number; [key: string]: any; } /** * 列配置 */ export interface ColumnConfig { key: string; label: string; width?: string; align?: 'left' | 'center' | 'right'; render?: (item: T, index: number) => React.ReactNode; } /** * 操作按钮配置 */ export interface ActionConfig { icon: React.ComponentType<{ className?: string }>; label: string; onClick: (item: T, index: number) => void | Promise; show?: (item: T, index: number) => boolean; disabled?: (item: T, index: number) => boolean; loading?: (item: T, index: number) => boolean; className?: string; variant?: 'default' | 'primary' | 'success' | 'danger' | 'warning'; } /** * 组件属性 */ export interface HistoryTableProps { // 数据 items: T[]; total: number; // 分页 page: number; pageSize: number; onPageChange: (page: number) => void; pageSizeOptions?: number[]; onPageSizeChange?: (pageSize: number) => void; // 列配置 columns: ColumnConfig[]; // 操作配置 actions?: ActionConfig[]; actionsLabel?: string; actionsWidth?: string; // 状态 isLoading?: boolean; // 自定义样式 title?: string; emptyText?: string; emptyIcon?: React.ReactNode; height?: string; className?: string; // 行配置 rowKey?: keyof T | ((item: T) => string | number); onRowClick?: (item: T, index: number) => void; rowClassName?: (item: T, index: number) => string; // 其他 showPagination?: boolean; stickyHeader?: boolean; } // ==================== 工具函数 ==================== /** * 获取操作按钮的样式类名 */ const getActionVariantClass = (variant?: string): string => { switch (variant) { case 'primary': return 'text-blue-600 hover:bg-blue-50'; case 'success': return 'text-green-600 hover:bg-green-50'; case 'danger': return 'text-red-600 hover:bg-red-50'; case 'warning': return 'text-yellow-600 hover:bg-yellow-50'; default: return 'text-gray-400 hover:text-gray-600 hover:bg-gray-50'; } }; // ==================== 组件 ==================== /** * 通用历史记录表格组件 */ function HistoryTable({ items, total, page, pageSize, onPageChange, pageSizeOptions = [], onPageSizeChange, columns, actions = [], actionsLabel = '操作', actionsWidth = '120px', isLoading = false, title, emptyText = '暂无数据', emptyIcon, height = '500px', className = '', rowKey = 'id', onRowClick, rowClassName, showPagination = true, stickyHeader = true }: HistoryTableProps) { /** * 获取行的唯一key */ const getRowKey = (item: T, index: number): string | number => { if (typeof rowKey === 'function') { return rowKey(item); } return item[rowKey] as string | number; }; /** * 获取行的className */ const getRowClassName = (item: T, index: number): string => { const baseClass = 'border-b border-gray-100 transition-colors'; const hoverClass = onRowClick ? 'hover:bg-blue-50 cursor-pointer' : 'hover:bg-gray-50'; const customClass = rowClassName ? rowClassName(item, index) : ''; return `${baseClass} ${hoverClass} ${customClass}`.trim(); }; /** * 处理行点击 */ const handleRowClick = (item: T, index: number) => { if (onRowClick) { onRowClick(item, index); } }; return (
{/* 标题 */} {title && (

{title}

)} {/* 内容区域 */} {isLoading ? (

加载中...

) : items.length === 0 ? (
{emptyIcon &&
{emptyIcon}
}

{emptyText}

) : ( <> {/* 表格 */}
{columns.map((col) => ( ))} {actions.length > 0 && ( )} {items.map((item, index) => ( handleRowClick(item, index)} > {columns.map((col) => ( ))} {actions.length > 0 && ( )} ))}
{col.label} {actionsLabel}
{col.render ? col.render(item, index) : (item as any)[col.key]} e.stopPropagation()}>
{actions.map((action, actionIndex) => { // 检查是否显示 if (action.show && !action.show(item, index)) { return null; } const Icon = action.icon; const isDisabled = action.disabled ? action.disabled(item, index) : false; const isLoading = action.loading ? action.loading(item, index) : false; const variantClass = getActionVariantClass(action.variant); return ( ); })}
{/* 分页 */} {showPagination && ( )} )}
); } export default HistoryTable;