调整,音乐模块

This commit is contained in:
zhongluofeng
2026-09-12 11:05:26 +08:00
parent 27ad5d89a5
commit d702ed0d31
71 changed files with 13647 additions and 387 deletions
+120
View File
@@ -0,0 +1,120 @@
<script setup lang="ts">
import { computed } from 'vue'
import { toast } from 'vue-sonner'
import { Cloud, Music2, MoreHorizontal, Play, Plus } from '@lucide/vue'
import type { PlayableItem } from '@/stores/feiniuStore'
import { useFeiniuStore } from '@/stores/feiniuStore'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
const props = defineProps<{
item: PlayableItem
active?: boolean
}>()
const store = useFeiniuStore()
const coverUrl = computed(() => {
if (props.item.source === 'feiniu' && props.item.coverId && store.mediaPrefix) {
return `${store.mediaPrefix}/cover?coverId=${encodeURIComponent(props.item.coverId)}&size=64`
}
return ''
})
const durationText = computed(() => store.fmtDuration(props.item.durationMs ? props.item.durationMs / 1000 : undefined))
function addToPlaylist(playlistId: string) {
store.addToPlaylist(playlistId, [props.item])
toast.success('已加入歌单')
}
async function uploadToFeiniu() {
try {
await store.uploadLocalTrack(props.item)
toast.success('已上传到飞牛曲库')
} catch (e) {
toast.error(String(e))
}
}
</script>
<template>
<div
class="group flex items-center gap-3 rounded-lg border px-3 py-2 transition-colors"
:class="active ? 'border-primary bg-primary/10' : 'border-border hover:bg-muted/40'"
>
<img
v-if="coverUrl"
:src="coverUrl"
class="size-10 shrink-0 rounded-md object-cover"
alt=""
loading="lazy"
@error="($event.target as HTMLImageElement).style.display = 'none'"
/>
<div v-else class="flex size-10 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground">
<Music2 class="size-4" />
</div>
<button
type="button"
class="flex size-10 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground opacity-0 transition-opacity group-hover:opacity-100"
:disabled="store.playing && store.current?.guid === item.guid"
@click="store.playItem(item)"
>
<Play class="size-4 translate-x-[1px]" />
</button>
<div class="min-w-0 flex-1">
<div class="truncate text-sm font-medium">{{ item.title }}</div>
<div class="truncate text-xs text-muted-foreground">
{{ item.artistNames }}<template v-if="item.artistNames && item.album"> · </template>{{ item.album }}
<span v-if="item.source === 'local'" class="ml-1 rounded bg-muted px-1 text-[10px]">本地</span>
</div>
</div>
<span class="shrink-0 text-xs tabular-nums text-muted-foreground">{{ durationText }}</span>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button size="icon" variant="ghost" class="size-8">
<MoreHorizontal class="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" class="w-48">
<DropdownMenuLabel>{{ item.title }}</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem @click="store.playItem(item)">
<Play class="size-4" /> 播放
</DropdownMenuItem>
<DropdownMenuItem
v-if="item.source === 'local' && store.fnosLoggedIn && store.libraryNasPath"
:disabled="store.uploading"
@click="uploadToFeiniu"
>
<Cloud class="size-4" /> 上传到飞牛曲库
</DropdownMenuItem>
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<Plus class="size-4" /> 加入歌单
</DropdownMenuSubTrigger>
<DropdownMenuSubContent class="w-48 max-h-64 overflow-y-auto">
<DropdownMenuItem v-if="store.playlists.length === 0" disabled>还没有歌单</DropdownMenuItem>
<DropdownMenuItem v-for="p in store.playlists" :key="p.id" @click="addToPlaylist(p.id)">
{{ p.name }}
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
</DropdownMenuContent>
</DropdownMenu>
</div>
</template>