From 465c631b39ce4948ad595b2019e6b8d5b25621d2 Mon Sep 17 00:00:00 2001 From: "Crane.z" <1481445951@qq.com> Date: Fri, 17 Jul 2026 16:25:30 +0800 Subject: [PATCH] feat:add run sql to client --- ui/chen/api/index.ts | 18 +++++++ ui/chen/components/QueryConsolePanel.vue | 50 +++++++++++++++++-- ui/chen/composables/useChenQueryConsole.ts | 10 ++++ ui/chen/composables/useChenWorkspaceTabs.ts | 1 + ui/chen/types.ts | 1 + ui/chen/workspaces/DatabaseSessionSurface.vue | 22 +++++++- 6 files changed, 96 insertions(+), 6 deletions(-) diff --git a/ui/chen/api/index.ts b/ui/chen/api/index.ts index 0ca5934c..12cf8628 100644 --- a/ui/chen/api/index.ts +++ b/ui/chen/api/index.ts @@ -50,6 +50,24 @@ export async function fetchChenProfile(chenToken: string) { return readJson(response); } +export async function uploadChenSqlFile( + chenToken: string, + file: File, + fetchImpl: typeof fetch = fetch +) { + const body = new FormData(); + body.append("file", file); + const response = await fetchImpl(chenPath("/api/console/upload"), { + method: "POST", + credentials: "include", + headers: buildHeaders(chenToken, getWebApiMutationHeaders()), + body + }); + const result = await readJson<{ path?: string }>(response); + if (!result.path?.trim()) throw new Error("Chen upload returned no SQL file path"); + return { path: result.path }; +} + export function sanitizeChenExportFileName(value: string, fallback = "chen-export") { const withoutControls = Array.from(value) .filter((character) => { diff --git a/ui/chen/components/QueryConsolePanel.vue b/ui/chen/components/QueryConsolePanel.vue index 138c07cd..0f8fd4c3 100644 --- a/ui/chen/components/QueryConsolePanel.vue +++ b/ui/chen/components/QueryConsolePanel.vue @@ -10,7 +10,6 @@ import { useChenSqlSnippets } from "~/chen/composables/useChenSqlSnippets"; const props = defineProps<{ tab: ChenQueryConsoleTab - contextLabel: string dbType: string canCopy: boolean }>(); @@ -18,6 +17,7 @@ const props = defineProps<{ const emit = defineEmits<{ run: [tab: ChenQueryConsoleTab, selectedSql: string] cancel: [tab: ChenQueryConsoleTab] + uploadSql: [tab: ChenQueryConsoleTab, file: File] dataViewAction: [tab: ChenQueryConsoleTab, result: ChenQueryResultTab, action: ChenDataViewAction, data?: ChenDataViewActionData] dismissMessage: [tab: ChenQueryConsoleTab] updateStatement: [tab: ChenQueryConsoleTab, value: string] @@ -26,6 +26,7 @@ const emit = defineEmits<{ }>(); const sqlEditor = ref<{ selectedText: () => string } | null>(null); +const sqlUploadInput = ref(null); const hasSelection = ref(false); const messageOpen = ref(false); const saveSnippetDialogOpen = ref(false); @@ -55,6 +56,32 @@ function requestErrorMessage(cause: unknown) { return cause instanceof Error ? cause.message : String(cause); } +function handleSqlFileChange(event: Event) { + const input = event.target as HTMLInputElement; + const file = input.files?.[0]; + input.value = ""; + if (!file) return; + + if (!file.name.toLowerCase().endsWith(".sql")) { + toast.add({ + title: "SQL upload failed", + description: "Choose a .sql file.", + color: "error" + }); + return; + } + if (file.size === 0) { + toast.add({ + title: "SQL upload failed", + description: "SQL file is empty.", + color: "error" + }); + return; + } + + emit("uploadSql", props.tab, file); +} + async function openSnippetDialog() { selectSnippetDialogOpen.value = true; try { @@ -158,9 +185,6 @@ onBeforeUnmount(clearMessageTimer); :disabled="!tab.state.canCancel" @click="emit('cancel', tab)" /> - - {{ tab.state.currentContext || contextLabel || 'Context' }} - Save + + + Upload SQL +