Skip to content

Commit ce36230

Browse files
authored
perf: page ui (#5469)
* perf: page ui * fix: icon * limit chat items * limit chat items
1 parent 76dc23c commit ce36230

File tree

36 files changed

+618
-321
lines changed

36 files changed

+618
-321
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# 背景
2+
3+
一个通用表格多选 hook/component, 它可以实现表格每一行数据的选择,并且在触发一次选择后,会有特殊的按键进行批量操作。
4+
5+
# 具体描述
6+
7+
当有一行被选中时,底部会出现悬浮层,可以进行批量操作(具体有哪些批量操作由外部决定)
8+
9+
![alt text](./image-1.png)
10+
11+
# 预期封装
12+
13+
1. 选中的值存储在 hook 里,便于判断是否触发底部悬浮层
14+
2. 悬浮层外层 Box 在 hook 里,child 由调用组件实现
15+
3. FastGPT/packages/web/hooks/useTableMultipleSelect.tsx 在这个文件下实现

document/content/docs/upgrading/4-12/4121.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ title: 'V4.12.1(进行中)'
33
description: 'FastGPT V4.12.1 更新说明'
44
---
55

6-
76
## 🚀 新增内容
87

98
1. Prompt 自动生成和优化。
@@ -14,6 +13,7 @@ description: 'FastGPT V4.12.1 更新说明'
1413
1. 工作流响应优化,主动指定响应值进入历史记录,而不是根据 key 决定。
1514
2. 避免工作流中,变量替换导致的死循环或深度递归风险。
1615
3. 对话日志导出,固定导出对话详情。
16+
4. 分页器 UI 优化。
1717

1818
## 🐛 修复
1919

document/data/doc-last-modified.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
"document/content/docs/upgrading/4-11/4110.mdx": "2025-08-05T23:20:39+08:00",
104104
"document/content/docs/upgrading/4-11/4111.mdx": "2025-08-07T22:49:09+08:00",
105105
"document/content/docs/upgrading/4-12/4120.mdx": "2025-08-12T22:45:19+08:00",
106-
"document/content/docs/upgrading/4-12/4121.mdx": "2025-08-15T14:27:32+08:00",
106+
"document/content/docs/upgrading/4-12/4121.mdx": "2025-08-15T17:10:04+08:00",
107107
"document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00",
108108
"document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00",
109109
"document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00",

packages/web/components/common/Icon/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export const iconPaths = {
5252
'common/errorFill': () => import('./icons/common/errorFill.svg'),
5353
'common/file/move': () => import('./icons/common/file/move.svg'),
5454
'common/fileNotFound': () => import('./icons/common/fileNotFound.svg'),
55+
'common/first_page': () => import('./icons/common/first_page.svg'),
5556
'common/folderFill': () => import('./icons/common/folderFill.svg'),
5657
'common/folderImport': () => import('./icons/common/folderImport.svg'),
5758
'common/fullScreenLight': () => import('./icons/common/fullScreenLight.svg'),
@@ -67,6 +68,7 @@ export const iconPaths = {
6768
'common/language/China': () => import('./icons/common/language/China.svg'),
6869
'common/language/en': () => import('./icons/common/language/en.svg'),
6970
'common/language/zh': () => import('./icons/common/language/zh.svg'),
71+
'common/latest_page': () => import('./icons/common/latest_page.svg'),
7072
'common/layer': () => import('./icons/common/layer.svg'),
7173
'common/leftArrowLight': () => import('./icons/common/leftArrowLight.svg'),
7274
'common/line': () => import('./icons/common/line.svg'),
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 2 additions & 3 deletions
Loading
Lines changed: 2 additions & 5 deletions
Loading

packages/web/hooks/usePagination.tsx

Lines changed: 129 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,39 @@
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';
414
import { useTranslation } from 'next-i18next';
515
import { useToast } from './useToast';
616
import { getErrText } from '@fastgpt/global/common/error/utils';
717
import {
818
useBoolean,
9-
useLockFn,
19+
useCreation,
1020
useMemoizedFn,
1121
useRequest,
1222
useScroll,
1323
useThrottleEffect
1424
} from 'ahooks';
1525

1626
import { type PaginationProps, type PaginationResponse } from '../common/fetch/type';
27+
import MyMenu from '../components/common/MyMenu';
28+
import { useSystem } from './useSystem';
1729

1830
const thresholdVal = 200;
1931

2032
export function usePagination<DataT, ResT = {}>(
2133
api: (data: PaginationProps<DataT>) => Promise<PaginationResponse<ResT>>,
2234
{
23-
pageSize = 10,
35+
defaultPageSize = 10,
36+
pageSizeOptions: defaultPageSizeOptions,
2437
params,
2538
defaultRequest = true,
2639
type = 'button',
@@ -31,7 +44,8 @@ export function usePagination<DataT, ResT = {}>(
3144
pollingInterval,
3245
pollingWhenHidden = false
3346
}: {
34-
pageSize?: number;
47+
defaultPageSize?: number;
48+
pageSizeOptions?: number[];
3549
params?: DataT;
3650
defaultRequest?: boolean;
3751
type?: 'button' | 'scroll';
@@ -45,11 +59,18 @@ export function usePagination<DataT, ResT = {}>(
4559
}
4660
) {
4761
const { toast } = useToast();
62+
const { isPc } = useSystem();
4863
const { t } = useTranslation();
4964

5065
const [isLoading, { setTrue, setFalse }] = useBoolean(false);
5166

5267
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+
5374
const [total, setTotal] = useState(0);
5475
const [data, setData] = useState<ResT[]>([]);
5576
const totalDataLength = useMemo(() => Math.max(total, data.length), [total, data.length]);
@@ -119,67 +140,110 @@ export function usePagination<DataT, ResT = {}>(
119140
const Pagination = useCallback(() => {
120141
const maxPage = Math.ceil(totalDataLength / pageSize);
121142

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
164166
}
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)}
166209
/>
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+
)}
169218
</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+
)}
180244
</Flex>
181245
);
182-
}, [isLoading, totalDataLength, pageSize, fetchData, pageNum]);
246+
}, [totalDataLength, isPc, pageSize, t, pageNum, pageSizeOptions, isLoading, fetchData]);
183247

184248
// Scroll pagination
185249
const DefaultRef = useRef<HTMLDivElement>(null);
@@ -259,6 +323,9 @@ export function usePagination<DataT, ResT = {}>(
259323
throttleWait: 100
260324
}
261325
);
326+
useEffect(() => {
327+
data.length > 0 && fetchData();
328+
}, [pageSize]);
262329

263330
useRequest(
264331
async () => {

0 commit comments

Comments
 (0)