fix(grid): preserve view state across result tab switches (#11521)

Co-authored-by: zipg <4047349+zipg@users.noreply.github.com>
This commit is contained in:
zipg
2026-10-11 12:47:46 +08:00
committed by GitHub
co-authored by zipg
parent 4f666d9f8f
commit 06de88a367
2 changed files with 45 additions and 0 deletions
@@ -1639,6 +1639,7 @@ async function closeCurrentQueryResult() {
}
async function selectResultRun(runId: string) {
if (runId !== props.activeTab.activeResultRunId) dispatchBeforeResultTabSwitch();
if (!(await queryStore.setActiveResultRun(props.activeTab.id, runId))) {
toast(t("tabs.missingResultRun"), 4000);
return false;
@@ -1647,6 +1648,17 @@ async function selectResultRun(runId: string) {
return true;
}
/**
* Result-set tabs share one query tab, but switching them still replaces the
* mounted DataGrid instance. Give the grid the same pre-switch boundary used
* by editor tabs so it snapshots its viewport and selection before the store
* changes the active result.
*/
function dispatchBeforeResultTabSwitch() {
if (typeof window === "undefined") return;
window.dispatchEvent(new CustomEvent("dbx:before-tab-switch", { detail: { tabId: props.activeTab.id, fromTabId: props.activeTab.id } }));
}
/**
* 点击结果标签:切换显示的同时固定该结果(issue #9975)。
* 未固定的结果会被下一次普通查询复用/覆盖,点击后固定即可保留下来。
@@ -1686,6 +1698,7 @@ function toggleResultAutoSave() {
}
function selectResultItem(item: (typeof visibleResultItems.value)[number]) {
if (item.index !== (props.activeTab.activeResultIndex ?? 0)) dispatchBeforeResultTabSwitch();
queryStore.setActiveResultIndex(props.activeTab.id, item.index);
emit("update:activeOutputView", props.activeTab.id, "result");
const range = resultSourceRange(props.activeTab.sql, item.result, item.index, activeEffectiveDatabaseType.value, activeSqlStatementParameterOptions.value) ?? null;
@@ -0,0 +1,32 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const contentAreaSource = readFileSync(new URL("../ContentArea.vue", import.meta.url), "utf8");
function functionSource(name: string, endMarker: string): string {
const start = contentAreaSource.indexOf(`function ${name}(`);
const end = contentAreaSource.indexOf(endMarker, start + 1);
expect(start).toBeGreaterThanOrEqual(0);
expect(end).toBeGreaterThan(start);
return contentAreaSource.slice(start, end);
}
describe("ContentArea result-tab view state handoff", () => {
it("dispatches the pre-switch boundary before changing the result index", () => {
const source = functionSource("selectResultItem", "function executionSummaryItemRange(");
expect(source.indexOf("dispatchBeforeResultTabSwitch()")).toBeGreaterThanOrEqual(0);
expect(source.indexOf("dispatchBeforeResultTabSwitch()")).toBeLessThan(source.indexOf("queryStore.setActiveResultIndex"));
});
it("dispatches the pre-switch boundary before changing the result run", () => {
const source = functionSource("selectResultRun", "/**\n * 点击结果标签");
expect(source.indexOf("dispatchBeforeResultTabSwitch()")).toBeGreaterThanOrEqual(0);
expect(source.indexOf("dispatchBeforeResultTabSwitch()")).toBeLessThan(source.indexOf("queryStore.setActiveResultRun"));
});
it("uses the shared before-tab-switch event consumed by DataGrid", () => {
const helper = functionSource("dispatchBeforeResultTabSwitch", "async function selectResultRunFromTab");
expect(helper).toContain('new CustomEvent("dbx:before-tab-switch"');
expect(helper).toContain("fromTabId: props.activeTab.id");
});
});