feat(播放队列): 添加播放队列功能并集成 Pinia 状态管理
新增播放队列功能,使用 Pini a进行状态管理。包括创建播放队列存储、在专辑详情页添加播放功能、以及在播放组件中监听播放状态的变化。同时,更新了依赖以支持 Pinia 的集成。
This commit is contained in:
		
							parent
							
								
									202e7951f3
								
							
						
					
					
						commit
						94a153ae11
					
				
							
								
								
									
										136
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										136
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -10,6 +10,7 @@
 | 
			
		|||
			"dependencies": {
 | 
			
		||||
				"@tailwindcss/vite": "^4.1.7",
 | 
			
		||||
				"axios": "^1.9.0",
 | 
			
		||||
				"pinia": "^3.0.2",
 | 
			
		||||
				"tailwindcss": "^4.1.7",
 | 
			
		||||
				"vue": "^3.5.13",
 | 
			
		||||
				"vue-router": "^4.5.1"
 | 
			
		||||
| 
						 | 
				
			
			@ -1354,6 +1355,30 @@
 | 
			
		|||
			"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/@vue/devtools-kit": {
 | 
			
		||||
			"version": "7.7.6",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.6.tgz",
 | 
			
		||||
			"integrity": "sha512-geu7ds7tem2Y7Wz+WgbnbZ6T5eadOvozHZ23Atk/8tksHMFOFylKi1xgGlQlVn0wlkEf4hu+vd5ctj1G4kFtwA==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@vue/devtools-shared": "^7.7.6",
 | 
			
		||||
				"birpc": "^2.3.0",
 | 
			
		||||
				"hookable": "^5.5.3",
 | 
			
		||||
				"mitt": "^3.0.1",
 | 
			
		||||
				"perfect-debounce": "^1.0.0",
 | 
			
		||||
				"speakingurl": "^14.0.1",
 | 
			
		||||
				"superjson": "^2.2.2"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/@vue/devtools-shared": {
 | 
			
		||||
			"version": "7.7.6",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.6.tgz",
 | 
			
		||||
			"integrity": "sha512-yFEgJZ/WblEsojQQceuyK6FzpFDx4kqrz2ohInxNj5/DnhoX023upTv4OD6lNPLAA5LLkbwPVb10o/7b+Y4FVA==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"rfdc": "^1.4.1"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/@vue/language-core": {
 | 
			
		||||
			"version": "2.2.10",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.10.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -1460,6 +1485,15 @@
 | 
			
		|||
			"dev": true,
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/birpc": {
 | 
			
		||||
			"version": "2.3.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.3.0.tgz",
 | 
			
		||||
			"integrity": "sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"funding": {
 | 
			
		||||
				"url": "https://github.com/sponsors/antfu"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/brace-expansion": {
 | 
			
		||||
			"version": "2.0.1",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -1504,6 +1538,21 @@
 | 
			
		|||
				"node": ">= 0.8"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/copy-anything": {
 | 
			
		||||
			"version": "3.0.5",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz",
 | 
			
		||||
			"integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"is-what": "^4.1.8"
 | 
			
		||||
			},
 | 
			
		||||
			"engines": {
 | 
			
		||||
				"node": ">=12.13"
 | 
			
		||||
			},
 | 
			
		||||
			"funding": {
 | 
			
		||||
				"url": "https://github.com/sponsors/mesqueeb"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/csstype": {
 | 
			
		||||
			"version": "3.1.3",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -1841,6 +1890,24 @@
 | 
			
		|||
				"he": "bin/he"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/hookable": {
 | 
			
		||||
			"version": "5.5.3",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
 | 
			
		||||
			"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/is-what": {
 | 
			
		||||
			"version": "4.1.16",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz",
 | 
			
		||||
			"integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"engines": {
 | 
			
		||||
				"node": ">=12.13"
 | 
			
		||||
			},
 | 
			
		||||
			"funding": {
 | 
			
		||||
				"url": "https://github.com/sponsors/mesqueeb"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/jiti": {
 | 
			
		||||
			"version": "2.4.2",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -2154,6 +2221,12 @@
 | 
			
		|||
				"node": ">= 18"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/mitt": {
 | 
			
		||||
			"version": "3.0.1",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
 | 
			
		||||
			"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/mkdirp": {
 | 
			
		||||
			"version": "3.0.1",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -2201,6 +2274,12 @@
 | 
			
		|||
			"dev": true,
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/perfect-debounce": {
 | 
			
		||||
			"version": "1.0.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
 | 
			
		||||
			"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/picocolors": {
 | 
			
		||||
			"version": "1.1.1",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -2219,6 +2298,36 @@
 | 
			
		|||
				"url": "https://github.com/sponsors/jonschlinkert"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/pinia": {
 | 
			
		||||
			"version": "3.0.2",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.2.tgz",
 | 
			
		||||
			"integrity": "sha512-sH2JK3wNY809JOeiiURUR0wehJ9/gd9qFN2Y828jCbxEzKEmEt0pzCXwqiSTfuRsK9vQsOflSdnbdBOGrhtn+g==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@vue/devtools-api": "^7.7.2"
 | 
			
		||||
			},
 | 
			
		||||
			"funding": {
 | 
			
		||||
				"url": "https://github.com/sponsors/posva"
 | 
			
		||||
			},
 | 
			
		||||
			"peerDependencies": {
 | 
			
		||||
				"typescript": ">=4.4.4",
 | 
			
		||||
				"vue": "^2.7.0 || ^3.5.11"
 | 
			
		||||
			},
 | 
			
		||||
			"peerDependenciesMeta": {
 | 
			
		||||
				"typescript": {
 | 
			
		||||
					"optional": true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/pinia/node_modules/@vue/devtools-api": {
 | 
			
		||||
			"version": "7.7.6",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.6.tgz",
 | 
			
		||||
			"integrity": "sha512-b2Xx0KvXZObePpXPYHvBRRJLDQn5nhKjXh7vUhMEtWxz1AYNFOVIsh5+HLP8xDGL7sy+Q7hXeUxPHB/KgbtsPw==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"@vue/devtools-kit": "^7.7.6"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/postcss": {
 | 
			
		||||
			"version": "8.5.3",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -2253,6 +2362,12 @@
 | 
			
		|||
			"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/rfdc": {
 | 
			
		||||
			"version": "1.4.1",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
 | 
			
		||||
			"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
 | 
			
		||||
			"license": "MIT"
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/rollup": {
 | 
			
		||||
			"version": "4.41.0",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -2301,6 +2416,27 @@
 | 
			
		|||
				"node": ">=0.10.0"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/speakingurl": {
 | 
			
		||||
			"version": "14.0.1",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
 | 
			
		||||
			"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
 | 
			
		||||
			"license": "BSD-3-Clause",
 | 
			
		||||
			"engines": {
 | 
			
		||||
				"node": ">=0.10.0"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/superjson": {
 | 
			
		||||
			"version": "2.2.2",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz",
 | 
			
		||||
			"integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"copy-anything": "^3.0.2"
 | 
			
		||||
			},
 | 
			
		||||
			"engines": {
 | 
			
		||||
				"node": ">=16"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/tailwindcss": {
 | 
			
		||||
			"version": "4.1.7",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.7.tgz",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@
 | 
			
		|||
	"dependencies": {
 | 
			
		||||
		"@tailwindcss/vite": "^4.1.7",
 | 
			
		||||
		"axios": "^1.9.0",
 | 
			
		||||
		"pinia": "^3.0.2",
 | 
			
		||||
		"tailwindcss": "^4.1.7",
 | 
			
		||||
		"vue": "^3.5.13",
 | 
			
		||||
		"vue-router": "^4.5.1"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,14 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { usePlayQueueStore } from '../stores/usePlayQueueStore'
 | 
			
		||||
import { watch } from 'vue'
 | 
			
		||||
 | 
			
		||||
const playQueue = usePlayQueueStore()
 | 
			
		||||
 | 
			
		||||
watch(() => playQueue.isPlaying, (newValue) => {
 | 
			
		||||
	console.log(newValue)
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
	<div class="bg-[#070909a7] backdrop-blur-3xl h-24 shadow-[0px_-1px_1rem_0px_rgba(0,0,0,0.1)] border-t border-t-[#1c1c1c] sticky bottom-0 w-full">
 | 
			
		||||
		
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
import { createApp } from 'vue'
 | 
			
		||||
import { createWebHashHistory, createRouter } from 'vue-router'
 | 
			
		||||
import './style.css'
 | 
			
		||||
import { createPinia } from 'pinia'
 | 
			
		||||
 | 
			
		||||
import App from './App.vue'
 | 
			
		||||
import HomePage from './pages/Home.vue'
 | 
			
		||||
import AlbumDetailView from './pages/AlbumDetail.vue'
 | 
			
		||||
| 
						 | 
				
			
			@ -15,4 +17,7 @@ const router = createRouter({
 | 
			
		|||
  routes
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
; createApp(App).use(router).mount('#app')
 | 
			
		||||
const pinia = createPinia()
 | 
			
		||||
 | 
			
		||||
createApp(App).use(router).use(pinia).mount('#app')
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,17 +2,19 @@
 | 
			
		|||
import { ref, onMounted } from 'vue'
 | 
			
		||||
import apis from '../apis'
 | 
			
		||||
import { useRoute } from 'vue-router'
 | 
			
		||||
import { usePlayQueueStore } from '../stores/usePlayQueueStore'
 | 
			
		||||
 | 
			
		||||
const album = ref<Album>()
 | 
			
		||||
 | 
			
		||||
const route = useRoute()
 | 
			
		||||
const albumId = route.params.albumId
 | 
			
		||||
 | 
			
		||||
const playQueue = usePlayQueueStore()
 | 
			
		||||
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
	try {
 | 
			
		||||
		const res = await apis.getAlbum(albumId as string)
 | 
			
		||||
		album.value = res
 | 
			
		||||
		console.log(res)
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		console.log(error)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +26,24 @@ function artistsOrganize(list: string[]) {
 | 
			
		|||
		return artist
 | 
			
		||||
	}).join(' / ')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function playTheAlbum() {
 | 
			
		||||
	if (playQueue.queueReplaceLock) {
 | 
			
		||||
		if (!confirm("当前操作会将你的待播列表清空、放入这张专辑所有曲目,并从新待播清单的开头播放。继续吗?")) { return }
 | 
			
		||||
		playQueue.queueReplaceLock = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	let newPlayQueue = []
 | 
			
		||||
	for (const track of album.value?.songs ?? []) {
 | 
			
		||||
		newPlayQueue.push({
 | 
			
		||||
			song: track,
 | 
			
		||||
			album: album.value
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	playQueue.list = newPlayQueue
 | 
			
		||||
	playQueue.currentIndex = 0
 | 
			
		||||
	playQueue.isPlaying = true
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +66,9 @@ function artistsOrganize(list: string[]) {
 | 
			
		|||
			<div class="flex justify-between items-center">
 | 
			
		||||
				<div class="flex gap-2">
 | 
			
		||||
					<button
 | 
			
		||||
						class="bg-sky-500/20 hover:bg-sky-500/30 active:bg-sky-600/30 active:shadow-inner border border-[#ffffff39] rounded-full w-56 h-10 text-base text-white flex justify-center items-center gap-2">
 | 
			
		||||
						class="bg-sky-500/20 hover:bg-sky-500/30 active:bg-sky-600/30 active:shadow-inner border border-[#ffffff39] rounded-full w-56 h-10 text-base text-white flex justify-center items-center gap-2"
 | 
			
		||||
						@click="playTheAlbum"
 | 
			
		||||
					>
 | 
			
		||||
						<div class="w-4 h-4">
 | 
			
		||||
							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
 | 
			
		||||
								<path
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										11
									
								
								src/stores/usePlayQueueStore.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/stores/usePlayQueueStore.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
import { defineStore } from "pinia"
 | 
			
		||||
import { ref } from "vue"
 | 
			
		||||
 | 
			
		||||
export const usePlayQueueStore = defineStore('queue', () =>{
 | 
			
		||||
  const list = ref<QueueItem[]>([])
 | 
			
		||||
  const currentIndex = ref<number>(0)
 | 
			
		||||
	const isPlaying = ref<boolean>(false)
 | 
			
		||||
	const queueReplaceLock = ref<boolean>(false)
 | 
			
		||||
 | 
			
		||||
  return { list, currentIndex, isPlaying, queueReplaceLock }
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										5
									
								
								src/vite-env.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								src/vite-env.d.ts
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -32,4 +32,9 @@ interface ApiResponse {
 | 
			
		|||
	code: number
 | 
			
		||||
	msg: string
 | 
			
		||||
	data: unknown
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface QueueItem {
 | 
			
		||||
  song: Song
 | 
			
		||||
  album?: Album
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user