Files
aurora-admin/frameworks/Table.jsx
T

164 lines
5.4 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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) => <Buttons/>}
*/
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 (
<div className="aa-table">
<table>
<thead>
<tr>
{selectable && (
<th className="col-selection" style={{ width: 48 }}>
<input
type="checkbox" className="checkbox"
checked={allSelected} ref={(el) => el && (el.indeterminate = someSelected)}
onChange={toggleAll}
/>
</th>
)}
{columns.map((col) => (
<th
key={col.key || col.title}
className={[
col.align === 'right' ? 'align-right' : '',
col.sortable ? 'sortable' : '',
col.fixed ? 'col-fixed' : ''
].filter(Boolean).join(' ')}
style={col.width ? { width: col.width } : null}
onClick={() => onSort(col)}
>
{col.title}
{col.sortable && (
<span className={`sort-ind${sortKey === col.key ? ' active' : ''}`}>
{sortKey === col.key ? (sortOrder === 'asc' ? '↑' : sortOrder === 'desc' ? '↓' : '↕') : '↕'}
</span>
)}
</th>
))}
</tr>
</thead>
<tbody>
{sortedData.length === 0 ? (
<tr><td className="empty" colSpan={colCount}>{emptyText}</td></tr>
) : (
sortedData.map((row, index) => (
<tr key={row[rowKey]} className={isSelected(row[rowKey]) ? 'is-selected' : ''}>
{selectable && (
<td className="col-selection">
<input
type="checkbox" className="checkbox"
checked={isSelected(row[rowKey])} onChange={() => toggleRow(row[rowKey])}
/>
</td>
)}
{columns.map((col) => (
<td
key={col.key || col.title}
className={[
col.align === 'right' ? 'align-right' : '',
col.fixed ? 'col-fixed' : '',
col.type === 'action' ? 'col-action' : ''
].filter(Boolean).join(' ')}
>
{col.type === 'action' && renderAction ? renderAction(row, index) : row[col.key]}
</td>
))}
</tr>
))
)}
</tbody>
</table>
{loading && (
<div className="loading-mask"><span className="spinner" /></div>
)}
</div>
);
}
/*
用法示例:
import Table from './Table';
const columns = [
{ key: 'name', title: '客户名称' },
{ key: 'amount', title: '合同金额', align: 'right', sortable: true },
{ type: 'action', title: '操作' }
];
<Table
columns={columns}
data={rows}
selectable
selectedKeys={keys}
onSelectionChange={setKeys}
renderAction={(row) => (<>
<button className="link-btn" onClick={() => edit(row)}>编辑</button>
<button className="link-btn danger" onClick={() => del(row)}>删除</button>
</>)}
/>
*/