适配移动端前端播放器

This commit is contained in:
2026-01-11 14:06:16 +08:00
parent 9dc90eeb85
commit 832c65ca5f
2 changed files with 231 additions and 26 deletions

View File

@@ -1,19 +1,130 @@
<script setup>
import { ref } from 'vue'
import { RouterLink, RouterView } from 'vue-router'
import { Menu } from 'lucide-vue-next'
import AudioPlayer from './components/AudioPlayer.vue'
const sidebarOpen = ref(false)
</script>
<template>
<header>
<nav>
<RouterLink to="/music">所有歌曲</RouterLink>
<RouterLink to="/settings">设置</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</header>
<div class="app-container">
<!-- 移动端遮罩层 -->
<div v-if="sidebarOpen" class="overlay" @click="sidebarOpen = false"></div>
<!-- 侧边栏 -->
<aside class="sidebar" :class="{ open: sidebarOpen }">
<nav>
<RouterLink to="/music" @click="sidebarOpen = false">所有歌曲</RouterLink>
<RouterLink to="/settings" @click="sidebarOpen = false">设置</RouterLink>
<RouterLink to="/about" @click="sidebarOpen = false">About</RouterLink>
</nav>
</aside>
<!-- 主内容区 -->
<main class="main-content">
<!-- 移动端汉堡菜单按钮 -->
<button class="menu-toggle" @click="sidebarOpen = !sidebarOpen">
<Menu />
</button>
<RouterView />
</main>
</div>
<RouterView />
<AudioPlayer />
</template>
<style scoped></style>
<style scoped>
.app-container {
display: flex;
min-height: 100vh;
}
.sidebar {
width: 200px;
background-color: #f5f5f5;
padding: 20px;
border-right: 1px solid #e0e0e0;
transition: transform 0.3s ease;
}
.sidebar nav {
display: flex;
flex-direction: column;
gap: 10px;
}
.sidebar a {
text-decoration: none;
color: #333;
padding: 10px;
border-radius: 4px;
transition: background-color 0.2s;
}
.sidebar a:hover {
background-color: #e0e0e0;
}
.sidebar a.router-link-active {
background-color: #42b883;
color: white;
}
.main-content {
flex: 1;
padding: 20px;
overflow-y: auto;
}
/* 汉堡菜单按钮 */
.menu-toggle {
display: none;
background: none;
border: none;
cursor: pointer;
padding: 10px;
margin-bottom: 20px;
}
/* 遮罩层 */
.overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 998;
}
/* 移动端响应式 */
@media (max-width: 768px) {
.sidebar {
position: fixed;
left: 0;
top: 0;
bottom: 0;
z-index: 999;
transform: translateX(-100%);
}
.sidebar.open {
transform: translateX(0);
}
.overlay {
display: block;
}
.menu-toggle {
display: flex;
}
.main-content {
padding: 10px;
}
}
</style>

View File

@@ -1,23 +1,25 @@
<template>
<div class="player">
<div class="player-info">
<div class="track-info">
<span class="track-title">{{ currentTrack.title }}</span>
<span class="track-artist">{{ currentTrack.artist }}</span>
<div class="player-left">
<div class="player-info">
<div class="track-info">
<span class="track-title">{{ currentTrack.title }}</span>
<span class="track-artist">{{ currentTrack.artist }}</span>
</div>
</div>
</div>
<div class="player-controls">
<button @click="player.seekBackward" class="control-btn">
<SkipBack />
</button>
<button @click="player.togglePlay" class="play-btn">
<Play v-if="!player.isPlaying" />
<Pause v-else />
</button>
<button @click="player.seekForward" class="control-btn">
<SkipForward />
</button>
<div class="player-controls">
<button @click="player.seekBackward" class="control-btn">
<SkipBack />
</button>
<button @click="player.togglePlay" class="play-btn">
<Play v-if="!player.isPlaying" />
<Pause v-else />
</button>
<button @click="player.seekForward" class="control-btn">
<SkipForward />
</button>
</div>
</div>
<div class="player-progress">
@@ -78,7 +80,6 @@ function onVolumeChange(e) {
left: 0;
right: 0;
display: flex;
/*flex-direction: column;*/
align-items: center;
gap: var(--size-3);
padding: var(--size-4);
@@ -87,9 +88,15 @@ function onVolumeChange(e) {
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
}
.player-left {
display: flex;
flex-direction: row;
}
.player-info {
display: flex;
justify-content: center;
min-width: 150px;
}
.track-info {
@@ -101,14 +108,23 @@ function onVolumeChange(e) {
font-weight: 600;
font-size: var(--font-size-1);
color: var(--stone-9);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 150px;
}
.track-artist {
font-size: var(--font-size-0);
color: var(--stone-7);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 150px;
}
.player-controls {
flex-grow: 1;
display: flex;
justify-content: center;
align-items: center;
@@ -150,6 +166,8 @@ function onVolumeChange(e) {
display: flex;
align-items: center;
gap: var(--size-3);
flex: 1;
min-width: 200px;
}
.time {
@@ -212,4 +230,80 @@ function onVolumeChange(e) {
border-radius: 50%;
cursor: pointer;
}
/* 移动端响应式 */
@media (max-width: 768px) {
.player {
flex-wrap: wrap;
padding: var(--size-2) var(--size-3);
gap: var(--size-2);
}
.player-left {
flex: 1;
width: 100%;
}
.player-info {
width: 100%;
order: 1;
min-width: auto;
flex: 1;
}
.track-info {
flex: 1;
display: flex;
align-items: center;
gap: var(--size-2);
text-align: unset;
}
.track-title,
.track-artist {
max-width: none;
}
.player-controls {
flex-grow: 0;
order: 2;
gap: var(--size-4);
}
.control-btn {
padding: var(--size-1);
width: var(--size-8);
height: var(--size-8);
}
.play-btn {
width: var(--size-8);
height: var(--size-8);
}
.player-progress {
order: 3;
width: 100%;
min-width: auto;
gap: var(--size-2);
}
.time {
font-size: 10px;
min-width: 30px;
}
.progress-bar {
height: 6px;
}
.progress-bar::-webkit-slider-thumb {
width: 16px;
height: 16px;
}
.player-volume {
display: none;
}
}
</style>