fix(desktop): align import and tab bar chrome

This commit is contained in:
t8y2 committed 2026-07-07 01:31:27 +08:00
1 parent 05e64ad43a
commit c27fee19d6
2 files changed
+63 -15

No files matched your search

@@ -102,6 +102,7 @@ const progressPercent = computed(() => {
if (!p || p.totalRows <= 0) return 0; if (!p || p.totalRows <= 0) return 0;
return Math.min(100, Math.round((p.rowsImported / p.totalRows) * 100)); return Math.min(100, Math.round((p.rowsImported / p.totalRows) * 100));
}); });
const currentStepIndex = computed(() => wizardSteps.findIndex((step) => step.value === wizardStep.value));
const targetLabel = computed(() => { const targetLabel = computed(() => {
const pieces = [selectedConnection.value?.name, props.prefillDatabase, props.prefillSchema, props.prefillTable].filter(Boolean); const pieces = [selectedConnection.value?.name, props.prefillDatabase, props.prefillSchema, props.prefillTable].filter(Boolean);
return pieces.join(" / "); return pieces.join(" / ");
@@ -269,6 +270,34 @@ function canOpenStep(step: TableImportWizardStep) {
return false; return false;
} }
function wizardStepIndex(step: TableImportWizardStep) {
return wizardSteps.findIndex((item) => item.value === step);
}
function isWizardStepActive(step: TableImportWizardStep) {
return step === wizardStep.value;
}
function isWizardStepComplete(step: TableImportWizardStep) {
return wizardStepIndex(step) < currentStepIndex.value;
}
function wizardStepConnectorClass(index: number, leading: boolean) {
return (leading ? index <= currentStepIndex.value : index < currentStepIndex.value) ? "bg-primary/60" : "bg-border";
}
function wizardStepTextClass(step: TableImportWizardStep) {
if (isWizardStepActive(step)) return "text-foreground";
if (isWizardStepComplete(step)) return "text-foreground hover:bg-muted/40";
return "text-muted-foreground";
}
function wizardStepCircleClass(step: TableImportWizardStep) {
if (isWizardStepActive(step)) return "border-primary bg-primary text-primary-foreground shadow-sm";
if (isWizardStepComplete(step)) return "border-primary/70 bg-background text-primary";
return "border-border bg-background text-muted-foreground";
}
async function goNext() { async function goNext() {
if (wizardStep.value === "options" && !preview.value) { if (wizardStep.value === "options" && !preview.value) {
await loadPreview(); await loadPreview();
@@ -361,8 +390,8 @@ watch([sourceFormat, delimiter, hasHeader, trimValues, emptyStringAsNull, select
<template> <template>
<Dialog v-model:open="open"> <Dialog v-model:open="open">
<DialogScrollContent class="sm:max-w-[980px] pt-12" :trap-focus="false" @interact-outside.prevent> <DialogScrollContent class="sm:max-w-[980px]" :trap-focus="false" @interact-outside.prevent>
<DialogHeader> <DialogHeader class="pr-8">
<DialogTitle class="flex items-center gap-2 text-base"> <DialogTitle class="flex items-center gap-2 text-base">
<FileUp class="h-4 w-4" /> <FileUp class="h-4 w-4" />
{{ t("tableImport.title") }} {{ t("tableImport.title") }}
@@ -370,26 +399,43 @@ watch([sourceFormat, delimiter, hasHeader, trimValues, emptyStringAsNull, select
</DialogHeader> </DialogHeader>
<div class="space-y-4 py-2"> <div class="space-y-4 py-2">
<div class="grid grid-cols-[1fr_auto] gap-2"> <div class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-2">
<input ref="fileInput" type="file" accept=".csv,.tsv,.txt,.json,.xlsx,.xlsm,.xls" class="hidden" @change="handleFileInputChange" /> <input ref="fileInput" type="file" accept=".csv,.tsv,.txt,.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="flex h-10 min-w-0 items-center gap-2 rounded-md border bg-muted/20 px-3">
<div class="truncate text-xs text-muted-foreground">{{ t("tableImport.target") }}</div> <span class="shrink-0 text-xs text-muted-foreground">{{ t("tableImport.target") }}</span>
<div class="truncate text-sm font-medium"> <span class="min-w-0 truncate text-sm font-medium">
{{ targetLabel || t("editor.noDatabase") }} {{ targetLabel || t("editor.noDatabase") }}
</div> </span>
</div> </div>
<Button variant="outline" size="sm" :disabled="running || loadingPreview" @click="selectFile"> <Button variant="outline" class="h-10 px-3" :disabled="running || loadingPreview" @click="selectFile">
<Loader2 v-if="loadingPreview" class="mr-1.5 h-3.5 w-3.5 animate-spin" /> <Loader2 v-if="loadingPreview" class="mr-1.5 h-3.5 w-3.5 animate-spin" />
<Upload v-else class="mr-1.5 h-3.5 w-3.5" /> <Upload v-else class="mr-1.5 h-3.5 w-3.5" />
{{ selectedSource ? t("tableImport.changeFile") : t("tableImport.selectFile") }} {{ selectedSource ? t("tableImport.changeFile") : t("tableImport.selectFile") }}
</Button> </Button>
</div> </div>
<div class="grid grid-cols-5 gap-1 rounded-md border bg-muted/20 p-1"> <nav class="rounded-md border bg-muted/20 px-3 py-2" :aria-label="t('tableImport.progress')">
<button v-for="step in wizardSteps" :key="step.value" type="button" class="h-8 rounded px-2 text-xs font-medium" :class="wizardStep === step.value ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground'" :disabled="!canOpenStep(step.value)" @click="wizardStep = step.value"> <ol class="grid grid-cols-5">
{{ t(step.labelKey) }} <li v-for="(step, index) in wizardSteps" :key="step.value" class="relative flex min-w-0 justify-center">
</button> <div v-if="index > 0" class="pointer-events-none absolute left-0 right-1/2 top-3.5 h-px" :class="wizardStepConnectorClass(index, true)" />
</div> <div v-if="index < wizardSteps.length - 1" class="pointer-events-none absolute left-1/2 right-0 top-3.5 h-px" :class="wizardStepConnectorClass(index, false)" />
<button
type="button"
class="relative z-10 flex min-w-0 flex-col items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors"
:class="[wizardStepTextClass(step.value), canOpenStep(step.value) ? 'cursor-pointer' : 'cursor-default']"
:disabled="!canOpenStep(step.value)"
:aria-current="isWizardStepActive(step.value) ? 'step' : undefined"
@click="wizardStep = step.value"
>
<span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-full border text-[11px] font-semibold" :class="wizardStepCircleClass(step.value)">
<Check v-if="isWizardStepComplete(step.value)" class="h-3.5 w-3.5" />
<span v-else>{{ index + 1 }}</span>
</span>
<span class="max-w-full truncate">{{ t(step.labelKey) }}</span>
</button>
</li>
</ol>
</nav>
<div v-if="wizardStep === 'source'" class="space-y-4"> <div v-if="wizardStep === 'source'" class="space-y-4">
<div class="rounded-md border border-dashed p-6 text-center"> <div class="rounded-md border border-dashed p-6 text-center">
@@ -423,6 +423,8 @@ const showFixedTabScrollbar = computed(() => hasFixedTabOverflow.value);
const showRegularTabOverflowControls = computed(() => regularTabs.value.length > 0 && hasTabOverflow.value); const showRegularTabOverflowControls = computed(() => regularTabs.value.length > 0 && hasTabOverflow.value);
const regularTabOverflowOpen = ref(false); const regularTabOverflowOpen = ref(false);
const fixedTabOverflowOpen = ref(false); const fixedTabOverflowOpen = ref(false);
const tabBarClass = computed(() => [isClassicLayout.value ? "bg-muted" : "border-b bg-background", hasFixedTabs.value ? "flex-col" : "", isClassicLayout.value && hasFixedTabs.value ? "border-b" : ""]);
const regularTabRowClass = computed(() => [isClassicLayout.value ? "h-9 items-stretch" : "h-10 items-center px-2", isClassicLayout.value && !hasFixedTabs.value ? "border-b" : ""]);
function tabMenuIcon(tab: QueryTab) { function tabMenuIcon(tab: QueryTab) {
if (tab.mode === "data" || tab.mode === "mongo" || tab.mode === "redis") return Table2; if (tab.mode === "data" || tab.mode === "mongo" || tab.mode === "redis") return Table2;
@@ -524,8 +526,8 @@ function onOverflowItemKeydown(event: KeyboardEvent, tabId: string, kind: "regul
</script> </script>
<template> <template>
<div v-if="queryStore.tabs.length > 0 || driverStoreOpen || settingsPageOpen" class="app-tab-bar relative flex w-full min-w-0 shrink-0 overflow-hidden border-b" :class="[isClassicLayout ? 'bg-muted' : 'bg-background', hasFixedTabs ? 'flex-col' : '']"> <div v-if="queryStore.tabs.length > 0 || driverStoreOpen || settingsPageOpen" class="app-tab-bar relative flex w-full min-w-0 shrink-0 overflow-hidden" :class="tabBarClass">
<div class="flex w-full min-w-0 shrink-0 overflow-hidden" :class="isClassicLayout ? 'h-9 items-stretch' : 'h-10 items-center px-2'"> <div class="flex w-full min-w-0 shrink-0 overflow-hidden" :class="regularTabRowClass">
<div class="app-tab-strip relative h-full min-w-0 flex-1 overflow-hidden"> <div class="app-tab-strip relative h-full min-w-0 flex-1 overflow-hidden">
<div v-if="showRegularTabScrollbar" class="app-tab-scrollbar" :class="{ 'app-tab-scrollbar--dragging': isScrollbarDragging }" @pointerdown="startScrollbarDrag"> <div v-if="showRegularTabScrollbar" class="app-tab-scrollbar" :class="{ 'app-tab-scrollbar--dragging': isScrollbarDragging }" @pointerdown="startScrollbarDrag">
<div class="app-tab-scrollbar__thumb" :style="tabScrollbarThumbStyle" /> <div class="app-tab-scrollbar__thumb" :style="tabScrollbarThumbStyle" />