@gulibs/react-vtable 0.0.17 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +952 -1149
- package/dist/components/batch-action-buttons.d.ts +4 -16
- package/dist/components/batch-action-buttons.d.ts.map +1 -1
- package/dist/components/batch-actions.d.ts +2 -17
- package/dist/components/batch-actions.d.ts.map +1 -1
- package/dist/hooks/use-pro-table.d.ts.map +1 -1
- package/dist/index.cjs +6 -6
- package/dist/index.js +1361 -1410
- package/dist/pro-table.d.ts.map +1 -1
- package/dist/types.d.ts +50 -27
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/value-type.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,13 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
+
A powerful and flexible React table component library built with shadcn/ui and TanStack Table.
|
|
6
|
+
|
|
5
7
|
[English](#english) | [中文](#chinese)
|
|
6
8
|
|
|
7
9
|
---
|
|
8
10
|
|
|
9
11
|
## English
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
### Table of Contents
|
|
14
|
+
|
|
15
|
+
- [Features](#features)
|
|
16
|
+
- [Prerequisites](#prerequisites)
|
|
17
|
+
- [Installation](#installation)
|
|
18
|
+
- [Quick Start](#quick-start)
|
|
19
|
+
- [Core Concepts](#core-concepts)
|
|
20
|
+
- [Complete API Reference](#complete-api-reference)
|
|
21
|
+
- [Advanced Examples](#advanced-examples)
|
|
22
|
+
- [Best Practices](#best-practices)
|
|
23
|
+
- [Development](#development)
|
|
24
|
+
- [License](#license)
|
|
25
|
+
|
|
26
|
+
### Features
|
|
27
|
+
|
|
28
|
+
- ✨ **Powerful Table**: Built on TanStack Table v8 for maximum flexibility and performance
|
|
29
|
+
- 🎨 **shadcn/ui Components**: Beautiful, accessible, and customizable UI components
|
|
30
|
+
- 🔍 **Search & Filter**: Advanced search with auto-filtering for local/remote data
|
|
31
|
+
- 📄 **Pagination**: Client-side and server-side pagination support
|
|
32
|
+
- ✅ **Row Selection**: Single/multiple selection with cross-page selection support
|
|
33
|
+
- 🎯 **Batch Actions**: Flexible batch operations with smart button grouping
|
|
34
|
+
- ✏️ **Inline Editing**: Edit rows inline with validation support
|
|
35
|
+
- 📌 **Fixed Columns**: Pin columns to left/right with automatic shadow effects
|
|
36
|
+
- 🌳 **Tree Table**: Hierarchical data with expand/collapse support
|
|
37
|
+
- 🔄 **Drag & Drop**: Row and column reordering with @dnd-kit
|
|
38
|
+
- 📱 **Responsive**: Mobile-friendly design
|
|
39
|
+
- 🌐 **i18n**: Built-in English and Chinese locales
|
|
40
|
+
- 🎭 **Rich Value Types**: 20+ built-in value types (date, money, status, etc.)
|
|
41
|
+
- 📋 **Copy to Clipboard**: One-click copy for cells
|
|
42
|
+
- 💬 **Tooltip**: Customizable tooltips for cells and headers
|
|
43
|
+
- 📏 **Text Ellipsis**: Single-line and multi-line text truncation
|
|
44
|
+
- 🔧 **TypeScript**: Full TypeScript support with comprehensive types
|
|
12
45
|
|
|
13
46
|
### Prerequisites
|
|
14
47
|
|
|
@@ -17,16 +50,24 @@ This library requires **Tailwind CSS** to be installed and configured in your pr
|
|
|
17
50
|
### Installation
|
|
18
51
|
|
|
19
52
|
```bash
|
|
20
|
-
#
|
|
53
|
+
# Using pnpm (recommended)
|
|
21
54
|
pnpm add @gulibs/react-vtable
|
|
22
55
|
|
|
23
|
-
#
|
|
24
|
-
|
|
56
|
+
# Using npm
|
|
57
|
+
npm install @gulibs/react-vtable
|
|
58
|
+
|
|
59
|
+
# Using yarn
|
|
60
|
+
yarn add @gulibs/react-vtable
|
|
61
|
+
|
|
62
|
+
# Peer dependencies (if not already installed)
|
|
63
|
+
pnpm add react react-dom @tanstack/react-table tailwindcss
|
|
25
64
|
```
|
|
26
65
|
|
|
27
66
|
### Configuration
|
|
28
67
|
|
|
29
|
-
####
|
|
68
|
+
#### Tailwind CSS Setup
|
|
69
|
+
|
|
70
|
+
Add the library path to your `tailwind.config.js`:
|
|
30
71
|
|
|
31
72
|
```javascript
|
|
32
73
|
// tailwind.config.js
|
|
@@ -39,636 +80,545 @@ export default {
|
|
|
39
80
|
}
|
|
40
81
|
```
|
|
41
82
|
|
|
42
|
-
> 💡 **Note**: The library automatically injects its CSS styles
|
|
83
|
+
> 💡 **Note**: The library automatically injects its CSS styles. No manual CSS imports are required.
|
|
43
84
|
|
|
44
|
-
###
|
|
85
|
+
### Quick Start
|
|
45
86
|
|
|
46
87
|
```tsx
|
|
47
88
|
import { ProTable } from '@gulibs/react-vtable'
|
|
48
|
-
// No need to manually import CSS, styles are automatically inlined
|
|
49
89
|
|
|
50
90
|
function App() {
|
|
51
91
|
const columns = [
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
key: 'name',
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
title: 'Age',
|
|
59
|
-
dataIndex: 'age',
|
|
60
|
-
key: 'age',
|
|
61
|
-
},
|
|
92
|
+
{ title: 'Name', dataIndex: 'name', key: 'name' },
|
|
93
|
+
{ title: 'Age', dataIndex: 'age', key: 'age' },
|
|
94
|
+
{ title: 'Email', dataIndex: 'email', key: 'email' },
|
|
62
95
|
]
|
|
63
96
|
|
|
64
97
|
const dataSource = [
|
|
65
|
-
{ name: 'John', age: 30 },
|
|
66
|
-
{ name: 'Jane', age: 25 },
|
|
98
|
+
{ id: 1, name: 'John Doe', age: 30, email: 'john@example.com' },
|
|
99
|
+
{ id: 2, name: 'Jane Smith', age: 25, email: 'jane@example.com' },
|
|
67
100
|
]
|
|
68
101
|
|
|
69
102
|
return (
|
|
70
103
|
<ProTable
|
|
71
104
|
columns={columns}
|
|
72
105
|
dataSource={dataSource}
|
|
73
|
-
rowKey="
|
|
106
|
+
rowKey="id"
|
|
74
107
|
/>
|
|
75
108
|
)
|
|
76
109
|
}
|
|
77
110
|
```
|
|
78
111
|
|
|
79
|
-
###
|
|
112
|
+
### Core Concepts
|
|
80
113
|
|
|
81
|
-
|
|
82
|
-
- **shadcn/ui Components**: Beautiful, accessible UI components
|
|
83
|
-
- **Drag & Drop**: Row and column reordering with @dnd-kit
|
|
84
|
-
- **Search & Filter**: Advanced search and filtering capabilities with default behavior for local and remote data modes
|
|
85
|
-
- **Pagination**: Built-in pagination with customizable options
|
|
86
|
-
- **Column Settings**: Show/hide columns, drag to reorder
|
|
87
|
-
- **Row Selection**: Single and multiple row selection with batch actions, **cross-page selection support** (selection state persists across pages when using `request` or `pagination.onChange`)
|
|
88
|
-
- **Editable Rows**: Inline editing capabilities with various value types
|
|
89
|
-
- **Fixed Columns**: Left and right fixed columns with automatic position calculation and subtle shadow effects (powered by TanStack Table column pinning)
|
|
90
|
-
- **Tree Table**: Hierarchical data display with expandable/collapsible rows, custom expand icons, and accordion mode
|
|
91
|
-
- **Internationalization**: Built-in i18n support with English and Chinese locales
|
|
92
|
-
- **Horizontal Scrolling**: Configurable table width with scroll support
|
|
93
|
-
- **Copy to Clipboard**: One-click copy functionality for cells
|
|
94
|
-
- **Text Ellipsis**: Single-line and multi-line text truncation with ellipsis
|
|
95
|
-
- **Tooltip**: Hover tooltips for cells and headers with customizable content
|
|
96
|
-
- **Responsive**: Mobile-friendly design
|
|
97
|
-
- **TypeScript**: Full TypeScript support
|
|
98
|
-
|
|
99
|
-
### Examples
|
|
100
|
-
|
|
101
|
-
#### Basic Table
|
|
114
|
+
#### 1. Data Modes
|
|
102
115
|
|
|
116
|
+
**Local Data Mode** (using `dataSource`):
|
|
103
117
|
```tsx
|
|
104
118
|
<ProTable
|
|
119
|
+
dataSource={localData}
|
|
105
120
|
columns={columns}
|
|
106
|
-
dataSource={dataSource}
|
|
107
121
|
rowKey="id"
|
|
108
122
|
/>
|
|
109
123
|
```
|
|
110
124
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
The table provides **default search behavior** that works automatically:
|
|
114
|
-
|
|
115
|
-
- **Local Data Mode** (no `request` prop): Search values are automatically converted to TanStack Table's `columnFilters`, and the table filters data in real-time using `getFilteredRowModel()`.
|
|
116
|
-
- **Remote Data Mode** (with `request` prop): Search triggers a new data fetch with search parameters passed to the backend.
|
|
117
|
-
|
|
118
|
-
The default filter function supports:
|
|
119
|
-
- Case-insensitive string matching (contains) for **text-like** fields
|
|
120
|
-
- **Exact match** for **enum/filter** fields (e.g. `valueEnum`, `filters`, `valueType: 'select' | 'status'`)
|
|
121
|
-
- Array-based multi-select filtering (exact match for enum/filter fields, contains match for text fields)
|
|
122
|
-
- Automatic handling of empty/null values
|
|
123
|
-
|
|
125
|
+
**Remote Data Mode** (using `request`):
|
|
124
126
|
```tsx
|
|
125
127
|
<ProTable
|
|
128
|
+
request={async (params) => {
|
|
129
|
+
// params includes: current, pageSize, filters, sorter
|
|
130
|
+
const res = await fetchData(params)
|
|
131
|
+
return {
|
|
132
|
+
data: res.items,
|
|
133
|
+
total: res.total,
|
|
134
|
+
success: true
|
|
135
|
+
}
|
|
136
|
+
}}
|
|
126
137
|
columns={columns}
|
|
127
|
-
dataSource={dataSource}
|
|
128
138
|
rowKey="id"
|
|
129
|
-
search={{
|
|
130
|
-
filterType: 'query',
|
|
131
|
-
searchText: 'Search',
|
|
132
|
-
resetText: 'Reset'
|
|
133
|
-
}}
|
|
134
139
|
/>
|
|
135
140
|
```
|
|
136
141
|
|
|
137
|
-
|
|
142
|
+
#### 2. Search Behavior
|
|
143
|
+
|
|
144
|
+
The table provides **automatic search behavior**:
|
|
145
|
+
|
|
146
|
+
- **Local Mode**: Converts search values to TanStack Table's `columnFilters` and filters in real-time
|
|
147
|
+
- **Remote Mode**: Triggers data fetch with search parameters sent to backend
|
|
148
|
+
|
|
149
|
+
Default filter supports:
|
|
150
|
+
- Case-insensitive substring matching for text fields
|
|
151
|
+
- Exact match for enum/select fields (`valueEnum`, `filters`, `valueType: 'select'`)
|
|
152
|
+
- Array-based multi-select filtering
|
|
153
|
+
- Automatic empty/null value handling
|
|
138
154
|
|
|
139
|
-
|
|
155
|
+
#### 3. Pagination Modes
|
|
140
156
|
|
|
157
|
+
**Client-side Pagination** (default with `dataSource`):
|
|
141
158
|
```tsx
|
|
142
159
|
<ProTable
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
rowKey="id"
|
|
146
|
-
search={{
|
|
147
|
-
filterType: 'query',
|
|
148
|
-
onSearch: (values) => {
|
|
149
|
-
// Custom search logic
|
|
150
|
-
console.log('Search values:', values)
|
|
151
|
-
},
|
|
152
|
-
onReset: () => {
|
|
153
|
-
// Custom reset logic
|
|
154
|
-
console.log('Reset search')
|
|
155
|
-
}
|
|
156
|
-
}}
|
|
160
|
+
dataSource={data}
|
|
161
|
+
pagination={{ pageSize: 10 }}
|
|
157
162
|
/>
|
|
158
163
|
```
|
|
159
164
|
|
|
160
|
-
|
|
161
|
-
|
|
165
|
+
**Server-side Pagination** (with `request`):
|
|
162
166
|
```tsx
|
|
163
167
|
<ProTable
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
rowKey="id"
|
|
167
|
-
pagination={{
|
|
168
|
-
pageSize: 10,
|
|
169
|
-
showSizeChanger: true
|
|
170
|
-
}}
|
|
168
|
+
request={fetchData}
|
|
169
|
+
pagination={{ pageSize: 10 }}
|
|
171
170
|
/>
|
|
172
171
|
```
|
|
173
172
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
In **Remote Data Mode** (when you pass `request`), pagination works like this:
|
|
177
|
-
|
|
178
|
-
- **`total` controls the page count**: return `total` from `request` (recommended) and/or pass `pagination.total`.
|
|
179
|
-
- **Do not pass `pagination.current/pageSize` as constants** (e.g. `current: 1`), unless you intend to lock the table to that page. If you want controlled pagination, store them in state and update in `pagination.onChange`.
|
|
180
|
-
|
|
181
|
-
**Uncontrolled (recommended if you don't need to control page state):**
|
|
182
|
-
|
|
173
|
+
**Manual Server Pagination** (without `request`):
|
|
183
174
|
```tsx
|
|
184
175
|
<ProTable
|
|
185
|
-
|
|
186
|
-
rowKey="id"
|
|
187
|
-
request={async (params) => {
|
|
188
|
-
// params includes: current, pageSize, ...filters, ...sorter
|
|
189
|
-
const res = await fetchUsers(params)
|
|
190
|
-
return { data: res.items, total: res.total, success: true }
|
|
191
|
-
}}
|
|
176
|
+
dataSource={currentPageData}
|
|
192
177
|
pagination={{
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
178
|
+
mode: 'server', // Important!
|
|
179
|
+
current: page,
|
|
180
|
+
pageSize: pageSize,
|
|
181
|
+
total: total,
|
|
182
|
+
onChange: (page, pageSize) => fetchPage(page, pageSize)
|
|
196
183
|
}}
|
|
197
184
|
/>
|
|
198
185
|
```
|
|
199
186
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
```tsx
|
|
203
|
-
function Page() {
|
|
204
|
-
const [pagination, setPagination] = useState({
|
|
205
|
-
current: 1,
|
|
206
|
-
pageSize: 10,
|
|
207
|
-
total: 0,
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
return (
|
|
211
|
-
<ProTable
|
|
212
|
-
columns={columns}
|
|
213
|
-
rowKey="id"
|
|
214
|
-
request={async (params) => {
|
|
215
|
-
const res = await fetchUsers(params)
|
|
216
|
-
setPagination((prev) => ({ ...prev, total: res.total }))
|
|
217
|
-
return { data: res.items, total: res.total, success: true }
|
|
218
|
-
}}
|
|
219
|
-
pagination={{
|
|
220
|
-
current: pagination.current,
|
|
221
|
-
pageSize: pagination.pageSize,
|
|
222
|
-
total: pagination.total,
|
|
223
|
-
showSizeChanger: true,
|
|
224
|
-
showQuickJumper: true,
|
|
225
|
-
onChange: (current, pageSize) => {
|
|
226
|
-
setPagination((prev) => ({ ...prev, current, pageSize }))
|
|
227
|
-
},
|
|
228
|
-
}}
|
|
229
|
-
/>
|
|
230
|
-
)
|
|
231
|
-
}
|
|
232
|
-
```
|
|
187
|
+
---
|
|
233
188
|
|
|
234
|
-
|
|
189
|
+
## Complete API Reference
|
|
235
190
|
|
|
236
|
-
|
|
237
|
-
set `pagination.mode: 'server'` to prevent TanStack Table from slicing again.
|
|
191
|
+
### ProTable Props
|
|
238
192
|
|
|
239
|
-
|
|
240
|
-
function Page() {
|
|
241
|
-
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 })
|
|
242
|
-
const [data, setData] = useState<any[]>([])
|
|
193
|
+
#### Core Props
|
|
243
194
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
195
|
+
| Property | Type | Default | Description |
|
|
196
|
+
|----------|------|---------|-------------|
|
|
197
|
+
| `columns` | `ProColumn<T>[]` | `[]` | Column definitions |
|
|
198
|
+
| `dataSource` | `T[]` | `[]` | Local data array |
|
|
199
|
+
| `request` | `(params) => Promise<Response>` | - | Remote data fetcher |
|
|
200
|
+
| `rowKey` | `string \| (record) => string` | `'id'` | Unique row identifier |
|
|
201
|
+
| `loading` | `boolean` | `false` | Loading state |
|
|
202
|
+
| `defaultData` | `T[]` | `[]` | Default data for initial render |
|
|
203
|
+
| `postData` | `(data: T[]) => T[]` | - | Transform data after fetch |
|
|
204
|
+
| `params` | `Record<string, any>` | - | Extra params for `request` |
|
|
249
205
|
|
|
250
|
-
|
|
251
|
-
void fetchPage(pagination.current, pagination.pageSize)
|
|
252
|
-
}, [])
|
|
206
|
+
#### Layout Props
|
|
253
207
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
208
|
+
| Property | Type | Default | Description |
|
|
209
|
+
|----------|------|---------|-------------|
|
|
210
|
+
| `size` | `'small' \| 'middle' \| 'large'` | `'middle'` | Table size |
|
|
211
|
+
| `bordered` | `boolean` | `false` | Show table borders |
|
|
212
|
+
| `tableLayout` | `'auto' \| 'fixed'` | `'auto'` | Table layout algorithm |
|
|
213
|
+
| `scroll` | `{ x?: number \| string \| true; y?: number \| string }` | - | Scrollable area config |
|
|
214
|
+
| `sticky` | `boolean \| { offsetHeader?: number }` | `false` | Sticky header config |
|
|
215
|
+
| `showHeader` | `boolean` | `true` | Show table header |
|
|
216
|
+
| `className` | `string` | - | Custom class name |
|
|
217
|
+
| `style` | `CSSProperties` | - | Custom styles |
|
|
218
|
+
| `tableStyle` | `CSSProperties` | - | Table element styles |
|
|
219
|
+
|
|
220
|
+
#### Feature Props
|
|
221
|
+
|
|
222
|
+
| Property | Type | Default | Description |
|
|
223
|
+
|----------|------|---------|-------------|
|
|
224
|
+
| `search` | `ProTableSearch \| false` | `false` | Search form config |
|
|
225
|
+
| `toolbar` | `ProTableToolBar \| false` | `false` | Toolbar config |
|
|
226
|
+
| `pagination` | `ProTablePagination \| false` | `false` | Pagination config |
|
|
227
|
+
| `rowSelection` | `ProTableRowSelection<T>` | - | Row selection config |
|
|
228
|
+
| `batchActions` | `ProTableBatchActions<T>` | - | Batch actions config |
|
|
229
|
+
| `editable` | `ProTableEditable<T>` | - | Inline editing config |
|
|
230
|
+
| `expandable` | `ProTableExpandable<T> \| false` | - | Tree table config |
|
|
231
|
+
| `draggable` | `ProTableDraggable \| false` | - | Drag & drop config |
|
|
232
|
+
| `columnsState` | `ProColumnState` | - | Column state management |
|
|
233
|
+
|
|
234
|
+
#### Callback Props
|
|
235
|
+
|
|
236
|
+
| Property | Type | Description |
|
|
237
|
+
|----------|------|-------------|
|
|
238
|
+
| `onChange` | `(pagination, filters, sorter, extra) => void` | Table change callback |
|
|
239
|
+
| `onLoad` | `(dataSource: T[]) => void` | Called after data loaded |
|
|
240
|
+
| `onLoadingChange` | `(loading: boolean) => void` | Loading state change |
|
|
241
|
+
| `onRequestError` | `(error: Error) => void` | Request error callback |
|
|
242
|
+
| `onSubmit` | `(params: any) => void` | Search submit callback |
|
|
243
|
+
| `onReset` | `() => void` | Search reset callback |
|
|
244
|
+
| `onRow` | `(record, index) => HTMLAttributes` | Row props callback |
|
|
245
|
+
|
|
246
|
+
#### Advanced Props
|
|
247
|
+
|
|
248
|
+
| Property | Type | Description |
|
|
249
|
+
|----------|------|-------------|
|
|
250
|
+
| `headerTitle` | `ReactNode` | Table header title |
|
|
251
|
+
| `headerSubTitle` | `ReactNode` | Table header subtitle |
|
|
252
|
+
| `footer` | `(data) => ReactNode` | Table footer render |
|
|
253
|
+
| `emptyRender` | `ReactNode` | Empty state render |
|
|
254
|
+
| `tableRender` | `(props, dom, domList) => ReactNode` | Custom table render |
|
|
255
|
+
| `tableExtraRender` | `(props, data) => ReactNode` | Extra content after table |
|
|
256
|
+
| `paginationRender` | `(opts) => ReactNode` | Custom pagination render |
|
|
257
|
+
| `searchFormRender` | `(props, dom) => ReactNode` | Custom search form render |
|
|
258
|
+
| `actionRef` | `Ref<ProTableAction<T>>` | Table action reference |
|
|
259
|
+
| `formRef` | `Ref<FormInstance>` | Search form reference |
|
|
260
|
+
| `locale` | `ProTableLocale` | i18n locale config |
|
|
270
261
|
|
|
271
|
-
|
|
262
|
+
---
|
|
272
263
|
|
|
273
|
-
|
|
274
|
-
pagination state and default Pagination component.
|
|
264
|
+
### ProColumn Configuration
|
|
275
265
|
|
|
276
|
-
|
|
277
|
-
- `paginationRender` receives `pagination`, `onChange`, and `defaultDom`.
|
|
278
|
-
- If you are in **server pagination mode** (`pagination.mode: 'server'`) you must also provide `pagination.onChange` to fetch data.
|
|
266
|
+
#### Basic Props
|
|
279
267
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
268
|
+
| Property | Type | Description |
|
|
269
|
+
|----------|------|-------------|
|
|
270
|
+
| `title` | `string` | Column header text |
|
|
271
|
+
| `dataIndex` | `keyof T \| string \| string[]` | Data field path (supports nested: `'user.name'` or `['user', 'name']`) |
|
|
272
|
+
| `dataPath` | `string` | Alternative to dataIndex (string path only: `'user.name'`) |
|
|
273
|
+
| `key` | `string` | Unique column key |
|
|
274
|
+
| `width` | `number \| string` | Column width (required for fixed columns) |
|
|
275
|
+
| `align` | `'left' \| 'center' \| 'right'` | Text alignment |
|
|
276
|
+
| `fixed` | `'left' \| 'right'` | Pin column to left/right |
|
|
277
|
+
|
|
278
|
+
#### Display Props
|
|
279
|
+
|
|
280
|
+
| Property | Type | Description |
|
|
281
|
+
|----------|------|-------------|
|
|
282
|
+
| `valueType` | `ProFieldValueType` | Value display type (20+ types) |
|
|
283
|
+
| `valueEnum` | `Record<string, { text: string; status?: string; color?: string }>` | Enum value mapping |
|
|
284
|
+
| `render` | `(value, record, index) => ReactNode` | Custom cell render (takes priority over `valueType`) |
|
|
285
|
+
| `ellipsis` | `boolean \| ProColumnEllipsis` | Text ellipsis config |
|
|
286
|
+
| `tooltip` | `boolean \| ProColumnTooltip` | Tooltip config |
|
|
287
|
+
| `headerEllipsis` | `boolean \| ProColumnEllipsis` | Header ellipsis config |
|
|
288
|
+
| `headerTooltip` | `boolean \| ProColumnTooltip` | Header tooltip config |
|
|
289
|
+
| `copyable` | `boolean \| ProColumnCopyable` | Enable copy to clipboard |
|
|
290
|
+
|
|
291
|
+
#### Search Props
|
|
292
|
+
|
|
293
|
+
| Property | Type | Description |
|
|
294
|
+
|----------|------|-------------|
|
|
295
|
+
| `search` | `boolean \| ProColumnSearch` | Include in search form |
|
|
296
|
+
| `hideInSearch` | `boolean` | Hide in search form |
|
|
297
|
+
| `searchFormItemProps` | `FormItemProps` | Search form item props |
|
|
298
|
+
| `renderFormItem` | `(value, onChange, field, column) => ReactNode` | Custom search input render |
|
|
299
|
+
|
|
300
|
+
#### Table Display Props
|
|
301
|
+
|
|
302
|
+
| Property | Type | Description |
|
|
303
|
+
|----------|------|-------------|
|
|
304
|
+
| `hideInTable` | `boolean` | Hide in table |
|
|
305
|
+
| `sorter` | `boolean \| ((a, b) => number)` | Enable sorting |
|
|
306
|
+
| `filters` | `Array<{ text: string; value: any }>` | Filter options |
|
|
307
|
+
| `onFilter` | `(value, record) => boolean` | Filter function |
|
|
313
308
|
|
|
314
|
-
####
|
|
309
|
+
#### Edit Props
|
|
315
310
|
|
|
316
|
-
|
|
317
|
-
|
|
311
|
+
| Property | Type | Description |
|
|
312
|
+
|----------|------|-------------|
|
|
313
|
+
| `editable` | `boolean \| ((record) => boolean)` | Enable inline editing |
|
|
314
|
+
| `hideInForm` | `boolean` | Hide in edit form |
|
|
315
|
+
| `formItemProps` | `FormItemProps` | Form item props |
|
|
316
|
+
| `fieldProps` | `Record<string, any>` | Field component props |
|
|
318
317
|
|
|
319
|
-
|
|
320
|
-
columns={columns}
|
|
321
|
-
dataSource={dataSource}
|
|
322
|
-
rowKey="id"
|
|
323
|
-
locale={zh_CN}
|
|
324
|
-
/>
|
|
325
|
-
```
|
|
318
|
+
---
|
|
326
319
|
|
|
327
|
-
|
|
320
|
+
### Value Types Reference
|
|
321
|
+
|
|
322
|
+
Supported `valueType` values:
|
|
323
|
+
|
|
324
|
+
| Type | Description | Example |
|
|
325
|
+
|------|-------------|---------|
|
|
326
|
+
| `text` | Plain text | "Hello World" |
|
|
327
|
+
| `textarea` | Multi-line text | Long content |
|
|
328
|
+
| `number` | Number | 123 |
|
|
329
|
+
| `money` | Currency | $1,234.56 |
|
|
330
|
+
| `percent` | Percentage | 75% |
|
|
331
|
+
| `date` | Date | 2024-01-01 |
|
|
332
|
+
| `dateTime` | Date with time | 2024-01-01 10:30:00 |
|
|
333
|
+
| `dateRange` | Date range | 2024-01-01 ~ 2024-01-31 |
|
|
334
|
+
| `select` | Select dropdown | Uses `valueEnum` |
|
|
335
|
+
| `status` | Status badge | Uses `valueEnum` |
|
|
336
|
+
| `tags` | Multiple tags | Array display |
|
|
337
|
+
| `switch` | Boolean switch | true/false |
|
|
338
|
+
| `avatar` | Avatar image | URL display |
|
|
339
|
+
| `image` | Image | URL display |
|
|
340
|
+
| `progress` | Progress bar | 0-100 |
|
|
341
|
+
| `code` | Code block | Monospace text |
|
|
342
|
+
| `fromNow` | Relative time | "2 hours ago" |
|
|
343
|
+
| `email` | Email address | With mailto link |
|
|
344
|
+
| `phone` | Phone number | Formatted |
|
|
345
|
+
| `url` | URL link | Clickable link |
|
|
346
|
+
| `color` | Color picker | Color swatch |
|
|
347
|
+
| `rate` | Rating stars | 1-5 stars |
|
|
348
|
+
| `custom` | Custom render | Use `render` function |
|
|
328
349
|
|
|
329
|
-
|
|
350
|
+
---
|
|
330
351
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
},
|
|
347
|
-
{
|
|
348
|
-
title: 'Actions',
|
|
349
|
-
dataIndex: 'actions',
|
|
350
|
-
key: 'actions',
|
|
351
|
-
width: 100,
|
|
352
|
-
fixed: 'right' // Fixed to the right
|
|
353
|
-
}
|
|
354
|
-
]}
|
|
355
|
-
dataSource={dataSource}
|
|
356
|
-
rowKey="id"
|
|
357
|
-
scroll={{ x: 1000 }} // Enable horizontal scrolling
|
|
358
|
-
tableLayout="fixed" // Recommended for fixed columns
|
|
359
|
-
/>
|
|
352
|
+
### Search Configuration
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
interface ProTableSearch {
|
|
356
|
+
filterType?: 'query' | 'light' // Query: form above table, Light: inline filters
|
|
357
|
+
searchText?: string // Search button text
|
|
358
|
+
resetText?: string // Reset button text
|
|
359
|
+
submitText?: string // Submit button text
|
|
360
|
+
labelWidth?: number | 'auto' // Label width
|
|
361
|
+
span?: number // Grid column span
|
|
362
|
+
defaultCollapsed?: boolean // Initially collapsed
|
|
363
|
+
collapsed?: boolean // Controlled collapse state
|
|
364
|
+
onCollapse?: (collapsed: boolean) => void
|
|
365
|
+
optionRender?: (config, props, dom) => ReactNode[] // Custom action buttons
|
|
366
|
+
}
|
|
360
367
|
```
|
|
361
368
|
|
|
362
|
-
**
|
|
363
|
-
- Fixed columns require a `width` property to calculate positions correctly
|
|
364
|
-
- The library automatically handles position calculations using TanStack Table's `getStart()` and `getAfter()` methods
|
|
365
|
-
- Subtle shadow effects are automatically applied to the last left-pinned column and first right-pinned column
|
|
366
|
-
- No additional CSS is required - all styles are applied inline
|
|
367
|
-
|
|
368
|
-
#### With Tree Table (Expandable Rows)
|
|
369
|
-
|
|
370
|
-
Tree tables support hierarchical data structures with expandable/collapsible rows. The data structure should include a `children` property (or custom field name) containing nested rows.
|
|
369
|
+
**Example:**
|
|
371
370
|
|
|
372
371
|
```tsx
|
|
373
372
|
<ProTable
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
{ id: 11, name: 'Child Node 1' },
|
|
381
|
-
{ id: 12, name: 'Child Node 2' }
|
|
382
|
-
]
|
|
383
|
-
}
|
|
384
|
-
]}
|
|
385
|
-
rowKey="id"
|
|
386
|
-
expandable={{
|
|
387
|
-
childrenColumnName: 'children', // Default: 'children'
|
|
388
|
-
defaultExpandAllRows: false, // Expand all rows by default
|
|
389
|
-
defaultExpandedRowKeys: [1], // Initially expanded rows
|
|
390
|
-
accordion: false, // Accordion mode (only one row expanded at a time)
|
|
391
|
-
showExpandColumn: true, // Show expand icon column
|
|
392
|
-
expandColumnWidth: 50, // Width of expand column
|
|
393
|
-
onExpand: (expanded, record) => {
|
|
394
|
-
console.log('Expand:', expanded, record)
|
|
395
|
-
},
|
|
396
|
-
onExpandedRowsChange: (expandedRowKeys) => {
|
|
397
|
-
console.log('Expanded keys:', expandedRowKeys)
|
|
398
|
-
},
|
|
399
|
-
expandIcon: ({ expanded, onExpand, record }) => (
|
|
400
|
-
<Button onClick={onExpand}>
|
|
401
|
-
{expanded ? '▼' : '▶'}
|
|
402
|
-
</Button>
|
|
403
|
-
)
|
|
373
|
+
search={{
|
|
374
|
+
filterType: 'query',
|
|
375
|
+
searchText: 'Search',
|
|
376
|
+
resetText: 'Reset',
|
|
377
|
+
labelWidth: 'auto',
|
|
378
|
+
defaultCollapsed: false,
|
|
404
379
|
}}
|
|
405
380
|
/>
|
|
406
381
|
```
|
|
407
382
|
|
|
408
|
-
|
|
409
|
-
- **Controlled/Uncontrolled Mode**: Use `expandedRowKeys` for controlled mode, or `defaultExpandedRowKeys` for uncontrolled
|
|
410
|
-
- **Default Expand All**: Set `defaultExpandAllRows: true` to expand all rows by default
|
|
411
|
-
- **Accordion Mode**: Set `accordion: true` to allow only one row to be expanded at a time
|
|
412
|
-
- **Custom Children Field**: Use `childrenColumnName` to specify a custom field name for children (default: `'children'`)
|
|
413
|
-
- **Custom Expand Icon**: Provide a custom `expandIcon` function to customize the expand/collapse icon
|
|
414
|
-
- **Expand Callbacks**: Use `onExpand` and `onExpandedRowsChange` to handle expand state changes
|
|
415
|
-
|
|
416
|
-
#### With Editable Rows
|
|
383
|
+
---
|
|
417
384
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
385
|
+
### Pagination Configuration
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
interface ProTablePagination {
|
|
389
|
+
mode?: 'client' | 'server' // Pagination mode
|
|
390
|
+
pageSize?: number // Items per page
|
|
391
|
+
current?: number // Current page (controlled)
|
|
392
|
+
total?: number // Total items
|
|
393
|
+
showSizeChanger?: boolean // Show page size selector
|
|
394
|
+
showQuickJumper?: boolean // Show quick jump input
|
|
395
|
+
showTotal?: (total, range) => ReactNode
|
|
396
|
+
pageSizeOptions?: string[] // Page size options
|
|
397
|
+
size?: 'default' | 'small'
|
|
398
|
+
simple?: boolean // Simple pagination
|
|
399
|
+
disabled?: boolean
|
|
400
|
+
onChange?: (page, pageSize) => void
|
|
401
|
+
onShowSizeChange?: (current, size) => void
|
|
402
|
+
}
|
|
431
403
|
```
|
|
432
404
|
|
|
433
|
-
|
|
405
|
+
---
|
|
434
406
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
}}
|
|
454
|
-
/>
|
|
407
|
+
### Row Selection Configuration
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
interface ProTableRowSelection<T> {
|
|
411
|
+
type?: 'checkbox' | 'radio' // Selection type
|
|
412
|
+
selectedRowKeys?: React.Key[] // Controlled selection
|
|
413
|
+
onChange?: (keys, rows) => void
|
|
414
|
+
preserveSelectedRowKeys?: boolean // Keep selection across pages (default: true)
|
|
415
|
+
getCheckboxProps?: (record) => any
|
|
416
|
+
selections?: boolean | any[]
|
|
417
|
+
hideSelectAll?: boolean
|
|
418
|
+
fixed?: boolean // Fix selection column
|
|
419
|
+
columnWidth?: number | string
|
|
420
|
+
renderCell?: (checked, record, index, originNode) => ReactNode
|
|
421
|
+
alwaysShowAlert?: boolean // Always show batch actions bar
|
|
422
|
+
batchActions?: ProTableBatchActions<T> // Batch operations config
|
|
423
|
+
}
|
|
455
424
|
```
|
|
456
425
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
When using `request` or `pagination.onChange` for remote data fetching, the table automatically maintains selection state across pages. This means:
|
|
426
|
+
#### Cross-Page Selection
|
|
460
427
|
|
|
461
|
-
|
|
462
|
-
- The batch actions bar shows the **total count** of selected rows across all pages
|
|
463
|
-
- When you return to a previously visited page, your selections are automatically restored
|
|
464
|
-
- The `onChange` callback receives all selected row keys across all pages
|
|
428
|
+
When using `request` or `pagination.onChange`, selection automatically persists across pages:
|
|
465
429
|
|
|
466
430
|
```tsx
|
|
467
431
|
<ProTable
|
|
468
|
-
|
|
469
|
-
rowKey="id"
|
|
470
|
-
request={async (params) => {
|
|
471
|
-
const res = await fetchUsers(params)
|
|
472
|
-
return { data: res.items, total: res.total, success: true }
|
|
473
|
-
}}
|
|
432
|
+
request={fetchData}
|
|
474
433
|
rowSelection={{
|
|
475
434
|
type: 'checkbox',
|
|
435
|
+
preserveSelectedRowKeys: true, // Default: true
|
|
476
436
|
onChange: (selectedRowKeys, selectedRows) => {
|
|
477
|
-
// selectedRowKeys
|
|
478
|
-
// selectedRows
|
|
479
|
-
console.log('Total selected
|
|
480
|
-
},
|
|
481
|
-
batchActions: {
|
|
482
|
-
onDelete: (rows) => {
|
|
483
|
-
// This will receive rows from current page only
|
|
484
|
-
// Use selectedRowKeys from onChange for full selection
|
|
485
|
-
console.log('Delete current page rows:', rows)
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}}
|
|
489
|
-
pagination={{
|
|
490
|
-
pageSize: 10,
|
|
491
|
-
showSizeChanger: true,
|
|
492
|
-
onChange: (current, pageSize) => {
|
|
493
|
-
// Selection state is automatically preserved when page changes
|
|
437
|
+
// selectedRowKeys: all selected keys across all pages
|
|
438
|
+
// selectedRows: rows from current page only
|
|
439
|
+
console.log('Total selected:', selectedRowKeys.length)
|
|
494
440
|
}
|
|
495
441
|
}}
|
|
496
442
|
/>
|
|
497
443
|
```
|
|
498
444
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
- The batch actions bar always shows the total count of selected rows across all pages
|
|
503
|
-
- Use `rowSelection.onChange` to get all selected keys if you need to perform operations on all selected rows
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
### Batch Actions Configuration (Updated API)
|
|
504
448
|
|
|
505
|
-
|
|
449
|
+
```typescript
|
|
450
|
+
interface ProTableBatchActions<T> {
|
|
451
|
+
actions?: Array<ProTableBatchAction<T>> // Batch action list
|
|
452
|
+
maxVisibleActions?: number // Max buttons to show directly (default: 3)
|
|
453
|
+
hideClearButton?: boolean // Hide clear selection button
|
|
454
|
+
}
|
|
506
455
|
|
|
507
|
-
|
|
456
|
+
interface ProTableBatchAction<T> {
|
|
457
|
+
key: string // Unique action key
|
|
458
|
+
label: string // Button text
|
|
459
|
+
icon?: ReactNode // Button icon
|
|
460
|
+
onClick: (rows: T[]) => void // Click handler
|
|
461
|
+
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'
|
|
462
|
+
clearSelection?: boolean // Clear selection after action (default: false)
|
|
463
|
+
showInMore?: boolean // Show in "More" menu (default: false)
|
|
464
|
+
isExportFormat?: boolean // Mark as export format (auto-groups multiple export formats)
|
|
465
|
+
}
|
|
466
|
+
```
|
|
508
467
|
|
|
509
|
-
|
|
510
|
-
- **Archive operation**: Automatically clears selection after archiving
|
|
511
|
-
- **Custom actions**: You can control whether to clear selection using the `clearSelection` option
|
|
468
|
+
**Example:**
|
|
512
469
|
|
|
513
470
|
```tsx
|
|
471
|
+
import { Trash2, Download, FileText, Mail } from 'lucide-react'
|
|
472
|
+
|
|
514
473
|
<ProTable
|
|
515
474
|
rowSelection={{
|
|
516
475
|
type: 'checkbox',
|
|
517
476
|
batchActions: {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
console.log('Delete:', rows)
|
|
521
|
-
},
|
|
522
|
-
onArchive: (rows) => {
|
|
523
|
-
// Archive operation - selection will be automatically cleared
|
|
524
|
-
console.log('Archive:', rows)
|
|
525
|
-
},
|
|
526
|
-
// Hide the clear button if needed
|
|
527
|
-
hideClearButton: false,
|
|
528
|
-
customActions: [
|
|
477
|
+
maxVisibleActions: 3, // Show max 3 buttons directly
|
|
478
|
+
actions: [
|
|
529
479
|
{
|
|
530
|
-
key: '
|
|
531
|
-
label: '
|
|
480
|
+
key: 'delete',
|
|
481
|
+
label: 'Delete',
|
|
482
|
+
icon: <Trash2 className="h-3 w-3 mr-1" />,
|
|
532
483
|
onClick: (rows) => {
|
|
533
|
-
console.log('
|
|
484
|
+
console.log('Delete:', rows)
|
|
534
485
|
},
|
|
535
|
-
|
|
536
|
-
clearSelection: true,
|
|
486
|
+
variant: 'destructive',
|
|
487
|
+
clearSelection: true, // Clear after delete
|
|
537
488
|
},
|
|
538
489
|
{
|
|
539
490
|
key: 'export',
|
|
540
491
|
label: 'Export',
|
|
492
|
+
icon: <Download className="h-3 w-3 mr-1" />,
|
|
541
493
|
onClick: (rows) => {
|
|
542
494
|
console.log('Export:', rows)
|
|
543
495
|
},
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
|
|
496
|
+
variant: 'outline',
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
key: 'exportExcel',
|
|
500
|
+
label: 'Export Excel',
|
|
501
|
+
icon: <FileText className="h-3 w-3 mr-1" />,
|
|
502
|
+
onClick: (rows) => {
|
|
503
|
+
console.log('Export Excel:', rows)
|
|
504
|
+
},
|
|
505
|
+
variant: 'outline',
|
|
506
|
+
isExportFormat: true, // Groups with other export formats
|
|
507
|
+
showInMore: true,
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
key: 'exportCSV',
|
|
511
|
+
label: 'Export CSV',
|
|
512
|
+
icon: <FileText className="h-3 w-3 mr-1" />,
|
|
513
|
+
onClick: (rows) => {
|
|
514
|
+
console.log('Export CSV:', rows)
|
|
515
|
+
},
|
|
516
|
+
variant: 'outline',
|
|
517
|
+
isExportFormat: true,
|
|
518
|
+
showInMore: true,
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
key: 'email',
|
|
522
|
+
label: 'Send Email',
|
|
523
|
+
icon: <Mail className="h-3 w-3 mr-1" />,
|
|
524
|
+
onClick: (rows) => {
|
|
525
|
+
console.log('Email:', rows)
|
|
526
|
+
},
|
|
527
|
+
variant: 'outline',
|
|
528
|
+
showInMore: true, // Move to "More" menu
|
|
529
|
+
},
|
|
530
|
+
],
|
|
531
|
+
},
|
|
548
532
|
}}
|
|
549
533
|
/>
|
|
550
534
|
```
|
|
551
535
|
|
|
552
|
-
|
|
536
|
+
**Features:**
|
|
537
|
+
- Smart button grouping: buttons exceeding `maxVisibleActions` auto-move to "More" (⋯) menu
|
|
538
|
+
- Export format grouping: multiple `isExportFormat: true` actions auto-merge into dropdown
|
|
539
|
+
- Flexible action control: use `clearSelection` to auto-clear selection after certain actions
|
|
553
540
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
// Multi-line ellipsis (2 rows) with tooltip
|
|
572
|
-
ellipsis: {
|
|
573
|
-
multiline: true,
|
|
574
|
-
rows: 2
|
|
575
|
-
},
|
|
576
|
-
tooltip: true
|
|
577
|
-
},
|
|
578
|
-
{
|
|
579
|
-
title: 'Custom Tooltip',
|
|
580
|
-
dataIndex: 'name',
|
|
581
|
-
key: 'name',
|
|
582
|
-
width: 150,
|
|
583
|
-
ellipsis: true,
|
|
584
|
-
tooltip: {
|
|
585
|
-
content: (value, record) => `Full: ${record.fullName || value}`,
|
|
586
|
-
side: 'top',
|
|
587
|
-
delayDuration: 300
|
|
588
|
-
}
|
|
589
|
-
},
|
|
590
|
-
{
|
|
591
|
-
title: 'Header with Ellipsis',
|
|
592
|
-
dataIndex: 'email',
|
|
593
|
-
key: 'email',
|
|
594
|
-
width: 180,
|
|
595
|
-
// Header-specific ellipsis and tooltip
|
|
596
|
-
headerEllipsis: true,
|
|
597
|
-
headerTooltip: {
|
|
598
|
-
content: 'This is the full header title',
|
|
599
|
-
side: 'bottom'
|
|
600
|
-
},
|
|
601
|
-
// Cell ellipsis and tooltip
|
|
602
|
-
ellipsis: true,
|
|
603
|
-
tooltip: true
|
|
604
|
-
}
|
|
605
|
-
]}
|
|
606
|
-
dataSource={dataSource}
|
|
607
|
-
rowKey="id"
|
|
608
|
-
/>
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
### Editable Configuration
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
interface ProTableEditable<T> {
|
|
547
|
+
type?: 'single' | 'multiple' // Edit mode
|
|
548
|
+
form?: any // React Hook Form instance
|
|
549
|
+
formProps?: Record<string, any>
|
|
550
|
+
onSave?: (key, record, originRow) => Promise<void>
|
|
551
|
+
onDelete?: (key, record) => Promise<void>
|
|
552
|
+
onCancel?: (key, record, originRow) => void
|
|
553
|
+
actionRender?: (record, config) => ReactNode[] // Custom action buttons
|
|
554
|
+
deletePopconfirmMessage?: ReactNode
|
|
555
|
+
onlyOneLineEditorAlertMessage?: ReactNode
|
|
556
|
+
onlyAddOneLineAlertMessage?: ReactNode
|
|
557
|
+
}
|
|
609
558
|
```
|
|
610
559
|
|
|
611
|
-
|
|
560
|
+
---
|
|
612
561
|
|
|
613
|
-
|
|
562
|
+
### Expandable Configuration (Tree Table)
|
|
614
563
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
564
|
+
```typescript
|
|
565
|
+
interface ProTableExpandable<T> {
|
|
566
|
+
childrenColumnName?: string // Children field name (default: 'children')
|
|
567
|
+
defaultExpandedRowKeys?: React.Key[] // Default expanded rows
|
|
568
|
+
expandedRowKeys?: React.Key[] // Controlled expanded rows
|
|
569
|
+
onExpand?: (expanded, record) => void
|
|
570
|
+
onExpandedRowsChange?: (keys) => void
|
|
571
|
+
expandIcon?: (props: {
|
|
572
|
+
expanded: boolean
|
|
573
|
+
onExpand: () => void
|
|
574
|
+
record: T
|
|
575
|
+
}) => ReactNode
|
|
576
|
+
showExpandColumn?: boolean // Show expand column (default: true)
|
|
577
|
+
expandColumnWidth?: number // Expand column width (default: 50)
|
|
578
|
+
defaultExpandAllRows?: boolean // Expand all by default
|
|
579
|
+
accordion?: boolean // Accordion mode (only one expanded)
|
|
580
|
+
}
|
|
581
|
+
```
|
|
632
582
|
|
|
633
|
-
|
|
583
|
+
---
|
|
634
584
|
|
|
635
|
-
|
|
636
|
-
|------|------|-------------|
|
|
637
|
-
| `title` | `string` | Column header text |
|
|
638
|
-
| `dataIndex` | `string` | Data field name |
|
|
639
|
-
| `key` | `string` | Unique column key |
|
|
640
|
-
| `width` | `number` | Column width in pixels |
|
|
641
|
-
| `fixed` | `'left' \| 'right'` | Fixed column position |
|
|
642
|
-
| `sorter` | `boolean` | Enable sorting |
|
|
643
|
-
| `editable` | `boolean` | Enable inline editing |
|
|
644
|
-
| `copyable` | `boolean` | Enable copy to clipboard |
|
|
645
|
-
| `ellipsis` | `boolean \| ProColumnEllipsis` | Enable text ellipsis (single-line or multi-line) |
|
|
646
|
-
| `tooltip` | `boolean \| ProColumnTooltip` | Enable tooltip on hover |
|
|
647
|
-
| `headerEllipsis` | `boolean \| ProColumnEllipsis` | Enable ellipsis for header (defaults to `ellipsis`) |
|
|
648
|
-
| `headerTooltip` | `boolean \| ProColumnTooltip` | Enable tooltip for header (defaults to `tooltip`) |
|
|
649
|
-
| `search` | `boolean` | Include in search form |
|
|
650
|
-
| `valueType` | `ProFieldValueType` | Value type for rendering |
|
|
651
|
-
| `valueEnum` | `Record<string, { text: string; status?: string }>` | Enum values for select |
|
|
652
|
-
| `filters` | `Array<{ text: string; value: any }>` | Filter options |
|
|
585
|
+
### Ellipsis Configuration
|
|
653
586
|
|
|
654
|
-
|
|
587
|
+
```typescript
|
|
588
|
+
interface ProColumnEllipsis {
|
|
589
|
+
multiline?: boolean // Multi-line ellipsis
|
|
590
|
+
rows?: number // Number of rows (1-5, default: 2)
|
|
591
|
+
}
|
|
592
|
+
```
|
|
655
593
|
|
|
656
|
-
|
|
594
|
+
**Examples:**
|
|
657
595
|
|
|
658
596
|
```tsx
|
|
659
597
|
// Single-line ellipsis
|
|
660
598
|
ellipsis: true
|
|
661
599
|
|
|
662
|
-
// Multi-line ellipsis
|
|
600
|
+
// Multi-line ellipsis (2 lines)
|
|
663
601
|
ellipsis: {
|
|
664
602
|
multiline: true,
|
|
665
|
-
rows: 2
|
|
603
|
+
rows: 2
|
|
666
604
|
}
|
|
667
605
|
```
|
|
668
606
|
|
|
669
|
-
|
|
607
|
+
---
|
|
670
608
|
|
|
671
|
-
|
|
609
|
+
### Tooltip Configuration
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
interface ProColumnTooltip<T> {
|
|
613
|
+
content?: ReactNode | ((value, record, index) => ReactNode)
|
|
614
|
+
side?: 'top' | 'right' | 'bottom' | 'left'
|
|
615
|
+
align?: 'start' | 'center' | 'end'
|
|
616
|
+
delayDuration?: number
|
|
617
|
+
disabled?: boolean
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
**Examples:**
|
|
672
622
|
|
|
673
623
|
```tsx
|
|
674
624
|
// Simple tooltip (shows cell value)
|
|
@@ -677,821 +627,674 @@ tooltip: true
|
|
|
677
627
|
// Custom tooltip
|
|
678
628
|
tooltip: {
|
|
679
629
|
content: 'Custom tooltip text',
|
|
680
|
-
side: 'top'
|
|
681
|
-
|
|
682
|
-
delayDuration: 300,
|
|
683
|
-
disabled: false
|
|
630
|
+
side: 'top',
|
|
631
|
+
delayDuration: 300
|
|
684
632
|
}
|
|
685
633
|
|
|
686
634
|
// Function-based tooltip
|
|
687
635
|
tooltip: {
|
|
688
|
-
content: (value, record
|
|
636
|
+
content: (value, record) => `Full: ${value} (ID: ${record.id})`,
|
|
689
637
|
side: 'top'
|
|
690
638
|
}
|
|
691
639
|
```
|
|
692
640
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
#### Expandable (Tree Table) Configuration
|
|
696
|
-
|
|
697
|
-
The `expandable` property supports the following configurations:
|
|
698
|
-
|
|
699
|
-
```tsx
|
|
700
|
-
expandable: {
|
|
701
|
-
// Custom children field name (default: 'children')
|
|
702
|
-
childrenColumnName?: string
|
|
703
|
-
|
|
704
|
-
// Default expanded row keys (uncontrolled mode)
|
|
705
|
-
defaultExpandedRowKeys?: React.Key[]
|
|
706
|
-
|
|
707
|
-
// Currently expanded row keys (controlled mode)
|
|
708
|
-
expandedRowKeys?: React.Key[]
|
|
709
|
-
|
|
710
|
-
// Expand/collapse callback
|
|
711
|
-
onExpand?: (expanded: boolean, record: T) => void
|
|
712
|
-
|
|
713
|
-
// Expanded rows change callback
|
|
714
|
-
onExpandedRowsChange?: (expandedRowKeys: React.Key[]) => void
|
|
715
|
-
|
|
716
|
-
// Custom expand icon
|
|
717
|
-
expandIcon?: (props: {
|
|
718
|
-
expanded: boolean
|
|
719
|
-
onExpand: () => void
|
|
720
|
-
record: T
|
|
721
|
-
}) => ReactNode
|
|
722
|
-
|
|
723
|
-
// Show expand column (default: true)
|
|
724
|
-
showExpandColumn?: boolean
|
|
725
|
-
|
|
726
|
-
// Expand column width (default: 50)
|
|
727
|
-
expandColumnWidth?: number
|
|
728
|
-
|
|
729
|
-
// Expand all rows by default (default: false)
|
|
730
|
-
defaultExpandAllRows?: boolean
|
|
641
|
+
---
|
|
731
642
|
|
|
732
|
-
|
|
733
|
-
|
|
643
|
+
### Action Reference
|
|
644
|
+
|
|
645
|
+
Access table methods via `actionRef`:
|
|
646
|
+
|
|
647
|
+
```typescript
|
|
648
|
+
interface ProTableAction<T> {
|
|
649
|
+
reload: (resetPageIndex?: boolean) => Promise<void> // Reload data
|
|
650
|
+
reloadAndRest: () => Promise<void> // Reload and reset to page 1
|
|
651
|
+
reset: () => void // Reset filters and search
|
|
652
|
+
clearSelected: () => void // Clear row selection
|
|
653
|
+
startEditable: (rowKey) => boolean // Start editing row
|
|
654
|
+
cancelEditable: (rowKey) => boolean // Cancel editing row
|
|
655
|
+
saveEditable: (rowKey, record) => boolean // Save edited row
|
|
656
|
+
getRowData: (rowKey) => T | undefined // Get row data
|
|
657
|
+
getTableData: () => T[] // Get all table data
|
|
658
|
+
setTableData: (data: T[]) => void // Set table data
|
|
734
659
|
}
|
|
735
660
|
```
|
|
736
661
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
```bash
|
|
740
|
-
# Install dependencies
|
|
741
|
-
pnpm install
|
|
742
|
-
|
|
743
|
-
# Build the library
|
|
744
|
-
pnpm run build
|
|
745
|
-
|
|
746
|
-
# Start development server
|
|
747
|
-
pnpm run dev
|
|
748
|
-
```
|
|
749
|
-
|
|
750
|
-
### License
|
|
751
|
-
|
|
752
|
-
MIT
|
|
753
|
-
|
|
754
|
-
---
|
|
755
|
-
|
|
756
|
-
## Chinese
|
|
757
|
-
|
|
758
|
-
一个基于 shadcn/ui 组件和 TanStack Table 构建的强大 React 表格组件库。
|
|
662
|
+
**Example:**
|
|
759
663
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
此库需要您的项目中安装并配置 **Tailwind CSS**。
|
|
664
|
+
```tsx
|
|
665
|
+
const actionRef = useRef<ProTableAction<DataType>>(null)
|
|
763
666
|
|
|
764
|
-
|
|
667
|
+
// Reload data
|
|
668
|
+
actionRef.current?.reload()
|
|
765
669
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
pnpm add @gulibs/react-vtable
|
|
670
|
+
// Reset filters
|
|
671
|
+
actionRef.current?.reset()
|
|
769
672
|
|
|
770
|
-
|
|
771
|
-
|
|
673
|
+
// Clear selection
|
|
674
|
+
actionRef.current?.clearSelected()
|
|
772
675
|
```
|
|
773
676
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
#### 配置 Tailwind CSS
|
|
777
|
-
|
|
778
|
-
```javascript
|
|
779
|
-
// tailwind.config.js
|
|
780
|
-
export default {
|
|
781
|
-
content: [
|
|
782
|
-
'./src/**/*.{js,ts,jsx,tsx}',
|
|
783
|
-
'./node_modules/@gulibs/react-vtable/dist/**/*.js'
|
|
784
|
-
],
|
|
785
|
-
// 无需额外配置 - 样式会自动注入
|
|
786
|
-
}
|
|
787
|
-
```
|
|
677
|
+
---
|
|
788
678
|
|
|
789
|
-
|
|
679
|
+
## Advanced Examples
|
|
790
680
|
|
|
791
|
-
###
|
|
681
|
+
### 1. Remote Data with Search and Pagination
|
|
792
682
|
|
|
793
683
|
```tsx
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
684
|
+
<ProTable
|
|
685
|
+
request={async (params) => {
|
|
686
|
+
// params includes: current, pageSize, keyword, ...filters, ...sorter
|
|
687
|
+
const response = await fetch('/api/users', {
|
|
688
|
+
method: 'POST',
|
|
689
|
+
body: JSON.stringify(params)
|
|
690
|
+
})
|
|
691
|
+
const data = await response.json()
|
|
692
|
+
return {
|
|
693
|
+
data: data.items,
|
|
694
|
+
total: data.total,
|
|
695
|
+
success: true
|
|
696
|
+
}
|
|
697
|
+
}}
|
|
698
|
+
columns={[
|
|
699
|
+
{ title: 'Name', dataIndex: 'name', search: true },
|
|
700
|
+
{ title: 'Email', dataIndex: 'email', search: true },
|
|
804
701
|
{
|
|
805
|
-
title: '
|
|
806
|
-
dataIndex: '
|
|
807
|
-
|
|
702
|
+
title: 'Status',
|
|
703
|
+
dataIndex: 'status',
|
|
704
|
+
valueType: 'select',
|
|
705
|
+
search: true,
|
|
706
|
+
valueEnum: {
|
|
707
|
+
active: { text: 'Active', status: 'success' },
|
|
708
|
+
inactive: { text: 'Inactive', status: 'default' },
|
|
709
|
+
},
|
|
808
710
|
},
|
|
809
|
-
]
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
return (
|
|
817
|
-
<ProTable
|
|
818
|
-
columns={columns}
|
|
819
|
-
dataSource={dataSource}
|
|
820
|
-
rowKey="name"
|
|
821
|
-
/>
|
|
822
|
-
)
|
|
823
|
-
}
|
|
824
|
-
```
|
|
825
|
-
|
|
826
|
-
### 功能特性
|
|
827
|
-
|
|
828
|
-
- **强大表格**: 基于 TanStack Table 构建,提供最大灵活性
|
|
829
|
-
- **shadcn/ui 组件**: 美观、可访问的 UI 组件
|
|
830
|
-
- **拖拽排序**: 使用 @dnd-kit 实现行和列重排序
|
|
831
|
-
- **搜索筛选**: 高级搜索和筛选功能,支持本地和远程数据模式的默认行为
|
|
832
|
-
- **分页**: 内置分页,支持自定义选项
|
|
833
|
-
- **列设置**: 显示 / 隐藏列,拖拽重排序
|
|
834
|
-
- **行选择**: 单选和多选行,支持批量操作,**跨页选中支持**(使用 `request` 或 `pagination.onChange` 时,选中状态会在切换页面时保留)
|
|
835
|
-
- **可编辑行**: 内联编辑功能,支持多种值类型
|
|
836
|
-
- **固定列**: 左右固定列,自动计算位置并应用柔和阴影效果(基于 TanStack Table column pinning)
|
|
837
|
-
- **树形表格**: 层次数据展示,支持展开 / 收起行、自定义展开图标和手风琴模式
|
|
838
|
-
- **国际化**: 内置 i18n 支持,提供中英文语言包
|
|
839
|
-
- **横向滚动**: 可配置表格宽度,支持滚动
|
|
840
|
-
- **复制功能**: 一键复制单元格内容
|
|
841
|
-
- **文本省略**: 单行和多行文本截断,支持省略号
|
|
842
|
-
- **提示框**: 单元格和表头悬停提示,支持自定义内容
|
|
843
|
-
- **响应式**: 移动端友好设计
|
|
844
|
-
- **TypeScript**: 完整的 TypeScript 支持
|
|
845
|
-
|
|
846
|
-
### 示例
|
|
847
|
-
|
|
848
|
-
#### 基础表格
|
|
849
|
-
|
|
850
|
-
```tsx
|
|
851
|
-
<ProTable
|
|
852
|
-
columns={columns}
|
|
853
|
-
dataSource={dataSource}
|
|
711
|
+
]}
|
|
712
|
+
search={{ filterType: 'query' }}
|
|
713
|
+
pagination={{
|
|
714
|
+
pageSize: 10,
|
|
715
|
+
showSizeChanger: true,
|
|
716
|
+
showQuickJumper: true,
|
|
717
|
+
}}
|
|
854
718
|
rowKey="id"
|
|
855
719
|
/>
|
|
856
720
|
```
|
|
857
721
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
表格提供了**默认搜索行为**,会自动工作:
|
|
861
|
-
|
|
862
|
-
- **本地数据模式**(无 `request` 属性):搜索值会自动转换为 TanStack Table 的 `columnFilters`,表格使用 `getFilteredRowModel()` 实时过滤数据。
|
|
863
|
-
- **远程数据模式**(有 `request` 属性):搜索会触发新的数据获取,搜索参数会传递给后端。
|
|
864
|
-
|
|
865
|
-
默认过滤函数支持:
|
|
866
|
-
- **文本类字段**:不区分大小写的字符串匹配(包含)
|
|
867
|
-
- **枚举 / 筛选类字段**:精确匹配(例如 `valueEnum`、`filters`、`valueType: 'select' | 'status'`)
|
|
868
|
-
- 基于数组的多选过滤(枚举 / 筛选类字段为精确匹配;文本类字段为包含匹配)
|
|
869
|
-
- 自动处理空值 /null 值
|
|
722
|
+
### 2. Fixed Columns with Horizontal Scroll
|
|
870
723
|
|
|
871
724
|
```tsx
|
|
872
725
|
<ProTable
|
|
873
|
-
columns={
|
|
874
|
-
|
|
726
|
+
columns={[
|
|
727
|
+
{
|
|
728
|
+
title: 'ID',
|
|
729
|
+
dataIndex: 'id',
|
|
730
|
+
width: 80,
|
|
731
|
+
fixed: 'left', // Pin to left
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
title: 'Name',
|
|
735
|
+
dataIndex: 'name',
|
|
736
|
+
width: 150,
|
|
737
|
+
fixed: 'left',
|
|
738
|
+
},
|
|
739
|
+
{ title: 'Email', dataIndex: 'email', width: 200 },
|
|
740
|
+
{ title: 'Phone', dataIndex: 'phone', width: 150 },
|
|
741
|
+
{ title: 'Address', dataIndex: 'address', width: 300 },
|
|
742
|
+
{
|
|
743
|
+
title: 'Actions',
|
|
744
|
+
key: 'actions',
|
|
745
|
+
width: 100,
|
|
746
|
+
fixed: 'right', // Pin to right
|
|
747
|
+
render: (_, record) => (
|
|
748
|
+
<Button size="sm">Edit</Button>
|
|
749
|
+
),
|
|
750
|
+
},
|
|
751
|
+
]}
|
|
752
|
+
dataSource={data}
|
|
753
|
+
scroll={{ x: 1200 }} // Enable horizontal scroll
|
|
754
|
+
tableLayout="fixed" // Required for fixed columns
|
|
875
755
|
rowKey="id"
|
|
876
|
-
search={{
|
|
877
|
-
filterType: 'query',
|
|
878
|
-
searchText: '搜索',
|
|
879
|
-
resetText: '重置'
|
|
880
|
-
}}
|
|
881
756
|
/>
|
|
882
757
|
```
|
|
883
758
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
您可以提供自定义的 `onSearch` 和 `onReset` 回调来覆盖默认行为:
|
|
759
|
+
### 3. Tree Table with Expandable Rows
|
|
887
760
|
|
|
888
761
|
```tsx
|
|
889
762
|
<ProTable
|
|
890
|
-
columns={
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
763
|
+
columns={[
|
|
764
|
+
{ title: 'Name', dataIndex: 'name', width: 200 },
|
|
765
|
+
{ title: 'Size', dataIndex: 'size' },
|
|
766
|
+
{ title: 'Type', dataIndex: 'type' },
|
|
767
|
+
]}
|
|
768
|
+
dataSource={[
|
|
769
|
+
{
|
|
770
|
+
id: 1,
|
|
771
|
+
name: 'Folder 1',
|
|
772
|
+
type: 'folder',
|
|
773
|
+
children: [
|
|
774
|
+
{ id: 11, name: 'File 1.1', type: 'file', size: '1.2 MB' },
|
|
775
|
+
{ id: 12, name: 'File 1.2', type: 'file', size: '2.5 MB' },
|
|
776
|
+
],
|
|
898
777
|
},
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
778
|
+
{
|
|
779
|
+
id: 2,
|
|
780
|
+
name: 'Folder 2',
|
|
781
|
+
type: 'folder',
|
|
782
|
+
children: [
|
|
783
|
+
{ id: 21, name: 'File 2.1', type: 'file', size: '3.1 MB' },
|
|
784
|
+
],
|
|
785
|
+
},
|
|
786
|
+
]}
|
|
787
|
+
expandable={{
|
|
788
|
+
defaultExpandAllRows: false,
|
|
789
|
+
accordion: false, // Allow multiple rows expanded
|
|
903
790
|
}}
|
|
791
|
+
rowKey="id"
|
|
904
792
|
/>
|
|
905
793
|
```
|
|
906
794
|
|
|
907
|
-
|
|
795
|
+
### 4. Inline Editing
|
|
908
796
|
|
|
909
797
|
```tsx
|
|
798
|
+
const [editableKeys, setEditableKeys] = useState<React.Key[]>([])
|
|
799
|
+
|
|
910
800
|
<ProTable
|
|
911
|
-
columns={
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
801
|
+
columns={[
|
|
802
|
+
{
|
|
803
|
+
title: 'Name',
|
|
804
|
+
dataIndex: 'name',
|
|
805
|
+
editable: true,
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
title: 'Age',
|
|
809
|
+
dataIndex: 'age',
|
|
810
|
+
valueType: 'number',
|
|
811
|
+
editable: true,
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
title: 'Email',
|
|
815
|
+
dataIndex: 'email',
|
|
816
|
+
editable: true,
|
|
817
|
+
},
|
|
818
|
+
]}
|
|
819
|
+
dataSource={data}
|
|
820
|
+
editable={{
|
|
821
|
+
type: 'multiple',
|
|
822
|
+
editableKeys,
|
|
823
|
+
onChange: setEditableKeys,
|
|
824
|
+
onSave: async (key, record, originRow) => {
|
|
825
|
+
// Save to backend
|
|
826
|
+
await updateUser(key, record)
|
|
827
|
+
message.success('Saved successfully')
|
|
828
|
+
},
|
|
917
829
|
}}
|
|
830
|
+
rowKey="id"
|
|
918
831
|
/>
|
|
919
832
|
```
|
|
920
833
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
在**远程数据模式**(传入 `request`)下,分页规则如下:
|
|
924
|
-
|
|
925
|
-
- **`total` 决定页码数量**:推荐让 `request` 返回 `{ total }`,也可以 / 同时传入 `pagination.total`。
|
|
926
|
-
- **不要把 `pagination.current/pageSize` 写死**(例如 `current: 1`),除非你就是想把表格锁死在那一页;如果需要受控分页,请把它们放到 state 里,并在 `pagination.onChange` 里更新。
|
|
927
|
-
|
|
928
|
-
**非受控(推荐:不需要外部同步分页状态时)**:
|
|
834
|
+
### 5. Drag & Drop Reordering
|
|
929
835
|
|
|
930
836
|
```tsx
|
|
931
837
|
<ProTable
|
|
932
838
|
columns={columns}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
839
|
+
dataSource={data}
|
|
840
|
+
draggable={{
|
|
841
|
+
enabled: true,
|
|
842
|
+
handle: true, // Show drag handle
|
|
843
|
+
onDragEnd: (result) => {
|
|
844
|
+
const { items, oldIndex, newIndex } = result
|
|
845
|
+
console.log('New order:', items)
|
|
846
|
+
// Update backend order
|
|
847
|
+
updateOrder(items.map(item => item.id))
|
|
848
|
+
},
|
|
943
849
|
}}
|
|
850
|
+
rowKey="id"
|
|
944
851
|
/>
|
|
945
852
|
```
|
|
946
853
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
```tsx
|
|
950
|
-
function Page() {
|
|
951
|
-
const [pagination, setPagination] = useState({
|
|
952
|
-
current: 1,
|
|
953
|
-
pageSize: 10,
|
|
954
|
-
total: 0,
|
|
955
|
-
})
|
|
956
|
-
|
|
957
|
-
return (
|
|
958
|
-
<ProTable
|
|
959
|
-
columns={columns}
|
|
960
|
-
rowKey="id"
|
|
961
|
-
request={async (params) => {
|
|
962
|
-
const res = await fetchUsers(params)
|
|
963
|
-
setPagination((prev) => ({ ...prev, total: res.total }))
|
|
964
|
-
return { data: res.items, total: res.total, success: true }
|
|
965
|
-
}}
|
|
966
|
-
pagination={{
|
|
967
|
-
current: pagination.current,
|
|
968
|
-
pageSize: pagination.pageSize,
|
|
969
|
-
total: pagination.total,
|
|
970
|
-
showSizeChanger: true,
|
|
971
|
-
showQuickJumper: true,
|
|
972
|
-
onChange: (current, pageSize) => {
|
|
973
|
-
setPagination((prev) => ({ ...prev, current, pageSize }))
|
|
974
|
-
},
|
|
975
|
-
}}
|
|
976
|
-
/>
|
|
977
|
-
)
|
|
978
|
-
}
|
|
979
|
-
```
|
|
980
|
-
|
|
981
|
-
#### 服务端分页(不传 `request`,外部自行请求)
|
|
982
|
-
|
|
983
|
-
如果你**不使用** `request`,但希望实现**服务端分页**(也就是 `dataSource` 只提供“当前页 items”),请设置
|
|
984
|
-
`pagination.mode: 'server'`,避免 TanStack Table 再次 slice 导致第 2 页为空 / 数据错乱。
|
|
985
|
-
|
|
986
|
-
```tsx
|
|
987
|
-
function Page() {
|
|
988
|
-
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 })
|
|
989
|
-
const [data, setData] = useState<any[]>([])
|
|
990
|
-
|
|
991
|
-
const fetchPage = async (current: number, pageSize: number) => {
|
|
992
|
-
const res = await fetchUsers({ current, pageSize })
|
|
993
|
-
setData(res.items)
|
|
994
|
-
setPagination((prev) => ({ ...prev, current, pageSize, total: res.total }))
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
useEffect(() => {
|
|
998
|
-
void fetchPage(pagination.current, pagination.pageSize)
|
|
999
|
-
}, [])
|
|
1000
|
-
|
|
1001
|
-
return (
|
|
1002
|
-
<ProTable
|
|
1003
|
-
columns={columns}
|
|
1004
|
-
rowKey="id"
|
|
1005
|
-
dataSource={data}
|
|
1006
|
-
pagination={{
|
|
1007
|
-
mode: "server",
|
|
1008
|
-
current: pagination.current,
|
|
1009
|
-
pageSize: pagination.pageSize,
|
|
1010
|
-
total: pagination.total,
|
|
1011
|
-
onChange: (current, pageSize) => void fetchPage(current, pageSize),
|
|
1012
|
-
}}
|
|
1013
|
-
/>
|
|
1014
|
-
)
|
|
1015
|
-
}
|
|
1016
|
-
```
|
|
1017
|
-
|
|
1018
|
-
#### 自定义分页渲染(`paginationRender`)
|
|
1019
|
-
|
|
1020
|
-
当你想把分页 UI 放到任意布局 / 卡片里,并且仍复用 ProTable 内置分页状态 / 逻辑时,使用 `paginationRender`:
|
|
1021
|
-
|
|
1022
|
-
- `paginationRender` 会拿到 `pagination`、`onChange`、`defaultDom`
|
|
1023
|
-
- 如果你是 **服务端分页模式**(`pagination.mode: 'server'`),务必同时提供 `pagination.onChange` 去请求数据
|
|
854
|
+
### 6. Custom Cell Rendering
|
|
1024
855
|
|
|
1025
856
|
```tsx
|
|
1026
857
|
<ProTable
|
|
1027
|
-
columns={
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
858
|
+
columns={[
|
|
859
|
+
{
|
|
860
|
+
title: 'Avatar',
|
|
861
|
+
dataIndex: 'avatar',
|
|
862
|
+
valueType: 'avatar',
|
|
863
|
+
render: (_, record) => (
|
|
864
|
+
<Avatar>
|
|
865
|
+
<AvatarImage src={record.avatar} />
|
|
866
|
+
<AvatarFallback>{record.name[0]}</AvatarFallback>
|
|
867
|
+
</Avatar>
|
|
868
|
+
),
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
title: 'Status',
|
|
872
|
+
dataIndex: 'status',
|
|
873
|
+
valueType: 'status',
|
|
874
|
+
valueEnum: {
|
|
875
|
+
active: { text: 'Active', status: 'success' },
|
|
876
|
+
inactive: { text: 'Inactive', status: 'default' },
|
|
877
|
+
pending: { text: 'Pending', status: 'warning' },
|
|
878
|
+
},
|
|
879
|
+
render: (_, record) => (
|
|
880
|
+
<Badge variant={record.status === 'active' ? 'success' : 'secondary'}>
|
|
881
|
+
{record.status}
|
|
882
|
+
</Badge>
|
|
883
|
+
),
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
title: 'Progress',
|
|
887
|
+
dataIndex: 'progress',
|
|
888
|
+
valueType: 'progress',
|
|
889
|
+
render: (_, record) => (
|
|
890
|
+
<div className="flex items-center gap-2">
|
|
891
|
+
<Progress value={record.progress} className="w-20" />
|
|
892
|
+
<span className="text-xs">{record.progress}%</span>
|
|
1050
893
|
</div>
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
894
|
+
),
|
|
895
|
+
},
|
|
896
|
+
]}
|
|
897
|
+
dataSource={data}
|
|
898
|
+
rowKey="id"
|
|
1055
899
|
/>
|
|
1056
900
|
```
|
|
1057
901
|
|
|
1058
|
-
|
|
902
|
+
### 7. Multi-line Ellipsis with Tooltip
|
|
1059
903
|
|
|
1060
904
|
```tsx
|
|
1061
|
-
import { ProTable, zh_CN } from '@gulibs/react-vtable'
|
|
1062
|
-
|
|
1063
905
|
<ProTable
|
|
1064
|
-
columns={
|
|
1065
|
-
|
|
906
|
+
columns={[
|
|
907
|
+
{
|
|
908
|
+
title: 'Description',
|
|
909
|
+
dataIndex: 'description',
|
|
910
|
+
width: 300,
|
|
911
|
+
ellipsis: {
|
|
912
|
+
multiline: true,
|
|
913
|
+
rows: 3, // Show 3 lines max
|
|
914
|
+
},
|
|
915
|
+
tooltip: {
|
|
916
|
+
content: (value) => value,
|
|
917
|
+
side: 'top',
|
|
918
|
+
},
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
title: 'Long Title Column with Header Tooltip',
|
|
922
|
+
dataIndex: 'data',
|
|
923
|
+
width: 150,
|
|
924
|
+
headerEllipsis: true, // Header ellipsis
|
|
925
|
+
headerTooltip: {
|
|
926
|
+
content: 'This is a very long header title that needs tooltip',
|
|
927
|
+
side: 'bottom',
|
|
928
|
+
},
|
|
929
|
+
ellipsis: true, // Cell ellipsis
|
|
930
|
+
tooltip: true, // Cell tooltip
|
|
931
|
+
},
|
|
932
|
+
]}
|
|
933
|
+
dataSource={data}
|
|
1066
934
|
rowKey="id"
|
|
1067
|
-
locale={zh_CN}
|
|
1068
935
|
/>
|
|
1069
936
|
```
|
|
1070
937
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
固定列功能基于 TanStack Table 的 column pinning 实现。库会自动计算位置并应用柔和的阴影效果来标识固定列边界。
|
|
938
|
+
### 8. Custom Search Form
|
|
1074
939
|
|
|
1075
940
|
```tsx
|
|
1076
941
|
<ProTable
|
|
1077
942
|
columns={[
|
|
1078
943
|
{
|
|
1079
|
-
title: '
|
|
1080
|
-
dataIndex: '
|
|
1081
|
-
|
|
1082
|
-
width: 80,
|
|
1083
|
-
fixed: 'left' // 固定在左侧
|
|
944
|
+
title: 'Name',
|
|
945
|
+
dataIndex: 'name',
|
|
946
|
+
search: true,
|
|
1084
947
|
},
|
|
1085
948
|
{
|
|
1086
|
-
title: '
|
|
1087
|
-
dataIndex: '
|
|
1088
|
-
|
|
1089
|
-
|
|
949
|
+
title: 'Date Range',
|
|
950
|
+
dataIndex: 'dateRange',
|
|
951
|
+
valueType: 'dateRange',
|
|
952
|
+
search: true,
|
|
953
|
+
renderFormItem: (value, onChange) => (
|
|
954
|
+
<DateRangePicker
|
|
955
|
+
value={value}
|
|
956
|
+
onChange={onChange}
|
|
957
|
+
/>
|
|
958
|
+
),
|
|
1090
959
|
},
|
|
1091
960
|
{
|
|
1092
|
-
title: '
|
|
1093
|
-
dataIndex: '
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
961
|
+
title: 'Custom Filter',
|
|
962
|
+
dataIndex: 'customField',
|
|
963
|
+
search: {
|
|
964
|
+
transform: (value) => {
|
|
965
|
+
// Transform before sending to backend
|
|
966
|
+
return { customFieldQuery: value.toUpperCase() }
|
|
967
|
+
},
|
|
968
|
+
},
|
|
969
|
+
renderFormItem: (value, onChange) => (
|
|
970
|
+
<CustomFilterComponent
|
|
971
|
+
value={value}
|
|
972
|
+
onChange={onChange}
|
|
973
|
+
/>
|
|
974
|
+
),
|
|
975
|
+
},
|
|
1098
976
|
]}
|
|
1099
|
-
|
|
977
|
+
request={fetchData}
|
|
978
|
+
search={{ filterType: 'query' }}
|
|
1100
979
|
rowKey="id"
|
|
1101
|
-
scroll={{ x: 1000 }} // 启用横向滚动
|
|
1102
|
-
tableLayout="fixed" // 固定列推荐使用 fixed 布局
|
|
1103
980
|
/>
|
|
1104
981
|
```
|
|
1105
982
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
- 最后一个左侧固定列和第一个右侧固定列会自动应用柔和的阴影效果
|
|
1110
|
-
- 无需额外 CSS - 所有样式都通过内联样式应用
|
|
983
|
+
---
|
|
984
|
+
|
|
985
|
+
## Best Practices
|
|
1111
986
|
|
|
1112
|
-
|
|
987
|
+
### 1. Performance Optimization
|
|
1113
988
|
|
|
1114
|
-
|
|
989
|
+
#### Use `rowKey` Correctly
|
|
990
|
+
Always provide a stable, unique `rowKey`:
|
|
1115
991
|
|
|
1116
992
|
```tsx
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
{ id: 12, name: '子节点 2' }
|
|
1126
|
-
]
|
|
1127
|
-
}
|
|
1128
|
-
]}
|
|
1129
|
-
rowKey="id"
|
|
1130
|
-
expandable={{
|
|
1131
|
-
childrenColumnName: 'children', // 默认: 'children'
|
|
1132
|
-
defaultExpandAllRows: false, // 默认展开所有行
|
|
1133
|
-
defaultExpandedRowKeys: [1], // 初始展开的行
|
|
1134
|
-
accordion: false, // 手风琴模式(同时只能展开一行)
|
|
1135
|
-
showExpandColumn: true, // 显示展开图标列
|
|
1136
|
-
expandColumnWidth: 50, // 展开列宽度
|
|
1137
|
-
onExpand: (expanded, record) => {
|
|
1138
|
-
console.log('展开:', expanded, record)
|
|
1139
|
-
},
|
|
1140
|
-
onExpandedRowsChange: (expandedRowKeys) => {
|
|
1141
|
-
console.log('展开的行:', expandedRowKeys)
|
|
1142
|
-
},
|
|
1143
|
-
expandIcon: ({ expanded, onExpand, record }) => (
|
|
1144
|
-
<Button onClick={onExpand}>
|
|
1145
|
-
{expanded ? '▼' : '▶'}
|
|
1146
|
-
</Button>
|
|
1147
|
-
)
|
|
1148
|
-
}}
|
|
1149
|
-
/>
|
|
993
|
+
// Good: Use unique ID
|
|
994
|
+
<ProTable rowKey="id" />
|
|
995
|
+
|
|
996
|
+
// Good: Use function for complex keys
|
|
997
|
+
<ProTable rowKey={(record) => `${record.type}-${record.id}`} />
|
|
998
|
+
|
|
999
|
+
// Bad: Use index (causes re-render issues)
|
|
1000
|
+
<ProTable rowKey={(record, index) => index} />
|
|
1150
1001
|
```
|
|
1151
1002
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1003
|
+
#### Memoize Large Data
|
|
1004
|
+
For large datasets, memoize your data:
|
|
1005
|
+
|
|
1006
|
+
```tsx
|
|
1007
|
+
const data = useMemo(() => generateLargeData(), [])
|
|
1008
|
+
|
|
1009
|
+
<ProTable dataSource={data} />
|
|
1010
|
+
```
|
|
1159
1011
|
|
|
1160
|
-
####
|
|
1012
|
+
#### Optimize Column Rendering
|
|
1013
|
+
Use `useMemo` for column definitions with complex render functions:
|
|
1161
1014
|
|
|
1015
|
+
```tsx
|
|
1016
|
+
const columns = useMemo(() => [
|
|
1017
|
+
{
|
|
1018
|
+
title: 'Name',
|
|
1019
|
+
render: (_, record) => <ComplexComponent record={record} />,
|
|
1020
|
+
},
|
|
1021
|
+
], [dependencies])
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
### 2. Request Best Practices
|
|
1025
|
+
|
|
1026
|
+
#### Handle Errors Gracefully
|
|
1162
1027
|
```tsx
|
|
1163
1028
|
<ProTable
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
// 处理保存逻辑
|
|
1029
|
+
request={async (params) => {
|
|
1030
|
+
try {
|
|
1031
|
+
const res = await fetchData(params)
|
|
1032
|
+
return { data: res.items, total: res.total, success: true }
|
|
1033
|
+
} catch (error) {
|
|
1034
|
+
message.error('Failed to load data')
|
|
1035
|
+
return { data: [], total: 0, success: false }
|
|
1172
1036
|
}
|
|
1173
1037
|
}}
|
|
1038
|
+
onRequestError={(error) => {
|
|
1039
|
+
console.error('Request error:', error)
|
|
1040
|
+
// Report to error tracking service
|
|
1041
|
+
}}
|
|
1174
1042
|
/>
|
|
1175
1043
|
```
|
|
1176
1044
|
|
|
1177
|
-
####
|
|
1045
|
+
#### Debounce Search Requests
|
|
1046
|
+
Use `params` prop to trigger re-fetch:
|
|
1178
1047
|
|
|
1179
1048
|
```tsx
|
|
1049
|
+
const [searchParams, setSearchParams] = useState({})
|
|
1050
|
+
const debouncedSearch = useMemo(
|
|
1051
|
+
() => debounce((value) => setSearchParams({ keyword: value }), 500),
|
|
1052
|
+
[]
|
|
1053
|
+
)
|
|
1054
|
+
|
|
1180
1055
|
<ProTable
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
onChange: (selectedRowKeys, selectedRows) => {
|
|
1187
|
-
console.log('已选择:', selectedRowKeys, selectedRows)
|
|
1056
|
+
request={fetchData}
|
|
1057
|
+
params={searchParams}
|
|
1058
|
+
toolbar={{
|
|
1059
|
+
search: {
|
|
1060
|
+
onSearch: debouncedSearch,
|
|
1188
1061
|
},
|
|
1189
|
-
batchActions: {
|
|
1190
|
-
onDelete: (rows) => {
|
|
1191
|
-
console.log('删除:', rows)
|
|
1192
|
-
},
|
|
1193
|
-
onExport: (rows) => {
|
|
1194
|
-
console.log('导出:', rows)
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
1062
|
}}
|
|
1198
1063
|
/>
|
|
1199
1064
|
```
|
|
1200
1065
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
当使用 `request` 或 `pagination.onChange` 进行远程数据获取时,表格会自动维护跨页选中状态。这意味着:
|
|
1066
|
+
### 3. State Management
|
|
1204
1067
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
- 当返回到之前访问过的页面时,之前的选中状态会自动恢复
|
|
1208
|
-
- `onChange` 回调会接收到所有页面的选中行 keys
|
|
1068
|
+
#### Controlled Components
|
|
1069
|
+
For complex state management, use controlled mode:
|
|
1209
1070
|
|
|
1210
1071
|
```tsx
|
|
1072
|
+
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
|
|
1073
|
+
const [pagination, setPagination] = useState({ current: 1, pageSize: 10 })
|
|
1074
|
+
|
|
1211
1075
|
<ProTable
|
|
1212
|
-
columns={columns}
|
|
1213
|
-
rowKey="id"
|
|
1214
|
-
request={async (params) => {
|
|
1215
|
-
const res = await fetchUsers(params)
|
|
1216
|
-
return { data: res.items, total: res.total, success: true }
|
|
1217
|
-
}}
|
|
1218
1076
|
rowSelection={{
|
|
1219
|
-
|
|
1220
|
-
onChange:
|
|
1221
|
-
// selectedRowKeys 包含所有页面的选中 keys
|
|
1222
|
-
// selectedRows 仅包含当前页的行数据
|
|
1223
|
-
console.log('所有页面选中的总数:', selectedRowKeys.length)
|
|
1224
|
-
},
|
|
1225
|
-
batchActions: {
|
|
1226
|
-
onDelete: (rows) => {
|
|
1227
|
-
// 这里只会收到当前页的行数据
|
|
1228
|
-
// 如需操作所有选中的行,请使用 onChange 中的 selectedRowKeys
|
|
1229
|
-
console.log('删除当前页行:', rows)
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1077
|
+
selectedRowKeys,
|
|
1078
|
+
onChange: setSelectedRowKeys,
|
|
1232
1079
|
}}
|
|
1233
1080
|
pagination={{
|
|
1234
|
-
|
|
1235
|
-
showSizeChanger: true,
|
|
1081
|
+
...pagination,
|
|
1236
1082
|
onChange: (current, pageSize) => {
|
|
1237
|
-
|
|
1238
|
-
}
|
|
1083
|
+
setPagination({ current, pageSize })
|
|
1084
|
+
},
|
|
1239
1085
|
}}
|
|
1240
1086
|
/>
|
|
1241
1087
|
```
|
|
1242
1088
|
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
- 使用 `dataSource`(本地数据)时,选中功能正常工作,但不需要跨页支持
|
|
1246
|
-
- 批量操作栏始终显示所有页面的选中总数
|
|
1247
|
-
- 如需对所有选中的行进行操作,请使用 `rowSelection.onChange` 获取所有选中的 keys
|
|
1089
|
+
#### Use Action Ref
|
|
1090
|
+
Access table methods via `actionRef`:
|
|
1248
1091
|
|
|
1249
|
-
|
|
1092
|
+
```tsx
|
|
1093
|
+
const actionRef = useRef<ProTableAction<DataType>>(null)
|
|
1250
1094
|
|
|
1251
|
-
|
|
1095
|
+
const handleRefresh = () => {
|
|
1096
|
+
actionRef.current?.reload()
|
|
1097
|
+
}
|
|
1252
1098
|
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1099
|
+
const handleReset = () => {
|
|
1100
|
+
actionRef.current?.reset()
|
|
1101
|
+
actionRef.current?.clearSelected()
|
|
1102
|
+
}
|
|
1257
1103
|
|
|
1258
|
-
|
|
1259
|
-
<ProTable
|
|
1260
|
-
rowSelection={{
|
|
1261
|
-
type: 'checkbox',
|
|
1262
|
-
batchActions: {
|
|
1263
|
-
onDelete: (rows) => {
|
|
1264
|
-
// 删除操作 - 选中状态会自动清空
|
|
1265
|
-
console.log('删除:', rows)
|
|
1266
|
-
},
|
|
1267
|
-
onArchive: (rows) => {
|
|
1268
|
-
// 归档操作 - 选中状态会自动清空
|
|
1269
|
-
console.log('归档:', rows)
|
|
1270
|
-
},
|
|
1271
|
-
// 如需隐藏清空按钮
|
|
1272
|
-
hideClearButton: false,
|
|
1273
|
-
customActions: [
|
|
1274
|
-
{
|
|
1275
|
-
key: 'approve',
|
|
1276
|
-
label: '批量审批',
|
|
1277
|
-
onClick: (rows) => {
|
|
1278
|
-
console.log('审批:', rows)
|
|
1279
|
-
},
|
|
1280
|
-
// 控制操作后是否清空选中状态
|
|
1281
|
-
clearSelection: true, // 设置为 true 表示操作后清空选中状态
|
|
1282
|
-
},
|
|
1283
|
-
{
|
|
1284
|
-
key: 'export',
|
|
1285
|
-
label: '导出',
|
|
1286
|
-
onClick: (rows) => {
|
|
1287
|
-
console.log('导出:', rows)
|
|
1288
|
-
},
|
|
1289
|
-
clearSelection: false, // 导出后保留选中状态(默认值)
|
|
1290
|
-
}
|
|
1291
|
-
]
|
|
1292
|
-
}
|
|
1293
|
-
}}
|
|
1294
|
-
/>
|
|
1104
|
+
<ProTable actionRef={actionRef} />
|
|
1295
1105
|
```
|
|
1296
1106
|
|
|
1297
|
-
|
|
1107
|
+
### 4. Accessibility
|
|
1298
1108
|
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
title: '描述',
|
|
1304
|
-
dataIndex: 'description',
|
|
1305
|
-
key: 'description',
|
|
1306
|
-
width: 200,
|
|
1307
|
-
// 单行省略,带提示框
|
|
1308
|
-
ellipsis: true,
|
|
1309
|
-
tooltip: true
|
|
1310
|
-
},
|
|
1311
|
-
{
|
|
1312
|
-
title: '内容',
|
|
1313
|
-
dataIndex: 'content',
|
|
1314
|
-
key: 'content',
|
|
1315
|
-
width: 250,
|
|
1316
|
-
// 多行省略(2行),带提示框
|
|
1317
|
-
ellipsis: {
|
|
1318
|
-
multiline: true,
|
|
1319
|
-
rows: 2
|
|
1320
|
-
},
|
|
1321
|
-
tooltip: true
|
|
1322
|
-
},
|
|
1323
|
-
{
|
|
1324
|
-
title: '自定义提示',
|
|
1325
|
-
dataIndex: 'name',
|
|
1326
|
-
key: 'name',
|
|
1327
|
-
width: 150,
|
|
1328
|
-
ellipsis: true,
|
|
1329
|
-
tooltip: {
|
|
1330
|
-
content: (value, record) => `完整内容: ${record.fullName || value}`,
|
|
1331
|
-
side: 'top',
|
|
1332
|
-
delayDuration: 300
|
|
1333
|
-
}
|
|
1334
|
-
},
|
|
1335
|
-
{
|
|
1336
|
-
title: '表头省略',
|
|
1337
|
-
dataIndex: 'email',
|
|
1338
|
-
key: 'email',
|
|
1339
|
-
width: 180,
|
|
1340
|
-
// 表头独立的省略和提示配置
|
|
1341
|
-
headerEllipsis: true,
|
|
1342
|
-
headerTooltip: {
|
|
1343
|
-
content: '这是完整的表头标题',
|
|
1344
|
-
side: 'bottom'
|
|
1345
|
-
},
|
|
1346
|
-
// 单元格省略和提示
|
|
1347
|
-
ellipsis: true,
|
|
1348
|
-
tooltip: true
|
|
1349
|
-
}
|
|
1350
|
-
]}
|
|
1351
|
-
dataSource={dataSource}
|
|
1352
|
-
rowKey="id"
|
|
1353
|
-
/>
|
|
1354
|
-
```
|
|
1109
|
+
- Always provide meaningful `title` for columns
|
|
1110
|
+
- Use `aria-label` for action buttons
|
|
1111
|
+
- Ensure color contrast for status badges
|
|
1112
|
+
- Test with keyboard navigation
|
|
1355
1113
|
|
|
1356
|
-
###
|
|
1357
|
-
|
|
1358
|
-
#### ProTable 属性
|
|
1359
|
-
|
|
1360
|
-
| 属性 | 类型 | 默认值 | 描述 |
|
|
1361
|
-
|------|------|--------|------|
|
|
1362
|
-
| `columns` | `ProColumn[]` | `[]` | 表格列定义 |
|
|
1363
|
-
| `dataSource` | `T[]` | `[]` | 表格数据源 |
|
|
1364
|
-
| `rowKey` | `string \| (record: T) => string` | `'id'` | 每行的唯一键 |
|
|
1365
|
-
| `loading` | `boolean` | `false` | 加载状态 |
|
|
1366
|
-
| `pagination` | `ProTablePagination \| false` | `false` | 分页配置 |
|
|
1367
|
-
| `search` | `ProTableSearch \| false` | `false` | 搜索表单配置 |
|
|
1368
|
-
| `rowSelection` | `ProTableRowSelection` | `undefined` | 行选择配置 |
|
|
1369
|
-
| `editable` | `ProTableEditable` | `undefined` | 可编辑行配置 |
|
|
1370
|
-
| `draggable` | `ProTableDraggable` | `undefined` | 拖拽配置 |
|
|
1371
|
-
| `expandable` | `ProTableExpandable \| false` | `undefined` | 树形表格展开配置 |
|
|
1372
|
-
| `scroll` | `{ x?: number \| true; y?: number \| string }` | `undefined` | 滚动配置 |
|
|
1373
|
-
| `tableLayout` | `'auto' \| 'fixed'` | `'auto'` | 表格布局模式 |
|
|
1374
|
-
| `locale` | `ProTableLocale` | `en_US` | 国际化语言包 |
|
|
1375
|
-
| `size` | `'small' \| 'middle' \| 'large'` | `'middle'` | 表格尺寸 |
|
|
1376
|
-
| `bordered` | `boolean` | `false` | 显示表格边框 |
|
|
1377
|
-
|
|
1378
|
-
#### 列配置
|
|
1379
|
-
|
|
1380
|
-
| 属性 | 类型 | 描述 |
|
|
1381
|
-
|------|------|------|
|
|
1382
|
-
| `title` | `string` | 列头文本 |
|
|
1383
|
-
| `dataIndex` | `string` | 数据字段名 |
|
|
1384
|
-
| `key` | `string` | 唯一列键 |
|
|
1385
|
-
| `width` | `number` | 列宽度(像素) |
|
|
1386
|
-
| `fixed` | `'left' \| 'right'` | 固定列位置 |
|
|
1387
|
-
| `sorter` | `boolean` | 启用排序 |
|
|
1388
|
-
| `editable` | `boolean` | 启用内联编辑 |
|
|
1389
|
-
| `copyable` | `boolean` | 启用复制到剪贴板 |
|
|
1390
|
-
| `ellipsis` | `boolean \| ProColumnEllipsis` | 启用文本省略(单行或多行) |
|
|
1391
|
-
| `tooltip` | `boolean \| ProColumnTooltip` | 启用悬停提示框 |
|
|
1392
|
-
| `headerEllipsis` | `boolean \| ProColumnEllipsis` | 启用表头省略(默认使用 `ellipsis`) |
|
|
1393
|
-
| `headerTooltip` | `boolean \| ProColumnTooltip` | 启用表头提示框(默认使用 `tooltip`) |
|
|
1394
|
-
| `search` | `boolean` | 包含在搜索表单中 |
|
|
1395
|
-
| `valueType` | `ProFieldValueType` | 值类型渲染 |
|
|
1396
|
-
| `valueEnum` | `Record<string, { text: string; status?: string }>` | 枚举值选择 |
|
|
1397
|
-
| `filters` | `Array<{ text: string; value: any }>` | 筛选选项 |
|
|
1398
|
-
|
|
1399
|
-
#### 省略号配置
|
|
1400
|
-
|
|
1401
|
-
`ellipsis` 和 `headerEllipsis` 属性支持以下配置:
|
|
1114
|
+
### 5. Internationalization
|
|
1402
1115
|
|
|
1403
1116
|
```tsx
|
|
1404
|
-
|
|
1405
|
-
|
|
1117
|
+
import { ProTable, zh_CN, en_US } from '@gulibs/react-vtable'
|
|
1118
|
+
import { useState } from 'react'
|
|
1406
1119
|
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1120
|
+
function App() {
|
|
1121
|
+
const [locale, setLocale] = useState(en_US)
|
|
1122
|
+
|
|
1123
|
+
return (
|
|
1124
|
+
<>
|
|
1125
|
+
<Button onClick={() => setLocale(zh_CN)}>中文</Button>
|
|
1126
|
+
<Button onClick={() => setLocale(en_US)}>English</Button>
|
|
1127
|
+
|
|
1128
|
+
<ProTable
|
|
1129
|
+
locale={locale}
|
|
1130
|
+
columns={columns}
|
|
1131
|
+
dataSource={data}
|
|
1132
|
+
/>
|
|
1133
|
+
</>
|
|
1134
|
+
)
|
|
1411
1135
|
}
|
|
1412
1136
|
```
|
|
1413
1137
|
|
|
1414
|
-
|
|
1138
|
+
### 6. Type Safety
|
|
1415
1139
|
|
|
1416
|
-
|
|
1140
|
+
Always provide TypeScript types for your data:
|
|
1417
1141
|
|
|
1418
1142
|
```tsx
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
side: 'top' | 'right' | 'bottom' | 'left',
|
|
1426
|
-
align: 'start' | 'center' | 'end',
|
|
1427
|
-
delayDuration: 300,
|
|
1428
|
-
disabled: false
|
|
1143
|
+
interface User {
|
|
1144
|
+
id: string
|
|
1145
|
+
name: string
|
|
1146
|
+
email: string
|
|
1147
|
+
age: number
|
|
1148
|
+
status: 'active' | 'inactive'
|
|
1429
1149
|
}
|
|
1430
1150
|
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1151
|
+
const columns: ProColumn<User>[] = [
|
|
1152
|
+
{
|
|
1153
|
+
title: 'Name',
|
|
1154
|
+
dataIndex: 'name',
|
|
1155
|
+
// TypeScript will validate dataIndex, valueType, render function params, etc.
|
|
1156
|
+
},
|
|
1157
|
+
]
|
|
1158
|
+
|
|
1159
|
+
<ProTable<User>
|
|
1160
|
+
columns={columns}
|
|
1161
|
+
dataSource={users}
|
|
1162
|
+
rowKey="id"
|
|
1163
|
+
/>
|
|
1436
1164
|
```
|
|
1437
1165
|
|
|
1438
|
-
|
|
1166
|
+
---
|
|
1439
1167
|
|
|
1440
|
-
|
|
1168
|
+
## Development
|
|
1441
1169
|
|
|
1442
|
-
|
|
1170
|
+
```bash
|
|
1171
|
+
# Install dependencies
|
|
1172
|
+
pnpm install
|
|
1443
1173
|
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
// 自定义子字段名(默认: 'children')
|
|
1447
|
-
childrenColumnName?: string
|
|
1174
|
+
# Start development server
|
|
1175
|
+
pnpm run dev
|
|
1448
1176
|
|
|
1449
|
-
|
|
1450
|
-
|
|
1177
|
+
# Build library
|
|
1178
|
+
pnpm run build
|
|
1451
1179
|
|
|
1452
|
-
|
|
1453
|
-
|
|
1180
|
+
# Lint code
|
|
1181
|
+
pnpm run lint
|
|
1182
|
+
```
|
|
1454
1183
|
|
|
1455
|
-
|
|
1456
|
-
onExpand?: (expanded: boolean, record: T) => void
|
|
1184
|
+
---
|
|
1457
1185
|
|
|
1458
|
-
|
|
1459
|
-
onExpandedRowsChange?: (expandedRowKeys: React.Key[]) => void
|
|
1186
|
+
## License
|
|
1460
1187
|
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1188
|
+
MIT © [@gulibs](https://github.com/gulibs)
|
|
1189
|
+
|
|
1190
|
+
---
|
|
1191
|
+
|
|
1192
|
+
## Chinese
|
|
1193
|
+
|
|
1194
|
+
## 中文文档
|
|
1467
1195
|
|
|
1468
|
-
|
|
1469
|
-
showExpandColumn?: boolean
|
|
1196
|
+
[查看完整中文文档 ↗](./README.zh-CN.md)
|
|
1470
1197
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1198
|
+
### 快速开始
|
|
1199
|
+
|
|
1200
|
+
```tsx
|
|
1201
|
+
import { ProTable, zh_CN } from '@gulibs/react-vtable'
|
|
1202
|
+
|
|
1203
|
+
function App() {
|
|
1204
|
+
const columns = [
|
|
1205
|
+
{ title: '姓名', dataIndex: 'name', key: 'name' },
|
|
1206
|
+
{ title: '年龄', dataIndex: 'age', key: 'age' },
|
|
1207
|
+
]
|
|
1473
1208
|
|
|
1474
|
-
|
|
1475
|
-
|
|
1209
|
+
const dataSource = [
|
|
1210
|
+
{ id: 1, name: '张三', age: 30 },
|
|
1211
|
+
{ id: 2, name: '李四', age: 25 },
|
|
1212
|
+
]
|
|
1476
1213
|
|
|
1477
|
-
|
|
1478
|
-
|
|
1214
|
+
return (
|
|
1215
|
+
<ProTable
|
|
1216
|
+
columns={columns}
|
|
1217
|
+
dataSource={dataSource}
|
|
1218
|
+
rowKey="id"
|
|
1219
|
+
locale={zh_CN}
|
|
1220
|
+
/>
|
|
1221
|
+
)
|
|
1479
1222
|
}
|
|
1480
1223
|
```
|
|
1481
1224
|
|
|
1482
|
-
###
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1225
|
+
### 主要特性
|
|
1226
|
+
|
|
1227
|
+
- ✨ 强大的表格:基于 TanStack Table v8
|
|
1228
|
+
- 🎨 精美组件:shadcn/ui 设计
|
|
1229
|
+
- 🔍 搜索筛选:自动处理本地/远程数据
|
|
1230
|
+
- 📄 分页支持:客户端和服务端分页
|
|
1231
|
+
- ✅ 行选择:支持跨页选中
|
|
1232
|
+
- 🎯 批量操作:灵活的批量操作配置
|
|
1233
|
+
- ✏️ 内联编辑:支持验证
|
|
1234
|
+
- 📌 固定列:左右固定,自动阴影
|
|
1235
|
+
- 🌳 树形表格:层次数据展示
|
|
1236
|
+
- 🔄 拖拽排序:行列拖拽重排
|
|
1237
|
+
- 📱 响应式设计:移动端友好
|
|
1238
|
+
- 🌐 国际化:内置中英文
|
|
1239
|
+
- 🎭 丰富类型:20+ 值类型
|
|
1240
|
+
- 💬 提示框:单元格和表头提示
|
|
1241
|
+
- 📏 文本省略:单行和多行截断
|
|
1242
|
+
- 🔧 TypeScript:完整类型支持
|
|
1243
|
+
|
|
1244
|
+
### 批量操作示例
|
|
1487
1245
|
|
|
1488
|
-
|
|
1489
|
-
|
|
1246
|
+
```tsx
|
|
1247
|
+
import { Trash2, Download, FileText } from 'lucide-react'
|
|
1490
1248
|
|
|
1491
|
-
|
|
1492
|
-
|
|
1249
|
+
<ProTable
|
|
1250
|
+
rowSelection={{
|
|
1251
|
+
type: 'checkbox',
|
|
1252
|
+
batchActions: {
|
|
1253
|
+
maxVisibleActions: 3,
|
|
1254
|
+
actions: [
|
|
1255
|
+
{
|
|
1256
|
+
key: 'delete',
|
|
1257
|
+
label: '删除',
|
|
1258
|
+
icon: <Trash2 className="h-3 w-3 mr-1" />,
|
|
1259
|
+
onClick: (rows) => console.log('删除:', rows),
|
|
1260
|
+
variant: 'destructive',
|
|
1261
|
+
clearSelection: true,
|
|
1262
|
+
},
|
|
1263
|
+
{
|
|
1264
|
+
key: 'export',
|
|
1265
|
+
label: '导出',
|
|
1266
|
+
icon: <Download className="h-3 w-3 mr-1" />,
|
|
1267
|
+
onClick: (rows) => console.log('导出:', rows),
|
|
1268
|
+
variant: 'outline',
|
|
1269
|
+
},
|
|
1270
|
+
{
|
|
1271
|
+
key: 'exportExcel',
|
|
1272
|
+
label: '导出 Excel',
|
|
1273
|
+
icon: <FileText className="h-3 w-3 mr-1" />,
|
|
1274
|
+
onClick: (rows) => console.log('导出 Excel:', rows),
|
|
1275
|
+
variant: 'outline',
|
|
1276
|
+
isExportFormat: true,
|
|
1277
|
+
showInMore: true,
|
|
1278
|
+
},
|
|
1279
|
+
],
|
|
1280
|
+
},
|
|
1281
|
+
}}
|
|
1282
|
+
dataSource={data}
|
|
1283
|
+
rowKey="id"
|
|
1284
|
+
locale={zh_CN}
|
|
1285
|
+
/>
|
|
1493
1286
|
```
|
|
1494
1287
|
|
|
1495
|
-
###
|
|
1288
|
+
### 更多文档
|
|
1289
|
+
|
|
1290
|
+
完整的中文文档和 API 参考,请查看英文部分。所有功能和 API 在中英文版本中都是一致的。
|
|
1291
|
+
|
|
1292
|
+
---
|
|
1293
|
+
|
|
1294
|
+
## Contributing
|
|
1295
|
+
|
|
1296
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
1297
|
+
|
|
1298
|
+
## Support
|
|
1496
1299
|
|
|
1497
|
-
|
|
1300
|
+
If you have any questions or need help, please open an issue on [GitHub](https://github.com/gulibs/react-vtable).
|