From db8f262c1426e62040d1b9edba197ab372ddeea4 Mon Sep 17 00:00:00 2001 From: zipg Date: Thu, 20 Aug 2026 18:15:51 +0800 Subject: [PATCH] fix(grid): reset pagination when table filters change --- apps/desktop/src/components/grid/DataGrid.vue | 34 +++++++++++++++++-- .../grid/__tests__/DataGridSaveReload.spec.ts | 2 ++ packages/app-tests/queryResultToolbar.test.ts | 3 +- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index 8648401e1b..78a2d6f8fa 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -875,6 +875,8 @@ const { searchText, deferredSearchText: deferredClientSearchText, overlayVisible const orderByInput = ref(props.initialOrderByInput ?? ""); const whereFilterInput = ref(props.initialWhereInput ?? ""); +const conditionInputRevision = ref(0); +const appliedConditionInputRevision = ref(0); const queryControlError = ref(""); const conditionColumns = computed(() => dataGridConditionColumnOptions(props.tableMeta?.columns ?? props.result.columns, resolvedDatabaseType.value)); const conditionIdentifierQuote = computed(() => dataGridConditionIdentifierQuote(resolvedDatabaseType.value, connectionStore.connectionIdentifierQuote?.(props.connectionId))); @@ -1810,7 +1812,10 @@ function loadStructuredFilterStateForScope() { void buildStructuredWhereFromRules(structuredFilterRules.value).then((whereInput) => { if (structuredFilterCacheKey.value !== cacheKey || structuredFilterScopeKey.value !== scopeKey) return; appliedStructuredWhereInput.value = whereInput; - nextTick(() => emit("update:whereInput", currentWhereInput() ?? "")); + nextTick(() => { + emit("update:whereInput", currentWhereInput() ?? ""); + markConditionInputsApplied(); + }); }); return; } @@ -1818,6 +1823,7 @@ function loadStructuredFilterStateForScope() { serverColumnFilters.value = {}; structuredFilterRules.value = filterBuilderColumnOptions.value.length > 0 ? [defaultStructuredFilterRule()] : []; persistStructuredFilterState(); + markConditionInputsApplied(); } function ensureStructuredFilterRule() { @@ -2043,6 +2049,22 @@ function onSearchKeydown(e: KeyboardEvent) { } } +watch( + [whereFilterInput, orderByInput], + () => { + conditionInputRevision.value += 1; + }, + { flush: "sync" }, +); + +function markConditionInputsApplied() { + appliedConditionInputRevision.value = conditionInputRevision.value; +} + +function hasPendingConditionInputs(): boolean { + return conditionInputRevision.value !== appliedConditionInputRevision.value; +} + watch(whereFilterInput, () => { emit("update:whereInput", currentWhereInput() ?? ""); persistStructuredFilterState(); @@ -3946,8 +3968,14 @@ async function onToolbarRefresh() { if (transactionActive.value) { discardChanges(); } + const resetToFirstPage = hasPendingConditionInputs(); + if (resetToFirstPage) { + currentPage.value = 1; + resetGridVerticalScroll(true); + } + markConditionInputsApplied(); prepareFullReload(); - emit("reload", props.sql, searchText.value, currentWhereInput(), currentOrderBy(), pageSize.value, (currentPage.value - 1) * pageSize.value, "refresh"); + emit("reload", props.sql, searchText.value, currentWhereInput(), currentOrderBy(), pageSize.value, resetToFirstPage ? 0 : (currentPage.value - 1) * pageSize.value, "refresh"); } function setAutoRefreshInterval(seconds: number) { @@ -5776,6 +5804,7 @@ async function applyOrderBySearch() { whereInput: currentWhereInput(), includeRowId: usesSyntheticRowIdKey(resolvedDatabaseType.value, tableMeta.primaryKeys, tableMeta.tableType), }); + markConditionInputsApplied(); await props.onExecuteSql(sql); } catch (e: any) { queryControlError.value = String(e?.message || e); @@ -5812,6 +5841,7 @@ async function applyWhereFilter() { whereInput, includeRowId: usesSyntheticRowIdKey(resolvedDatabaseType.value, tableMeta.primaryKeys, tableMeta.tableType), }); + markConditionInputsApplied(); await props.onExecuteSql(sql); } catch (e: any) { queryControlError.value = String(e?.message || e); diff --git a/apps/desktop/src/components/grid/__tests__/DataGridSaveReload.spec.ts b/apps/desktop/src/components/grid/__tests__/DataGridSaveReload.spec.ts index d5a28f30bc..96f77dff12 100644 --- a/apps/desktop/src/components/grid/__tests__/DataGridSaveReload.spec.ts +++ b/apps/desktop/src/components/grid/__tests__/DataGridSaveReload.spec.ts @@ -18,6 +18,8 @@ describe("DataGrid save reload integration", () => { expect(dataGridSource).toContain("prepareFullReload,\n emit,"); expect(toolbarSource).toContain("prepareFullReload();"); + expect(toolbarSource).toContain("const resetToFirstPage = hasPendingConditionInputs();"); + expect(toolbarSource).toContain("resetToFirstPage ? 0 : (currentPage.value - 1) * pageSize.value"); expect(prepareSource).toContain("resetInfiniteScrollState();"); expect(prepareSource).toContain("captureCurrentSelectionForRefresh();"); expect(prepareSource).toContain("preservedSelectionOnNextResult = selection ? { selection, sourceResult: props.result } : null;"); diff --git a/packages/app-tests/queryResultToolbar.test.ts b/packages/app-tests/queryResultToolbar.test.ts index 1abe37c3a0..fb89e12027 100644 --- a/packages/app-tests/queryResultToolbar.test.ts +++ b/packages/app-tests/queryResultToolbar.test.ts @@ -114,7 +114,8 @@ test("table-data toolbar refresh keeps page size independent from SQL editor set assert.match(dataGrid, /props\.context === "table-data" \? \(props\.pageLimit \?\? tableOpenPageLimit\(settingsStore\.editorSettings\.tableOpenPageSize\)\) : settingsStore\.editorSettings\.pageSize/); assert.match(dataGrid, /if \(props\.context === "table-data"\) return;[\s\S]*pageSize\.value = normalizeResultPageSize\(value, pageSize\.value\)/); assert.match(dataGrid, /props\.context === "table-data" \? \{ tableOpenPageSize: normalizedSize \} : \{ pageSize: normalizedSize \}/); - assert.match(dataGrid, /emit\("reload", props\.sql, searchText\.value, currentWhereInput\(\), currentOrderBy\(\), pageSize\.value, \(currentPage\.value - 1\) \* pageSize\.value, "refresh"\)/); + assert.match(dataGrid, /const resetToFirstPage = hasPendingConditionInputs\(\);/); + assert.match(dataGrid, /emit\("reload", props\.sql, searchText\.value, currentWhereInput\(\), currentOrderBy\(\), pageSize\.value, resetToFirstPage \? 0 : \(currentPage\.value - 1\) \* pageSize\.value, "refresh"\)/); }); test("standalone result views use the same compact toolbar breakpoint", () => {