mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-28 16:09:17 +00:00
feat: execute selected sql (#13)
Co-authored-by: SuLe <sule@SuLedeMacBook-Air.local>
This commit is contained in:
+33
-12
@@ -33,6 +33,7 @@ import { setLocale, currentLocale, type Locale } from "@/i18n";
|
||||
import { getCurrentWindow, type Theme } from "@tauri-apps/api/window";
|
||||
import { getCurrentWebview } from "@tauri-apps/api/webview";
|
||||
import * as api from "@/lib/tauri";
|
||||
import { resolveExecutableSql } from "@/lib/sqlExecutionTarget";
|
||||
|
||||
const { t } = useI18n();
|
||||
const connectionStore = useConnectionStore();
|
||||
@@ -44,6 +45,8 @@ const { message: toastMessage, visible: toastVisible, toast } = useToast();
|
||||
const showConnectionDialog = ref(false);
|
||||
const showHistory = ref(false);
|
||||
const dangerSql = ref("");
|
||||
const pendingDangerSql = ref("");
|
||||
const selectedSql = ref("");
|
||||
const showDangerDialog = ref(false);
|
||||
const databaseOptions = ref<Record<string, string[]>>({});
|
||||
const loadingDatabaseOptions = ref<Record<string, boolean>>({});
|
||||
@@ -66,6 +69,16 @@ const activeTab = computed(() =>
|
||||
queryStore.tabs.find((t) => t.id === queryStore.activeTabId)
|
||||
);
|
||||
|
||||
const executableSql = computed(() => {
|
||||
const tab = activeTab.value;
|
||||
return tab ? resolveExecutableSql(tab.sql, selectedSql.value) : "";
|
||||
});
|
||||
|
||||
watch(() => queryStore.activeTabId, () => {
|
||||
selectedSql.value = "";
|
||||
pendingDangerSql.value = "";
|
||||
});
|
||||
|
||||
const activeConnection = computed(() => {
|
||||
const tab = activeTab.value;
|
||||
return tab ? connectionStore.getConfig(tab.connectionId) : undefined;
|
||||
@@ -191,6 +204,10 @@ function onEditorUpdate(val: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function onEditorSelectionChange(val: string) {
|
||||
selectedSql.value = val;
|
||||
}
|
||||
|
||||
function newQuery() {
|
||||
if (!connectionStore.activeConnectionId) return;
|
||||
const conn = connectionStore.connections.find(
|
||||
@@ -222,24 +239,25 @@ function isDangerousSql(sql: string): boolean {
|
||||
return DANGER_RE.test(stripSqlComments(sql));
|
||||
}
|
||||
|
||||
function tryExecute() {
|
||||
function tryExecute(sqlOverride?: string) {
|
||||
const tab = activeTab.value;
|
||||
if (!tab || !tab.sql.trim()) return;
|
||||
if (isDangerousSql(tab.sql)) {
|
||||
dangerSql.value = tab.sql;
|
||||
const sql = sqlOverride ?? executableSql.value;
|
||||
if (!tab || !sql.trim()) return;
|
||||
if (isDangerousSql(sql)) {
|
||||
dangerSql.value = sql;
|
||||
pendingDangerSql.value = sql;
|
||||
showDangerDialog.value = true;
|
||||
} else {
|
||||
doExecute();
|
||||
doExecute(sql);
|
||||
}
|
||||
}
|
||||
|
||||
async function doExecute() {
|
||||
async function doExecute(sql = executableSql.value) {
|
||||
const tab = activeTab.value;
|
||||
if (!tab) return;
|
||||
if (!tab || !sql.trim()) return;
|
||||
const connName = connectionStore.getConfig(tab.connectionId)?.name || "";
|
||||
const sql = tab.sql;
|
||||
const start = Date.now();
|
||||
await queryStore.executeCurrentTab();
|
||||
await queryStore.executeCurrentSql(sql);
|
||||
const elapsed = Date.now() - start;
|
||||
const success = !tab.result?.columns.includes("Error");
|
||||
historyStore.add({
|
||||
@@ -253,7 +271,9 @@ async function doExecute() {
|
||||
}
|
||||
|
||||
function onDangerConfirm() {
|
||||
doExecute();
|
||||
const sql = pendingDangerSql.value || executableSql.value;
|
||||
pendingDangerSql.value = "";
|
||||
doExecute(sql);
|
||||
}
|
||||
|
||||
function onHistoryRestore(sql: string) {
|
||||
@@ -658,7 +678,8 @@ async function setupFileDrop() {
|
||||
class="flex-1"
|
||||
:model-value="activeTab.sql"
|
||||
@update:model-value="onEditorUpdate"
|
||||
@execute="tryExecute()"
|
||||
@selection-change="onEditorSelectionChange"
|
||||
@execute="tryExecute"
|
||||
/>
|
||||
</div>
|
||||
</Pane>
|
||||
@@ -670,7 +691,7 @@ async function setupFileDrop() {
|
||||
@replace-sql="replaceActiveSql"
|
||||
@append-sql="appendActiveSql"
|
||||
/>
|
||||
<DataGrid v-if="activeTab.result" :key="activeTab.id" class="flex-1 min-h-0" :result="activeTab.result" :sql="activeTab.sql" />
|
||||
<DataGrid v-if="activeTab.result" :key="activeTab.id" class="flex-1 min-h-0" :result="activeTab.result" :sql="activeTab.lastExecutedSql || activeTab.sql" />
|
||||
<div v-else class="flex-1 min-h-0 flex items-center justify-center text-muted-foreground text-sm">
|
||||
{{ t('editor.pressToExecute') }}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, watch, shallowRef } from "vue";
|
||||
import type { EditorView as EditorViewType } from "@codemirror/view";
|
||||
import { resolveExecutableSql } from "@/lib/sqlExecutionTarget";
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string;
|
||||
@@ -9,7 +10,8 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:modelValue": [value: string];
|
||||
execute: [];
|
||||
"selectionChange": [value: string];
|
||||
execute: [sql: string];
|
||||
}>();
|
||||
|
||||
const editorRef = ref<HTMLDivElement>();
|
||||
@@ -58,6 +60,15 @@ function resetZoom() {
|
||||
setFontSize(DEFAULT_FONT_SIZE);
|
||||
}
|
||||
|
||||
function selectedSqlFromView(currentView: EditorViewType): string {
|
||||
const selection = currentView.state.selection.main;
|
||||
return currentView.state.sliceDoc(selection.from, selection.to);
|
||||
}
|
||||
|
||||
function executableSqlFromView(currentView: EditorViewType): string {
|
||||
return resolveExecutableSql(currentView.state.doc.toString(), selectedSqlFromView(currentView));
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (!editorRef.value) return;
|
||||
|
||||
@@ -111,7 +122,7 @@ onMounted(async () => {
|
||||
{
|
||||
key: "Mod-Enter",
|
||||
run: () => {
|
||||
emit("execute");
|
||||
if (view.value) emit("execute", executableSqlFromView(view.value));
|
||||
return true;
|
||||
},
|
||||
},
|
||||
@@ -128,6 +139,9 @@ onMounted(async () => {
|
||||
if (update.docChanged) {
|
||||
emit("update:modelValue", update.state.doc.toString());
|
||||
}
|
||||
if (update.selectionSet || update.docChanged) {
|
||||
emit("selectionChange", selectedSqlFromView(update.view));
|
||||
}
|
||||
}),
|
||||
fontSizeTheme.of(fontTheme(EditorView, fontSize.value)),
|
||||
EditorView.domEventHandlers({
|
||||
|
||||
@@ -6,7 +6,7 @@ export default {
|
||||
newConnection: "New Connection",
|
||||
newQuery: "New Query",
|
||||
execute: "Execute",
|
||||
executeShortcut: "Execute (Cmd+Enter)",
|
||||
executeShortcut: "Execute selection/query (Cmd+Enter)",
|
||||
},
|
||||
sidebar: {
|
||||
connections: "CONNECTIONS",
|
||||
|
||||
@@ -6,7 +6,7 @@ export default {
|
||||
newConnection: "新建连接",
|
||||
newQuery: "新建查询",
|
||||
execute: "执行",
|
||||
executeShortcut: "执行 (Cmd+Enter)",
|
||||
executeShortcut: "执行选中/全部 (Cmd+Enter)",
|
||||
},
|
||||
sidebar: {
|
||||
connections: "连接",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export function resolveExecutableSql(fullSql: string, selectedSql: string): string {
|
||||
const trimmedSelection = selectedSql.trim();
|
||||
return trimmedSelection ? trimmedSelection : fullSql;
|
||||
}
|
||||
@@ -55,6 +55,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
if (!tab || tab.database === database) return;
|
||||
tab.database = database;
|
||||
tab.result = undefined;
|
||||
tab.lastExecutedSql = undefined;
|
||||
tab.tableMeta = undefined;
|
||||
}
|
||||
|
||||
@@ -64,6 +65,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
tab.connectionId = connectionId;
|
||||
tab.database = database;
|
||||
tab.result = undefined;
|
||||
tab.lastExecutedSql = undefined;
|
||||
tab.tableMeta = undefined;
|
||||
}
|
||||
|
||||
@@ -76,9 +78,17 @@ export const useQueryStore = defineStore("query", () => {
|
||||
const tab = tabs.value.find((t) => t.id === activeTabId.value);
|
||||
if (!tab || !tab.sql.trim()) return;
|
||||
|
||||
await executeCurrentSql(tab.sql);
|
||||
}
|
||||
|
||||
async function executeCurrentSql(sql: string) {
|
||||
const tab = tabs.value.find((t) => t.id === activeTabId.value);
|
||||
if (!tab || !sql.trim()) return;
|
||||
|
||||
tab.isExecuting = true;
|
||||
tab.lastExecutedSql = sql;
|
||||
try {
|
||||
tab.result = await api.executeQuery(tab.connectionId, tab.database, tab.sql);
|
||||
tab.result = await api.executeQuery(tab.connectionId, tab.database, sql);
|
||||
} catch (e: any) {
|
||||
tab.result = {
|
||||
columns: ["Error"],
|
||||
@@ -110,5 +120,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
updateConnection,
|
||||
setTableMeta,
|
||||
executeCurrentTab,
|
||||
executeCurrentSql,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -95,6 +95,7 @@ export interface QueryTab {
|
||||
connectionId: string;
|
||||
database: string;
|
||||
sql: string;
|
||||
lastExecutedSql?: string;
|
||||
result?: QueryResult;
|
||||
isExecuting: boolean;
|
||||
mode: "data" | "query" | "redis" | "mongo";
|
||||
|
||||
Reference in New Issue
Block a user