music / src /components /search /SourceSelector.vue
ahutchen's picture
refactor(search): 重构搜索功能并添加搜索历史页面
a1eddda
<template>
<div class="source-selector-overlay" @click="$emit('close')">
<div class="source-selector-content" @click.stop>
<div class="selector-header">
<h3>选择音乐源</h3>
<button class="close-btn" @click="$emit('close')">
<i class="fas fa-times"></i>
</button>
</div>
<div class="sources-list">
<button
v-for="source in musicSources"
:key="source.code"
class="source-item"
:class="{ active: source.code === currentSource }"
@click="selectSource(source.code)"
>
<div class="source-info">
<span class="source-name">{{ source.name }}</span>
<span class="source-priority">优先级 {{ source.priority }}</span>
</div>
<i v-if="source.code === currentSource" class="fas fa-check check-icon"></i>
</button>
</div>
<div class="selector-footer">
<p class="footer-tip">建议优先使用网易云音乐,资源更丰富</p>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useSearchStore } from '@/stores/search'
import { MUSIC_SOURCES } from '@/services/musicApi'
const emit = defineEmits(['close', 'select'])
const searchStore = useSearchStore()
// 计算属性
const currentSource = computed(() => searchStore.currentSource)
const musicSources = computed(() =>
MUSIC_SOURCES.sort((a, b) => a.priority - b.priority)
)
// 方法
const selectSource = (sourceCode) => {
emit('select', sourceCode)
emit('close')
}
</script>
<style scoped>
.source-selector-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
display: flex;
align-items: flex-end;
z-index: 2000;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.source-selector-content {
width: 100%;
max-height: 70vh;
background: var(--bg-card);
backdrop-filter: blur(20px);
border-radius: 20px 20px 0 0;
animation: slideUp 0.3s ease;
overflow: hidden;
border: 1px solid var(--border-strong);
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.2);
}
/* PC端适配 */
@media (min-width: 768px) {
.source-selector-overlay {
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.5);
}
.source-selector-content {
width: 400px;
max-height: 500px;
border-radius: 16px;
animation: scaleIn 0.3s ease;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.9) translateY(20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
.selector-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 24px;
border-bottom: 1px solid var(--border-strong);
background: var(--overlay-lighter);
}
.selector-header h3 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin: 0;
}
.close-btn {
width: 32px;
height: 32px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.1);
border: none;
color: var(--text-secondary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
transition: var(--transition-fast);
}
.close-btn:hover {
background: rgba(255, 255, 255, 0.2);
color: var(--text-primary);
}
.sources-list {
max-height: calc(70vh - 160px);
overflow-y: auto;
padding: 8px 0;
background: var(--bg-card);
}
.source-item {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 24px;
background: transparent;
border: none;
cursor: pointer;
transition: var(--transition-fast);
border-bottom: 1px solid var(--border-lighter);
}
.source-item:hover {
background: var(--overlay-lighter);
border-bottom: 1px solid var(--border-light);
}
.source-item.active {
background: var(--bg-gradient-3);
color: var(--accent-red);
border-bottom: 1px solid var(--accent-red);
}
.source-info {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.source-name {
font-size: 16px;
font-weight: 500;
color: var(--text-primary);
}
.source-item.active .source-name {
color: var(--accent-red);
}
.source-priority {
font-size: 12px;
color: var(--text-disabled);
}
.check-icon {
color: var(--accent-red);
font-size: 16px;
}
.selector-footer {
padding: 16px 24px;
border-top: 1px solid var(--border-strong);
background: rgba(255, 255, 255, 0.02);
}
.footer-tip {
font-size: 12px;
color: var(--text-disabled);
text-align: center;
margin: 0;
line-height: 1.4;
}
/* 滚动条样式 */
.sources-list::-webkit-scrollbar {
width: 6px;
}
.sources-list::-webkit-scrollbar-track {
background: transparent;
}
.sources-list::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 3px;
}
.sources-list::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
</style>