mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-04-29 17:36:35 +08:00
fix: 修复镜像列表滚动条问题 #238
This commit is contained in:
@@ -28,7 +28,6 @@ import { Label } from '@/components/ui/label'
|
|||||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
||||||
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
|
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
|
||||||
import {
|
import {
|
||||||
HardDrive,
|
HardDrive,
|
||||||
Upload,
|
Upload,
|
||||||
@@ -512,7 +511,7 @@ onUnmounted(() => {
|
|||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Dialog :open="open" @update:open="emit('update:open', $event)">
|
<Dialog :open="open" @update:open="emit('update:open', $event)">
|
||||||
<DialogContent class="sm:max-w-[600px] max-h-[90vh] overflow-hidden flex flex-col p-0">
|
<DialogContent class="sm:max-w-[600px] max-h-[90vh] overflow-hidden flex flex-col p-0">
|
||||||
<DialogHeader class="px-6 pt-6">
|
<DialogHeader class="px-6 pt-6 shrink-0">
|
||||||
<DialogTitle class="flex items-center gap-2">
|
<DialogTitle class="flex items-center gap-2">
|
||||||
<HardDrive class="h-5 w-5" />
|
<HardDrive class="h-5 w-5" />
|
||||||
{{ t('msd.title') }}
|
{{ t('msd.title') }}
|
||||||
@@ -551,10 +550,11 @@ onUnmounted(() => {
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<Separator />
|
<Separator class="shrink-0" />
|
||||||
|
|
||||||
<Tabs v-model="activeTab" class="flex-1 flex flex-col overflow-hidden px-6 pb-6 pt-4">
|
<div class="flex-1 min-h-0 flex flex-col px-6 pb-6 pt-4">
|
||||||
<TabsList class="w-full grid grid-cols-2">
|
<Tabs v-model="activeTab" class="flex-1 flex flex-col min-h-0">
|
||||||
|
<TabsList class="w-full grid grid-cols-2 shrink-0">
|
||||||
<TabsTrigger value="images">
|
<TabsTrigger value="images">
|
||||||
<Disc class="h-4 w-4 mr-1.5" />
|
<Disc class="h-4 w-4 mr-1.5" />
|
||||||
{{ t('msd.images') }}
|
{{ t('msd.images') }}
|
||||||
@@ -566,15 +566,13 @@ onUnmounted(() => {
|
|||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<!-- Tab Description -->
|
<!-- Tab Description -->
|
||||||
<p class="text-xs text-muted-foreground mt-2 mb-1">
|
<p class="text-xs text-muted-foreground mt-2 mb-1 shrink-0">
|
||||||
{{ activeTab === 'images' ? t('msd.imagesDesc') : t('msd.driveDesc') }}
|
{{ activeTab === 'images' ? t('msd.imagesDesc') : t('msd.driveDesc') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ScrollArea class="flex-1 mt-2">
|
<TabsContent value="images" class="flex-1 min-h-0 m-0 flex flex-col space-y-3">
|
||||||
<!-- Images Tab -->
|
|
||||||
<TabsContent value="images" class="m-0 space-y-3 pr-4">
|
|
||||||
<!-- Compact Upload Toolbar -->
|
<!-- Compact Upload Toolbar -->
|
||||||
<div class="flex items-center gap-2 min-w-0">
|
<div class="shrink-0 flex items-center gap-2 min-w-0">
|
||||||
<label class="flex-1">
|
<label class="flex-1">
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
@@ -598,10 +596,10 @@ onUnmounted(() => {
|
|||||||
{{ t('msd.downloadFromUrl') }}
|
{{ t('msd.downloadFromUrl') }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Progress v-if="uploading" :model-value="uploadProgress" class="h-1" />
|
<Progress v-if="uploading" :model-value="uploadProgress" class="h-1 shrink-0" />
|
||||||
|
|
||||||
<!-- Options - Vertical compact layout -->
|
<!-- Options - Vertical compact layout -->
|
||||||
<div class="flex flex-wrap items-center gap-x-4 gap-y-2 p-2 rounded-lg bg-muted/50 text-xs min-w-0">
|
<div class="shrink-0 flex flex-wrap items-center gap-x-4 gap-y-2 p-2 rounded-lg bg-muted/50 text-xs min-w-0">
|
||||||
<div class="flex items-center gap-1.5">
|
<div class="flex items-center gap-1.5">
|
||||||
<span class="text-muted-foreground whitespace-nowrap">{{ t('msd.storageMode') }}:</span>
|
<span class="text-muted-foreground whitespace-nowrap">{{ t('msd.storageMode') }}:</span>
|
||||||
<HelpTooltip :content="mountMode === 'flash' ? t('help.flashMode') : t('help.cdromMode')" icon-size="sm" />
|
<HelpTooltip :content="mountMode === 'flash' ? t('help.flashMode') : t('help.cdromMode')" icon-size="sm" />
|
||||||
@@ -629,19 +627,20 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Image List -->
|
<!-- Image List -->
|
||||||
<div class="space-y-2 min-w-0">
|
<div class="flex-1 min-h-0 flex flex-col space-y-2 min-w-0">
|
||||||
<div class="flex items-center justify-between">
|
<div class="shrink-0 flex items-center justify-between">
|
||||||
<h4 class="text-sm font-medium">{{ t('msd.imageList') }}</h4>
|
<h4 class="text-sm font-medium">{{ t('msd.imageList') }}</h4>
|
||||||
<Button variant="ghost" size="icon" class="h-7 w-7" @click="loadImages">
|
<Button variant="ghost" size="icon" class="h-7 w-7" @click="loadImages">
|
||||||
<RefreshCw class="h-3.5 w-3.5" :class="{ 'animate-spin': loadingImages }" />
|
<RefreshCw class="h-3.5 w-3.5" :class="{ 'animate-spin': loadingImages }" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="images.length === 0" class="text-center py-6 text-muted-foreground text-sm">
|
<div v-if="images.length === 0" class="shrink-0 text-center py-6 text-muted-foreground text-sm">
|
||||||
{{ t('msd.noImages') }}
|
{{ t('msd.noImages') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="space-y-2">
|
<div v-else class="flex-1 min-h-0 overflow-y-auto pr-2 custom-scrollbar">
|
||||||
|
<div class="space-y-2">
|
||||||
<div
|
<div
|
||||||
v-for="image in images"
|
v-for="image in images"
|
||||||
:key="image.id"
|
:key="image.id"
|
||||||
@@ -719,9 +718,10 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- System Storage Footer -->
|
<!-- System Storage Footer -->
|
||||||
<div v-if="systemStore.diskSpace" class="pt-2 border-t mt-2">
|
<div v-if="systemStore.diskSpace" class="shrink-0 pt-2 border-t mt-2">
|
||||||
<p class="text-[11px] text-muted-foreground text-center">
|
<p class="text-[11px] text-muted-foreground text-center">
|
||||||
{{ t('msd.systemAvailable') }}: {{ formatBytes(systemStore.diskSpace.available) }}
|
{{ t('msd.systemAvailable') }}: {{ formatBytes(systemStore.diskSpace.available) }}
|
||||||
</p>
|
</p>
|
||||||
@@ -729,10 +729,9 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<!-- Drive Tab -->
|
<TabsContent value="drive" class="flex-1 min-h-0 m-0 flex flex-col space-y-4">
|
||||||
<TabsContent value="drive" class="m-0 space-y-4 pr-4">
|
|
||||||
<template v-if="!driveInitialized">
|
<template v-if="!driveInitialized">
|
||||||
<div class="text-center py-8 space-y-4">
|
<div class="shrink-0 text-center py-8 space-y-4">
|
||||||
<HardDrive class="h-10 w-10 mx-auto text-muted-foreground" />
|
<HardDrive class="h-10 w-10 mx-auto text-muted-foreground" />
|
||||||
<p class="text-sm text-muted-foreground">{{ t('msd.driveNotInitialized') }}</p>
|
<p class="text-sm text-muted-foreground">{{ t('msd.driveNotInitialized') }}</p>
|
||||||
<Button size="sm" @click="initializeDrive">
|
<Button size="sm" @click="initializeDrive">
|
||||||
@@ -743,7 +742,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<!-- Drive Info Card -->
|
<!-- Drive Info Card -->
|
||||||
<div class="p-3 rounded-lg border space-y-3" :class="msdConnected && msdMode === 'drive' ? 'border-primary bg-primary/5' : 'bg-muted/50'">
|
<div class="shrink-0 p-3 rounded-lg border space-y-3" :class="msdConnected && msdMode === 'drive' ? 'border-primary bg-primary/5' : 'bg-muted/50'">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<HardDrive class="h-4 w-4 text-muted-foreground" />
|
<HardDrive class="h-4 w-4 text-muted-foreground" />
|
||||||
@@ -801,9 +800,9 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- File Browser -->
|
<!-- File Browser -->
|
||||||
<div class="space-y-2">
|
<div class="flex-1 min-h-0 flex flex-col space-y-2">
|
||||||
<!-- Toolbar -->
|
<!-- Toolbar -->
|
||||||
<div class="flex items-center justify-between gap-2">
|
<div class="shrink-0 flex items-center justify-between gap-2">
|
||||||
<div class="flex items-center gap-1 min-w-0 flex-1">
|
<div class="flex items-center gap-1 min-w-0 flex-1">
|
||||||
<Button
|
<Button
|
||||||
v-if="currentPath !== '/'"
|
v-if="currentPath !== '/'"
|
||||||
@@ -827,7 +826,7 @@ onUnmounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1 shrink-0">
|
<div class="shrink-0 flex items-center gap-1 shrink-0">
|
||||||
<label>
|
<label>
|
||||||
<input type="file" class="hidden" :disabled="uploadingFile" @change="handleFileUpload" />
|
<input type="file" class="hidden" :disabled="uploadingFile" @change="handleFileUpload" />
|
||||||
<Button variant="ghost" size="icon" as="span" class="h-7 w-7 cursor-pointer">
|
<Button variant="ghost" size="icon" as="span" class="h-7 w-7 cursor-pointer">
|
||||||
@@ -843,14 +842,15 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Progress v-if="uploadingFile" :model-value="fileUploadProgress" class="h-1" />
|
<Progress v-if="uploadingFile" :model-value="fileUploadProgress" class="h-1 shrink-0" />
|
||||||
|
|
||||||
<!-- File List -->
|
<!-- File List -->
|
||||||
<div v-if="driveFiles.length === 0" class="text-center py-6 text-muted-foreground text-sm">
|
<div v-if="driveFiles.length === 0" class="shrink-0 text-center py-6 text-muted-foreground text-sm">
|
||||||
{{ t('msd.emptyFolder') }}
|
{{ t('msd.emptyFolder') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="space-y-1">
|
<div v-else class="flex-1 min-h-0 overflow-y-auto pr-2 custom-scrollbar">
|
||||||
|
<div class="space-y-1">
|
||||||
<div
|
<div
|
||||||
v-for="file in driveFiles"
|
v-for="file in driveFiles"
|
||||||
:key="file.path"
|
:key="file.path"
|
||||||
@@ -900,10 +900,11 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</ScrollArea>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
@@ -1102,3 +1103,28 @@ onUnmounted(() => {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.custom-scrollbar::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||||
|
background: hsl(var(--muted-foreground) / 0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: hsl(var(--muted-foreground) / 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Firefox */
|
||||||
|
.custom-scrollbar {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: hsl(var(--muted-foreground) / 0.3) transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { Input } from '@/components/ui/input'
|
|||||||
import { Switch } from '@/components/ui/switch'
|
import { Switch } from '@/components/ui/switch'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -294,8 +293,8 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Sheet :open="open" @update:open="emit('update:open', $event)">
|
<Sheet :open="open" @update:open="emit('update:open', $event)">
|
||||||
<SheetContent side="right" class="w-full sm:max-w-lg overflow-hidden flex flex-col">
|
<SheetContent side="right" class="w-full sm:max-w-lg overflow-hidden flex flex-col h-[dvh]">
|
||||||
<SheetHeader>
|
<SheetHeader class="shrink-0">
|
||||||
<div class="flex items-center justify-between pr-8">
|
<div class="flex items-center justify-between pr-8">
|
||||||
<div>
|
<div>
|
||||||
<SheetTitle class="flex items-center gap-2">
|
<SheetTitle class="flex items-center gap-2">
|
||||||
@@ -314,10 +313,10 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</SheetHeader>
|
</SheetHeader>
|
||||||
|
|
||||||
<Separator class="my-4" />
|
<Separator class="my-4 shrink-0" />
|
||||||
|
|
||||||
<Tabs v-model="activeTab" class="flex-1 flex flex-col overflow-hidden">
|
<Tabs v-model="activeTab" class="flex-1 flex flex-col min-h-0 overflow-hidden">
|
||||||
<TabsList class="w-full grid grid-cols-2">
|
<TabsList class="w-full grid grid-cols-2 shrink-0">
|
||||||
<TabsTrigger value="images">
|
<TabsTrigger value="images">
|
||||||
<Disc class="h-4 w-4 mr-1.5" />
|
<Disc class="h-4 w-4 mr-1.5" />
|
||||||
{{ t('msd.images') }}
|
{{ t('msd.images') }}
|
||||||
@@ -328,11 +327,11 @@ onMounted(async () => {
|
|||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<ScrollArea class="flex-1 mt-4">
|
<div class="flex-1 min-h-0 mt-4 flex flex-col">
|
||||||
<!-- Images Tab -->
|
<!-- Images Tab -->
|
||||||
<TabsContent value="images" class="m-0 space-y-4">
|
<TabsContent value="images" class="flex-1 min-h-0 m-0 flex flex-col space-y-4">
|
||||||
<!-- Upload Area -->
|
<!-- Upload Area -->
|
||||||
<div class="space-y-3">
|
<div class="shrink-0 space-y-3">
|
||||||
<label class="block">
|
<label class="block">
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
@@ -352,7 +351,7 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Options -->
|
<!-- Options -->
|
||||||
<div class="flex items-center gap-4 p-3 rounded-lg bg-muted/50">
|
<div class="shrink-0 flex items-center gap-4 p-3 rounded-lg bg-muted/50">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Switch id="cdrom" v-model:checked="cdromMode" />
|
<Switch id="cdrom" v-model:checked="cdromMode" />
|
||||||
<Label for="cdrom" class="text-xs">{{ t('msd.cdromMode') }}</Label>
|
<Label for="cdrom" class="text-xs">{{ t('msd.cdromMode') }}</Label>
|
||||||
@@ -364,19 +363,20 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Image List -->
|
<!-- Image List -->
|
||||||
<div class="space-y-2">
|
<div class="flex-1 min-h-0 flex flex-col space-y-2">
|
||||||
<div class="flex items-center justify-between">
|
<div class="shrink-0 flex items-center justify-between">
|
||||||
<h4 class="text-sm font-medium">{{ t('msd.imageList') }}</h4>
|
<h4 class="text-sm font-medium">{{ t('msd.imageList') }}</h4>
|
||||||
<Button variant="ghost" size="icon" class="h-7 w-7" @click="loadImages">
|
<Button variant="ghost" size="icon" class="h-7 w-7" @click="loadImages">
|
||||||
<RefreshCw class="h-3.5 w-3.5" :class="{ 'animate-spin': loadingImages }" />
|
<RefreshCw class="h-3.5 w-3.5" :class="{ 'animate-spin': loadingImages }" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="images.length === 0" class="text-center py-6 text-muted-foreground text-sm">
|
<div v-if="images.length === 0" class="shrink-0 text-center py-6 text-muted-foreground text-sm">
|
||||||
{{ t('msd.noImages') }}
|
{{ t('msd.noImages') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="space-y-1.5">
|
<ScrollArea v-else class="flex-1 min-h-0 pr-4">
|
||||||
|
<div class="space-y-1.5">
|
||||||
<div
|
<div
|
||||||
v-for="image in images"
|
v-for="image in images"
|
||||||
:key="image.id"
|
:key="image.id"
|
||||||
@@ -414,13 +414,14 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<!-- Drive Tab -->
|
<!-- Drive Tab -->
|
||||||
<TabsContent value="drive" class="m-0 space-y-4">
|
<TabsContent value="drive" class="flex-1 min-h-0 m-0 flex flex-col space-y-4">
|
||||||
<template v-if="!driveInitialized">
|
<template v-if="!driveInitialized">
|
||||||
<div class="text-center py-8 space-y-4">
|
<div class="shrink-0 text-center py-8 space-y-4">
|
||||||
<HardDrive class="h-10 w-10 mx-auto text-muted-foreground" />
|
<HardDrive class="h-10 w-10 mx-auto text-muted-foreground" />
|
||||||
<p class="text-sm text-muted-foreground">{{ t('msd.driveNotInitialized') }}</p>
|
<p class="text-sm text-muted-foreground">{{ t('msd.driveNotInitialized') }}</p>
|
||||||
<Button size="sm" @click="initializeDrive">
|
<Button size="sm" @click="initializeDrive">
|
||||||
@@ -431,7 +432,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<!-- Drive Info -->
|
<!-- Drive Info -->
|
||||||
<div class="p-3 rounded-lg bg-muted/50 space-y-2">
|
<div class="shrink-0 p-3 rounded-lg bg-muted/50 space-y-2">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
<p class="text-xs text-muted-foreground">{{ t('msd.driveSize') }}: {{ (driveInfo?.size || 0) / 1024 / 1024 }}MB</p>
|
<p class="text-xs text-muted-foreground">{{ t('msd.driveSize') }}: {{ (driveInfo?.size || 0) / 1024 / 1024 }}MB</p>
|
||||||
@@ -459,9 +460,9 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- File Browser -->
|
<!-- File Browser -->
|
||||||
<div class="space-y-2">
|
<div class="flex-1 min-h-0 flex flex-col space-y-2">
|
||||||
<!-- Toolbar -->
|
<!-- Toolbar -->
|
||||||
<div class="flex items-center justify-between gap-2">
|
<div class="shrink-0 flex items-center justify-between gap-2">
|
||||||
<div class="flex items-center gap-1 min-w-0 flex-1">
|
<div class="flex items-center gap-1 min-w-0 flex-1">
|
||||||
<Button
|
<Button
|
||||||
v-if="currentPath !== '/'"
|
v-if="currentPath !== '/'"
|
||||||
@@ -485,7 +486,7 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1 shrink-0">
|
<div class="shrink-0 flex items-center gap-1 shrink-0">
|
||||||
<label>
|
<label>
|
||||||
<input type="file" class="hidden" :disabled="uploadingFile" @change="handleFileUpload" />
|
<input type="file" class="hidden" :disabled="uploadingFile" @change="handleFileUpload" />
|
||||||
<Button variant="ghost" size="icon" as="span" class="h-7 w-7 cursor-pointer">
|
<Button variant="ghost" size="icon" as="span" class="h-7 w-7 cursor-pointer">
|
||||||
@@ -501,14 +502,15 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Progress v-if="uploadingFile" :model-value="fileUploadProgress" class="h-1" />
|
<Progress v-if="uploadingFile" :model-value="fileUploadProgress" class="h-1 shrink-0" />
|
||||||
|
|
||||||
<!-- File List -->
|
<!-- File List -->
|
||||||
<div v-if="driveFiles.length === 0" class="text-center py-6 text-muted-foreground text-sm">
|
<div v-if="driveFiles.length === 0" class="shrink-0 text-center py-6 text-muted-foreground text-sm">
|
||||||
{{ t('msd.emptyFolder') }}
|
{{ t('msd.emptyFolder') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="space-y-1">
|
<ScrollArea v-else class="flex-1 min-h-0 pr-4">
|
||||||
|
<div class="space-y-1">
|
||||||
<div
|
<div
|
||||||
v-for="file in driveFiles"
|
v-for="file in driveFiles"
|
||||||
:key="file.path"
|
:key="file.path"
|
||||||
@@ -550,10 +552,11 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</ScrollArea>
|
</div>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
</Sheet>
|
</Sheet>
|
||||||
@@ -588,3 +591,28 @@ onMounted(async () => {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.custom-scrollbar::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||||
|
background: hsl(var(--muted-foreground) / 0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: hsl(var(--muted-foreground) / 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Firefox */
|
||||||
|
.custom-scrollbar {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: hsl(var(--muted-foreground) / 0.3) transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user