File size: 3,004 Bytes
40f23a9 8f2df2b 40f23a9 8f2df2b 40f23a9 a1eddda 40f23a9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
<template>
<div
class="app-tabbar"
v-show="!shouldHideTabbar"
>
<div class="tabbar-content">
<router-link
v-for="tab in tabs"
:key="tab.name"
:to="tab.path"
class="tab-item"
:class="{ active: currentRoute === tab.name }"
@click="handleTabClick(tab)"
>
<i :class="tab.icon" class="tab-icon"></i>
<span class="tab-label">{{ tab.label }}</span>
</router-link>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const tabs = [
{
name: 'Home',
path: '/home',
label: '首页',
icon: 'fas fa-home'
},
{
name: 'Favorites',
path: '/favorites',
label: '我喜欢',
icon: 'fas fa-heart'
},
{
name: 'Playlists',
path: '/playlists',
label: '歌单',
icon: 'fas fa-music'
},
{
name: 'PlayQueue',
path: '/play-queue',
label: '播放列表',
icon: 'fas fa-list'
}
]
const currentRoute = computed(() => route.name)
// 判断是否应该隐藏tabbar
const shouldHideTabbar = computed(() => {
return route.meta?.fullScreen || false
})
const handleTabClick = (tab) => {
// 触觉反馈(如果支持)
if (navigator.vibrate) {
navigator.vibrate(10)
}
}
</script>
<style scoped>
.app-tabbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: var(--bg-card);
backdrop-filter: blur(20px);
border-top: 1px solid var(--border-strong);
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
/* iOS PWA 模式底部间距 */
@supports (-webkit-touch-callout: none) {
@media all and (display-mode: standalone) {
.app-tabbar {
padding-bottom: 20px;
}
}
}
.tabbar-content {
display: flex;
height: var(--tabbar-height);
max-width: 480px;
margin: 0 auto;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-decoration: none;
color: var(--text-disabled);
transition: var(--transition-fast);
min-height: var(--touch-target);
position: relative;
padding: 4px;
}
.tab-item:hover,
.tab-item.active {
color: var(--primary-color);
}
.tab-item.active::after {
content: '';
position: absolute;
bottom: -1px;
left: 50%;
transform: translateX(-50%);
width: 24px;
height: 2px;
background: var(--primary-color);
border-radius: 1px;
/* 使用与进度条不同的视觉效果 */
box-shadow: 0 -1px 4px var(--glow-color);
opacity: 0.9;
}
.tab-icon {
font-size: 20px;
margin-bottom: 2px;
}
.tab-label {
font-size: 10px;
font-weight: 500;
line-height: 1;
}
/* 响应式适配 */
@media (max-width: 320px) {
.tab-label {
font-size: 9px;
}
.tab-icon {
font-size: 18px;
}
.app-tabbar {
border-top: 1px solid var(--border-strong);
}
}
/* PC端隐藏底部标签栏 */
@media (min-width: 1024px) {
.app-tabbar {
display: none;
}
}
</style> |