fix(web): align file picker behavior
This commit is contained in:
parent
97b77ec46d
commit
dc2164dd62
|
|
@ -26,6 +26,7 @@ type ConfigTab = "connection" | "ssh";
|
|||
const { t } = useI18n();
|
||||
const { toast } = useToast();
|
||||
const open = defineModel<boolean>("open", { default: false });
|
||||
const isDesktop = isTauriRuntime();
|
||||
|
||||
const props = defineProps<{
|
||||
editConfig?: ConnectionConfig;
|
||||
|
|
@ -770,7 +771,7 @@ async function browseDbFilePath() {
|
|||
<Label class="text-right">{{ t("connection.filePath") }}</Label>
|
||||
<div class="col-span-3 flex items-center gap-1">
|
||||
<Input v-model="form.host" class="flex-1" placeholder="/path/to/database.db" />
|
||||
<Tooltip>
|
||||
<Tooltip v-if="isDesktop">
|
||||
<TooltipTrigger as-child>
|
||||
<Button variant="outline" size="icon" class="h-9 w-9 shrink-0" @click="browseDbFilePath">
|
||||
<FolderOpen class="h-4 w-4" />
|
||||
|
|
@ -980,7 +981,7 @@ async function browseDbFilePath() {
|
|||
placeholder="~/.ssh/id_rsa"
|
||||
:disabled="!form.ssh_enabled"
|
||||
/>
|
||||
<Tooltip>
|
||||
<Tooltip v-if="isDesktop">
|
||||
<TooltipTrigger as-child>
|
||||
<Button
|
||||
variant="outline"
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ const cancelling = ref(false);
|
|||
const importId = ref("");
|
||||
const progress = ref<api.TableImportProgress | null>(null);
|
||||
const errorMessage = ref("");
|
||||
const fileInput = ref<HTMLInputElement | null>(null);
|
||||
|
||||
const selectedConnection = computed(() =>
|
||||
props.prefillConnectionId ? store.getConfig(props.prefillConnectionId) : undefined,
|
||||
|
|
@ -118,8 +119,34 @@ async function loadTargetColumns() {
|
|||
}
|
||||
}
|
||||
|
||||
async function previewSelectedImportFile(fileOrPath: string | File) {
|
||||
if (isTauriRuntime()) {
|
||||
return api.previewTableImportFile(fileOrPath as string);
|
||||
}
|
||||
const { previewTableImportFile } = await import("@/lib/http");
|
||||
return previewTableImportFile(fileOrPath as File);
|
||||
}
|
||||
|
||||
async function loadPreview(fileOrPath: string | File) {
|
||||
loadingPreview.value = true;
|
||||
errorMessage.value = "";
|
||||
try {
|
||||
preview.value = await previewSelectedImportFile(fileOrPath);
|
||||
applyAutoMapping();
|
||||
} catch (e: any) {
|
||||
preview.value = null;
|
||||
columnMapping.value = {};
|
||||
errorMessage.value = String(e?.message || e);
|
||||
} finally {
|
||||
loadingPreview.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function selectFile() {
|
||||
if (!isTauriRuntime()) return;
|
||||
if (!isTauriRuntime()) {
|
||||
fileInput.value?.click();
|
||||
return;
|
||||
}
|
||||
const { open } = await import("@tauri-apps/plugin-dialog");
|
||||
const selected = await open({
|
||||
multiple: false,
|
||||
|
|
@ -132,18 +159,15 @@ async function selectFile() {
|
|||
});
|
||||
if (!selected || Array.isArray(selected)) return;
|
||||
|
||||
loadingPreview.value = true;
|
||||
errorMessage.value = "";
|
||||
try {
|
||||
preview.value = await api.previewTableImportFile(selected);
|
||||
applyAutoMapping();
|
||||
} catch (e: any) {
|
||||
preview.value = null;
|
||||
columnMapping.value = {};
|
||||
errorMessage.value = String(e?.message || e);
|
||||
} finally {
|
||||
loadingPreview.value = false;
|
||||
}
|
||||
await loadPreview(selected);
|
||||
}
|
||||
|
||||
async function handleFileInputChange(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const file = input.files?.[0];
|
||||
input.value = "";
|
||||
if (!file || running.value) return;
|
||||
await loadPreview(file);
|
||||
}
|
||||
|
||||
function updateMapping(sourceColumn: string, value: any) {
|
||||
|
|
@ -227,6 +251,13 @@ watch(open, (value) => {
|
|||
|
||||
<div class="space-y-4 py-2">
|
||||
<div class="grid grid-cols-[1fr_auto] gap-2">
|
||||
<input
|
||||
ref="fileInput"
|
||||
type="file"
|
||||
accept=".csv,.tsv,.json,.xlsx,.xlsm,.xls"
|
||||
class="hidden"
|
||||
@change="handleFileInputChange"
|
||||
/>
|
||||
<div class="min-w-0 rounded-md border bg-muted/20 px-3 py-2">
|
||||
<div class="truncate text-xs text-muted-foreground">{{ t("tableImport.target") }}</div>
|
||||
<div class="truncate text-sm font-medium">
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ const props = defineProps<{
|
|||
|
||||
const store = useConnectionStore();
|
||||
|
||||
const fileInput = ref<HTMLInputElement | null>(null);
|
||||
const filePath = ref("");
|
||||
const preview = ref<SqlFilePreview | null>(null);
|
||||
const selectingFile = ref(false);
|
||||
|
|
@ -198,11 +199,19 @@ async function loadDatabasesForConnection(id: string) {
|
|||
}
|
||||
}
|
||||
|
||||
async function loadPreview(path: string) {
|
||||
async function previewSelectedSqlFile(fileOrPath: string | File) {
|
||||
if (isTauriRuntime()) {
|
||||
return previewSqlFile(fileOrPath as string);
|
||||
}
|
||||
const { previewSqlFile: previewWebSqlFile } = await import("@/lib/http");
|
||||
return previewWebSqlFile(fileOrPath as File);
|
||||
}
|
||||
|
||||
async function loadPreview(fileOrPath: string | File) {
|
||||
loadingPreview.value = true;
|
||||
preview.value = null;
|
||||
try {
|
||||
preview.value = await previewSqlFile(path);
|
||||
preview.value = await previewSelectedSqlFile(fileOrPath);
|
||||
filePath.value = preview.value.filePath;
|
||||
resetExecution();
|
||||
} catch (e: any) {
|
||||
|
|
@ -214,7 +223,10 @@ async function loadPreview(path: string) {
|
|||
|
||||
async function selectFile() {
|
||||
if (running.value) return;
|
||||
if (!isTauriRuntime()) return;
|
||||
if (!isTauriRuntime()) {
|
||||
fileInput.value?.click();
|
||||
return;
|
||||
}
|
||||
selectingFile.value = true;
|
||||
try {
|
||||
const { open } = await import("@tauri-apps/plugin-dialog");
|
||||
|
|
@ -232,6 +244,27 @@ async function selectFile() {
|
|||
}
|
||||
}
|
||||
|
||||
async function handleFileInputChange(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const file = input.files?.[0];
|
||||
input.value = "";
|
||||
if (!file || running.value) return;
|
||||
selectingFile.value = true;
|
||||
try {
|
||||
await loadPreview(file);
|
||||
} finally {
|
||||
selectingFile.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function listenProgress(id: string, handler: (next: SqlFileProgress) => void): Promise<() => void> {
|
||||
if (isTauriRuntime()) {
|
||||
return listenSqlFileProgress(handler);
|
||||
}
|
||||
const { listenSqlFileProgressById } = await import("@/lib/http");
|
||||
return listenSqlFileProgressById(id, handler);
|
||||
}
|
||||
|
||||
async function startExecution() {
|
||||
if (!canStart.value || !preview.value) return;
|
||||
|
||||
|
|
@ -253,7 +286,7 @@ async function startExecution() {
|
|||
return;
|
||||
}
|
||||
|
||||
unlisten = await listenSqlFileProgress((next) => {
|
||||
unlisten = await listenProgress(id, (next) => {
|
||||
if (next.executionId !== id) return;
|
||||
progress.value = next;
|
||||
terminalStatus.value = next.status;
|
||||
|
|
@ -351,6 +384,7 @@ watch(open, (value) => {
|
|||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<input ref="fileInput" type="file" accept=".sql,text/sql" class="hidden" @change="handleFileInputChange" />
|
||||
<Input
|
||||
:model-value="filePath"
|
||||
readonly
|
||||
|
|
|
|||
Loading…
Reference in New Issue