From 7d2c17559f68b6cf1aedb5088aadeb3178bbb9cc Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Thu, 11 Jun 2026 01:24:26 +0800 Subject: [PATCH] feat: support drag-and-drop .sql files into the app window --- apps/desktop/src/composables/useFileDrop.ts | 141 ++++++++++++++------ 1 file changed, 97 insertions(+), 44 deletions(-) diff --git a/apps/desktop/src/composables/useFileDrop.ts b/apps/desktop/src/composables/useFileDrop.ts index d70647c74..34227b9e4 100644 --- a/apps/desktop/src/composables/useFileDrop.ts +++ b/apps/desktop/src/composables/useFileDrop.ts @@ -1,5 +1,6 @@ import { useI18n } from "vue-i18n"; import { uuid } from "@/lib/utils"; +import { isTauriRuntime } from "@/lib/tauriRuntime"; import { useConnectionStore } from "@/stores/connectionStore"; import { useQueryStore } from "@/stores/queryStore"; import { useToast } from "@/composables/useToast"; @@ -15,6 +16,10 @@ function getDbType(path: string): "sqlite" | "duckdb" | null { return null; } +function isSqlFilePath(path: string): boolean { + return /\.sql$/i.test(path); +} + function getDataFileQuery(path: string): Promise { return api.buildDroppedFilePreviewSql({ path }); } @@ -25,60 +30,108 @@ export function useFileDrop() { const queryStore = useQueryStore(); const { toast } = useToast(); - async function setupFileDrop() { - const { getCurrentWebview } = await import("@tauri-apps/api/webview"); - const webview = getCurrentWebview(); - await webview.onDragDropEvent(async (event) => { - if (event.payload.type !== "drop") return; - for (const path of event.payload.paths) { - const name = path.split("/").pop()?.split("\\").pop() || path; + async function openDroppedSqlFile(name: string, content: string) { + const connectionId = connectionStore.activeConnectionId || connectionStore.connections[0]?.id || ""; + const connection = connectionId ? connectionStore.getConfig(connectionId) : undefined; + const database = connection?.database || ""; + const tabId = queryStore.createTab(connectionId, database, name, "query"); + queryStore.updateSql(tabId, content); + toast(t("welcome.fileOpened", { name })); + } - const dataQuery = await getDataFileQuery(path); - if (dataQuery) { + async function setupFileDrop() { + if (isTauriRuntime()) { + const { getCurrentWebview } = await import("@tauri-apps/api/webview"); + const webview = getCurrentWebview(); + await webview.onDragDropEvent(async (event) => { + if (event.payload.type !== "drop") return; + for (const path of event.payload.paths) { + const name = path.split("/").pop()?.split("\\").pop() || path; + + const dataQuery = await getDataFileQuery(path); + if (dataQuery) { + const config: ConnectionConfig = { + id: uuid(), + name: `[Preview] ${name}`, + db_type: "duckdb", + driver_profile: "duckdb", + driver_label: "DuckDB", + url_params: "", + host: ":memory:", + port: 0, + username: "", + password: "", + }; + const connectionId = await api.connectDb(config); + connectionStore.addEphemeralConnection({ ...config, id: connectionId }); + const tabId = queryStore.createTab(connectionId, "", name, "query"); + queryStore.updateSql(tabId, dataQuery); + queryStore.executeCurrentTab(); + toast(t("welcome.fileOpened", { name })); + continue; + } + + if (isSqlFilePath(path)) { + try { + const content = await api.readExternalSqlFile(path); + await openDroppedSqlFile(name, content); + } catch (e: any) { + toast(t("toolbar.sqlOpenFailed", { message: e?.message || String(e) }), 5000); + } + continue; + } + + const dbType = getDbType(path); + if (!dbType) continue; const config: ConnectionConfig = { id: uuid(), - name: `[Preview] ${name}`, - db_type: "duckdb", - driver_profile: "duckdb", - driver_label: "DuckDB", + name, + db_type: dbType, + driver_profile: dbType, + driver_label: dbType === "duckdb" ? "DuckDB" : "SQLite", url_params: "", - host: ":memory:", + host: path, port: 0, username: "", password: "", }; - const connectionId = await api.connectDb(config); - connectionStore.addEphemeralConnection({ ...config, id: connectionId }); - const tabId = queryStore.createTab(connectionId, "", name, "query"); - queryStore.updateSql(tabId, dataQuery); - queryStore.executeCurrentTab(); - toast(t("welcome.fileOpened", { name })); - continue; + try { + await connectionStore.addConnection(config); + void connectionStore.connect(config); + toast(t("welcome.fileOpened", { name })); + } catch (e: any) { + toast(t("connection.saveFailed", { message: e?.message || String(e) }), 5000); + } } - - const dbType = getDbType(path); - if (!dbType) continue; - const config: ConnectionConfig = { - id: uuid(), - name, - db_type: dbType, - driver_profile: dbType, - driver_label: dbType === "duckdb" ? "DuckDB" : "SQLite", - url_params: "", - host: path, - port: 0, - username: "", - password: "", - }; - try { - await connectionStore.addConnection(config); - void connectionStore.connect(config); - toast(t("welcome.fileOpened", { name })); - } catch (e: any) { - toast(t("connection.saveFailed", { message: e?.message || String(e) }), 5000); + }); + } else { + document.addEventListener("drop", (event: DragEvent) => { + const files = event.dataTransfer?.files; + if (!files || files.length === 0) return; + for (let i = 0; i < files.length; i++) { + const file = files[i]; + if (!isSqlFilePath(file.name)) continue; + event.preventDefault(); + const reader = new FileReader(); + reader.onload = () => { + if (typeof reader.result === "string") { + void openDroppedSqlFile(file.name, reader.result); + } + }; + reader.readAsText(file); } - } - }); + }); + document.addEventListener("dragover", (event: DragEvent) => { + const files = event.dataTransfer?.files; + if (!files || files.length === 0) return; + for (let i = 0; i < files.length; i++) { + if (isSqlFilePath(files[i].name)) { + event.preventDefault(); + return; + } + } + }); + } } return { setupFileDrop };