@gulibs/react-vtable 0.0.18 → 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 +992 -1293
- 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 +1359 -1408
- package/dist/types.d.ts +50 -27
- package/dist/types.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,294 +80,646 @@ 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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
|
115
|
+
|
|
116
|
+
**Local Data Mode** (using `dataSource`):
|
|
117
|
+
```tsx
|
|
118
|
+
<ProTable
|
|
119
|
+
dataSource={localData}
|
|
120
|
+
columns={columns}
|
|
121
|
+
rowKey="id"
|
|
122
|
+
/>
|
|
123
|
+
```
|
|
102
124
|
|
|
125
|
+
**Remote Data Mode** (using `request`):
|
|
103
126
|
```tsx
|
|
104
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
|
+
}}
|
|
105
137
|
columns={columns}
|
|
106
|
-
dataSource={dataSource}
|
|
107
138
|
rowKey="id"
|
|
108
139
|
/>
|
|
109
140
|
```
|
|
110
141
|
|
|
111
|
-
####
|
|
142
|
+
#### 2. Search Behavior
|
|
112
143
|
|
|
113
|
-
The table provides **
|
|
144
|
+
The table provides **automatic search behavior**:
|
|
114
145
|
|
|
115
|
-
- **Local
|
|
116
|
-
- **Remote
|
|
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
|
|
117
148
|
|
|
118
|
-
|
|
119
|
-
- Case-insensitive
|
|
120
|
-
-
|
|
121
|
-
- Array-based multi-select filtering
|
|
122
|
-
- Automatic
|
|
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
|
|
123
154
|
|
|
155
|
+
#### 3. Pagination Modes
|
|
156
|
+
|
|
157
|
+
**Client-side Pagination** (default with `dataSource`):
|
|
124
158
|
```tsx
|
|
125
159
|
<ProTable
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
160
|
+
dataSource={data}
|
|
161
|
+
pagination={{ pageSize: 10 }}
|
|
162
|
+
/>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Server-side Pagination** (with `request`):
|
|
166
|
+
```tsx
|
|
167
|
+
<ProTable
|
|
168
|
+
request={fetchData}
|
|
169
|
+
pagination={{ pageSize: 10 }}
|
|
170
|
+
/>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Manual Server Pagination** (without `request`):
|
|
174
|
+
```tsx
|
|
175
|
+
<ProTable
|
|
176
|
+
dataSource={currentPageData}
|
|
177
|
+
pagination={{
|
|
178
|
+
mode: 'server', // Important!
|
|
179
|
+
current: page,
|
|
180
|
+
pageSize: pageSize,
|
|
181
|
+
total: total,
|
|
182
|
+
onChange: (page, pageSize) => fetchPage(page, pageSize)
|
|
133
183
|
}}
|
|
134
184
|
/>
|
|
135
185
|
```
|
|
136
186
|
|
|
137
|
-
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Complete API Reference
|
|
190
|
+
|
|
191
|
+
### ProTable Props
|
|
192
|
+
|
|
193
|
+
#### Core Props
|
|
194
|
+
|
|
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` |
|
|
205
|
+
|
|
206
|
+
#### Layout Props
|
|
207
|
+
|
|
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 |
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
### ProColumn Configuration
|
|
265
|
+
|
|
266
|
+
#### Basic Props
|
|
267
|
+
|
|
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 |
|
|
308
|
+
|
|
309
|
+
#### Edit Props
|
|
310
|
+
|
|
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 |
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
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 |
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
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
|
+
}
|
|
367
|
+
```
|
|
138
368
|
|
|
139
|
-
|
|
369
|
+
**Example:**
|
|
140
370
|
|
|
141
371
|
```tsx
|
|
142
372
|
<ProTable
|
|
143
|
-
columns={columns}
|
|
144
|
-
dataSource={dataSource}
|
|
145
|
-
rowKey="id"
|
|
146
373
|
search={{
|
|
147
374
|
filterType: 'query',
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
onReset: () => {
|
|
153
|
-
// Custom reset logic
|
|
154
|
-
console.log('Reset search')
|
|
155
|
-
}
|
|
375
|
+
searchText: 'Search',
|
|
376
|
+
resetText: 'Reset',
|
|
377
|
+
labelWidth: 'auto',
|
|
378
|
+
defaultCollapsed: false,
|
|
156
379
|
}}
|
|
157
380
|
/>
|
|
158
381
|
```
|
|
159
382
|
|
|
160
|
-
|
|
383
|
+
---
|
|
384
|
+
|
|
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
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
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
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
#### Cross-Page Selection
|
|
427
|
+
|
|
428
|
+
When using `request` or `pagination.onChange`, selection automatically persists across pages:
|
|
161
429
|
|
|
162
430
|
```tsx
|
|
163
431
|
<ProTable
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
432
|
+
request={fetchData}
|
|
433
|
+
rowSelection={{
|
|
434
|
+
type: 'checkbox',
|
|
435
|
+
preserveSelectedRowKeys: true, // Default: true
|
|
436
|
+
onChange: (selectedRowKeys, selectedRows) => {
|
|
437
|
+
// selectedRowKeys: all selected keys across all pages
|
|
438
|
+
// selectedRows: rows from current page only
|
|
439
|
+
console.log('Total selected:', selectedRowKeys.length)
|
|
440
|
+
}
|
|
170
441
|
}}
|
|
171
442
|
/>
|
|
172
443
|
```
|
|
173
444
|
|
|
174
|
-
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
### Batch Actions Configuration (Updated API)
|
|
175
448
|
|
|
176
|
-
|
|
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
|
+
}
|
|
177
455
|
|
|
178
|
-
|
|
179
|
-
|
|
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
|
+
```
|
|
180
467
|
|
|
181
|
-
**
|
|
468
|
+
**Example:**
|
|
182
469
|
|
|
183
470
|
```tsx
|
|
471
|
+
import { Trash2, Download, FileText, Mail } from 'lucide-react'
|
|
472
|
+
|
|
184
473
|
<ProTable
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
474
|
+
rowSelection={{
|
|
475
|
+
type: 'checkbox',
|
|
476
|
+
batchActions: {
|
|
477
|
+
maxVisibleActions: 3, // Show max 3 buttons directly
|
|
478
|
+
actions: [
|
|
479
|
+
{
|
|
480
|
+
key: 'delete',
|
|
481
|
+
label: 'Delete',
|
|
482
|
+
icon: <Trash2 className="h-3 w-3 mr-1" />,
|
|
483
|
+
onClick: (rows) => {
|
|
484
|
+
console.log('Delete:', rows)
|
|
485
|
+
},
|
|
486
|
+
variant: 'destructive',
|
|
487
|
+
clearSelection: true, // Clear after delete
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
key: 'export',
|
|
491
|
+
label: 'Export',
|
|
492
|
+
icon: <Download className="h-3 w-3 mr-1" />,
|
|
493
|
+
onClick: (rows) => {
|
|
494
|
+
console.log('Export:', rows)
|
|
495
|
+
},
|
|
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
|
+
},
|
|
196
532
|
}}
|
|
197
533
|
/>
|
|
198
534
|
```
|
|
199
535
|
|
|
200
|
-
**
|
|
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
|
|
201
540
|
|
|
202
|
-
|
|
203
|
-
function Page() {
|
|
204
|
-
const [pagination, setPagination] = useState({
|
|
205
|
-
current: 1,
|
|
206
|
-
pageSize: 10,
|
|
207
|
-
total: 0,
|
|
208
|
-
})
|
|
541
|
+
---
|
|
209
542
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
showQuickJumper: true,
|
|
225
|
-
onChange: (current, pageSize) => {
|
|
226
|
-
setPagination((prev) => ({ ...prev, current, pageSize }))
|
|
227
|
-
},
|
|
228
|
-
}}
|
|
229
|
-
/>
|
|
230
|
-
)
|
|
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
|
|
231
557
|
}
|
|
232
558
|
```
|
|
233
559
|
|
|
234
|
-
|
|
560
|
+
---
|
|
235
561
|
|
|
236
|
-
|
|
237
|
-
set `pagination.mode: 'server'` to prevent TanStack Table from slicing again.
|
|
562
|
+
### Expandable Configuration (Tree Table)
|
|
238
563
|
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
+
```
|
|
243
582
|
|
|
244
|
-
|
|
245
|
-
const res = await fetchUsers({ current, pageSize })
|
|
246
|
-
setData(res.items)
|
|
247
|
-
setPagination((prev) => ({ ...prev, current, pageSize, total: res.total }))
|
|
248
|
-
}
|
|
583
|
+
---
|
|
249
584
|
|
|
250
|
-
|
|
251
|
-
void fetchPage(pagination.current, pagination.pageSize)
|
|
252
|
-
}, [])
|
|
585
|
+
### Ellipsis Configuration
|
|
253
586
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
587
|
+
```typescript
|
|
588
|
+
interface ProColumnEllipsis {
|
|
589
|
+
multiline?: boolean // Multi-line ellipsis
|
|
590
|
+
rows?: number // Number of rows (1-5, default: 2)
|
|
591
|
+
}
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
**Examples:**
|
|
595
|
+
|
|
596
|
+
```tsx
|
|
597
|
+
// Single-line ellipsis
|
|
598
|
+
ellipsis: true
|
|
599
|
+
|
|
600
|
+
// Multi-line ellipsis (2 lines)
|
|
601
|
+
ellipsis: {
|
|
602
|
+
multiline: true,
|
|
603
|
+
rows: 2
|
|
268
604
|
}
|
|
269
605
|
```
|
|
270
606
|
|
|
271
|
-
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
### Tooltip Configuration
|
|
272
610
|
|
|
273
|
-
|
|
274
|
-
|
|
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
|
+
```
|
|
275
620
|
|
|
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.
|
|
621
|
+
**Examples:**
|
|
279
622
|
|
|
280
623
|
```tsx
|
|
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
|
-
|
|
624
|
+
// Simple tooltip (shows cell value)
|
|
625
|
+
tooltip: true
|
|
626
|
+
|
|
627
|
+
// Custom tooltip
|
|
628
|
+
tooltip: {
|
|
629
|
+
content: 'Custom tooltip text',
|
|
630
|
+
side: 'top',
|
|
631
|
+
delayDuration: 300
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Function-based tooltip
|
|
635
|
+
tooltip: {
|
|
636
|
+
content: (value, record) => `Full: ${value} (ID: ${record.id})`,
|
|
637
|
+
side: 'top'
|
|
638
|
+
}
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
---
|
|
642
|
+
|
|
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
|
|
659
|
+
}
|
|
312
660
|
```
|
|
313
661
|
|
|
314
|
-
|
|
662
|
+
**Example:**
|
|
315
663
|
|
|
316
664
|
```tsx
|
|
317
|
-
|
|
665
|
+
const actionRef = useRef<ProTableAction<DataType>>(null)
|
|
666
|
+
|
|
667
|
+
// Reload data
|
|
668
|
+
actionRef.current?.reload()
|
|
669
|
+
|
|
670
|
+
// Reset filters
|
|
671
|
+
actionRef.current?.reset()
|
|
672
|
+
|
|
673
|
+
// Clear selection
|
|
674
|
+
actionRef.current?.clearSelected()
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
---
|
|
678
|
+
|
|
679
|
+
## Advanced Examples
|
|
318
680
|
|
|
681
|
+
### 1. Remote Data with Search and Pagination
|
|
682
|
+
|
|
683
|
+
```tsx
|
|
319
684
|
<ProTable
|
|
320
|
-
|
|
321
|
-
|
|
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 },
|
|
701
|
+
{
|
|
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
|
+
},
|
|
710
|
+
},
|
|
711
|
+
]}
|
|
712
|
+
search={{ filterType: 'query' }}
|
|
713
|
+
pagination={{
|
|
714
|
+
pageSize: 10,
|
|
715
|
+
showSizeChanger: true,
|
|
716
|
+
showQuickJumper: true,
|
|
717
|
+
}}
|
|
322
718
|
rowKey="id"
|
|
323
|
-
locale={zh_CN}
|
|
324
719
|
/>
|
|
325
720
|
```
|
|
326
721
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
Fixed columns are implemented using TanStack Table's column pinning feature. The library automatically calculates positions and applies subtle shadow effects to indicate fixed column boundaries.
|
|
722
|
+
### 2. Fixed Columns with Horizontal Scroll
|
|
330
723
|
|
|
331
724
|
```tsx
|
|
332
725
|
<ProTable
|
|
@@ -334,1268 +727,574 @@ Fixed columns are implemented using TanStack Table's column pinning feature. The
|
|
|
334
727
|
{
|
|
335
728
|
title: 'ID',
|
|
336
729
|
dataIndex: 'id',
|
|
337
|
-
key: 'id',
|
|
338
730
|
width: 80,
|
|
339
|
-
fixed: 'left' //
|
|
731
|
+
fixed: 'left', // Pin to left
|
|
340
732
|
},
|
|
341
733
|
{
|
|
342
734
|
title: 'Name',
|
|
343
735
|
dataIndex: 'name',
|
|
344
|
-
|
|
345
|
-
|
|
736
|
+
width: 150,
|
|
737
|
+
fixed: 'left',
|
|
346
738
|
},
|
|
739
|
+
{ title: 'Email', dataIndex: 'email', width: 200 },
|
|
740
|
+
{ title: 'Phone', dataIndex: 'phone', width: 150 },
|
|
741
|
+
{ title: 'Address', dataIndex: 'address', width: 300 },
|
|
347
742
|
{
|
|
348
743
|
title: 'Actions',
|
|
349
|
-
dataIndex: 'actions',
|
|
350
744
|
key: 'actions',
|
|
351
745
|
width: 100,
|
|
352
|
-
fixed: 'right' //
|
|
353
|
-
|
|
746
|
+
fixed: 'right', // Pin to right
|
|
747
|
+
render: (_, record) => (
|
|
748
|
+
<Button size="sm">Edit</Button>
|
|
749
|
+
),
|
|
750
|
+
},
|
|
354
751
|
]}
|
|
355
|
-
dataSource={
|
|
752
|
+
dataSource={data}
|
|
753
|
+
scroll={{ x: 1200 }} // Enable horizontal scroll
|
|
754
|
+
tableLayout="fixed" // Required for fixed columns
|
|
356
755
|
rowKey="id"
|
|
357
|
-
scroll={{ x: 1000 }} // Enable horizontal scrolling
|
|
358
|
-
tableLayout="fixed" // Recommended for fixed columns
|
|
359
756
|
/>
|
|
360
757
|
```
|
|
361
758
|
|
|
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.
|
|
759
|
+
### 3. Tree Table with Expandable Rows
|
|
371
760
|
|
|
372
761
|
```tsx
|
|
373
762
|
<ProTable
|
|
374
|
-
columns={
|
|
763
|
+
columns={[
|
|
764
|
+
{ title: 'Name', dataIndex: 'name', width: 200 },
|
|
765
|
+
{ title: 'Size', dataIndex: 'size' },
|
|
766
|
+
{ title: 'Type', dataIndex: 'type' },
|
|
767
|
+
]}
|
|
375
768
|
dataSource={[
|
|
376
769
|
{
|
|
377
770
|
id: 1,
|
|
378
|
-
name: '
|
|
771
|
+
name: 'Folder 1',
|
|
772
|
+
type: 'folder',
|
|
379
773
|
children: [
|
|
380
|
-
{ id: 11, name: '
|
|
381
|
-
{ id: 12, name: '
|
|
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)
|
|
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
|
+
],
|
|
395
777
|
},
|
|
396
|
-
|
|
397
|
-
|
|
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
|
+
],
|
|
398
785
|
},
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
)
|
|
786
|
+
]}
|
|
787
|
+
expandable={{
|
|
788
|
+
defaultExpandAllRows: false,
|
|
789
|
+
accordion: false, // Allow multiple rows expanded
|
|
404
790
|
}}
|
|
791
|
+
rowKey="id"
|
|
405
792
|
/>
|
|
406
793
|
```
|
|
407
794
|
|
|
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
|
|
795
|
+
### 4. Inline Editing
|
|
417
796
|
|
|
418
797
|
```tsx
|
|
798
|
+
const [editableKeys, setEditableKeys] = useState<React.Key[]>([])
|
|
799
|
+
|
|
419
800
|
<ProTable
|
|
420
|
-
columns={
|
|
421
|
-
|
|
422
|
-
|
|
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}
|
|
423
820
|
editable={{
|
|
424
821
|
type: 'multiple',
|
|
822
|
+
editableKeys,
|
|
823
|
+
onChange: setEditableKeys,
|
|
425
824
|
onSave: async (key, record, originRow) => {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
825
|
+
// Save to backend
|
|
826
|
+
await updateUser(key, record)
|
|
827
|
+
message.success('Saved successfully')
|
|
828
|
+
},
|
|
429
829
|
}}
|
|
830
|
+
rowKey="id"
|
|
430
831
|
/>
|
|
431
832
|
```
|
|
432
833
|
|
|
433
|
-
|
|
834
|
+
### 5. Drag & Drop Reordering
|
|
434
835
|
|
|
435
836
|
```tsx
|
|
436
837
|
<ProTable
|
|
437
838
|
columns={columns}
|
|
438
|
-
dataSource={
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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))
|
|
444
848
|
},
|
|
445
|
-
batchActions: {
|
|
446
|
-
onDelete: (rows) => {
|
|
447
|
-
console.log('Delete:', rows)
|
|
448
|
-
},
|
|
449
|
-
onExport: (rows) => {
|
|
450
|
-
console.log('Export:', rows)
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
849
|
}}
|
|
850
|
+
rowKey="id"
|
|
454
851
|
/>
|
|
455
852
|
```
|
|
456
853
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
When using `request` or `pagination.onChange` for remote data fetching, the table automatically maintains selection state across pages. This means:
|
|
460
|
-
|
|
461
|
-
- Selections made on one page are preserved when navigating to other pages
|
|
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
|
|
854
|
+
### 6. Custom Cell Rendering
|
|
465
855
|
|
|
466
856
|
```tsx
|
|
467
857
|
<ProTable
|
|
468
|
-
columns={
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
console.log('Total selected across all pages:', selectedRowKeys.length)
|
|
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
|
+
),
|
|
480
869
|
},
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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>
|
|
893
|
+
</div>
|
|
894
|
+
),
|
|
895
|
+
},
|
|
896
|
+
]}
|
|
897
|
+
dataSource={data}
|
|
898
|
+
rowKey="id"
|
|
496
899
|
/>
|
|
497
900
|
```
|
|
498
901
|
|
|
499
|
-
|
|
500
|
-
- Cross-page selection works automatically when using `request` or `pagination.onChange`
|
|
501
|
-
- With `dataSource` (local data), selection works normally but doesn't need cross-page support
|
|
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
|
|
504
|
-
|
|
505
|
-
**Automatic Selection Clearing:**
|
|
506
|
-
|
|
507
|
-
After certain batch operations, the selection state is automatically cleared:
|
|
508
|
-
|
|
509
|
-
- **Delete operation**: Automatically clears selection after deletion (since the data is removed)
|
|
510
|
-
- **Archive operation**: Automatically clears selection after archiving
|
|
511
|
-
- **Custom actions**: You can control whether to clear selection using the `clearSelection` option
|
|
902
|
+
### 7. Multi-line Ellipsis with Tooltip
|
|
512
903
|
|
|
513
904
|
```tsx
|
|
514
905
|
<ProTable
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
906
|
+
columns={[
|
|
907
|
+
{
|
|
908
|
+
title: 'Description',
|
|
909
|
+
dataIndex: 'description',
|
|
910
|
+
width: 300,
|
|
911
|
+
ellipsis: {
|
|
912
|
+
multiline: true,
|
|
913
|
+
rows: 3, // Show 3 lines max
|
|
521
914
|
},
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
915
|
+
tooltip: {
|
|
916
|
+
content: (value) => value,
|
|
917
|
+
side: 'top',
|
|
525
918
|
},
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
console.log('Export:', rows)
|
|
543
|
-
},
|
|
544
|
-
clearSelection: false, // Keep selection after export (default)
|
|
545
|
-
}
|
|
546
|
-
]
|
|
547
|
-
}
|
|
548
|
-
}}
|
|
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}
|
|
934
|
+
rowKey="id"
|
|
549
935
|
/>
|
|
550
936
|
```
|
|
551
937
|
|
|
552
|
-
|
|
938
|
+
### 8. Custom Search Form
|
|
553
939
|
|
|
554
940
|
```tsx
|
|
555
941
|
<ProTable
|
|
556
942
|
columns={[
|
|
557
943
|
{
|
|
558
|
-
title: '
|
|
559
|
-
dataIndex: '
|
|
560
|
-
|
|
561
|
-
width: 200,
|
|
562
|
-
// Single-line ellipsis with tooltip
|
|
563
|
-
ellipsis: true,
|
|
564
|
-
tooltip: true
|
|
565
|
-
},
|
|
566
|
-
{
|
|
567
|
-
title: 'Content',
|
|
568
|
-
dataIndex: 'content',
|
|
569
|
-
key: 'content',
|
|
570
|
-
width: 250,
|
|
571
|
-
// Multi-line ellipsis (2 rows) with tooltip
|
|
572
|
-
ellipsis: {
|
|
573
|
-
multiline: true,
|
|
574
|
-
rows: 2
|
|
575
|
-
},
|
|
576
|
-
tooltip: true
|
|
944
|
+
title: 'Name',
|
|
945
|
+
dataIndex: 'name',
|
|
946
|
+
search: true,
|
|
577
947
|
},
|
|
578
948
|
{
|
|
579
|
-
title: '
|
|
580
|
-
dataIndex: '
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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
|
+
),
|
|
589
959
|
},
|
|
590
960
|
{
|
|
591
|
-
title: '
|
|
592
|
-
dataIndex: '
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
content: 'This is the full header title',
|
|
599
|
-
side: 'bottom'
|
|
961
|
+
title: 'Custom Filter',
|
|
962
|
+
dataIndex: 'customField',
|
|
963
|
+
search: {
|
|
964
|
+
transform: (value) => {
|
|
965
|
+
// Transform before sending to backend
|
|
966
|
+
return { customFieldQuery: value.toUpperCase() }
|
|
967
|
+
},
|
|
600
968
|
},
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
969
|
+
renderFormItem: (value, onChange) => (
|
|
970
|
+
<CustomFilterComponent
|
|
971
|
+
value={value}
|
|
972
|
+
onChange={onChange}
|
|
973
|
+
/>
|
|
974
|
+
),
|
|
975
|
+
},
|
|
605
976
|
]}
|
|
606
|
-
|
|
977
|
+
request={fetchData}
|
|
978
|
+
search={{ filterType: 'query' }}
|
|
607
979
|
rowKey="id"
|
|
608
980
|
/>
|
|
609
981
|
```
|
|
610
982
|
|
|
611
|
-
|
|
983
|
+
---
|
|
612
984
|
|
|
613
|
-
|
|
985
|
+
## Best Practices
|
|
614
986
|
|
|
615
|
-
|
|
616
|
-
|------|------|---------|-------------|
|
|
617
|
-
| `columns` | `ProColumn[]` | `[]` | Table column definitions |
|
|
618
|
-
| `dataSource` | `T[]` | `[]` | Table data source |
|
|
619
|
-
| `rowKey` | `string \| (record: T) => string` | `'id'` | Unique key for each row |
|
|
620
|
-
| `loading` | `boolean` | `false` | Loading state |
|
|
621
|
-
| `pagination` | `ProTablePagination \| false` | `false` | Pagination configuration |
|
|
622
|
-
| `search` | `ProTableSearch \| false` | `false` | Search form configuration |
|
|
623
|
-
| `rowSelection` | `ProTableRowSelection` | `undefined` | Row selection configuration |
|
|
624
|
-
| `editable` | `ProTableEditable` | `undefined` | Editable rows configuration |
|
|
625
|
-
| `draggable` | `ProTableDraggable` | `undefined` | Drag and drop configuration |
|
|
626
|
-
| `expandable` | `ProTableExpandable \| false` | `undefined` | Tree table expandable configuration |
|
|
627
|
-
| `scroll` | `{ x?: number \| true; y?: number \| string }` | `undefined` | Scroll configuration |
|
|
628
|
-
| `tableLayout` | `'auto' \| 'fixed'` | `'auto'` | Table layout mode |
|
|
629
|
-
| `locale` | `ProTableLocale` | `en_US` | Internationalization locale |
|
|
630
|
-
| `size` | `'small' \| 'middle' \| 'large'` | `'middle'` | Table size |
|
|
631
|
-
| `bordered` | `boolean` | `false` | Show table borders |
|
|
987
|
+
### 1. Performance Optimization
|
|
632
988
|
|
|
633
|
-
####
|
|
989
|
+
#### Use `rowKey` Correctly
|
|
990
|
+
Always provide a stable, unique `rowKey`:
|
|
634
991
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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 |
|
|
653
|
-
| `render` | `(value: any, record: T, index: number) => ReactNode` | Custom render function |
|
|
992
|
+
```tsx
|
|
993
|
+
// Good: Use unique ID
|
|
994
|
+
<ProTable rowKey="id" />
|
|
654
995
|
|
|
655
|
-
|
|
996
|
+
// Good: Use function for complex keys
|
|
997
|
+
<ProTable rowKey={(record) => `${record.type}-${record.id}`} />
|
|
656
998
|
|
|
657
|
-
|
|
999
|
+
// Bad: Use index (causes re-render issues)
|
|
1000
|
+
<ProTable rowKey={(record, index) => index} />
|
|
1001
|
+
```
|
|
658
1002
|
|
|
659
|
-
|
|
660
|
-
|
|
1003
|
+
#### Memoize Large Data
|
|
1004
|
+
For large datasets, memoize your data:
|
|
661
1005
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
2. **`valueType`** (if provided) - Used for table cell rendering when `render` is not provided
|
|
665
|
-
3. **Default rendering** - Raw value display
|
|
1006
|
+
```tsx
|
|
1007
|
+
const data = useMemo(() => generateLargeData(), [])
|
|
666
1008
|
|
|
667
|
-
|
|
1009
|
+
<ProTable dataSource={data} />
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
#### Optimize Column Rendering
|
|
1013
|
+
Use `useMemo` for column definitions with complex render functions:
|
|
668
1014
|
|
|
669
1015
|
```tsx
|
|
670
|
-
const columns = [
|
|
1016
|
+
const columns = useMemo(() => [
|
|
671
1017
|
{
|
|
672
|
-
title:
|
|
673
|
-
|
|
674
|
-
key: "level",
|
|
675
|
-
valueType: "select", // Used for search form
|
|
676
|
-
search: true,
|
|
677
|
-
valueEnum: {
|
|
678
|
-
"1": { text: "Level 1", status: "default" },
|
|
679
|
-
"2": { text: "Level 2", status: "default" },
|
|
680
|
-
"3": { text: "Level 3", status: "default" },
|
|
681
|
-
},
|
|
682
|
-
// Custom render function takes priority for table cell rendering
|
|
683
|
-
render: (_value, record) => {
|
|
684
|
-
const levelNum = parseInt(record.level);
|
|
685
|
-
const colors = [
|
|
686
|
-
"bg-blue-100 text-blue-800",
|
|
687
|
-
"bg-green-100 text-green-800",
|
|
688
|
-
"bg-yellow-100 text-yellow-800",
|
|
689
|
-
];
|
|
690
|
-
return (
|
|
691
|
-
<Badge className={colors[levelNum - 1]}>
|
|
692
|
-
L{record.level}
|
|
693
|
-
</Badge>
|
|
694
|
-
);
|
|
695
|
-
},
|
|
1018
|
+
title: 'Name',
|
|
1019
|
+
render: (_, record) => <ComplexComponent record={record} />,
|
|
696
1020
|
},
|
|
697
|
-
]
|
|
1021
|
+
], [dependencies])
|
|
698
1022
|
```
|
|
699
1023
|
|
|
700
|
-
|
|
701
|
-
- **Search form**: Shows a select dropdown (because of `valueType: "select"`)
|
|
702
|
-
- **Table cell**: Shows a custom Badge component (because of `render` function)
|
|
703
|
-
|
|
704
|
-
**Note**: The `select` valueType also supports `valueEnum` for rendering when no `render` function is provided.
|
|
1024
|
+
### 2. Request Best Practices
|
|
705
1025
|
|
|
706
|
-
####
|
|
1026
|
+
#### Handle Errors Gracefully
|
|
1027
|
+
```tsx
|
|
1028
|
+
<ProTable
|
|
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 }
|
|
1036
|
+
}
|
|
1037
|
+
}}
|
|
1038
|
+
onRequestError={(error) => {
|
|
1039
|
+
console.error('Request error:', error)
|
|
1040
|
+
// Report to error tracking service
|
|
1041
|
+
}}
|
|
1042
|
+
/>
|
|
1043
|
+
```
|
|
707
1044
|
|
|
708
|
-
|
|
1045
|
+
#### Debounce Search Requests
|
|
1046
|
+
Use `params` prop to trigger re-fetch:
|
|
709
1047
|
|
|
710
1048
|
```tsx
|
|
711
|
-
|
|
712
|
-
|
|
1049
|
+
const [searchParams, setSearchParams] = useState({})
|
|
1050
|
+
const debouncedSearch = useMemo(
|
|
1051
|
+
() => debounce((value) => setSearchParams({ keyword: value }), 500),
|
|
1052
|
+
[]
|
|
1053
|
+
)
|
|
713
1054
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
1055
|
+
<ProTable
|
|
1056
|
+
request={fetchData}
|
|
1057
|
+
params={searchParams}
|
|
1058
|
+
toolbar={{
|
|
1059
|
+
search: {
|
|
1060
|
+
onSearch: debouncedSearch,
|
|
1061
|
+
},
|
|
1062
|
+
}}
|
|
1063
|
+
/>
|
|
719
1064
|
```
|
|
720
1065
|
|
|
721
|
-
|
|
1066
|
+
### 3. State Management
|
|
722
1067
|
|
|
723
|
-
|
|
1068
|
+
#### Controlled Components
|
|
1069
|
+
For complex state management, use controlled mode:
|
|
724
1070
|
|
|
725
1071
|
```tsx
|
|
726
|
-
|
|
727
|
-
|
|
1072
|
+
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
|
|
1073
|
+
const [pagination, setPagination] = useState({ current: 1, pageSize: 10 })
|
|
728
1074
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
1075
|
+
<ProTable
|
|
1076
|
+
rowSelection={{
|
|
1077
|
+
selectedRowKeys,
|
|
1078
|
+
onChange: setSelectedRowKeys,
|
|
1079
|
+
}}
|
|
1080
|
+
pagination={{
|
|
1081
|
+
...pagination,
|
|
1082
|
+
onChange: (current, pageSize) => {
|
|
1083
|
+
setPagination({ current, pageSize })
|
|
1084
|
+
},
|
|
1085
|
+
}}
|
|
1086
|
+
/>
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
#### Use Action Ref
|
|
1090
|
+
Access table methods via `actionRef`:
|
|
1091
|
+
|
|
1092
|
+
```tsx
|
|
1093
|
+
const actionRef = useRef<ProTableAction<DataType>>(null)
|
|
1094
|
+
|
|
1095
|
+
const handleRefresh = () => {
|
|
1096
|
+
actionRef.current?.reload()
|
|
736
1097
|
}
|
|
737
1098
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
side: 'top'
|
|
1099
|
+
const handleReset = () => {
|
|
1100
|
+
actionRef.current?.reset()
|
|
1101
|
+
actionRef.current?.clearSelected()
|
|
742
1102
|
}
|
|
1103
|
+
|
|
1104
|
+
<ProTable actionRef={actionRef} />
|
|
743
1105
|
```
|
|
744
1106
|
|
|
745
|
-
|
|
1107
|
+
### 4. Accessibility
|
|
746
1108
|
|
|
747
|
-
|
|
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
|
|
748
1113
|
|
|
749
|
-
|
|
1114
|
+
### 5. Internationalization
|
|
750
1115
|
|
|
751
1116
|
```tsx
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
childrenColumnName?: string
|
|
755
|
-
|
|
756
|
-
// Default expanded row keys (uncontrolled mode)
|
|
757
|
-
defaultExpandedRowKeys?: React.Key[]
|
|
1117
|
+
import { ProTable, zh_CN, en_US } from '@gulibs/react-vtable'
|
|
1118
|
+
import { useState } from 'react'
|
|
758
1119
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
// Expand/collapse callback
|
|
763
|
-
onExpand?: (expanded: boolean, record: T) => void
|
|
1120
|
+
function App() {
|
|
1121
|
+
const [locale, setLocale] = useState(en_US)
|
|
764
1122
|
|
|
765
|
-
|
|
766
|
-
|
|
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
|
+
)
|
|
1135
|
+
}
|
|
1136
|
+
```
|
|
767
1137
|
|
|
768
|
-
|
|
769
|
-
expandIcon?: (props: {
|
|
770
|
-
expanded: boolean
|
|
771
|
-
onExpand: () => void
|
|
772
|
-
record: T
|
|
773
|
-
}) => ReactNode
|
|
1138
|
+
### 6. Type Safety
|
|
774
1139
|
|
|
775
|
-
|
|
776
|
-
showExpandColumn?: boolean
|
|
1140
|
+
Always provide TypeScript types for your data:
|
|
777
1141
|
|
|
778
|
-
|
|
779
|
-
|
|
1142
|
+
```tsx
|
|
1143
|
+
interface User {
|
|
1144
|
+
id: string
|
|
1145
|
+
name: string
|
|
1146
|
+
email: string
|
|
1147
|
+
age: number
|
|
1148
|
+
status: 'active' | 'inactive'
|
|
1149
|
+
}
|
|
780
1150
|
|
|
781
|
-
|
|
782
|
-
|
|
1151
|
+
const columns: ProColumn<User>[] = [
|
|
1152
|
+
{
|
|
1153
|
+
title: 'Name',
|
|
1154
|
+
dataIndex: 'name',
|
|
1155
|
+
// TypeScript will validate dataIndex, valueType, render function params, etc.
|
|
1156
|
+
},
|
|
1157
|
+
]
|
|
783
1158
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
}
|
|
1159
|
+
<ProTable<User>
|
|
1160
|
+
columns={columns}
|
|
1161
|
+
dataSource={users}
|
|
1162
|
+
rowKey="id"
|
|
1163
|
+
/>
|
|
787
1164
|
```
|
|
788
1165
|
|
|
789
|
-
|
|
1166
|
+
---
|
|
1167
|
+
|
|
1168
|
+
## Development
|
|
790
1169
|
|
|
791
1170
|
```bash
|
|
792
1171
|
# Install dependencies
|
|
793
1172
|
pnpm install
|
|
794
1173
|
|
|
795
|
-
# Build the library
|
|
796
|
-
pnpm run build
|
|
797
|
-
|
|
798
1174
|
# Start development server
|
|
799
1175
|
pnpm run dev
|
|
800
|
-
```
|
|
801
1176
|
|
|
802
|
-
|
|
1177
|
+
# Build library
|
|
1178
|
+
pnpm run build
|
|
803
1179
|
|
|
804
|
-
|
|
1180
|
+
# Lint code
|
|
1181
|
+
pnpm run lint
|
|
1182
|
+
```
|
|
805
1183
|
|
|
806
1184
|
---
|
|
807
1185
|
|
|
808
|
-
##
|
|
809
|
-
|
|
810
|
-
一个基于 shadcn/ui 组件和 TanStack Table 构建的强大 React 表格组件库。
|
|
811
|
-
|
|
812
|
-
### 前置要求
|
|
813
|
-
|
|
814
|
-
此库需要您的项目中安装并配置 **Tailwind CSS**。
|
|
815
|
-
|
|
816
|
-
### 安装
|
|
817
|
-
|
|
818
|
-
```bash
|
|
819
|
-
# 安装库
|
|
820
|
-
pnpm add @gulibs/react-vtable
|
|
1186
|
+
## License
|
|
821
1187
|
|
|
822
|
-
|
|
823
|
-
pnpm add react react-dom tailwindcss
|
|
824
|
-
```
|
|
1188
|
+
MIT © [@gulibs](https://github.com/gulibs)
|
|
825
1189
|
|
|
826
|
-
|
|
1190
|
+
---
|
|
827
1191
|
|
|
828
|
-
|
|
1192
|
+
## Chinese
|
|
829
1193
|
|
|
830
|
-
|
|
831
|
-
// tailwind.config.js
|
|
832
|
-
export default {
|
|
833
|
-
content: [
|
|
834
|
-
'./src/**/*.{js,ts,jsx,tsx}',
|
|
835
|
-
'./node_modules/@gulibs/react-vtable/dist/**/*.js'
|
|
836
|
-
],
|
|
837
|
-
// 无需额外配置 - 样式会自动注入
|
|
838
|
-
}
|
|
839
|
-
```
|
|
1194
|
+
## 中文文档
|
|
840
1195
|
|
|
841
|
-
|
|
1196
|
+
[查看完整中文文档 ↗](./README.zh-CN.md)
|
|
842
1197
|
|
|
843
|
-
###
|
|
1198
|
+
### 快速开始
|
|
844
1199
|
|
|
845
1200
|
```tsx
|
|
846
|
-
import { ProTable } from '@gulibs/react-vtable'
|
|
847
|
-
// 无需手动导入 CSS,样式已自动内联
|
|
1201
|
+
import { ProTable, zh_CN } from '@gulibs/react-vtable'
|
|
848
1202
|
|
|
849
1203
|
function App() {
|
|
850
1204
|
const columns = [
|
|
851
|
-
{
|
|
852
|
-
|
|
853
|
-
dataIndex: 'name',
|
|
854
|
-
key: 'name',
|
|
855
|
-
},
|
|
856
|
-
{
|
|
857
|
-
title: '年龄',
|
|
858
|
-
dataIndex: 'age',
|
|
859
|
-
key: 'age',
|
|
860
|
-
},
|
|
1205
|
+
{ title: '姓名', dataIndex: 'name', key: 'name' },
|
|
1206
|
+
{ title: '年龄', dataIndex: 'age', key: 'age' },
|
|
861
1207
|
]
|
|
862
1208
|
|
|
863
1209
|
const dataSource = [
|
|
864
|
-
{ name: '张三', age: 30 },
|
|
865
|
-
{ name: '李四', age: 25 },
|
|
1210
|
+
{ id: 1, name: '张三', age: 30 },
|
|
1211
|
+
{ id: 2, name: '李四', age: 25 },
|
|
866
1212
|
]
|
|
867
1213
|
|
|
868
1214
|
return (
|
|
869
1215
|
<ProTable
|
|
870
1216
|
columns={columns}
|
|
871
1217
|
dataSource={dataSource}
|
|
872
|
-
rowKey="
|
|
1218
|
+
rowKey="id"
|
|
1219
|
+
locale={zh_CN}
|
|
873
1220
|
/>
|
|
874
1221
|
)
|
|
875
1222
|
}
|
|
876
1223
|
```
|
|
877
1224
|
|
|
878
|
-
###
|
|
879
|
-
|
|
880
|
-
-
|
|
881
|
-
-
|
|
882
|
-
-
|
|
883
|
-
-
|
|
884
|
-
-
|
|
885
|
-
-
|
|
886
|
-
-
|
|
887
|
-
-
|
|
888
|
-
-
|
|
889
|
-
-
|
|
890
|
-
-
|
|
891
|
-
-
|
|
892
|
-
-
|
|
893
|
-
-
|
|
894
|
-
-
|
|
895
|
-
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
### 示例
|
|
899
|
-
|
|
900
|
-
#### 基础表格
|
|
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
|
+
### 批量操作示例
|
|
901
1245
|
|
|
902
1246
|
```tsx
|
|
1247
|
+
import { Trash2, Download, FileText } from 'lucide-react'
|
|
1248
|
+
|
|
903
1249
|
<ProTable
|
|
904
|
-
|
|
905
|
-
|
|
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}
|
|
906
1283
|
rowKey="id"
|
|
1284
|
+
locale={zh_CN}
|
|
907
1285
|
/>
|
|
908
1286
|
```
|
|
909
1287
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
表格提供了**默认搜索行为**,会自动工作:
|
|
913
|
-
|
|
914
|
-
- **本地数据模式**(无 `request` 属性):搜索值会自动转换为 TanStack Table 的 `columnFilters`,表格使用 `getFilteredRowModel()` 实时过滤数据。
|
|
915
|
-
- **远程数据模式**(有 `request` 属性):搜索会触发新的数据获取,搜索参数会传递给后端。
|
|
1288
|
+
### 更多文档
|
|
916
1289
|
|
|
917
|
-
|
|
918
|
-
- **文本类字段**:不区分大小写的字符串匹配(包含)
|
|
919
|
-
- **枚举 / 筛选类字段**:精确匹配(例如 `valueEnum`、`filters`、`valueType: 'select' | 'status'`)
|
|
920
|
-
- 基于数组的多选过滤(枚举 / 筛选类字段为精确匹配;文本类字段为包含匹配)
|
|
921
|
-
- 自动处理空值 /null 值
|
|
1290
|
+
完整的中文文档和 API 参考,请查看英文部分。所有功能和 API 在中英文版本中都是一致的。
|
|
922
1291
|
|
|
923
|
-
|
|
924
|
-
<ProTable
|
|
925
|
-
columns={columns}
|
|
926
|
-
dataSource={dataSource}
|
|
927
|
-
rowKey="id"
|
|
928
|
-
search={{
|
|
929
|
-
filterType: 'query',
|
|
930
|
-
searchText: '搜索',
|
|
931
|
-
resetText: '重置'
|
|
932
|
-
}}
|
|
933
|
-
/>
|
|
934
|
-
```
|
|
935
|
-
|
|
936
|
-
**自定义搜索行为:**
|
|
1292
|
+
---
|
|
937
1293
|
|
|
938
|
-
|
|
1294
|
+
## Contributing
|
|
939
1295
|
|
|
940
|
-
|
|
941
|
-
<ProTable
|
|
942
|
-
columns={columns}
|
|
943
|
-
dataSource={dataSource}
|
|
944
|
-
rowKey="id"
|
|
945
|
-
search={{
|
|
946
|
-
filterType: 'query',
|
|
947
|
-
onSearch: (values) => {
|
|
948
|
-
// 自定义搜索逻辑
|
|
949
|
-
console.log('搜索值:', values)
|
|
950
|
-
},
|
|
951
|
-
onReset: () => {
|
|
952
|
-
// 自定义重置逻辑
|
|
953
|
-
console.log('重置搜索')
|
|
954
|
-
}
|
|
955
|
-
}}
|
|
956
|
-
/>
|
|
957
|
-
```
|
|
958
|
-
|
|
959
|
-
#### 带分页
|
|
960
|
-
|
|
961
|
-
```tsx
|
|
962
|
-
<ProTable
|
|
963
|
-
columns={columns}
|
|
964
|
-
dataSource={dataSource}
|
|
965
|
-
rowKey="id"
|
|
966
|
-
pagination={{
|
|
967
|
-
pageSize: 10,
|
|
968
|
-
showSizeChanger: true
|
|
969
|
-
}}
|
|
970
|
-
/>
|
|
971
|
-
```
|
|
972
|
-
|
|
973
|
-
#### 远程分页(配合 `request`)
|
|
974
|
-
|
|
975
|
-
在**远程数据模式**(传入 `request`)下,分页规则如下:
|
|
976
|
-
|
|
977
|
-
- **`total` 决定页码数量**:推荐让 `request` 返回 `{ total }`,也可以 / 同时传入 `pagination.total`。
|
|
978
|
-
- **不要把 `pagination.current/pageSize` 写死**(例如 `current: 1`),除非你就是想把表格锁死在那一页;如果需要受控分页,请把它们放到 state 里,并在 `pagination.onChange` 里更新。
|
|
979
|
-
|
|
980
|
-
**非受控(推荐:不需要外部同步分页状态时)**:
|
|
981
|
-
|
|
982
|
-
```tsx
|
|
983
|
-
<ProTable
|
|
984
|
-
columns={columns}
|
|
985
|
-
rowKey="id"
|
|
986
|
-
request={async (params) => {
|
|
987
|
-
// params 包含:current, pageSize, ...filters, ...sorter
|
|
988
|
-
const res = await fetchUsers(params)
|
|
989
|
-
return { data: res.items, total: res.total, success: true }
|
|
990
|
-
}}
|
|
991
|
-
pagination={{
|
|
992
|
-
pageSize: 10,
|
|
993
|
-
showSizeChanger: true,
|
|
994
|
-
showQuickJumper: true,
|
|
995
|
-
}}
|
|
996
|
-
/>
|
|
997
|
-
```
|
|
998
|
-
|
|
999
|
-
**受控(需要把分页状态同步到外部 state / URL 时)**:
|
|
1000
|
-
|
|
1001
|
-
```tsx
|
|
1002
|
-
function Page() {
|
|
1003
|
-
const [pagination, setPagination] = useState({
|
|
1004
|
-
current: 1,
|
|
1005
|
-
pageSize: 10,
|
|
1006
|
-
total: 0,
|
|
1007
|
-
})
|
|
1008
|
-
|
|
1009
|
-
return (
|
|
1010
|
-
<ProTable
|
|
1011
|
-
columns={columns}
|
|
1012
|
-
rowKey="id"
|
|
1013
|
-
request={async (params) => {
|
|
1014
|
-
const res = await fetchUsers(params)
|
|
1015
|
-
setPagination((prev) => ({ ...prev, total: res.total }))
|
|
1016
|
-
return { data: res.items, total: res.total, success: true }
|
|
1017
|
-
}}
|
|
1018
|
-
pagination={{
|
|
1019
|
-
current: pagination.current,
|
|
1020
|
-
pageSize: pagination.pageSize,
|
|
1021
|
-
total: pagination.total,
|
|
1022
|
-
showSizeChanger: true,
|
|
1023
|
-
showQuickJumper: true,
|
|
1024
|
-
onChange: (current, pageSize) => {
|
|
1025
|
-
setPagination((prev) => ({ ...prev, current, pageSize }))
|
|
1026
|
-
},
|
|
1027
|
-
}}
|
|
1028
|
-
/>
|
|
1029
|
-
)
|
|
1030
|
-
}
|
|
1031
|
-
```
|
|
1032
|
-
|
|
1033
|
-
#### 服务端分页(不传 `request`,外部自行请求)
|
|
1034
|
-
|
|
1035
|
-
如果你**不使用** `request`,但希望实现**服务端分页**(也就是 `dataSource` 只提供“当前页 items”),请设置
|
|
1036
|
-
`pagination.mode: 'server'`,避免 TanStack Table 再次 slice 导致第 2 页为空 / 数据错乱。
|
|
1037
|
-
|
|
1038
|
-
```tsx
|
|
1039
|
-
function Page() {
|
|
1040
|
-
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 })
|
|
1041
|
-
const [data, setData] = useState<any[]>([])
|
|
1042
|
-
|
|
1043
|
-
const fetchPage = async (current: number, pageSize: number) => {
|
|
1044
|
-
const res = await fetchUsers({ current, pageSize })
|
|
1045
|
-
setData(res.items)
|
|
1046
|
-
setPagination((prev) => ({ ...prev, current, pageSize, total: res.total }))
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
useEffect(() => {
|
|
1050
|
-
void fetchPage(pagination.current, pagination.pageSize)
|
|
1051
|
-
}, [])
|
|
1052
|
-
|
|
1053
|
-
return (
|
|
1054
|
-
<ProTable
|
|
1055
|
-
columns={columns}
|
|
1056
|
-
rowKey="id"
|
|
1057
|
-
dataSource={data}
|
|
1058
|
-
pagination={{
|
|
1059
|
-
mode: "server",
|
|
1060
|
-
current: pagination.current,
|
|
1061
|
-
pageSize: pagination.pageSize,
|
|
1062
|
-
total: pagination.total,
|
|
1063
|
-
onChange: (current, pageSize) => void fetchPage(current, pageSize),
|
|
1064
|
-
}}
|
|
1065
|
-
/>
|
|
1066
|
-
)
|
|
1067
|
-
}
|
|
1068
|
-
```
|
|
1069
|
-
|
|
1070
|
-
#### 自定义分页渲染(`paginationRender`)
|
|
1071
|
-
|
|
1072
|
-
当你想把分页 UI 放到任意布局 / 卡片里,并且仍复用 ProTable 内置分页状态 / 逻辑时,使用 `paginationRender`:
|
|
1073
|
-
|
|
1074
|
-
- `paginationRender` 会拿到 `pagination`、`onChange`、`defaultDom`
|
|
1075
|
-
- 如果你是 **服务端分页模式**(`pagination.mode: 'server'`),务必同时提供 `pagination.onChange` 去请求数据
|
|
1076
|
-
|
|
1077
|
-
```tsx
|
|
1078
|
-
<ProTable
|
|
1079
|
-
columns={columns}
|
|
1080
|
-
rowKey="id"
|
|
1081
|
-
dataSource={data}
|
|
1082
|
-
pagination={{
|
|
1083
|
-
mode: "server",
|
|
1084
|
-
current,
|
|
1085
|
-
pageSize,
|
|
1086
|
-
total,
|
|
1087
|
-
onChange: (page, pageSize) => void fetchPage(page, pageSize),
|
|
1088
|
-
}}
|
|
1089
|
-
paginationRender={({ pagination, onChange, defaultDom }) => (
|
|
1090
|
-
<div className="space-y-2">
|
|
1091
|
-
<div className="flex items-center justify-between rounded border p-2">
|
|
1092
|
-
<div className="text-sm text-muted-foreground">
|
|
1093
|
-
自定义分页:第 {pagination.current} 页 / 每页 {pagination.pageSize} 条 / 共 {pagination.total} 条
|
|
1094
|
-
</div>
|
|
1095
|
-
<div className="flex gap-2">
|
|
1096
|
-
<button onClick={() => onChange(Math.max(1, pagination.current - 1), pagination.pageSize)}>
|
|
1097
|
-
上一页
|
|
1098
|
-
</button>
|
|
1099
|
-
<button onClick={() => onChange(pagination.current + 1, pagination.pageSize)}>
|
|
1100
|
-
下一页
|
|
1101
|
-
</button>
|
|
1102
|
-
</div>
|
|
1103
|
-
</div>
|
|
1104
|
-
{defaultDom}
|
|
1105
|
-
</div>
|
|
1106
|
-
)}
|
|
1107
|
-
/>
|
|
1108
|
-
```
|
|
1109
|
-
|
|
1110
|
-
#### 带国际化
|
|
1111
|
-
|
|
1112
|
-
```tsx
|
|
1113
|
-
import { ProTable, zh_CN } from '@gulibs/react-vtable'
|
|
1114
|
-
|
|
1115
|
-
<ProTable
|
|
1116
|
-
columns={columns}
|
|
1117
|
-
dataSource={dataSource}
|
|
1118
|
-
rowKey="id"
|
|
1119
|
-
locale={zh_CN}
|
|
1120
|
-
/>
|
|
1121
|
-
```
|
|
1122
|
-
|
|
1123
|
-
#### 带固定列
|
|
1124
|
-
|
|
1125
|
-
固定列功能基于 TanStack Table 的 column pinning 实现。库会自动计算位置并应用柔和的阴影效果来标识固定列边界。
|
|
1126
|
-
|
|
1127
|
-
```tsx
|
|
1128
|
-
<ProTable
|
|
1129
|
-
columns={[
|
|
1130
|
-
{
|
|
1131
|
-
title: 'ID',
|
|
1132
|
-
dataIndex: 'id',
|
|
1133
|
-
key: 'id',
|
|
1134
|
-
width: 80,
|
|
1135
|
-
fixed: 'left' // 固定在左侧
|
|
1136
|
-
},
|
|
1137
|
-
{
|
|
1138
|
-
title: '姓名',
|
|
1139
|
-
dataIndex: 'name',
|
|
1140
|
-
key: 'name',
|
|
1141
|
-
width: 120
|
|
1142
|
-
},
|
|
1143
|
-
{
|
|
1144
|
-
title: '操作',
|
|
1145
|
-
dataIndex: 'actions',
|
|
1146
|
-
key: 'actions',
|
|
1147
|
-
width: 100,
|
|
1148
|
-
fixed: 'right' // 固定在右侧
|
|
1149
|
-
}
|
|
1150
|
-
]}
|
|
1151
|
-
dataSource={dataSource}
|
|
1152
|
-
rowKey="id"
|
|
1153
|
-
scroll={{ x: 1000 }} // 启用横向滚动
|
|
1154
|
-
tableLayout="fixed" // 固定列推荐使用 fixed 布局
|
|
1155
|
-
/>
|
|
1156
|
-
```
|
|
1157
|
-
|
|
1158
|
-
**重要提示:**
|
|
1159
|
-
- 固定列需要设置 `width` 属性才能正确计算位置
|
|
1160
|
-
- 库会自动使用 TanStack Table 的 `getStart()` 和 `getAfter()` 方法计算位置
|
|
1161
|
-
- 最后一个左侧固定列和第一个右侧固定列会自动应用柔和的阴影效果
|
|
1162
|
-
- 无需额外 CSS - 所有样式都通过内联样式应用
|
|
1163
|
-
|
|
1164
|
-
#### 带树形表格(可展开行)
|
|
1165
|
-
|
|
1166
|
-
树形表格支持层次数据结构,可展开 / 收起行。数据结构应包含 `children` 属性(或自定义字段名)来存储嵌套行。
|
|
1167
|
-
|
|
1168
|
-
```tsx
|
|
1169
|
-
<ProTable
|
|
1170
|
-
columns={columns}
|
|
1171
|
-
dataSource={[
|
|
1172
|
-
{
|
|
1173
|
-
id: 1,
|
|
1174
|
-
name: '父节点',
|
|
1175
|
-
children: [
|
|
1176
|
-
{ id: 11, name: '子节点 1' },
|
|
1177
|
-
{ id: 12, name: '子节点 2' }
|
|
1178
|
-
]
|
|
1179
|
-
}
|
|
1180
|
-
]}
|
|
1181
|
-
rowKey="id"
|
|
1182
|
-
expandable={{
|
|
1183
|
-
childrenColumnName: 'children', // 默认: 'children'
|
|
1184
|
-
defaultExpandAllRows: false, // 默认展开所有行
|
|
1185
|
-
defaultExpandedRowKeys: [1], // 初始展开的行
|
|
1186
|
-
accordion: false, // 手风琴模式(同时只能展开一行)
|
|
1187
|
-
showExpandColumn: true, // 显示展开图标列
|
|
1188
|
-
expandColumnWidth: 50, // 展开列宽度
|
|
1189
|
-
onExpand: (expanded, record) => {
|
|
1190
|
-
console.log('展开:', expanded, record)
|
|
1191
|
-
},
|
|
1192
|
-
onExpandedRowsChange: (expandedRowKeys) => {
|
|
1193
|
-
console.log('展开的行:', expandedRowKeys)
|
|
1194
|
-
},
|
|
1195
|
-
expandIcon: ({ expanded, onExpand, record }) => (
|
|
1196
|
-
<Button onClick={onExpand}>
|
|
1197
|
-
{expanded ? '▼' : '▶'}
|
|
1198
|
-
</Button>
|
|
1199
|
-
)
|
|
1200
|
-
}}
|
|
1201
|
-
/>
|
|
1202
|
-
```
|
|
1203
|
-
|
|
1204
|
-
**树形表格特性:**
|
|
1205
|
-
- **受控 / 非受控模式**: 使用 `expandedRowKeys` 实现受控模式,或使用 `defaultExpandedRowKeys` 实现非受控模式
|
|
1206
|
-
- **默认全部展开**: 设置 `defaultExpandAllRows: true` 来默认展开所有行
|
|
1207
|
-
- **手风琴模式**: 设置 `accordion: true` 来同时只允许展开一行
|
|
1208
|
-
- **自定义子字段**: 使用 `childrenColumnName` 指定自定义的子字段名(默认:`'children'`)
|
|
1209
|
-
- **自定义展开图标**: 提供自定义 `expandIcon` 函数来定制展开 / 收起图标
|
|
1210
|
-
- **展开回调**: 使用 `onExpand` 和 `onExpandedRowsChange` 来处理展开状态变化
|
|
1211
|
-
|
|
1212
|
-
#### 带可编辑行
|
|
1213
|
-
|
|
1214
|
-
```tsx
|
|
1215
|
-
<ProTable
|
|
1216
|
-
columns={columns}
|
|
1217
|
-
dataSource={dataSource}
|
|
1218
|
-
rowKey="id"
|
|
1219
|
-
editable={{
|
|
1220
|
-
type: 'multiple',
|
|
1221
|
-
onSave: async (key, record, originRow) => {
|
|
1222
|
-
console.log('保存:', { key, record, originRow })
|
|
1223
|
-
// 处理保存逻辑
|
|
1224
|
-
}
|
|
1225
|
-
}}
|
|
1226
|
-
/>
|
|
1227
|
-
```
|
|
1228
|
-
|
|
1229
|
-
#### 带行选择和批量操作
|
|
1230
|
-
|
|
1231
|
-
```tsx
|
|
1232
|
-
<ProTable
|
|
1233
|
-
columns={columns}
|
|
1234
|
-
dataSource={dataSource}
|
|
1235
|
-
rowKey="id"
|
|
1236
|
-
rowSelection={{
|
|
1237
|
-
type: 'checkbox',
|
|
1238
|
-
onChange: (selectedRowKeys, selectedRows) => {
|
|
1239
|
-
console.log('已选择:', selectedRowKeys, selectedRows)
|
|
1240
|
-
},
|
|
1241
|
-
batchActions: {
|
|
1242
|
-
onDelete: (rows) => {
|
|
1243
|
-
console.log('删除:', rows)
|
|
1244
|
-
},
|
|
1245
|
-
onExport: (rows) => {
|
|
1246
|
-
console.log('导出:', rows)
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
}}
|
|
1250
|
-
/>
|
|
1251
|
-
```
|
|
1252
|
-
|
|
1253
|
-
**跨页选中支持:**
|
|
1254
|
-
|
|
1255
|
-
当使用 `request` 或 `pagination.onChange` 进行远程数据获取时,表格会自动维护跨页选中状态。这意味着:
|
|
1256
|
-
|
|
1257
|
-
- 在某一页选择的行,在切换到其他页面时会保留
|
|
1258
|
-
- 批量操作栏会显示**所有页面**的选中总数
|
|
1259
|
-
- 当返回到之前访问过的页面时,之前的选中状态会自动恢复
|
|
1260
|
-
- `onChange` 回调会接收到所有页面的选中行 keys
|
|
1261
|
-
|
|
1262
|
-
```tsx
|
|
1263
|
-
<ProTable
|
|
1264
|
-
columns={columns}
|
|
1265
|
-
rowKey="id"
|
|
1266
|
-
request={async (params) => {
|
|
1267
|
-
const res = await fetchUsers(params)
|
|
1268
|
-
return { data: res.items, total: res.total, success: true }
|
|
1269
|
-
}}
|
|
1270
|
-
rowSelection={{
|
|
1271
|
-
type: 'checkbox',
|
|
1272
|
-
onChange: (selectedRowKeys, selectedRows) => {
|
|
1273
|
-
// selectedRowKeys 包含所有页面的选中 keys
|
|
1274
|
-
// selectedRows 仅包含当前页的行数据
|
|
1275
|
-
console.log('所有页面选中的总数:', selectedRowKeys.length)
|
|
1276
|
-
},
|
|
1277
|
-
batchActions: {
|
|
1278
|
-
onDelete: (rows) => {
|
|
1279
|
-
// 这里只会收到当前页的行数据
|
|
1280
|
-
// 如需操作所有选中的行,请使用 onChange 中的 selectedRowKeys
|
|
1281
|
-
console.log('删除当前页行:', rows)
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
}}
|
|
1285
|
-
pagination={{
|
|
1286
|
-
pageSize: 10,
|
|
1287
|
-
showSizeChanger: true,
|
|
1288
|
-
onChange: (current, pageSize) => {
|
|
1289
|
-
// 切换页面时,选中状态会自动保留
|
|
1290
|
-
}
|
|
1291
|
-
}}
|
|
1292
|
-
/>
|
|
1293
|
-
```
|
|
1294
|
-
|
|
1295
|
-
**重要提示:**
|
|
1296
|
-
- 跨页选中在使用 `request` 或 `pagination.onChange` 时会自动生效
|
|
1297
|
-
- 使用 `dataSource`(本地数据)时,选中功能正常工作,但不需要跨页支持
|
|
1298
|
-
- 批量操作栏始终显示所有页面的选中总数
|
|
1299
|
-
- 如需对所有选中的行进行操作,请使用 `rowSelection.onChange` 获取所有选中的 keys
|
|
1300
|
-
|
|
1301
|
-
**自动清空选中状态:**
|
|
1302
|
-
|
|
1303
|
-
某些批量操作后,选中状态会自动清空:
|
|
1304
|
-
|
|
1305
|
-
- **删除操作**:删除后自动清空选中状态(因为数据已被删除)
|
|
1306
|
-
- **归档操作**:归档后自动清空选中状态
|
|
1307
|
-
- **自定义操作**:可以通过 `clearSelection` 选项控制是否清空选中状态
|
|
1308
|
-
- **隐藏清空按钮**:可以通过 `hideClearButton` 选项隐藏清空按钮
|
|
1309
|
-
|
|
1310
|
-
```tsx
|
|
1311
|
-
<ProTable
|
|
1312
|
-
rowSelection={{
|
|
1313
|
-
type: 'checkbox',
|
|
1314
|
-
batchActions: {
|
|
1315
|
-
onDelete: (rows) => {
|
|
1316
|
-
// 删除操作 - 选中状态会自动清空
|
|
1317
|
-
console.log('删除:', rows)
|
|
1318
|
-
},
|
|
1319
|
-
onArchive: (rows) => {
|
|
1320
|
-
// 归档操作 - 选中状态会自动清空
|
|
1321
|
-
console.log('归档:', rows)
|
|
1322
|
-
},
|
|
1323
|
-
// 如需隐藏清空按钮
|
|
1324
|
-
hideClearButton: false,
|
|
1325
|
-
customActions: [
|
|
1326
|
-
{
|
|
1327
|
-
key: 'approve',
|
|
1328
|
-
label: '批量审批',
|
|
1329
|
-
onClick: (rows) => {
|
|
1330
|
-
console.log('审批:', rows)
|
|
1331
|
-
},
|
|
1332
|
-
// 控制操作后是否清空选中状态
|
|
1333
|
-
clearSelection: true, // 设置为 true 表示操作后清空选中状态
|
|
1334
|
-
},
|
|
1335
|
-
{
|
|
1336
|
-
key: 'export',
|
|
1337
|
-
label: '导出',
|
|
1338
|
-
onClick: (rows) => {
|
|
1339
|
-
console.log('导出:', rows)
|
|
1340
|
-
},
|
|
1341
|
-
clearSelection: false, // 导出后保留选中状态(默认值)
|
|
1342
|
-
}
|
|
1343
|
-
]
|
|
1344
|
-
}
|
|
1345
|
-
}}
|
|
1346
|
-
/>
|
|
1347
|
-
```
|
|
1348
|
-
|
|
1349
|
-
#### 带文本省略和提示框
|
|
1350
|
-
|
|
1351
|
-
```tsx
|
|
1352
|
-
<ProTable
|
|
1353
|
-
columns={[
|
|
1354
|
-
{
|
|
1355
|
-
title: '描述',
|
|
1356
|
-
dataIndex: 'description',
|
|
1357
|
-
key: 'description',
|
|
1358
|
-
width: 200,
|
|
1359
|
-
// 单行省略,带提示框
|
|
1360
|
-
ellipsis: true,
|
|
1361
|
-
tooltip: true
|
|
1362
|
-
},
|
|
1363
|
-
{
|
|
1364
|
-
title: '内容',
|
|
1365
|
-
dataIndex: 'content',
|
|
1366
|
-
key: 'content',
|
|
1367
|
-
width: 250,
|
|
1368
|
-
// 多行省略(2行),带提示框
|
|
1369
|
-
ellipsis: {
|
|
1370
|
-
multiline: true,
|
|
1371
|
-
rows: 2
|
|
1372
|
-
},
|
|
1373
|
-
tooltip: true
|
|
1374
|
-
},
|
|
1375
|
-
{
|
|
1376
|
-
title: '自定义提示',
|
|
1377
|
-
dataIndex: 'name',
|
|
1378
|
-
key: 'name',
|
|
1379
|
-
width: 150,
|
|
1380
|
-
ellipsis: true,
|
|
1381
|
-
tooltip: {
|
|
1382
|
-
content: (value, record) => `完整内容: ${record.fullName || value}`,
|
|
1383
|
-
side: 'top',
|
|
1384
|
-
delayDuration: 300
|
|
1385
|
-
}
|
|
1386
|
-
},
|
|
1387
|
-
{
|
|
1388
|
-
title: '表头省略',
|
|
1389
|
-
dataIndex: 'email',
|
|
1390
|
-
key: 'email',
|
|
1391
|
-
width: 180,
|
|
1392
|
-
// 表头独立的省略和提示配置
|
|
1393
|
-
headerEllipsis: true,
|
|
1394
|
-
headerTooltip: {
|
|
1395
|
-
content: '这是完整的表头标题',
|
|
1396
|
-
side: 'bottom'
|
|
1397
|
-
},
|
|
1398
|
-
// 单元格省略和提示
|
|
1399
|
-
ellipsis: true,
|
|
1400
|
-
tooltip: true
|
|
1401
|
-
}
|
|
1402
|
-
]}
|
|
1403
|
-
dataSource={dataSource}
|
|
1404
|
-
rowKey="id"
|
|
1405
|
-
/>
|
|
1406
|
-
```
|
|
1407
|
-
|
|
1408
|
-
### API 参考
|
|
1409
|
-
|
|
1410
|
-
#### ProTable 属性
|
|
1411
|
-
|
|
1412
|
-
| 属性 | 类型 | 默认值 | 描述 |
|
|
1413
|
-
|------|------|--------|------|
|
|
1414
|
-
| `columns` | `ProColumn[]` | `[]` | 表格列定义 |
|
|
1415
|
-
| `dataSource` | `T[]` | `[]` | 表格数据源 |
|
|
1416
|
-
| `rowKey` | `string \| (record: T) => string` | `'id'` | 每行的唯一键 |
|
|
1417
|
-
| `loading` | `boolean` | `false` | 加载状态 |
|
|
1418
|
-
| `pagination` | `ProTablePagination \| false` | `false` | 分页配置 |
|
|
1419
|
-
| `search` | `ProTableSearch \| false` | `false` | 搜索表单配置 |
|
|
1420
|
-
| `rowSelection` | `ProTableRowSelection` | `undefined` | 行选择配置 |
|
|
1421
|
-
| `editable` | `ProTableEditable` | `undefined` | 可编辑行配置 |
|
|
1422
|
-
| `draggable` | `ProTableDraggable` | `undefined` | 拖拽配置 |
|
|
1423
|
-
| `expandable` | `ProTableExpandable \| false` | `undefined` | 树形表格展开配置 |
|
|
1424
|
-
| `scroll` | `{ x?: number \| true; y?: number \| string }` | `undefined` | 滚动配置 |
|
|
1425
|
-
| `tableLayout` | `'auto' \| 'fixed'` | `'auto'` | 表格布局模式 |
|
|
1426
|
-
| `locale` | `ProTableLocale` | `en_US` | 国际化语言包 |
|
|
1427
|
-
| `size` | `'small' \| 'middle' \| 'large'` | `'middle'` | 表格尺寸 |
|
|
1428
|
-
| `bordered` | `boolean` | `false` | 显示表格边框 |
|
|
1429
|
-
|
|
1430
|
-
#### 列配置
|
|
1431
|
-
|
|
1432
|
-
| 属性 | 类型 | 描述 |
|
|
1433
|
-
|------|------|------|
|
|
1434
|
-
| `title` | `string` | 列头文本 |
|
|
1435
|
-
| `dataIndex` | `string` | 数据字段名 |
|
|
1436
|
-
| `key` | `string` | 唯一列键 |
|
|
1437
|
-
| `width` | `number` | 列宽度(像素) |
|
|
1438
|
-
| `fixed` | `'left' \| 'right'` | 固定列位置 |
|
|
1439
|
-
| `sorter` | `boolean` | 启用排序 |
|
|
1440
|
-
| `editable` | `boolean` | 启用内联编辑 |
|
|
1441
|
-
| `copyable` | `boolean` | 启用复制到剪贴板 |
|
|
1442
|
-
| `ellipsis` | `boolean \| ProColumnEllipsis` | 启用文本省略(单行或多行) |
|
|
1443
|
-
| `tooltip` | `boolean \| ProColumnTooltip` | 启用悬停提示框 |
|
|
1444
|
-
| `headerEllipsis` | `boolean \| ProColumnEllipsis` | 启用表头省略(默认使用 `ellipsis`) |
|
|
1445
|
-
| `headerTooltip` | `boolean \| ProColumnTooltip` | 启用表头提示框(默认使用 `tooltip`) |
|
|
1446
|
-
| `search` | `boolean` | 包含在搜索表单中 |
|
|
1447
|
-
| `valueType` | `ProFieldValueType` | 值类型渲染 |
|
|
1448
|
-
| `valueEnum` | `Record<string, { text: string; status?: string }>` | 枚举值选择 |
|
|
1449
|
-
| `filters` | `Array<{ text: string; value: any }>` | 筛选选项 |
|
|
1450
|
-
| `render` | `(value: any, record: T, index: number) => ReactNode` | 自定义渲染函数 |
|
|
1451
|
-
|
|
1452
|
-
#### render 函数与 valueType 优先级
|
|
1453
|
-
|
|
1454
|
-
当同时配置 `render` 和 `valueType` 时,`render` 函数在表格单元格渲染中具有**优先权**。这允许您:
|
|
1455
|
-
|
|
1456
|
-
- 使用 `valueType` 进行**搜索表单**渲染(例如,`valueType: "select"` 会在搜索表单中生成下拉选择框)
|
|
1457
|
-
- 使用自定义 `render` 函数进行**表格单元格**渲染(例如,自定义徽章、图标或复杂布局)
|
|
1458
|
-
|
|
1459
|
-
**渲染优先级:**
|
|
1460
|
-
1. **`render` 函数**(如果提供)- 用于表格单元格渲染
|
|
1461
|
-
2. **`valueType`**(如果提供)- 当没有 `render` 时用于表格单元格渲染
|
|
1462
|
-
3. **默认渲染** - 原始值显示
|
|
1463
|
-
|
|
1464
|
-
**示例:**
|
|
1465
|
-
|
|
1466
|
-
```tsx
|
|
1467
|
-
const columns = [
|
|
1468
|
-
{
|
|
1469
|
-
title: "等级",
|
|
1470
|
-
dataIndex: "level",
|
|
1471
|
-
key: "level",
|
|
1472
|
-
valueType: "select", // 用于搜索表单
|
|
1473
|
-
search: true,
|
|
1474
|
-
valueEnum: {
|
|
1475
|
-
"1": { text: "一级", status: "default" },
|
|
1476
|
-
"2": { text: "二级", status: "default" },
|
|
1477
|
-
"3": { text: "三级", status: "default" },
|
|
1478
|
-
},
|
|
1479
|
-
// 自定义渲染函数在表格单元格渲染中具有优先权
|
|
1480
|
-
render: (_value, record) => {
|
|
1481
|
-
const levelNum = parseInt(record.level);
|
|
1482
|
-
const colors = [
|
|
1483
|
-
"bg-blue-100 text-blue-800",
|
|
1484
|
-
"bg-green-100 text-green-800",
|
|
1485
|
-
"bg-yellow-100 text-yellow-800",
|
|
1486
|
-
];
|
|
1487
|
-
return (
|
|
1488
|
-
<Badge className={colors[levelNum - 1]}>
|
|
1489
|
-
L{record.level}
|
|
1490
|
-
</Badge>
|
|
1491
|
-
);
|
|
1492
|
-
},
|
|
1493
|
-
},
|
|
1494
|
-
];
|
|
1495
|
-
```
|
|
1496
|
-
|
|
1497
|
-
在这个示例中:
|
|
1498
|
-
- **搜索表单**:显示下拉选择框(因为 `valueType: "select"`)
|
|
1499
|
-
- **表格单元格**:显示自定义 Badge 组件(因为 `render` 函数)
|
|
1500
|
-
|
|
1501
|
-
**注意**:`select` 类型的 `valueType` 在没有 `render` 函数时也支持使用 `valueEnum` 进行渲染。
|
|
1502
|
-
|
|
1503
|
-
#### 省略号配置
|
|
1504
|
-
|
|
1505
|
-
`ellipsis` 和 `headerEllipsis` 属性支持以下配置:
|
|
1506
|
-
|
|
1507
|
-
```tsx
|
|
1508
|
-
// 单行省略
|
|
1509
|
-
ellipsis: true
|
|
1510
|
-
|
|
1511
|
-
// 多行省略
|
|
1512
|
-
ellipsis: {
|
|
1513
|
-
multiline: true,
|
|
1514
|
-
rows: 2 // 行数(1-5)
|
|
1515
|
-
}
|
|
1516
|
-
```
|
|
1517
|
-
|
|
1518
|
-
#### 提示框配置
|
|
1519
|
-
|
|
1520
|
-
`tooltip` 和 `headerTooltip` 属性支持以下配置:
|
|
1521
|
-
|
|
1522
|
-
```tsx
|
|
1523
|
-
// 简单提示框(显示单元格值)
|
|
1524
|
-
tooltip: true
|
|
1525
|
-
|
|
1526
|
-
// 自定义提示框
|
|
1527
|
-
tooltip: {
|
|
1528
|
-
content: '自定义提示文本',
|
|
1529
|
-
side: 'top' | 'right' | 'bottom' | 'left',
|
|
1530
|
-
align: 'start' | 'center' | 'end',
|
|
1531
|
-
delayDuration: 300,
|
|
1532
|
-
disabled: false
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
// 函数式提示框
|
|
1536
|
-
tooltip: {
|
|
1537
|
-
content: (value, record, index) => `完整内容: ${value}\nID: ${record.id}`,
|
|
1538
|
-
side: 'top'
|
|
1539
|
-
}
|
|
1540
|
-
```
|
|
1541
|
-
|
|
1542
|
-
**注意**: 当 `ellipsis` 启用且 `tooltip` 为 `true` 时,如果文本被截断,提示框会自动显示完整的原始值(而不是渲染后的值)。
|
|
1543
|
-
|
|
1544
|
-
#### 展开配置(树形表格)
|
|
1545
|
-
|
|
1546
|
-
`expandable` 属性支持以下配置:
|
|
1547
|
-
|
|
1548
|
-
```tsx
|
|
1549
|
-
expandable: {
|
|
1550
|
-
// 自定义子字段名(默认: 'children')
|
|
1551
|
-
childrenColumnName?: string
|
|
1552
|
-
|
|
1553
|
-
// 默认展开的行 key 数组(非受控模式)
|
|
1554
|
-
defaultExpandedRowKeys?: React.Key[]
|
|
1555
|
-
|
|
1556
|
-
// 当前展开的行 key 数组(受控模式)
|
|
1557
|
-
expandedRowKeys?: React.Key[]
|
|
1558
|
-
|
|
1559
|
-
// 展开/收起回调
|
|
1560
|
-
onExpand?: (expanded: boolean, record: T) => void
|
|
1561
|
-
|
|
1562
|
-
// 展开的行 key 变化回调
|
|
1563
|
-
onExpandedRowsChange?: (expandedRowKeys: React.Key[]) => void
|
|
1564
|
-
|
|
1565
|
-
// 自定义展开图标
|
|
1566
|
-
expandIcon?: (props: {
|
|
1567
|
-
expanded: boolean
|
|
1568
|
-
onExpand: () => void
|
|
1569
|
-
record: T
|
|
1570
|
-
}) => ReactNode
|
|
1571
|
-
|
|
1572
|
-
// 显示展开列(默认: true)
|
|
1573
|
-
showExpandColumn?: boolean
|
|
1574
|
-
|
|
1575
|
-
// 展开列宽度(默认: 50)
|
|
1576
|
-
expandColumnWidth?: number
|
|
1577
|
-
|
|
1578
|
-
// 默认展开所有行(默认: false)
|
|
1579
|
-
defaultExpandAllRows?: boolean
|
|
1580
|
-
|
|
1581
|
-
// 手风琴模式 - 同时只能展开一行(默认: false)
|
|
1582
|
-
accordion?: boolean
|
|
1583
|
-
}
|
|
1584
|
-
```
|
|
1585
|
-
|
|
1586
|
-
### 开发
|
|
1587
|
-
|
|
1588
|
-
```bash
|
|
1589
|
-
# 安装依赖
|
|
1590
|
-
pnpm install
|
|
1591
|
-
|
|
1592
|
-
# 构建库
|
|
1593
|
-
pnpm run build
|
|
1594
|
-
|
|
1595
|
-
# 启动开发服务器
|
|
1596
|
-
pnpm run dev
|
|
1597
|
-
```
|
|
1296
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
1598
1297
|
|
|
1599
|
-
|
|
1298
|
+
## Support
|
|
1600
1299
|
|
|
1601
|
-
|
|
1300
|
+
If you have any questions or need help, please open an issue on [GitHub](https://github.com/gulibs/react-vtable).
|