d4000cd292
2. 文档的基本信息修改改用接口。 3. 重新完善角色权限管理的页面逻辑。 4.将评查点列表中的返回逻辑改用浏览器的记忆返回。
179 lines
5.1 KiB
TypeScript
179 lines
5.1 KiB
TypeScript
import React from 'react';
|
|
|
|
interface TableColumn<T> {
|
|
title: React.ReactNode;
|
|
dataIndex?: keyof T;
|
|
key?: string;
|
|
width?: number | string;
|
|
render?: (value: any, record: T, index: number) => React.ReactNode;
|
|
align?: 'left' | 'center' | 'right';
|
|
className?: string;
|
|
}
|
|
|
|
interface TableProps<T> {
|
|
columns: TableColumn<T>[];
|
|
dataSource: T[];
|
|
rowKey: keyof T | ((record: T) => string);
|
|
loading?: boolean;
|
|
bordered?: boolean;
|
|
emptyText?: React.ReactNode;
|
|
className?: string;
|
|
onRow?: (record: T, index: number) => React.HTMLAttributes<HTMLTableRowElement>;
|
|
scroll?: {
|
|
x?: number | string;
|
|
y?: number | string;
|
|
};
|
|
}
|
|
|
|
export function Table<T extends Record<string, any>>({
|
|
columns,
|
|
dataSource,
|
|
rowKey,
|
|
loading = false,
|
|
bordered = false,
|
|
emptyText = '暂无数据',
|
|
className = '',
|
|
onRow,
|
|
scroll,
|
|
}: TableProps<T>) {
|
|
// 防御性检查:确保 dataSource 始终是数组
|
|
const safeDataSource = dataSource || [];
|
|
|
|
const getRowKey = (record: T, index: number): string => {
|
|
if (typeof rowKey === 'function') {
|
|
return rowKey(record);
|
|
}
|
|
return String(record[rowKey]);
|
|
};
|
|
|
|
// 是否启用固定表头滚动
|
|
const hasScrollY = scroll?.y !== undefined;
|
|
|
|
// 渲染表头
|
|
const renderHeader = () => (
|
|
<thead>
|
|
<tr>
|
|
{columns.map((column, index) => (
|
|
<th
|
|
key={column.key || column.dataIndex?.toString() || index}
|
|
className={column.className}
|
|
style={{
|
|
width: column.width,
|
|
textAlign: column.align || 'left',
|
|
}}
|
|
>
|
|
{column.title}
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
);
|
|
|
|
// 渲染表体
|
|
const renderBody = () => (
|
|
<tbody>
|
|
{safeDataSource.length > 0 ? (
|
|
safeDataSource.map((record, index) => (
|
|
<tr
|
|
key={getRowKey(record, index)}
|
|
{...(onRow ? onRow(record, index) : {})}
|
|
>
|
|
{columns.map((column, colIndex) => (
|
|
<td
|
|
key={column.key || column.dataIndex?.toString() || colIndex}
|
|
style={{ textAlign: column.align || 'left' }}
|
|
>
|
|
{column.render
|
|
? column.render(
|
|
column.dataIndex ? record[column.dataIndex] : undefined,
|
|
record,
|
|
index
|
|
)
|
|
: column.dataIndex
|
|
? record[column.dataIndex]
|
|
: undefined}
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))
|
|
) : (
|
|
<tr>
|
|
<td
|
|
colSpan={columns.length}
|
|
className="ant-table-empty py-6 text-center text-gray-500"
|
|
>
|
|
{emptyText}
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
);
|
|
|
|
// 如果有垂直滚动,使用固定表头布局
|
|
if (hasScrollY) {
|
|
return (
|
|
<div className={`ant-table-wrapper ${className} ${loading ? 'ant-table-loading' : ''}`}>
|
|
<div className="ant-table-container">
|
|
{/* 固定表头 */}
|
|
<div className="ant-table-header">
|
|
<table className={`ant-table ${bordered ? 'ant-table-bordered' : ''}`} style={{ tableLayout: 'fixed' }}>
|
|
<colgroup>
|
|
{columns.map((column, index) => (
|
|
<col key={index} style={{ width: column.width }} />
|
|
))}
|
|
</colgroup>
|
|
{renderHeader()}
|
|
</table>
|
|
</div>
|
|
{/* 可滚动表体 */}
|
|
<div
|
|
className="ant-table-body"
|
|
style={{
|
|
maxHeight: typeof scroll.y === 'number' ? `${scroll.y}px` : scroll.y,
|
|
overflowY: 'auto',
|
|
overflowX: scroll?.x ? 'auto' : 'hidden'
|
|
}}
|
|
>
|
|
<table className={`ant-table ${bordered ? 'ant-table-bordered' : ''}`} style={{ tableLayout: 'fixed' }}>
|
|
<colgroup>
|
|
{columns.map((column, index) => (
|
|
<col key={index} style={{ width: column.width }} />
|
|
))}
|
|
</colgroup>
|
|
{renderBody()}
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{loading && (
|
|
<div className="ant-table-loading-indicator">
|
|
<div className="flex items-center space-x-2">
|
|
<div className="w-5 h-5 border-2 border-primary border-t-transparent rounded-full animate-spin"></div>
|
|
<span className="text-gray-600">加载中...</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 默认布局(无滚动)
|
|
return (
|
|
<div className={`ant-table-wrapper ${className} ${loading ? 'ant-table-loading' : ''}`}>
|
|
<table className={`ant-table ${bordered ? 'ant-table-bordered' : ''}`}>
|
|
{renderHeader()}
|
|
{renderBody()}
|
|
</table>
|
|
|
|
{loading && (
|
|
<div className="ant-table-loading-indicator">
|
|
<div className="flex items-center space-x-2">
|
|
<div className="w-5 h-5 border-2 border-primary border-t-transparent rounded-full animate-spin"></div>
|
|
<span className="text-gray-600">加载中...</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|