feat(app): open sql files in editor

This commit is contained in:
t8y2
2026-05-17 13:59:42 +08:00
parent c3bc9d5d07
commit 7188465161
13 changed files with 576 additions and 15 deletions
+31 -1
View File
@@ -36,6 +36,7 @@ import { resolveDefaultDatabase } from "@/lib/defaultDatabase";
import { buildExecutableObjectSourceSql, objectSourceSaveExecutionMode } from "@/lib/objectSourceEditor";
import { resolveExecutableSql } from "@/lib/sqlExecutionTarget";
import { isTauriRuntime } from "@/lib/tauriRuntime";
import { sqlFileTitleFromPath } from "@/lib/sqlFileOpen";
import {
isCloseTabShortcut,
isExecuteSqlShortcut,
@@ -152,7 +153,7 @@ const { getDatabaseOptions } = useDatabaseOptions();
const { openLineageTarget, openDatabaseSearchTarget, onStructureEditorSaved, openTableTarget } =
useNavigationTargets(dialogs);
const { onExecuteSql, onReloadData, onPaginate, onSort } = useDataGridActions(activeTab);
const { setupTauriListeners, cleanupTauriListeners } = useTauriEvents({ openTableTarget });
const { setupTauriListeners, cleanupTauriListeners } = useTauriEvents({ openTableTarget, openSqlFilePath });
const appVersion = ref("");
const isClassicLayout = computed(() => settingsStore.editorSettings.appLayout === "classic");
@@ -341,6 +342,34 @@ async function openSqlFile() {
}
}
async function openSqlFilePath(path: string) {
if (!isTauriRuntime()) return;
try {
const { readTextFile } = await import("@tauri-apps/plugin-fs");
const content = await readTextFile(path);
const connectionId =
connectionStore.activeConnectionId || activeTab.value?.connectionId || connectionStore.connections[0]?.id || "";
const connection = connectionId ? connectionStore.getConfig(connectionId) : undefined;
const database = activeTab.value?.database || (connection ? resolveDefaultDatabase(connection, []) : "");
const tabId = queryStore.createTab(connectionId, database, sqlFileTitleFromPath(path), "query");
queryStore.updateSql(tabId, content);
} catch (e: any) {
toast(t("toolbar.sqlOpenFailed", { message: e?.message || String(e) }), 5000);
}
}
async function openPendingSqlFiles() {
if (!isTauriRuntime()) return;
try {
const paths = await api.pendingOpenSqlFiles();
for (const path of paths) {
await openSqlFilePath(path);
}
} catch {
/* ignore startup file-open probing errors */
}
}
async function newQuery() {
const connId = connectionStore.activeConnectionId || connectionStore.connections[0]?.id;
if (!connId) return;
@@ -614,6 +643,7 @@ onMounted(async () => {
})
.catch(() => {});
setupTauriListeners();
void openPendingSqlFiles();
console.log(`[STARTUP] onMounted sync done: ${(performance.now() - mountStart).toFixed(0)}ms`);
});
+19 -1
View File
@@ -2,7 +2,10 @@ import { useConnectionStore } from "@/stores/connectionStore";
import { useQueryStore } from "@/stores/queryStore";
import type { NavigationTarget } from "@/composables/useNavigationTargets";
export function useTauriEvents(deps: { openTableTarget: (target: NavigationTarget) => Promise<void> }) {
export function useTauriEvents(deps: {
openTableTarget: (target: NavigationTarget) => Promise<void>;
openSqlFilePath: (path: string) => Promise<void>;
}) {
const connectionStore = useConnectionStore();
const queryStore = useQueryStore();
const unlistenHandles: Array<() => void> = [];
@@ -66,6 +69,21 @@ export function useTauriEvents(deps: { openTableTarget: (target: NavigationTarge
console.error("[DBX] mcp-execute-query error:", e);
}
}).then((unlisten) => unlistenHandles.push(unlisten));
listen<string[]>("dbx-open-sql-files", async (event) => {
try {
for (const path of event.payload) {
await deps.openSqlFilePath(path);
}
import("@tauri-apps/api/window").then(({ getCurrentWindow }) =>
getCurrentWindow()
.setFocus()
.catch(() => {}),
);
} catch (e) {
console.error("[DBX] dbx-open-sql-files error:", e);
}
}).then((unlisten) => unlistenHandles.push(unlisten));
})
.catch(() => {});
}
+1
View File
@@ -89,6 +89,7 @@ export const previewSqlFile = forward("previewSqlFile");
export const executeSqlFile = forward("executeSqlFile");
export const cancelSqlFileExecution = forward("cancelSqlFileExecution");
export const listenSqlFileProgress = forward("listenSqlFileProgress");
export const pendingOpenSqlFiles = forward("pendingOpenSqlFiles");
// Data Transfer
export const startTransfer = forward("startTransfer");
+4
View File
@@ -416,6 +416,10 @@ export async function listenSqlFileProgress(_handler: (progress: SqlFileProgress
return () => {};
}
export async function pendingOpenSqlFiles(): Promise<string[]> {
return [];
}
// ---------------------------------------------------------------------------
// Data Transfer
// ---------------------------------------------------------------------------
+14
View File
@@ -0,0 +1,14 @@
export function isSqlFilePath(path: string): boolean {
return /\.sql$/i.test(path.trim());
}
export function sqlFileTitleFromPath(path: string): string {
const trimmed = path.trim();
const normalized = trimmed.replace(/\\/g, "/");
const name = normalized.split("/").filter(Boolean).pop();
return name || "script.sql";
}
export function externalSqlFilePaths(paths: string[]): string[] {
return paths.filter(isSqlFilePath);
}
+4
View File
@@ -80,6 +80,10 @@ export async function loadAiConfig(): Promise<AiConfig | null> {
return invoke("load_ai_config");
}
export async function pendingOpenSqlFiles(): Promise<string[]> {
return invoke("pending_open_sql_files");
}
// --- AI Conversations ---
export interface AiChatMessage {