1
- import { useRef , useState , useCallback , type RefObject , type ReactNode , useMemo } from 'react' ;
2
- import { IconButton , Flex , Box , Input , type BoxProps } from '@chakra-ui/react' ;
3
- import { ArrowBackIcon , ArrowForwardIcon } from '@chakra-ui/icons' ;
1
+ import {
2
+ useRef ,
3
+ useState ,
4
+ useCallback ,
5
+ type RefObject ,
6
+ type ReactNode ,
7
+ useMemo ,
8
+ useEffect
9
+ } from 'react' ;
10
+ import type { FlexProps } from '@chakra-ui/react' ;
11
+ import { Flex , Box , type BoxProps } from '@chakra-ui/react' ;
12
+ import MyIcon from '../components/common/Icon' ;
13
+ import type { IconNameType } from '../components/common/Icon/type' ;
4
14
import { useTranslation } from 'next-i18next' ;
5
15
import { useToast } from './useToast' ;
6
16
import { getErrText } from '@fastgpt/global/common/error/utils' ;
7
17
import {
8
18
useBoolean ,
9
- useLockFn ,
19
+ useCreation ,
10
20
useMemoizedFn ,
11
21
useRequest ,
12
22
useScroll ,
13
23
useThrottleEffect
14
24
} from 'ahooks' ;
15
25
16
26
import { type PaginationProps , type PaginationResponse } from '../common/fetch/type' ;
27
+ import MyMenu from '../components/common/MyMenu' ;
28
+ import { useSystem } from './useSystem' ;
17
29
18
30
const thresholdVal = 200 ;
19
31
20
32
export function usePagination < DataT , ResT = { } > (
21
33
api : ( data : PaginationProps < DataT > ) => Promise < PaginationResponse < ResT > > ,
22
34
{
23
- pageSize = 10 ,
35
+ defaultPageSize = 10 ,
36
+ pageSizeOptions : defaultPageSizeOptions ,
24
37
params,
25
38
defaultRequest = true ,
26
39
type = 'button' ,
@@ -31,7 +44,8 @@ export function usePagination<DataT, ResT = {}>(
31
44
pollingInterval,
32
45
pollingWhenHidden = false
33
46
} : {
34
- pageSize ?: number ;
47
+ defaultPageSize ?: number ;
48
+ pageSizeOptions ?: number [ ] ;
35
49
params ?: DataT ;
36
50
defaultRequest ?: boolean ;
37
51
type ?: 'button' | 'scroll' ;
@@ -45,11 +59,18 @@ export function usePagination<DataT, ResT = {}>(
45
59
}
46
60
) {
47
61
const { toast } = useToast ( ) ;
62
+ const { isPc } = useSystem ( ) ;
48
63
const { t } = useTranslation ( ) ;
49
64
50
65
const [ isLoading , { setTrue, setFalse } ] = useBoolean ( false ) ;
51
66
52
67
const [ pageNum , setPageNum ] = useState ( 1 ) ;
68
+ const [ pageSize , setPageSize ] = useState ( defaultPageSize ) ;
69
+ const pageSizeOptions = useCreation (
70
+ ( ) => defaultPageSizeOptions || [ 10 , 20 , 50 , 100 ] ,
71
+ [ defaultPageSizeOptions ]
72
+ ) ;
73
+
53
74
const [ total , setTotal ] = useState ( 0 ) ;
54
75
const [ data , setData ] = useState < ResT [ ] > ( [ ] ) ;
55
76
const totalDataLength = useMemo ( ( ) => Math . max ( total , data . length ) , [ total , data . length ] ) ;
@@ -119,67 +140,110 @@ export function usePagination<DataT, ResT = {}>(
119
140
const Pagination = useCallback ( ( ) => {
120
141
const maxPage = Math . ceil ( totalDataLength / pageSize ) ;
121
142
122
- return (
123
- < Flex alignItems = { 'center' } justifyContent = { 'end' } >
124
- < IconButton
125
- isDisabled = { pageNum === 1 }
126
- icon = { < ArrowBackIcon /> }
127
- aria-label = { 'left' }
128
- size = { 'smSquare' }
129
- isLoading = { isLoading }
130
- onClick = { ( ) => fetchData ( pageNum - 1 ) }
131
- />
132
- < Flex mx = { 2 } alignItems = { 'center' } >
133
- < Input
134
- defaultValue = { pageNum }
135
- w = { '50px' }
136
- h = { '30px' }
137
- size = { 'xs' }
138
- type = { 'number' }
139
- min = { 1 }
140
- max = { maxPage }
141
- onBlur = { ( e ) => {
142
- const val = + e . target . value ;
143
- if ( val === pageNum ) return ;
144
- if ( val >= maxPage ) {
145
- fetchData ( maxPage ) ;
146
- } else if ( val < 1 ) {
147
- fetchData ( 1 ) ;
148
- } else {
149
- fetchData ( + e . target . value ) ;
150
- }
151
- } }
152
- onKeyDown = { ( e ) => {
153
- // @ts -ignore
154
- const val = + e . target . value ;
155
- if ( val && e . key === 'Enter' ) {
156
- if ( val === pageNum ) return ;
157
- if ( val >= maxPage ) {
158
- fetchData ( maxPage ) ;
159
- } else if ( val < 1 ) {
160
- fetchData ( 1 ) ;
161
- } else {
162
- fetchData ( val ) ;
163
- }
143
+ const IconButton = ( {
144
+ icon,
145
+ isDisabled,
146
+ onClick,
147
+ ...props
148
+ } : {
149
+ icon : IconNameType ;
150
+ isDisabled ?: boolean ;
151
+ onClick : ( ) => void ;
152
+ } & FlexProps ) => {
153
+ isDisabled = isDisabled || isLoading ;
154
+ return (
155
+ < Flex
156
+ alignItems = { 'center' }
157
+ justifyContent = { 'center' }
158
+ borderRadius = { 'full' }
159
+ w = { '24px' }
160
+ h = { '24px' }
161
+ cursor = { 'pointer' }
162
+ bg = { 'myGray.150' }
163
+ { ...( isDisabled
164
+ ? {
165
+ opacity : 0.5
164
166
}
165
- } }
167
+ : {
168
+ onClick
169
+ } ) }
170
+ { ...props }
171
+ >
172
+ < MyIcon name = { icon } w = { '6px' } color = { 'myGray.900' } />
173
+ </ Flex >
174
+ ) ;
175
+ } ;
176
+
177
+ return (
178
+ < Flex alignItems = { 'center' } justifyContent = { 'center' } fontSize = { 'sm' } userSelect = { 'none' } >
179
+ { isPc && < Box color = { 'myGray.500' } > { t ( 'common:total_num' , { num : totalDataLength } ) } </ Box > }
180
+
181
+ < Flex alignItems = { 'center' } ml = { 6 } mr = { 4 } >
182
+ { isPc && (
183
+ < IconButton
184
+ mr = { 2 }
185
+ isDisabled = { pageNum === 1 }
186
+ icon = "common/first_page"
187
+ onClick = { ( ) => fetchData ( 1 ) }
188
+ />
189
+ ) }
190
+ < IconButton
191
+ isDisabled = { pageNum === 1 }
192
+ icon = "common/leftArrowLight"
193
+ onClick = { ( ) => fetchData ( pageNum - 1 ) }
194
+ />
195
+ < Box ml = { 4 } color = { 'myGray.500' } >
196
+ { pageNum }
197
+ </ Box >
198
+ < Box mx = { 1 } color = { 'myGray.500' } >
199
+ /
200
+ </ Box >
201
+ < Box mr = { 4 } color = { 'myGray.900' } >
202
+ { maxPage }
203
+ </ Box >
204
+
205
+ < IconButton
206
+ isDisabled = { pageNum === maxPage }
207
+ icon = "common/rightArrowLight"
208
+ onClick = { ( ) => fetchData ( pageNum + 1 ) }
166
209
/>
167
- < Box mx = { 2 } > /</ Box >
168
- { maxPage }
210
+ { isPc && (
211
+ < IconButton
212
+ ml = { 2 }
213
+ isDisabled = { pageNum === maxPage }
214
+ icon = "common/latest_page"
215
+ onClick = { ( ) => fetchData ( maxPage ) }
216
+ />
217
+ ) }
169
218
</ Flex >
170
- < IconButton
171
- isDisabled = { pageNum === maxPage }
172
- icon = { < ArrowForwardIcon /> }
173
- aria-label = { 'left' }
174
- size = { 'sm' }
175
- isLoading = { isLoading }
176
- w = { '28px' }
177
- h = { '28px' }
178
- onClick = { ( ) => fetchData ( pageNum + 1 ) }
179
- />
219
+
220
+ { isPc && (
221
+ < MyMenu
222
+ menuList = { [
223
+ {
224
+ label : '' ,
225
+ children : pageSizeOptions . map ( ( item ) => ( {
226
+ label : `${ item } ` ,
227
+ isActive : pageSize === item ,
228
+ onClick : ( ) => setPageSize ( item )
229
+ } ) )
230
+ }
231
+ ] }
232
+ Button = {
233
+ < Flex alignItems = { 'center' } cursor = { 'pointer' } >
234
+ < Box color = { 'myGray.900' } > { pageSize } </ Box >
235
+ < Box mx = { 1 } color = { 'myGray.500' } >
236
+ /
237
+ </ Box >
238
+ < Box color = { 'myGray.500' } > { t ( 'common:page' ) } </ Box >
239
+ < MyIcon ml = { 1 } name = { 'core/chat/chevronDown' } w = { '14px' } color = { 'myGray.900' } />
240
+ </ Flex >
241
+ }
242
+ />
243
+ ) }
180
244
</ Flex >
181
245
) ;
182
- } , [ isLoading , totalDataLength , pageSize , fetchData , pageNum ] ) ;
246
+ } , [ totalDataLength , isPc , pageSize , t , pageNum , pageSizeOptions , isLoading , fetchData ] ) ;
183
247
184
248
// Scroll pagination
185
249
const DefaultRef = useRef < HTMLDivElement > ( null ) ;
@@ -259,6 +323,9 @@ export function usePagination<DataT, ResT = {}>(
259
323
throttleWait : 100
260
324
}
261
325
) ;
326
+ useEffect ( ( ) => {
327
+ data . length > 0 && fetchData ( ) ;
328
+ } , [ pageSize ] ) ;
262
329
263
330
useRequest (
264
331
async ( ) => {
0 commit comments