From c155443d4bef2d51aeeffb0574c11d42d8f3afdc Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Thu, 18 Jun 2026 14:27:53 +0800 Subject: [PATCH] feat(grid): add pinned query result history --- .../src/components/layout/ContentArea.vue | 70 ++++++++--- apps/desktop/src/i18n/locales/en.ts | 3 + apps/desktop/src/i18n/locales/ja.ts | 3 + apps/desktop/src/i18n/locales/zh-CN.ts | 3 + apps/desktop/src/lib/openTabsPersistence.ts | 3 + apps/desktop/src/stores/queryStore.ts | 114 ++++++++++++++++-- apps/desktop/src/types/database.ts | 1 + 7 files changed, 167 insertions(+), 30 deletions(-) diff --git a/apps/desktop/src/components/layout/ContentArea.vue b/apps/desktop/src/components/layout/ContentArea.vue index e1489bfe1..d660609c9 100644 --- a/apps/desktop/src/components/layout/ContentArea.vue +++ b/apps/desktop/src/components/layout/ContentArea.vue @@ -2,7 +2,7 @@ import { computed, ref, defineAsyncComponent, watch, nextTick, onMounted, onUnmounted } from "vue"; import type { CSSProperties } from "vue"; import { useI18n } from "vue-i18n"; -import { Check, Columns3, Loader2, Search, Bot, GitBranch, BarChart3, TableProperties, ChevronDown, ChevronUp, Inbox, RefreshCcw, Wrench, Toolbox, ListChecks, Database, FileUp, Download, X } from "@lucide/vue"; +import { Check, Columns3, Loader2, Search, Bot, GitBranch, BarChart3, TableProperties, ChevronDown, ChevronUp, Inbox, RefreshCcw, Wrench, Toolbox, ListChecks, Database, FileUp, Download, X, Pin } from "@lucide/vue"; import { Splitpanes, Pane } from "splitpanes"; import "splitpanes/dist/splitpanes.css"; import { Button } from "@/components/ui/button"; @@ -230,9 +230,11 @@ const activeQueryError = computed(() => { const hasQueryOutput = computed(() => !!props.activeTab.result || !!props.activeTab.explainPlan || !!props.activeTab.explainError || props.activeTab.isExecuting === true || props.activeTab.isExplaining === true); const tabularResults = computed(() => tabularResultItems(props.activeTab.results)); const resultRuns = computed(() => resultRunItems(props.activeTab)); +const activeResultRunItem = computed(() => resultRuns.value.find((run) => run.active)); const activeResultGridCacheKey = computed(() => resultGridCacheKey(props.activeTab)); const resultArchiveExporting = ref(false); const canExportResultArchive = computed(() => props.activeTab.mode === "query" && (!!props.activeTab.result || !!props.activeTab.results?.length || !!props.activeTab.resultRuns?.length)); +const resultAutoSave = computed(() => props.activeTab.resultAutoSave === true); watch( () => tabularResults.value.map((item) => item.index).join(","), () => { @@ -482,6 +484,19 @@ function removeResultRun(runId: string) { if (removed && removedActiveRun) emit("update:activeOutputView", "result"); } +async function selectResultRun(runId: string) { + if (!(await queryStore.setActiveResultRun(props.activeTab.id, runId))) { + toast(t("tabs.missingResultRun"), 4000); + return; + } + emit("update:activeOutputView", "result"); +} + +function toggleResultAutoSave() { + const enabled = queryStore.toggleResultAutoSave(props.activeTab.id); + toast(t(enabled ? "tabs.autoKeepResultsEnabled" : "tabs.autoKeepResultsDisabled"), 2500); +} + function handleModRTarget(target: Element): boolean { if (target.closest("[data-query-editor-root]")) return queryEditorRef.value?.openReplace() ?? false; if (target.closest("[data-cell-detail-editor-root]")) return dataGridRef.value?.openCellDetailSearch() ?? false; @@ -545,26 +560,45 @@ defineExpose({ focusSearch, refreshData, handleModRTarget }); {{ t("tabs.tableData") }} +