Skip to content

Commit e14d745

Browse files
committed
feat: support configurable default result page size
1 parent 17a5b0d commit e14d745

14 files changed

Lines changed: 179 additions & 73 deletions

File tree

chat2db-community-client/scripts/i18n-source-hashes.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"stream.ts": "8418dd25b0fd54b2ef2b15a2573edf0a4876efb4e69936ff82a157ea5cf42be4",
2828
"team.ts": "eb1fb03bdfeb93169ef1d2e347deb5b95071c10c8c93c35cfe2cb5b6606779ea",
2929
"userGuide.ts": "0103ba35af95a3a0ff7a414130b68a7dd5f5f8196e59cd951631ac2090cb04fe",
30-
"workspace.ts": "67841fb5124b2946caaf5af26a3853947441583e434b72c78854f1272b63180f"
30+
"workspace.ts": "10db8d9edac96c0855016b7919025856c51f8abf1fca4f45b463120ff25be212"
3131
},
3232
"ko-KR": {
3333
"ai.ts": "391a6e14c58d8401bf3bc245eca5bdd4b66efd8225a877bfbbcae01a1c1c019a",
@@ -54,7 +54,7 @@
5454
"stream.ts": "8418dd25b0fd54b2ef2b15a2573edf0a4876efb4e69936ff82a157ea5cf42be4",
5555
"team.ts": "eb1fb03bdfeb93169ef1d2e347deb5b95071c10c8c93c35cfe2cb5b6606779ea",
5656
"userGuide.ts": "0103ba35af95a3a0ff7a414130b68a7dd5f5f8196e59cd951631ac2090cb04fe",
57-
"workspace.ts": "67841fb5124b2946caaf5af26a3853947441583e434b72c78854f1272b63180f"
57+
"workspace.ts": "10db8d9edac96c0855016b7919025856c51f8abf1fca4f45b463120ff25be212"
5858
}
5959
}
6060
}

chat2db-community-client/src/components/Pagination/index.tsx

Lines changed: 108 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
import { useEffect, useState } from 'react';
1+
import { useEffect, useRef, useState } from 'react';
22
import { InputNumber, Tooltip, Dropdown } from 'antd';
33
import { IResultConfig } from '@/typings';
44
import i18n from '@/i18n';
55
import _ from 'lodash';
66
import { IconButton, ToolbarBtn } from '@chat2db/ui';
77
import LoadingGracile from '@/components/Loading/LoadingGracile';
88
import { useStyles } from './style';
9-
import { DownOutlined } from '@ant-design/icons';
9+
import { CheckOutlined, DownOutlined } from '@ant-design/icons';
10+
import { RESULT_PAGE_SIZE_OPTIONS } from '@/constants/pagination';
11+
import { useGlobalStore } from '@/store/global';
12+
import { settingSelectors } from '@/store/global/selectors';
1013

