import React, { useState, useMemo } from 'react'; import './Table.css'; /** * Aurora Admin Table(React) * 对齐 table.json 契约: * - feature: sortable / selectable / batch-actions(含全选) * - state: hover / selected / loading / empty * - 表头/行高48px、分割线 #E8ECF1、悬停 #F5F7FA、选中 #F0F5FF、单元格左右16px、数字右对齐、操作列右对齐 * * columns: [{ key, title, align?:'left'|'right', sortable?, width?, fixed?, type?:'action' }] * 受控选择:selectedKeys={keys} onSelectionChange={setKeys} * 操作列:renderAction={(row, index) => } */ function sortData(data, key, order) { if (!key) return data; const dir = order === 'asc' ? 1 : -1; return [...data].sort((a, b) => { const av = a[key], bv = b[key]; if (typeof av === 'number' && typeof bv === 'number') return (av - bv) * dir; return String(av).localeCompare(String(bv), 'zh') * dir; }); } export default function Table({ columns = [], data = [], rowKey = 'id', selectable = false, selectedKeys = [], loading = false, emptyText = '暂无数据', onSelectionChange, onSortChange, renderAction }) { const [sortKey, setSortKey] = useState(null); const [sortOrder, setSortOrder] = useState(null); // asc | desc | null const sortedData = useMemo( () => sortData(data, sortKey, sortOrder), [data, sortKey, sortOrder] ); const allSelected = data.length > 0 && selectedKeys.length === data.length; const someSelected = selectedKeys.length > 0 && selectedKeys.length < data.length; const isSelected = (id) => selectedKeys.includes(id); const emitSelection = (next) => onSelectionChange && onSelectionChange(next); const toggleRow = (id) => { const next = isSelected(id) ? selectedKeys.filter((x) => x !== id) : [...selectedKeys, id]; emitSelection(next); }; const toggleAll = () => { const next = allSelected ? [] : data.map((r) => r[rowKey]); emitSelection(next); }; const onSort = (col) => { if (!col.sortable) return; let k = sortKey, o = sortOrder; if (k !== col.key) { k = col.key; o = 'asc'; } else { o = o === 'asc' ? 'desc' : o === 'desc' ? null : 'asc'; if (o === null) k = null; } setSortKey(k); setSortOrder(o); onSortChange && onSortChange({ key: k, order: o }); }; const colCount = columns.length + (selectable ? 1 : 0); return (
{selectable && ( )} {columns.map((col) => ( ))} {sortedData.length === 0 ? ( ) : ( sortedData.map((row, index) => ( {selectable && ( )} {columns.map((col) => ( ))} )) )}
el && (el.indeterminate = someSelected)} onChange={toggleAll} /> onSort(col)} > {col.title} {col.sortable && ( {sortKey === col.key ? (sortOrder === 'asc' ? '↑' : sortOrder === 'desc' ? '↓' : '↕') : '↕'} )}
{emptyText}
toggleRow(row[rowKey])} /> {col.type === 'action' && renderAction ? renderAction(row, index) : row[col.key]}
{loading && (
)}
); } /* 用法示例: import Table from './Table'; const columns = [ { key: 'name', title: '客户名称' }, { key: 'amount', title: '合同金额', align: 'right', sortable: true }, { type: 'action', title: '操作' } ]; (<> )} /> */