feat: 统一和完善 UI

This commit is contained in:
mofeng-git
2026-07-19 21:43:27 +08:00
parent 5d871e8b21
commit 275c2ca343
25 changed files with 431 additions and 402 deletions

View File

@@ -153,8 +153,8 @@ const downloadProgress = ref<{
} | null>(null)
const TWO_POINT_TWO_GB = 2.2 * 1024 * 1024 * 1024
const tabTriggerClass = 'h-9 rounded-md border-0 bg-transparent text-center text-muted-foreground shadow-none hover:text-foreground data-[state=active]:border-0 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm'
const segmentedGroupClass = 'grid w-full grid-cols-2 items-center gap-1 rounded-md border border-border bg-muted p-1'
const tabTriggerClass = 'h-8 rounded-md border-0 bg-transparent text-center text-muted-foreground shadow-none hover:text-foreground data-[state=active]:border-0 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm'
const segmentedGroupClass = 'grid w-full grid-cols-2 items-center gap-1 rounded-md border border-border bg-muted p-0.5'
const segmentedItemClass = 'h-8 w-full justify-center rounded-md border-0 bg-transparent px-3 text-center text-xs text-muted-foreground shadow-none hover:bg-transparent hover:text-foreground data-[state=on]:border-0 data-[state=on]:bg-background data-[state=on]:text-foreground data-[state=on]:shadow-sm data-[state=on]:hover:bg-background'
const diskMode = computed(() => systemStore.msd?.diskMode ?? 'single')
@@ -688,17 +688,17 @@ onUnmounted(() => {
<TooltipProvider>
<Dialog :open="open" @update:open="emit('update:open', $event)">
<DialogContent class="max-h-[90vh] overflow-hidden p-0 sm:max-w-[760px] flex flex-col">
<DialogHeader class="px-6 pt-6 shrink-0">
<DialogHeader class="px-5 pt-4 shrink-0">
<DialogTitle class="flex items-center gap-2">
<HardDrive class="h-5 w-5" />
<HardDrive class="size-5" />
{{ t('msd.title') }}
</DialogTitle>
<DialogDescription as="div" class="mt-1 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<span class="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1">
<span :class="msdConnected ? 'text-success' : 'text-muted-foreground'" class="flex items-center gap-1.5">
<span class="relative flex h-2 w-2">
<span class="relative flex size-2">
<span v-if="msdConnected" class="absolute inline-flex h-full w-full animate-ping rounded-full bg-success opacity-75"></span>
<span :class="msdConnected ? 'bg-success' : 'bg-muted-foreground'" class="relative inline-flex h-2 w-2 rounded-full"></span>
<span :class="msdConnected ? 'bg-success' : 'bg-muted-foreground'" class="relative inline-flex size-2 rounded-full"></span>
</span>
{{ msdConnected ? t('common.connected') : t('common.disconnected') }}
</span>
@@ -720,8 +720,8 @@ onUnmounted(() => {
<span class="font-medium text-foreground">{{ t('msd.diskMode') }}</span>
<Tooltip>
<TooltipTrigger as-child>
<span class="inline-flex h-4 w-4 items-center justify-center rounded-full text-muted-foreground hover:text-foreground">
<Info class="h-3.5 w-3.5" />
<span class="inline-flex size-4 items-center justify-center rounded-full text-muted-foreground hover:text-foreground">
<Info class="size-3.5" />
</span>
</TooltipTrigger>
<TooltipContent>
@@ -747,15 +747,15 @@ onUnmounted(() => {
<Separator class="shrink-0" />
<div class="flex-1 min-h-0 flex flex-col px-6 pb-6 pt-4">
<div class="flex-1 min-h-0 flex flex-col px-5 pb-4 pt-3">
<Tabs v-model="activeTab" class="flex-1 flex flex-col min-h-0">
<TabsList class="grid h-auto w-full shrink-0 grid-cols-2 gap-1 rounded-md border border-border bg-muted p-1">
<TabsList class="grid h-auto w-full shrink-0 grid-cols-2 gap-1 rounded-md border border-border bg-muted p-0.5">
<TabsTrigger value="images" :class="tabTriggerClass">
<Disc class="h-4 w-4 mr-1.5" />
<Disc class="size-4 mr-1.5" />
{{ t('msd.images') }}
</TabsTrigger>
<TabsTrigger value="drive" :class="tabTriggerClass">
<HardDrive class="h-4 w-4 mr-1.5" />
<HardDrive class="size-4 mr-1.5" />
{{ t('msd.drive') }}
</TabsTrigger>
</TabsList>
@@ -775,7 +775,7 @@ onUnmounted(() => {
@change="handleImageUpload"
/>
<Button variant="outline" size="sm" as="span" class="h-8 cursor-pointer px-2.5 text-xs">
<Upload class="mr-1.5 h-3.5 w-3.5" />
<Upload class="mr-1.5 size-3.5" />
{{ t('msd.uploadImage') }}
</Button>
</label>
@@ -785,11 +785,11 @@ onUnmounted(() => {
class="h-8 px-2.5 text-xs"
@click="showUrlDialog = true"
>
<Globe class="mr-1.5 h-3.5 w-3.5" />
<Globe class="mr-1.5 size-3.5" />
{{ t('msd.downloadFromUrl') }}
</Button>
<Button variant="ghost" size="icon" class="h-8 w-8" @click="loadImages">
<RefreshCw class="h-3.5 w-3.5" :class="{ 'animate-spin': loadingImages }" />
<Button variant="ghost" size="icon-sm" @click="loadImages">
<RefreshCw class="size-3.5" :class="{ 'animate-spin': loadingImages }" />
</Button>
</div>
</div>
@@ -817,8 +817,8 @@ onUnmounted(() => {
>
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<div class="flex min-w-0 flex-1 items-start gap-2">
<span v-if="mountedImage(image.id)" class="mt-1.5 h-2 w-2 shrink-0 rounded-full bg-primary" />
<Disc v-else class="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
<span v-if="mountedImage(image.id)" class="mt-1.5 size-2 shrink-0 rounded-full bg-primary" />
<Disc v-else class="mt-0.5 size-4 shrink-0 text-muted-foreground" />
<div class="w-0 flex-1">
<Tooltip>
<TooltipTrigger as-child>
@@ -836,7 +836,7 @@ onUnmounted(() => {
variant="outline"
class="h-4 cursor-help border-warning/50 px-1.5 text-[10px] text-warning"
>
<AlertCircle class="h-2.5 w-2.5 mr-0.5" />
<AlertCircle class="size-2.5 mr-0.5" />
{{ t('msd.largeFileWarning') }}
</Badge>
</TooltipTrigger>
@@ -856,7 +856,7 @@ onUnmounted(() => {
:disabled="operationInProgress"
@click="unmountImageById(image.id)"
>
<Unlink class="h-3.5 w-3.5 mr-1" />
<Unlink class="size-3.5 mr-1" />
{{ t('msd.disconnect') }}
</Button>
</template>
@@ -868,7 +868,7 @@ onUnmounted(() => {
:disabled="operationInProgress || mediaSlotsFull"
@click="connectImage(image)"
>
<Link v-if="!connecting" class="h-3.5 w-3.5 mr-1" />
<Link v-if="!connecting" class="size-3.5 mr-1" />
<span v-if="connecting">{{ t('common.connecting') }}...</span>
<span v-else>{{ t('msd.connect') }}</span>
</Button>
@@ -876,11 +876,11 @@ onUnmounted(() => {
<Button
variant="ghost"
size="icon"
class="h-8 w-8 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
class="size-8 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
:disabled="operationInProgress || !!mountedImage(image.id)"
@click="confirmDelete('image', image.id, image.name)"
>
<Trash2 class="h-3.5 w-3.5" />
<Trash2 class="size-3.5" />
</Button>
</div>
</div>
@@ -900,7 +900,7 @@ onUnmounted(() => {
<TabsContent value="drive" class="m-0 flex min-h-0 flex-1 flex-col space-y-4 pt-3">
<template v-if="!driveInitialized">
<div class="shrink-0 text-center py-8 space-y-4">
<HardDrive class="h-10 w-10 mx-auto text-muted-foreground" />
<HardDrive class="size-10 mx-auto text-muted-foreground" />
<p class="text-sm text-muted-foreground">{{ t('msd.driveNotInitialized') }}</p>
<Button size="sm" @click="initializeDrive">
{{ t('msd.initializeDrive') }}
@@ -920,7 +920,7 @@ onUnmounted(() => {
>
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<HardDrive class="h-4 w-4 text-muted-foreground" />
<HardDrive class="size-4 text-muted-foreground" />
<span class="text-sm font-medium">{{ t('msd.drive') }}</span>
<!-- Show size badge only when info is available -->
<Badge v-if="driveInfo" variant="outline" class="text-xs">
@@ -933,8 +933,8 @@ onUnmounted(() => {
</Badge>
<Tooltip>
<TooltipTrigger as-child>
<span class="inline-flex h-4 w-4 items-center justify-center text-muted-foreground hover:text-foreground">
<Info class="h-3.5 w-3.5" />
<span class="inline-flex size-4 items-center justify-center text-muted-foreground hover:text-foreground">
<Info class="size-3.5" />
</span>
</TooltipTrigger>
<TooltipContent>
@@ -958,9 +958,9 @@ onUnmounted(() => {
</template>
<template v-else-if="driveConnectedToTarget">
<Badge variant="default" class="h-8 px-2 text-xs">
<span class="relative flex h-1.5 w-1.5 mr-1.5">
<span class="relative flex size-1.5 mr-1.5">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-primary-foreground opacity-75"></span>
<span class="relative inline-flex h-1.5 w-1.5 rounded-full bg-primary-foreground"></span>
<span class="relative inline-flex size-1.5 rounded-full bg-primary-foreground"></span>
</span>
{{ t('common.connected') }}
</Badge>
@@ -972,7 +972,7 @@ onUnmounted(() => {
:disabled="operationInProgress"
@click="unmountMedia(driveMedia)"
>
<Unlink class="h-3.5 w-3.5 mr-1" />
<Unlink class="size-3.5 mr-1" />
{{ t('msd.disconnect') }}
</Button>
</template>
@@ -984,7 +984,7 @@ onUnmounted(() => {
:disabled="operationInProgress || mediaSlotsFull || !!driveError"
@click="connectDrive"
>
<Link v-if="!connecting" class="h-3.5 w-3.5 mr-1" />
<Link v-if="!connecting" class="size-3.5 mr-1" />
<span v-if="connecting">{{ t('common.connecting') }}...</span>
<span v-else>{{ t('msd.connect') }}</span>
</Button>
@@ -992,11 +992,11 @@ onUnmounted(() => {
<Button
variant="ghost"
size="icon"
class="h-8 w-8 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
class="size-8 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
:disabled="operationInProgress || driveConnectedToTarget"
@click="showDeleteDriveDialog = true"
>
<Trash2 class="h-3.5 w-3.5" />
<Trash2 class="size-3.5" />
</Button>
</div>
</div>
@@ -1024,15 +1024,15 @@ onUnmounted(() => {
v-if="currentPath !== '/'"
variant="ghost"
size="icon"
class="h-8 w-8 shrink-0"
class="size-8 shrink-0"
:disabled="driveConnectedToTarget"
@click="navigateUp"
>
<ArrowLeft class="h-3.5 w-3.5" />
<ArrowLeft class="size-3.5" />
</Button>
<nav class="flex items-center text-xs min-w-0 overflow-hidden">
<template v-for="(crumb, index) in breadcrumbs" :key="crumb.path">
<ChevronRight v-if="index > 0" class="h-3 w-3 text-muted-foreground mx-0.5 shrink-0" />
<ChevronRight v-if="index > 0" class="size-3 text-muted-foreground mx-0.5 shrink-0" />
<Button
variant="link"
size="sm"
@@ -1059,10 +1059,10 @@ onUnmounted(() => {
variant="ghost"
size="icon"
as="span"
class="h-8 w-8"
class="size-8"
:class="driveConnectedToTarget ? 'cursor-not-allowed opacity-40' : 'cursor-pointer'"
>
<Upload class="h-3.5 w-3.5" />
<Upload class="size-3.5" />
</Button>
</label>
</TooltipTrigger>
@@ -1076,11 +1076,11 @@ onUnmounted(() => {
<Button
variant="ghost"
size="icon"
class="h-8 w-8"
class="size-8"
:disabled="driveConnectedToTarget"
@click="showNewFolderDialog = true"
>
<FolderPlus class="h-3.5 w-3.5" />
<FolderPlus class="size-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent v-if="driveConnectedToTarget">
@@ -1090,11 +1090,11 @@ onUnmounted(() => {
<Button
variant="ghost"
size="icon"
class="h-8 w-8"
class="size-8"
:disabled="driveConnectedToTarget"
@click="loadDriveFiles"
>
<RefreshCw class="h-3.5 w-3.5" :class="{ 'animate-spin': loadingDrive }" />
<RefreshCw class="size-3.5" :class="{ 'animate-spin': loadingDrive }" />
</Button>
</div>
</div>
@@ -1129,8 +1129,8 @@ onUnmounted(() => {
class="flex items-center gap-2 cursor-pointer flex-1 min-w-0"
@click="file.is_dir && navigateTo(file.path)"
>
<Folder v-if="file.is_dir" class="h-4 w-4 shrink-0 text-info" />
<File v-else class="h-4 w-4 text-muted-foreground shrink-0" />
<Folder v-if="file.is_dir" class="size-4 shrink-0 text-info" />
<File v-else class="size-4 text-muted-foreground shrink-0" />
<div class="min-w-0">
<Tooltip>
<TooltipTrigger as-child>
@@ -1150,22 +1150,22 @@ onUnmounted(() => {
v-if="!file.is_dir"
variant="ghost"
size="icon"
class="h-8 w-8"
class="size-8"
as="a"
:href="msdApi.downloadDriveFile(file.path)"
download
>
<Download class="h-3.5 w-3.5" />
<Download class="size-3.5" />
</Button>
<!-- ③ Delete disabled when drive connected to target -->
<Button
variant="ghost"
size="icon"
class="h-8 w-8 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
class="size-8 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
:disabled="driveConnectedToTarget"
@click="confirmDelete('file', file.path, file.name)"
>
<Trash2 class="h-3.5 w-3.5" />
<Trash2 class="size-3.5" />
</Button>
</div>
</div>
@@ -1339,7 +1339,7 @@ onUnmounted(() => {
{{ t('common.cancel') }}
</Button>
<Button @click="confirmImageMount" :disabled="connecting || mediaSlotsFull">
<Link v-if="!connecting" class="h-4 w-4 mr-1" />
<Link v-if="!connecting" class="size-4 mr-1" />
<span v-if="connecting">{{ t('common.connecting') }}...</span>
<span v-else>{{ t('msd.connect') }}</span>
</Button>
@@ -1366,7 +1366,7 @@ onUnmounted(() => {
<DialogContent>
<DialogHeader>
<DialogTitle class="flex items-center gap-2">
<Globe class="h-5 w-5" />
<Globe class="size-5" />
{{ t('msd.downloadFromUrl') }}
</DialogTitle>
<DialogDescription>{{ t('msd.downloadFromUrlDesc') }}</DialogDescription>
@@ -1425,11 +1425,11 @@ onUnmounted(() => {
:disabled="!downloadUrl.trim()"
@click="startUrlDownload"
>
<Download class="h-4 w-4 mr-1" />
<Download class="size-4 mr-1" />
{{ t('msd.download') }}
</Button>
<Button v-else variant="destructive" @click="cancelUrlDownload">
<X class="h-4 w-4 mr-1" />
<X class="size-4 mr-1" />
{{ t('common.cancel') }}
</Button>
</DialogFooter>