1114
interface IProps {
1215
onPageSizeChange?: (pageSize: number) => void;
@@ -23,6 +26,13 @@ export default function Pagination(props: IProps) {
2326
const { styles } = useStyles({ inputNumberWidth });
2427
const [inputValue, setInputValue] = useState<number | null>(1);
2528
const [totalLoading, setTotalLoading] = useState(false);
29+
const [pageSizeMenuOpen, setPageSizeMenuOpen] = useState(false);
30+
const [customPageSize, setCustomPageSize] = useState<number | null>(null);
31+
const keepPageSizeMenuOpenRef = useRef(false);
32+
const { defaultPageSize, setBaseSetting } = useGlobalStore((state) => ({
33+
defaultPageSize: settingSelectors.currentBaseSetting(state).defaultPageSize,
34+
setBaseSetting: state.setBaseSetting,
35+
}));
2636

2737
useEffect(() => {
2838
setInputValue(paginationConfig?.pageNo ?? 1);
@@ -115,72 +125,102 @@ export default function Pagination(props: IProps) {
115125
return true;
116126
};
117127

118-
const items: any = [
119-
{
120-
label: '10',
121-
value: 10,
122-
onClick: () => {
123-
onPageSizeChange && onPageSizeChange(10);
124-
},
125-
},
126-
{
127-
label: '100',
128-
value: 100,
129-
onClick: () => {
130-
onPageSizeChange && onPageSizeChange(100);
131-
},
132-
},
133-
{
134-
label: '200',
135-
value: 200,
136-
onClick: () => {
137-
onPageSizeChange && onPageSizeChange(200);
138-
},
139-
},
140-
{
141-
label: '500',
142-
value: 500,
143-
onClick: () => {
144-
onPageSizeChange && onPageSizeChange(500);
145-
},
146-
},
147-
{
148-
label: '1000',
149-
value: 1000,
150-
onClick: () => {
151-
onPageSizeChange && onPageSizeChange(1000);
152-
},
153-
},
154-
{
155-
label: '5000',
156-
value: 5000,
157-
onClick: () => {
158-
onPageSizeChange && onPageSizeChange(5000);
159-
},
160-
},
161-
{
162-
label: '10000',
163-
value: 10000,
164-
onClick: () => {
165-
onPageSizeChange && onPageSizeChange(10000);
166-
},
167-
},
128+
const isPresetDefaultPageSize = RESULT_PAGE_SIZE_OPTIONS.some((pageSize) => pageSize === defaultPageSize);
129+
130+
const items = [
131+
...RESULT_PAGE_SIZE_OPTIONS.map((pageSize) => ({
132+
key: String(pageSize),
133+
label: (
134+
<div className={styles.pageSizeOption}>
135+
<span>{pageSize}</span>
136+
{pageSize === defaultPageSize && (
137+
<span className={styles.defaultPageSize}>
138+
<CheckOutlined />
139+
{i18n('workspace.table.defaultPageSize')}
140+
</span>
141+
)}
142+
</div>
143+
),
144+
})),
145+
{ type: 'divider' as const },
168146
{
169-
label: '50000',
170-
value: 50000,
171-
onClick: () => {
172-
onPageSizeChange && onPageSizeChange(50000);
173-
},
147+
key: 'custom',
148+
label: (
149+
<div
150+
className={styles.customPageSize}
151+
onClick={(event) => event.stopPropagation()}
152+
onKeyDown={(event) => event.stopPropagation()}
153+
>
154+
<InputNumber
155+
className={styles.customPageSizeInput}
156+
size="small"
157+
min={1}
158+
max={100000}
159+
precision={0}
160+
controls={false}
161+
autoFocus
162+
value={customPageSize}
163+
placeholder={i18n('workspace.table.customPageSize')}
164+
onChange={setCustomPageSize}
165+
onPressEnter={() => handleApplyCustomPageSize()}
166+
/>
167+
{!isPresetDefaultPageSize && customPageSize === defaultPageSize && (
168+
<span className={styles.defaultPageSize}>
169+
<CheckOutlined />
170+
{i18n('workspace.table.defaultPageSize')}
171+
</span>
172+
)}
173+
</div>
174+
),
174175
},
176+
{ type: 'divider' as const },
175177
{
176-
label: '100000',
177-
value: 100000,
178-
onClick: () => {
179-
onPageSizeChange && onPageSizeChange(100000);
180-
},
178+
key: 'set-default',
179+
label: i18n('workspace.table.setDefaultPageSize'),
180+
disabled: paginationConfig.pageSize === defaultPageSize,
181181
},
182182
];
183183

184+
function handleApplyCustomPageSize() {
185+
if (!customPageSize) {
186+
return;
187+
}
188+
keepPageSizeMenuOpenRef.current = true;
189+
setPageSizeMenuOpen(true);
190+
setBaseSetting({ defaultPageSize: customPageSize });
191+
onPageSizeChange?.(customPageSize);
192+
}
193+
194+
const handlePageSizeMenuClick = ({ key }: { key: string }) => {
195+
if (key === 'set-default') {
196+
setBaseSetting({ defaultPageSize: paginationConfig.pageSize });
197+
setPageSizeMenuOpen(false);
198+
return;
199+
}
200+
keepPageSizeMenuOpenRef.current = true;
201+
setPageSizeMenuOpen(true);
202+
onPageSizeChange?.(Number(key));
203+
};
204+
205+
const handlePageSizeMenuOpenChange = (open: boolean, info: { source: 'trigger' | 'menu' }) => {
206+
if (open) {
207+
const isPresetCurrentPageSize = RESULT_PAGE_SIZE_OPTIONS.some(
208+
(pageSize) => pageSize === paginationConfig.pageSize,
209+
);
210+
setCustomPageSize(
211+
!isPresetDefaultPageSize ? defaultPageSize : !isPresetCurrentPageSize ? paginationConfig.pageSize : null,
212+
);
213+
}
214+
if (!open && info.source === 'menu' && keepPageSizeMenuOpenRef.current) {
215+
keepPageSizeMenuOpenRef.current = false;
216+
return;
217+
}
218+
if (!open) {
219+
keepPageSizeMenuOpenRef.current = false;
220+
}
221+
setPageSizeMenuOpen(open);
222+
};
223+
184224
return (
185225
<div className={styles.paginationWrapper}>
186226
<IconButton
@@ -218,7 +258,12 @@ export default function Pagination(props: IProps) {
218258
onClick={() => handleClickIcon('last')}
219259
/>
220260

221-
<Dropdown destroyPopupOnHide menu={{ items }}>
261+
<Dropdown
262+
destroyPopupOnHide
263+
open={pageSizeMenuOpen}
264+
onOpenChange={handlePageSizeMenuOpenChange}
265+
menu={{ items, selectedKeys: [String(paginationConfig.pageSize)], onClick: handlePageSizeMenuClick }}
266+
>
222267
<div className={styles.selectSize}>
223268
{paginationConfig?.pageSize ?? 200}
224269
<DownOutlined />

chat2db-community-client/src/components/Pagination/style.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,33 @@ export const useStyles = createStyles(({ css, token }, { inputNumberWidth }: { i
7171
color: ${token.colorPrimaryText};
7272
}
7373
`,
74+
pageSizeOption: css`
75+
min-width: 150px;
76+
display: flex;
77+
align-items: center;
78+
justify-content: space-between;
79+
gap: 16px;
80+
`,
81+
defaultPageSize: css`
82+
display: inline-flex;
83+
align-items: center;
84+
gap: 4px;
85+
color: ${token.colorTextSecondary};
86+
font-size: 12px;
87+
flex-shrink: 0;
88+
white-space: nowrap;
89+
`,
90+
customPageSize: css`
91+
min-width: 150px;
92+
display: flex;
93+
align-items: center;
94+
gap: 12px;
95+
`,
96+
customPageSizeInput: css`
97+
width: 90px;
98+
flex: 1;
99+
min-width: 0;
100+
`,
74101
totalButton: css`
75102
flex-shrink: 0;
76103
font-size: 13px;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const DEFAULT_RESULT_PAGE_SIZE = 1000;
2+
3+
export const RESULT_PAGE_SIZE_OPTIONS = [10, 100, 200, 500, 1000, 5000, 10000, 50000, 100000] as const;

chat2db-community-client/src/constants/settings.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { AIType } from '@/typings/ai';
22
import { GlobalAISettings, GlobalBaseSettings, GlobalAppConfig, DataTableSettings } from '@/typings/settings';
33
import { getUserComputerLanguage } from '@/utils';
4+
import { DEFAULT_RESULT_PAGE_SIZE } from './pagination';
45

56
export enum LangType {
67
EN_US = 'en-US',
@@ -34,6 +35,7 @@ export const DEFAULT_BASE_SETTINGS: GlobalBaseSettings = {
3435
language: getUserComputerLanguage(),
3536
customFont: '',
3637
customFontSize: 13,
38+
defaultPageSize: DEFAULT_RESULT_PAGE_SIZE,
3739
enableMcp: false,
3840
};
3941

chat2db-community-client/src/hooks/useSqlExecutor.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
startSqlExecution,
1111
} from '@/service/sqlExecutionStream';
1212
import { v4 as uuidv4 } from 'uuid';
13+
import { useGlobalStore } from '@/store/global';
14+
import { settingSelectors } from '@/store/global/selectors';
1315

1416
interface IUseSqlExecutorProps {
1517
// Whether to return only one piece of data
@@ -19,6 +21,7 @@ interface IUseSqlExecutorProps {
1921

2022
const useSqlExecutor = (props?: IUseSqlExecutorProps) => {
2123
const { onlyOne, onExecutionEvent } = props || {};
24+
const defaultPageSize = useGlobalStore((state) => settingSelectors.currentBaseSetting(state).defaultPageSize);
2225
const [executing, setExecuting] = useState(false);
2326
const [executionId, setExecutionId] = useState<string>();
2427
// interrupt request
@@ -36,6 +39,11 @@ const useSqlExecutor = (props?: IUseSqlExecutorProps) => {
3639

3740
// execute sql
3841
const executeSQL = useCallback((params: IExecuteSqlParams): Promise<IManageResultData[]> => {
42+
const executeSqlParams = {
43+
...params,
44+
pageNo: params.pageNo ?? 1,
45+
pageSize: params.pageSize ?? defaultPageSize,
46+
};
3947
if (isDesktop && onExecutionEvent) {
4048
const requestUuid = uuidv4();
4149
setExecuting(true);
@@ -60,7 +68,7 @@ const useSqlExecutor = (props?: IUseSqlExecutorProps) => {
6068
}
6169
}
6270
});
63-
startSqlExecution(params, requestUuid)
71+
startSqlExecution(executeSqlParams, requestUuid)
6472
.then((res) => {
6573
if (!res?.executionId) {
6674
subscription.unsubscribe?.();
@@ -79,8 +87,6 @@ const useSqlExecutor = (props?: IUseSqlExecutorProps) => {
7987
}
8088
return new Promise((resolve, reject) => {
8189
// Parameters for executing sql
82-
const executeSqlParams = params;
83-
8490
setExecuting(true);
8591

8692
// execute sql
@@ -99,7 +105,7 @@ const useSqlExecutor = (props?: IUseSqlExecutorProps) => {
99105
setExecuting(false);
100106
});
101107
});
102-
}, [onExecutionEvent]);
108+
}, [defaultPageSize, onExecutionEvent]);
103109

104110
// Stop executing sql
105111
const stopExecuteSQL = useCallback(() => {

chat2db-community-client/src/hooks/useViewTable.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import { useCallback, useState } from 'react';
22
import { IManageResultData, IViewTableParams } from '@/typings';
33
import executeSqlServer from '@/service/executeSql';
44
import useAbortRequest from './useAbortRequest';
5-
6-
5+
import { useGlobalStore } from '@/store/global';
6+
import { settingSelectors } from '@/store/global/selectors';
77

88
const useViewTable = () => {
9+
const defaultPageSize = useGlobalStore((state) => settingSelectors.currentBaseSetting(state).defaultPageSize);
910
const [executing, setExecuting] = useState(false);
1011
// interrupt request
1112
const [initSignal, abortRequest] = useAbortRequest();
@@ -15,7 +16,11 @@ const useViewTable = () => {
1516

1617
return new Promise((resolve, reject) => {
1718
// Parameters for executing sql
18-
const viewTableParams = params;
19+
const viewTableParams = {
20+
...params,
21+
pageNo: params.pageNo ?? 1,
22+
pageSize: params.pageSize ?? defaultPageSize,
23+
};
1924

2025
setExecuting(true);
2126

@@ -34,7 +39,7 @@ const useViewTable = () => {
3439
setExecuting(false);
3540
});
3641
});
37-
}, [])
42+
}, [defaultPageSize])
3843

3944
// Stop executing sql
4045
const stopExecuteSQL = useCallback(() => {

chat2db-community-client/src/i18n/en-US/workspace.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ export default {
3232
'The table name you entered is not the same as the table name you want to delete, please confirm again',
3333
'workspace.table.total': 'Total',
3434
'workspace.table.total.tip': 'Load total number of rows',
35+
'workspace.table.defaultPageSize': 'Default',
36+
'workspace.table.setDefaultPageSize': 'Set as Default',
37+
'workspace.table.customPageSize': 'Custom...',
3538
'workspace.table.export.all.csv': 'Export results as a CSV',
3639
'workspace.table.export.all.xlsx': 'Export results as a xlsx',
3740
'workspace.table.export.all.insert': 'Export results as INSERT SQL',

chat2db-community-client/src/i18n/es-ES/workspace.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ export default {
3232
'El nombre introducido no coincide con el de la tabla que desea eliminar. Vuelva a comprobarlo',
3333
'workspace.table.total': 'Total',
3434
'workspace.table.total.tip': 'Cargar el número total de filas',
35+
'workspace.table.defaultPageSize': 'Predeterminado',
36+
'workspace.table.setDefaultPageSize': 'Establecer como predeterminado',
37+
'workspace.table.customPageSize': 'Personalizado...',
3538
'workspace.table.export.all.csv': 'Exportar los resultados como CSV',
3639
'workspace.table.export.all.xlsx': 'Exportar los resultados como XLSX',
3740
'workspace.table.export.all.insert': 'Exportar los resultados como SQL INSERT',

0 commit comments

Comments
 (0)