nex_design/docs/components/ListTable.md

635 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# ListTable 组件
## 组件说明
列表表格组件,基于 Ant Design Table 组件封装,提供统一的表格样式、行选择、分页、滚动和行点击等功能。**支持跨页全选功能**。
## 组件位置
```
src/components/ListTable/ListTable.jsx
src/components/ListTable/ListTable.css
```
## 参数说明
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| columns | Array<ColumnConfig> | 是 | - | 表格列配置数组 |
| dataSource | Array<Object> | 是 | - | 表格数据源 |
| rowKey | string | 否 | 'id' | 行数据的唯一标识字段名 |
| selectedRowKeys | Array<string\|number> | 否 | [] | 选中行的 key 数组 |
| onSelectionChange | function(keys: Array) | 否 | - | 行选择变化回调 |
| **isAllPagesSelected** | boolean | 否 | false | 是否跨页全选所有数据 |
| **totalCount** | number | 否 | - | 总数据量(用于跨页全选显示) |
| **onSelectAllPages** | function() | 否 | - | 选择所有页回调 |
| **onClearSelection** | function() | 否 | - | 清除选择回调 |
| pagination | Object\|false | 否 | 默认配置 | 分页配置false 表示不分页 |
| scroll | Object | 否 | { x: 1200 } | 表格滚动配置 |
| onRowClick | function(record: Object) | 否 | - | 行点击回调 |
| selectedRow | Object | 否 | - | 当前选中的行数据对象 |
| loading | boolean | 否 | false | 表格加载状态 |
| className | string | 否 | '' | 自定义类名 |
### ColumnConfig 列配置
继承自 Ant Design Table 的列配置,常用属性:
| 属性名 | 类型 | 说明 |
|--------|------|------|
| title | string\|ReactNode | 列标题 |
| dataIndex | string | 数据字段名 |
| key | string | 列唯一标识 |
| width | number | 列宽度 |
| align | 'left'\|'center'\|'right' | 对齐方式 |
| fixed | 'left'\|'right' | 固定列 |
| render | function(value, record, index) | 自定义渲染函数 |
### 默认分页配置
```javascript
{
pageSize: 10,
showSizeChanger: true,
showQuickJumper: true,
}
```
**注意**:当传入 `onSelectAllPages``onClearSelection` 时,组件会自动在 `showTotal` 位置显示选择信息和跨页全选操作:
- **无选中**:已选择 0 项
- **部分选中**:已选择 3 项 [选择全部 100 项] [清除]
- **跨页全选**:已选择 100 项 [清除选择]
## 使用示例
### 基础用法
```jsx
import ListTable from '../components/ListTable/ListTable'
function MyPage() {
const columns = [
{
title: '序号',
dataIndex: 'id',
key: 'id',
width: 80,
align: 'center',
},
{
title: '用户名',
dataIndex: 'userName',
key: 'userName',
width: 150,
},
{
title: '姓名',
dataIndex: 'name',
key: 'name',
width: 120,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 100,
render: (status) => (
<Tag color={status === 'enabled' ? 'green' : 'default'}>
{status === 'enabled' ? '启用' : '停用'}
</Tag>
),
},
]
const dataSource = [
{ id: 1, userName: 'admin', name: '管理员', status: 'enabled' },
{ id: 2, userName: 'user', name: '张三', status: 'disabled' },
]
return (
<ListTable
columns={columns}
dataSource={dataSource}
/>
)
}
```
### 带行选择
```jsx
import { useState } from 'react'
import ListTable from '../components/ListTable/ListTable'
function UserListPage() {
const [selectedRowKeys, setSelectedRowKeys] = useState([])
return (
<div>
{/* 显示选中的数量 */}
<div>已选择 {selectedRowKeys.length} </div>
<ListTable
columns={columns}
dataSource={dataSource}
selectedRowKeys={selectedRowKeys}
onSelectionChange={setSelectedRowKeys}
/>
</div>
)
}
```
### 带行点击和高亮
```jsx
import { useState } from 'react'
import ListTable from '../components/ListTable/ListTable'
function UserListPage() {
const [selectedUser, setSelectedUser] = useState(null)
const handleRowClick = (record) => {
setSelectedUser(record)
// 打开详情抽屉等操作
setShowDetailDrawer(true)
}
return (
<ListTable
columns={columns}
dataSource={dataSource}
onRowClick={handleRowClick}
selectedRow={selectedUser}
/>
)
}
```
### 自定义分页
```jsx
<ListTable
columns={columns}
dataSource={dataSource}
pagination={{
pageSize: 20,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `总计 ${total} 条记录`,
pageSizeOptions: ['10', '20', '50', '100'],
}}
/>
```
### 禁用分页
```jsx
<ListTable
columns={columns}
dataSource={dataSource}
pagination={false}
/>
```
### 带加载状态
```jsx
import { useState, useEffect } from 'react'
function UserListPage() {
const [loading, setLoading] = useState(false)
const [dataSource, setDataSource] = useState([])
const fetchData = async () => {
setLoading(true)
try {
const data = await api.fetchUsers()
setDataSource(data)
} finally {
setLoading(false)
}
}
useEffect(() => {
fetchData()
}, [])
return (
<ListTable
columns={columns}
dataSource={dataSource}
loading={loading}
/>
)
}
```
### 横向滚动(列较多时)
```jsx
<ListTable
columns={columns}
dataSource={dataSource}
scroll={{ x: 1600 }} // 内容宽度超过容器时出现横向滚动
/>
```
### 固定列
```jsx
const columns = [
{
title: '序号',
dataIndex: 'id',
key: 'id',
width: 80,
fixed: 'left', // 固定在左侧
},
// ... 其他列
{
title: '操作',
key: 'action',
width: 200,
fixed: 'right', // 固定在右侧
render: (_, record) => (
<Space>
<Button type="link" size="small">编辑</Button>
<Button type="link" size="small" danger>删除</Button>
</Space>
),
},
]
<ListTable
columns={columns}
dataSource={dataSource}
scroll={{ x: 1600 }}
/>
```
### 跨页全选(重要功能)
支持选择所有页的数据,而不仅仅是当前页。
```jsx
import { useState, useMemo } from 'react'
import ListTable from '../components/ListTable/ListTable'
function UserListPage() {
const [allData] = useState([...]) // 所有数据
const [currentPage, setCurrentPage] = useState(1)
const [pageSize, setPageSize] = useState(10)
// 选择状态
const [selectedRowKeys, setSelectedRowKeys] = useState([])
const [isAllPagesSelected, setIsAllPagesSelected] = useState(false)
// 当前页数据
const currentPageData = useMemo(() => {
const start = (currentPage - 1) * pageSize
const end = start + pageSize
return allData.slice(start, end)
}, [allData, currentPage, pageSize])
// 选择变化处理(重要:支持跨页选择)
const handleSelectionChange = (keys) => {
// keys 是当前页面操作后的选中项
// 需要合并其他页的选中项,以保持跨页选择状态
const currentPageKeys = currentPageData.map(item => item.id)
const otherPagesSelectedKeys = selectedRowKeys.filter(
key => !currentPageKeys.includes(key)
)
const newSelectedKeys = [...otherPagesSelectedKeys, ...keys]
setSelectedRowKeys(newSelectedKeys)
setIsAllPagesSelected(false)
}
// 选择所有页
const handleSelectAllPages = () => {
setIsAllPagesSelected(true)
setSelectedRowKeys(allData.map(item => item.id))
}
// 清除选择
const handleClearSelection = () => {
setSelectedRowKeys([])
setIsAllPagesSelected(false)
}
return (
<ListTable
columns={columns}
dataSource={currentPageData}
selectedRowKeys={selectedRowKeys}
onSelectionChange={handleSelectionChange}
// 跨页全选相关 props
isAllPagesSelected={isAllPagesSelected}
totalCount={allData.length}
onSelectAllPages={handleSelectAllPages}
onClearSelection={handleClearSelection}
pagination={{
current: currentPage,
pageSize: pageSize,
total: allData.length,
onChange: (page, size) => {
setCurrentPage(page)
setPageSize(size)
},
}}
/>
)
}
```
**跨页选择的关键点**
1. **状态管理**:需要维护 `selectedRowKeys``isAllPagesSelected` 两个状态
2. **选择合并**:在 `onSelectionChange` 中合并其他页的选择
3. **全选操作**:将所有数据的 key 设置到 `selectedRowKeys`
4. **传递回调**:传入 `onSelectAllPages``onClearSelection`
**工作原理**
```javascript
// 第 1 页选中 row1, row2
selectedRowKeys = ['row1', 'row2']
// 切换到第 2 页,选中 row11
// handleSelectionChange 自动合并
selectedRowKeys = ['row1', 'row2', 'row11']
// 点击"选择全部"
isAllPagesSelected = true
selectedRowKeys = ['row1', 'row2', ..., 'row100']
```
### 完整示例(包含跨页全选)
```jsx
import { useState, useMemo } from 'react'
import { Button, Modal, message } from 'antd'
import ListTable from '../components/ListTable/ListTable'
import ListActionBar from '../components/ListActionBar/ListActionBar'
import { DeleteOutlined, ExportOutlined } from '@ant-design/icons'
function UserListPage() {
const [allUsers] = useState([...]) // 所有用户数据
const [currentPage, setCurrentPage] = useState(1)
const [pageSize, setPageSize] = useState(10)
const [selectedRowKeys, setSelectedRowKeys] = useState([])
const [isAllPagesSelected, setIsAllPagesSelected] = useState(false)
const currentPageData = useMemo(() => {
const start = (currentPage - 1) * pageSize
return allUsers.slice(start, start + pageSize)
}, [allUsers, currentPage, pageSize])
const handleSelectionChange = (keys) => {
const currentPageKeys = currentPageData.map(item => item.id)
const otherPagesSelectedKeys = selectedRowKeys.filter(
key => !currentPageKeys.includes(key)
)
setSelectedRowKeys([...otherPagesSelectedKeys, ...keys])
setIsAllPagesSelected(false)
}
const handleSelectAllPages = () => {
setIsAllPagesSelected(true)
setSelectedRowKeys(allUsers.map(item => item.id))
message.success(`已选择全部 ${allUsers.length} 条数据`)
}
const handleClearSelection = () => {
setSelectedRowKeys([])
setIsAllPagesSelected(false)
}
const handleBatchDelete = () => {
Modal.confirm({
title: '确认删除',
content: isAllPagesSelected
? `确定要删除全部 ${allUsers.length} 条数据吗?`
: `确定要删除选中的 ${selectedRowKeys.length} 条数据吗?`,
onOk: () => {
// 执行删除
message.success('删除成功')
handleClearSelection()
},
})
}
return (
<div>
{/* 操作栏 */}
<ListActionBar
actions={[...]}
batchActions={[
{
key: 'delete',
label: '批量删除',
icon: <DeleteOutlined />,
danger: true,
disabled: selectedRowKeys.length === 0,
onClick: handleBatchDelete,
},
]}
search={...}
/>
{/* 表格 */}
<ListTable
columns={columns}
dataSource={currentPageData}
selectedRowKeys={selectedRowKeys}
onSelectionChange={handleSelectionChange}
isAllPagesSelected={isAllPagesSelected}
totalCount={allUsers.length}
onSelectAllPages={handleSelectAllPages}
onClearSelection={handleClearSelection}
pagination={{
current: currentPage,
pageSize: pageSize,
total: allUsers.length,
onChange: (page, size) => {
setCurrentPage(page)
setPageSize(size)
},
}}
/>
</div>
)
}
```
### 完整示例(不使用跨页全选)
```jsx
import { useState } from 'react'
import ListTable from '../components/ListTable/ListTable'
import { Tag, Button, Space } from 'antd'
import { EditOutlined, DeleteOutlined } from '@ant-design/icons'
function UserListPage() {
const [selectedRowKeys, setSelectedRowKeys] = useState([])
const [selectedUser, setSelectedUser] = useState(null)
const [showDetailDrawer, setShowDetailDrawer] = useState(false)
const columns = [
{
title: '序号',
dataIndex: 'id',
key: 'id',
width: 80,
align: 'center',
},
{
title: '用户名',
dataIndex: 'userName',
key: 'userName',
width: 150,
},
{
title: '姓名',
dataIndex: 'name',
key: 'name',
width: 120,
},
{
title: '用户分组',
dataIndex: 'group',
key: 'group',
width: 150,
render: (text) => <Tag color="blue">{text}</Tag>,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 100,
align: 'center',
render: (status) => (
<Tag color={status === 'enabled' ? 'green' : 'default'}>
{status === 'enabled' ? '启用' : '停用'}
</Tag>
),
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
render: (_, record) => (
<Space size="small" onClick={(e) => e.stopPropagation()}>
<Button
type="link"
size="small"
icon={<EditOutlined />}
onClick={() => handleEdit(record)}
>
编辑
</Button>
<Button
type="link"
size="small"
icon={<DeleteOutlined />}
danger
onClick={() => handleDelete(record)}
>
删除
</Button>
</Space>
),
},
]
const handleRowClick = (record) => {
setSelectedUser(record)
setShowDetailDrawer(true)
}
return (
<ListTable
columns={columns}
dataSource={filteredUsers}
selectedRowKeys={selectedRowKeys}
onSelectionChange={setSelectedRowKeys}
onRowClick={handleRowClick}
selectedRow={selectedUser}
scroll={{ x: 1400 }}
/>
)
}
```
## 样式定制
组件提供以下 CSS 类名供自定义样式:
- `.list-table-container` - 表格容器
- `.row-selected` - 选中行的类名
- `.table-selection-info` - 分页器中的选择信息容器
- `.selection-count` - 选择数量文本
- `.count-highlight` - 高亮数字
- `.selection-action` - 操作链接(选择全部、清除)
### 固定高度设计
ListTable 组件采用固定表格体高度设计,确保页面布局的稳定性:
- **尺寸**:使用 Ant Design `size="middle"` 属性
- **行高**47pxAnt Design middle 尺寸默认值)
- **表格体高度**470px固定高度内部滚动
- **显示行数**10 行470px ÷ 47px = 10
- **分页器高度**56pxAnt Design 默认)
- **容器内边距**16px × 2
**设计说明**
- 组件使用 Ant Design 的标准 `size="middle"` 属性,不自定义行高
- 表格体固定 470px 高度,恰好显示 10 行数据
- 超过 10 行的内容通过表格体内部滚动查看
- 当数据不足 10 行时,表格体仍保持 470px 高度,确保布局稳定
- 分页器上边距 16px与表格体保持适当间距
## 使用场景
1. **用户列表** - 显示和管理用户数据
2. **设备列表** - 显示和管理设备信息
3. **订单列表** - 显示订单数据
4. **任何需要表格展示的数据列表**
## 注意事项
1. `columns` 配置中的 `key` 必须唯一
2. `dataSource` 中的每条数据必须有 `rowKey` 指定的唯一标识字段(默认为 `id`
3. 操作列中的点击事件需要使用 `e.stopPropagation()` 阻止事件冒泡,避免触发行点击
4. 当列数较多时,建议设置合适的 `scroll.x` 值并固定首尾列
5. `selectedRow` 用于高亮显示,`selectedRowKeys` 用于多选
6. 使用 `render` 函数时,要注意性能,避免在渲染函数中进行复杂计算
7. **跨页全选**:必须在 `onSelectionChange` 中合并其他页的选择,否则切换页面会丢失选择状态
8. **跨页全选**:传入 `onSelectAllPages``onClearSelection` 后,组件会自动显示选择信息和全选操作
9. **跨页全选**`totalCount` 用于显示总数量,如果不传则使用 `pagination.total`
## 相关组件
- [ListActionBar](./ListActionBar.md) - 列表操作栏组件(支持批量操作)
- [CrossPageSelection](./CrossPageSelection.md) - 跨页全选功能完整文档
---
**更新日志**
- v1.1.0 (2025-11-18)
- ✨ 新增跨页全选功能支持
- ✨ 自动在分页器显示选择信息和全选操作
- 📝 完善文档和使用示例
- v1.0.0 (2025-11-15)
- 🎉 初始版本
- ✨ 基础表格功能
- ✨ 行选择、分页、滚动支持