feat(editor): preview dml changes as readonly select
This commit is contained in:
1 parent
d59b86bd57
commit
333c3a3f44
25 files changed
+848
-4
No files matched your search
@@ -223,6 +223,7 @@ const emit = defineEmits<{
|
||||
editorUpdate: [tabId: string, value: string];
|
||||
editorSelectionChange: [value: string];
|
||||
editorCursorChange: [pos: number];
|
||||
previewChangesAvailable: [value: boolean];
|
||||
editorViewportChange: [tabId: string, viewport: { scrollTop: number; scrollLeft: number }];
|
||||
editorSelectionStateChange: [tabId: string, selection: { anchor: number; head: number }];
|
||||
formatError: [];
|
||||
@@ -1131,6 +1132,10 @@ function requestQueryEditorExecuteInNewResultTab() {
|
||||
return queryEditorRef.value?.requestExecuteInNewResultTab();
|
||||
}
|
||||
|
||||
function requestQueryEditorPreviewChanges(stackSql?: string) {
|
||||
return queryEditorRef.value?.requestPreviewChanges?.(stackSql);
|
||||
}
|
||||
|
||||
function shouldBlockQueryEditorExecutionShortcut(event: KeyboardEvent) {
|
||||
return queryEditorRef.value?.shouldBlockExecutionShortcut?.(event) ?? false;
|
||||
}
|
||||
@@ -1185,6 +1190,7 @@ defineExpose({
|
||||
requestQueryEditorExecute,
|
||||
captureQueryEditorExecutionSnapshot,
|
||||
requestQueryEditorExecuteInNewResultTab,
|
||||
requestQueryEditorPreviewChanges,
|
||||
shouldBlockQueryEditorExecutionShortcut,
|
||||
acceptQueryEditorExecutionViewport,
|
||||
cancelQueryEditorExecutionViewport,
|
||||
@@ -1241,6 +1247,7 @@ defineExpose({
|
||||
@selection-change="emit('editorSelectionChange', $event)"
|
||||
@send-selection-to-ai="emit('sendSelectionToAi', $event)"
|
||||
@cursor-change="emit('editorCursorChange', $event)"
|
||||
@preview-changes-available="emit('previewChangesAvailable', $event)"
|
||||
@viewport-change="emit('editorViewportChange', activeTab.id, $event)"
|
||||
@selection-state-change="emit('editorSelectionStateChange', activeTab.id, $event)"
|
||||
@format-error="emit('formatError')"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch, watchEffect } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Play, CirclePlay, Loader2, Square, Database, Check, Table2, AlignLeft, GitBranch, Save, FolderOpen, X, Shield, Download, RotateCcw, AlertTriangle, ClipboardPaste, Minimize2, SpellCheck2 } from "@lucide/vue";
|
||||
import { Play, CirclePlay, Loader2, Square, Database, Check, Table2, AlignLeft, GitBranch, Save, FolderOpen, X, Shield, Download, RotateCcw, AlertTriangle, ClipboardPaste, Minimize2, SpellCheck2, Eye } from "@lucide/vue";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { SearchableSelect } from "@/components/ui/searchable-select";
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
|
||||
@@ -21,12 +21,15 @@ import { connectionIsDorisFamilyCatalogCapable } from "@/lib/database/databaseFe
|
||||
import { hexToRgba } from "@/lib/common/color";
|
||||
import { productionContextForDatabase } from "@/lib/database/productionSafety";
|
||||
import { formatShortcutDisplay } from "@/lib/editor/shortcutDisplay";
|
||||
import { looksLikeDmlStatement } from "@/lib/sql/dmlChangePreview";
|
||||
import type { QueryTab, ConnectionConfig } from "@/types/database";
|
||||
|
||||
const props = defineProps<{
|
||||
activeTab: QueryTab;
|
||||
activeConnection?: ConnectionConfig;
|
||||
executableSql: string;
|
||||
/** 来自 QueryEditor 的实时“当前语句是否为可预览 DML”信号;未提供时退回可编辑文档启发式。 */
|
||||
canPreviewChanges?: boolean;
|
||||
explainMode?: string;
|
||||
blockDangerousRedisCommands?: boolean;
|
||||
sqlKeywordCase: "preserve" | "upper" | "lower";
|
||||
@@ -46,6 +49,7 @@ const emit = defineEmits<{
|
||||
execute: [source: "pointer" | "keyboard"];
|
||||
executePointerDown: [];
|
||||
cancel: [];
|
||||
previewChanges: [];
|
||||
explain: [];
|
||||
"update:explainMode": [mode: "explain" | "autotrace"];
|
||||
formatSql: [];
|
||||
@@ -131,6 +135,11 @@ const schemaDatabaseKey = computed(() => props.activeTab.database || (isSingleDb
|
||||
const saveTooltip = computed(() => (props.activeTab.objectSource ? t("objects.saveSource") : t("toolbar.saveSql")));
|
||||
const executeShortcutDisplay = computed(() => formatShortcutDisplay(settingsStore.editorSettings.shortcuts.executeSql));
|
||||
const executeShortcutTooltip = computed(() => t("toolbar.executeShortcut", { shortcut: executeShortcutDisplay.value }));
|
||||
// executableSql 在无选区时可能是整篇文档;只要有 DML 语句出现就显示预览按钮,
|
||||
// 具体"当前语句"由编辑器(QueryEditor)按执行模式解析。
|
||||
const DML_KEYWORD_RE = /(^|\s)(update|insert|delete)\s/i;
|
||||
const canPreviewDml = computed(() => looksLikeDmlStatement(props.executableSql) || DML_KEYWORD_RE.test(props.executableSql));
|
||||
const previewButtonVisible = computed(() => props.canPreviewChanges ?? canPreviewDml.value);
|
||||
// DM calls it autotrace, Postgres EXPLAIN ANALYZE, SQL Server the actual execution
|
||||
// plan (SET STATISTICS XML); all three execute the statement.
|
||||
const supportsExplainAnalyze = computed(() => {
|
||||
@@ -301,6 +310,14 @@ async function changeCatalog(selectedCatalog: string) {
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{{ activeTab.isExecuting ? t("toolbar.stopQuery") : executeShortcutTooltip }}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip v-if="previewButtonVisible">
|
||||
<TooltipTrigger as-child>
|
||||
<Button variant="ghost" size="icon" class="h-6 w-6 text-sky-600 hover:bg-sky-500/10 hover:text-sky-700" :disabled="activeTab.isExecuting || activeTab.isCancelling" :aria-label="t('editor.previewChanges')" @mousedown.prevent @click="emit('previewChanges')">
|
||||
<Eye class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{{ t("editor.previewChanges") }}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip v-if="supportsExplain">
|
||||
<TooltipTrigger as-child>
|
||||
<Button
|
||||
|
||||
Reference in new issue
Block a user