UserTable.tsx 2.14 KB
// REQ-USR-003: 用户列表表格(受控分页 / 单选 rowSelection / 行双击 / 空态,BR1/BR6/BR11/BR12/BR14/D8)
import { Empty, Table } from 'antd';
import type { TablePaginationConfig } from 'antd';
import type { UserVO } from '../../../api/types';
import { buildUserColumns } from './columns';
import { PAGE_SIZE_OPTIONS, TEXT_EMPTY, totalText } from './constants';
import styles from './UserList.module.css';

export interface UserTableProps {
  rows: UserVO[];
  loading: boolean;
  total: number;
  pageNum: number;
  pageSize: number;
  onChangePage(pageNum: number, pageSize: number): void;
  onRowDoubleClick(row: UserVO): void;
  selectedRowKey?: number | null;
  onSelectRow?(key: number | null): void;
}

export default function UserTable({
  rows,
  loading,
  total,
  pageNum,
  pageSize,
  onChangePage,
  onRowDoubleClick,
  selectedRowKey,
  onSelectRow,
}: UserTableProps) {
  const pagination: TablePaginationConfig = {
    current: pageNum,
    pageSize,
    total,
    showSizeChanger: true,
    pageSizeOptions: PAGE_SIZE_OPTIONS.map(String),
    showTotal: (t) => totalText(t),
  };

  return (
    <div className={styles.tableShell} data-testid="user-table">
      <Table<UserVO>
        rowKey="id"
        columns={buildUserColumns({ pageNum, pageSize })}
        dataSource={rows}
        loading={loading}
        size="small"
        scroll={{ x: 'max-content' }}
        pagination={pagination}
        onChange={(p) => {
          // 受控分页:原样上报新的 (pageNum, pageSize),越界 / 改页大小回退由页面 hook 处理(BR11)
          onChangePage(p.current ?? pageNum, p.pageSize ?? pageSize);
        }}
        rowSelection={{
          type: 'radio',
          selectedRowKeys: selectedRowKey != null ? [selectedRowKey] : [],
          onChange: (keys) => {
            // 仅复刻单选标记语义(spec D8),不参与查询
            onSelectRow?.(keys.length ? Number(keys[0]) : null);
          },
        }}
        onRow={(row) => ({
          onDoubleClick: () => onRowDoubleClick(row), // BR12
        })}
        locale={{ emptyText: <Empty description={TEXT_EMPTY} /> }}
      />
    </div>
  );
}