mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-30 00:19:35 +00:00
fix(grid): respect page size row limits
This commit is contained in:
@@ -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">
|
||||
|
||||
@@ -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="
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 扫描数量",
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user