fix(grid): respect page size row limits

This commit is contained in:
t8y2
2026-05-19 11:31:38 +08:00
parent 95d70eede6
commit 261d43c1b7
13 changed files with 302 additions and 102 deletions
@@ -24,7 +24,6 @@ import { loadEditorTheme, editorFontTheme } from "@/lib/editorThemes";
import { isTauriRuntime } from "@/lib/tauriRuntime";
import { aiTestConnection } from "@/lib/api";
import { eventToShortcut } from "@/lib/keyboardShortcuts";
import { MAX_RESULT_PAGE_SIZE, MIN_RESULT_PAGE_SIZE, normalizeResultPageSize } from "@/lib/paginationPageSize";
import {
SHORTCUT_DEFINITIONS,
findShortcutConflict,
@@ -52,7 +51,6 @@ const editFontFamily = ref(settingsStore.editorSettings.fontFamily);
const editFontSize = ref(settingsStore.editorSettings.fontSize);
const editTheme = ref(settingsStore.editorSettings.theme);
const editExecuteMode = ref(settingsStore.editorSettings.executeMode);
const editPageSize = ref(settingsStore.editorSettings.pageSize);
const editWordWrap = ref(settingsStore.editorSettings.wordWrap);
const editAppLayout = ref(settingsStore.editorSettings.appLayout);
const editRedisScanPageSize = ref(settingsStore.editorSettings.redisScanPageSize);
@@ -69,7 +67,6 @@ watch(
editFontSize.value = settingsStore.editorSettings.fontSize;
editTheme.value = settingsStore.editorSettings.theme;
editExecuteMode.value = settingsStore.editorSettings.executeMode;
editPageSize.value = settingsStore.editorSettings.pageSize;
editWordWrap.value = settingsStore.editorSettings.wordWrap;
editAppLayout.value = settingsStore.editorSettings.appLayout;
editRedisScanPageSize.value = settingsStore.editorSettings.redisScanPageSize;
@@ -93,7 +90,6 @@ function hasChanges(): boolean {
editFontSize.value !== settingsStore.editorSettings.fontSize ||
editTheme.value !== settingsStore.editorSettings.theme ||
editExecuteMode.value !== settingsStore.editorSettings.executeMode ||
editPageSize.value !== settingsStore.editorSettings.pageSize ||
editWordWrap.value !== settingsStore.editorSettings.wordWrap ||
editAppLayout.value !== settingsStore.editorSettings.appLayout ||
editRedisScanPageSize.value !== settingsStore.editorSettings.redisScanPageSize ||
@@ -109,7 +105,6 @@ function applySettings() {
fontSize: editFontSize.value,
theme: editTheme.value,
executeMode: editExecuteMode.value,
pageSize: normalizeResultPageSize(editPageSize.value),
wordWrap: editWordWrap.value,
appLayout: editAppLayout.value,
redisScanPageSize: editRedisScanPageSize.value,
@@ -124,7 +119,6 @@ function resetDefaults() {
editFontSize.value = DEFAULT_EDITOR_SETTINGS.fontSize;
editTheme.value = DEFAULT_EDITOR_SETTINGS.theme;
editExecuteMode.value = DEFAULT_EDITOR_SETTINGS.executeMode;
editPageSize.value = DEFAULT_EDITOR_SETTINGS.pageSize;
editWordWrap.value = DEFAULT_EDITOR_SETTINGS.wordWrap;
editAppLayout.value = DEFAULT_EDITOR_SETTINGS.appLayout;
editRedisScanPageSize.value = DEFAULT_EDITOR_SETTINGS.redisScanPageSize;
@@ -136,10 +130,6 @@ function onExecuteModeChange(v: any) {
if (v === "all" || v === "current") editExecuteMode.value = v;
}
function onPageSizeInput(event: Event) {
editPageSize.value = normalizeResultPageSize((event.target as HTMLInputElement).value);
}
function onFontFamilyChange(v: any) {
if (typeof v === "string") editFontFamily.value = v;
}
@@ -574,26 +564,6 @@ watch(
</SelectContent>
</Select>
</div>
<div class="space-y-2">
<div class="flex items-center justify-between gap-3">
<Label for="editor-result-page-size">{{ t("settings.resultPageSize") }}</Label>
<span class="text-xs text-muted-foreground tabular-nums">
{{ t("settings.resultPageSizeOption", { count: editPageSize }) }}
</span>
</div>
<Input
id="editor-result-page-size"
type="number"
inputmode="numeric"
:min="MIN_RESULT_PAGE_SIZE"
:max="MAX_RESULT_PAGE_SIZE"
step="1"
:model-value="editPageSize"
@input="onPageSizeInput"
/>
<p class="text-xs text-muted-foreground">{{ t("settings.resultPageSizeDescription") }}</p>
</div>
</div>
<div class="grid gap-4 md:grid-cols-2">
+29 -10
View File
@@ -1130,6 +1130,12 @@ const customPageSizeInput = ref(String(pageSize.value));
watch(pageSize, (value) => {
customPageSizeInput.value = String(value);
});
watch(
() => settingsStore.editorSettings.pageSize,
(value) => {
pageSize.value = normalizeResultPageSize(value, pageSize.value);
},
);
watch(
() => [props.pageOffset, props.pageLimit],
([offset, limit]) => {
@@ -1141,6 +1147,9 @@ watch(
);
const canGoNextPage = computed(() => props.result.has_more === true || props.result.rows.length >= pageSize.value);
const canJumpLastPage = computed(() => canGoNextPage.value && (!!props.tableMeta || !!props.countSql));
const showTruncationWarning = computed(
() => props.result.truncated === true && typeof props.pageLimit !== "number" && props.result.has_more !== true,
);
const isResultsContext = computed(() => props.context === "results");
const resultEditStatus = computed(() => {
if (!isResultsContext.value || !hasData.value) return null;
@@ -2773,10 +2782,10 @@ defineExpose({
</div>
<!-- Truncation warning banner -->
<div
v-if="result.truncated"
v-if="showTruncationWarning"
class="shrink-0 px-3 py-1 bg-amber-500/10 border-b border-amber-500/20 text-xs text-amber-600 dark:text-amber-400 flex items-center gap-1.5"
>
<span>{{ t("grid.truncatedHint") }}</span>
<span>{{ t("grid.truncatedHint", { count: pageSize }) }}</span>
</div>
<!-- Content area: table + DDL drawer -->
<div class="flex-1 flex min-h-0 overflow-hidden">
@@ -3947,7 +3956,7 @@ defineExpose({
<!-- Bottom status bar -->
<div class="flex items-center gap-2 px-3 py-1 border-t text-xs text-muted-foreground bg-muted/30 shrink-0">
<span v-if="hasData">{{ t("grid.totalRows", { count: result.rows.length }) }}</span>
<span v-if="result.truncated" class="text-amber-500 text-xs ml-1">(truncated)</span>
<span v-if="showTruncationWarning" class="text-amber-500 text-xs ml-1">(truncated)</span>
<span v-if="!hasData">{{ t("grid.rowsAffected", { count: result.affected_rows }) }}</span>
<span>{{ result.execution_time_ms }}ms</span>
<span v-if="selectedRowCount > 0 || hasCellSelection" class="text-foreground">{{ selectionSummary }}</span>
@@ -3966,26 +3975,36 @@ defineExpose({
{{ pageSize }}{{ t("grid.rowsPerPageShort") }}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" class="w-52">
<DropdownMenuContent align="end" class="w-36">
<DropdownMenuItem v-for="s in pageSizeOptions" :key="s" @click="changePageSize(s)">
{{ s }} {{ t("grid.rowsPerPageShort") }}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuLabel class="text-xs">{{ t("grid.customRowsPerPage") }}</DropdownMenuLabel>
<div class="flex items-center gap-1.5 px-2 pb-2" @click.stop @keydown.stop>
<div class="flex items-center gap-1 px-2 pb-2" @click.stop @keydown.stop>
<Input
v-model="customPageSizeInput"
type="number"
inputmode="numeric"
:min="MIN_RESULT_PAGE_SIZE"
:max="MAX_RESULT_PAGE_SIZE"
class="h-7 text-xs"
class="h-7 w-24 text-xs tabular-nums [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
@keydown.enter.prevent.stop="applyCustomPageSize"
/>
<Button variant="outline" size="sm" class="h-7 px-2 text-xs" @click.stop="applyCustomPageSize">
<Check class="h-3 w-3" />
{{ t("grid.applyPageSize") }}
</Button>
<Tooltip>
<TooltipTrigger as-child>
<Button
variant="outline"
size="icon"
class="h-7 w-7 shrink-0"
:aria-label="t('grid.applyPageSize')"
@click.stop="applyCustomPageSize"
>
<Check class="h-3.5 w-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">{{ t("grid.applyPageSize") }}</TooltipContent>
</Tooltip>
</div>
</DropdownMenuContent>
</DropdownMenu>
@@ -534,6 +534,8 @@ defineExpose({ focusSearch, refreshData });
:connection-id="activeTab.connectionId"
:database="activeTab.database"
:table-meta="activeTab.tableMeta"
:page-offset="activeTab.resultPageOffset"
:page-limit="activeTab.resultPageLimit"
:on-execute-sql="async (sql: string) => emit('executeSql', sql)"
@update:where-input="(v: string) => (activeTab.whereInput = v)"
@reload="
+1 -5
View File
@@ -403,7 +403,7 @@ export default {
"metadata-unavailable": "DBX could not load table metadata, so result editing is disabled.",
},
sortUnsupported: "This SQL does not support full-result sorting. Try again with a single SELECT query.",
truncatedHint: "Results truncated to 10,000 rows. Use LIMIT/OFFSET in your query to paginate.",
truncatedHint: "Results truncated to {count} rows. Use the footer pagination or adjust rows per page.",
},
welcome: {
title: "Database Workspace",
@@ -1139,10 +1139,6 @@ export default {
executeMode: "Execute Mode (Cmd+Enter)",
executeModeAll: "Execute all SQL",
executeModeCurrent: "Execute statement at cursor",
resultPageSize: "Query result rows per page",
resultPageSizeDescription:
"Used for new queries, table browsing, and result pagination. Very large values may slow queries and rendering.",
resultPageSizeOption: "{count} rows/page",
wordWrap: "Word wrap",
wordWrapDescription: "Wrap long SQL lines within the editor width",
redisScanPageSize: "Redis scan count",
+1 -4
View File
@@ -395,7 +395,7 @@ export default {
"metadata-unavailable": "无法读取目标表元数据,暂不能启用结果编辑。",
},
sortUnsupported: "当前 SQL 不支持全量排序,请改为单条 SELECT 查询后再尝试。",
truncatedHint: "结果已截断,仅显示前 10,000 行。如需更多数据,请使用 LIMIT/OFFSET 分页查询。",
truncatedHint: "结果已截断,仅显示前 {count} 行。可通过底部分页继续加载,或调整每页行数。",
},
welcome: {
title: "数据库工作台",
@@ -1116,9 +1116,6 @@ export default {
executeMode: "执行模式 (Cmd+Enter)",
executeModeAll: "执行全部 SQL",
executeModeCurrent: "执行光标所在语句",
resultPageSize: "查询结果每页行数",
resultPageSizeDescription: "用于新查询、表数据浏览和分页跳转。设置过大可能会降低查询和渲染速度。",
resultPageSizeOption: "{count} 行/页",
wordWrap: "自动换行",
wordWrapDescription: "长 SQL 在编辑器宽度内自动折行显示",
redisScanPageSize: "Redis 扫描数量",
+4 -2
View File
@@ -466,9 +466,9 @@ export const useQueryStore = defineStore("query", () => {
const connStore = useConnectionStore();
const conn = connStore.getConfig(tab.connectionId);
const useAgentCursor = !!conn?.db_type && AGENT_DRIVER_TYPES.has(conn.db_type);
const settingsStore = useSettingsStore();
await closeResultSession(tab, options?.pagination?.sessionId);
if (tab.mode === "query") {
const settingsStore = useSettingsStore();
const pagination = options?.pagination ?? { limit: settingsStore.editorSettings.pageSize, offset: 0 };
const plan = buildQueryPaginationExecutionPlan({
sql,
@@ -483,6 +483,8 @@ export const useQueryStore = defineStore("query", () => {
pageOffset = plan.pageOffset;
countSql = plan.countSql;
useAgentResultSession = plan.useAgentResultSession;
} else if (tab.mode === "data") {
pageLimit = settingsStore.editorSettings.pageSize;
}
const mongoFind = conn?.db_type === "mongodb" ? parseMongoFindCommand(sql) : null;
if (mongoFind) {
@@ -522,7 +524,7 @@ export const useQueryStore = defineStore("query", () => {
typeof pageLimit === "number"
? useAgentResultSession
? {
maxRows: 10000,
maxRows: pageLimit,
fetchSize: pageLimit,
pageSize: pageLimit,
resultSessionId: options?.pagination?.sessionId,