|
|
@@ -0,0 +1,217 @@
|
|
|
+<template>
|
|
|
+ <view class="page">
|
|
|
+ <!-- 搜索栏 -->
|
|
|
+ <view class="search-bar">
|
|
|
+ <input
|
|
|
+ v-model="searchQuery"
|
|
|
+ class="search-input"
|
|
|
+ placeholder="搜索音频..."
|
|
|
+ @confirm="handleSearch"
|
|
|
+ />
|
|
|
+ <button class="search-btn" @click="handleSearch">搜索</button>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 搜索结果 -->
|
|
|
+ <scroll-view scroll-y class="results" v-if="searchResults.length > 0">
|
|
|
+ <view class="result-list">
|
|
|
+ <view
|
|
|
+ v-for="item in searchResults"
|
|
|
+ :key="item.id"
|
|
|
+ class="result-item"
|
|
|
+ @click="playAudio(item)"
|
|
|
+ >
|
|
|
+ <view class="result-cover" :style="{ background: getCoverGradient(item.voiceId) }">
|
|
|
+ <text class="cover-icon">🎵</text>
|
|
|
+ </view>
|
|
|
+ <view class="result-info">
|
|
|
+ <text class="result-title">{{ item.title }}</text>
|
|
|
+ <text class="result-meta">{{ item.wordCount || 0 }}字</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+
|
|
|
+ <!-- 无结果 -->
|
|
|
+ <view v-else-if="hasSearched && searchResults.length === 0" class="no-result">
|
|
|
+ <text class="no-result-icon">🔍</text>
|
|
|
+ <text class="no-result-text">未找到相关音频</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 初始状态 -->
|
|
|
+ <view v-else class="initial-state">
|
|
|
+ <text class="hint-text">输入关键词搜索音频</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref } from 'vue';
|
|
|
+import { onLoad } from '@dcloudio/uni-app';
|
|
|
+import { useAudioStore } from '../../store/audio';
|
|
|
+import { get } from '../../utils/request';
|
|
|
+import type { AudioItem } from '../../types';
|
|
|
+
|
|
|
+const audioStore = useAudioStore();
|
|
|
+
|
|
|
+const searchQuery = ref('');
|
|
|
+const searchResults = ref<AudioItem[]>([]);
|
|
|
+const hasSearched = ref(false);
|
|
|
+
|
|
|
+// 音色封面颜色映射
|
|
|
+const voiceColors: Record<string, string[]> = {
|
|
|
+ cherry: ['#667eea', '#764ba2'],
|
|
|
+ ethan: ['#f093fb', '#f5576c'],
|
|
|
+ serena: ['#4facfe', '#00f2fe'],
|
|
|
+ chelsie: ['#43e97b', '#38f9d7'],
|
|
|
+ momo: ['#fa709a', '#fee140'],
|
|
|
+ vivian: ['#a8edea', '#fed6e3'],
|
|
|
+ moon: ['#5ee7df', '#b490ca'],
|
|
|
+ maia: ['#d299c2', '#fef9d7'],
|
|
|
+ kai: ['#89f7fe', '#66a6ff'],
|
|
|
+ nofish: ['#cd9cf2', '#f6f3ff'],
|
|
|
+};
|
|
|
+
|
|
|
+function getCoverGradient(voiceId: string): string {
|
|
|
+ const colors = voiceColors[voiceId] || ['#667eea', '#764ba2'];
|
|
|
+ return `linear-gradient(135deg, ${colors[0]} 0%, ${colors[1]} 100%)`;
|
|
|
+}
|
|
|
+
|
|
|
+async function handleSearch() {
|
|
|
+ if (!searchQuery.value.trim()) return;
|
|
|
+
|
|
|
+ hasSearched.value = true;
|
|
|
+ try {
|
|
|
+ const result = await get<AudioItem[]>('/search', { q: searchQuery.value });
|
|
|
+ searchResults.value = result || [];
|
|
|
+ } catch (error) {
|
|
|
+ console.error('搜索失败:', error);
|
|
|
+ searchResults.value = [];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function playAudio(item: AudioItem) {
|
|
|
+ audioStore.play(item);
|
|
|
+ uni.navigateTo({ url: `/pages/player/index?id=${item.id}` });
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.page {
|
|
|
+ min-height: 100vh;
|
|
|
+ background: #f5f5f5;
|
|
|
+ padding-top: 44rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.search-bar {
|
|
|
+ display: flex;
|
|
|
+ gap: 16rpx;
|
|
|
+ padding: 24rpx 32rpx;
|
|
|
+ background: #ffffff;
|
|
|
+}
|
|
|
+
|
|
|
+.search-input {
|
|
|
+ flex: 1;
|
|
|
+ height: 72rpx;
|
|
|
+ background: #f5f5f5;
|
|
|
+ border-radius: 36rpx;
|
|
|
+ padding: 0 32rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.search-btn {
|
|
|
+ width: 120rpx;
|
|
|
+ height: 72rpx;
|
|
|
+ background: #4f46e5;
|
|
|
+ color: #ffffff;
|
|
|
+ border-radius: 36rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.search-btn::after {
|
|
|
+ border: none;
|
|
|
+}
|
|
|
+
|
|
|
+.results {
|
|
|
+ height: calc(100vh - 140rpx);
|
|
|
+}
|
|
|
+
|
|
|
+.result-list {
|
|
|
+ padding: 24rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.result-item {
|
|
|
+ display: flex;
|
|
|
+ gap: 20rpx;
|
|
|
+ padding: 20rpx;
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ margin-bottom: 16rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.result-cover {
|
|
|
+ width: 100rpx;
|
|
|
+ height: 100rpx;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.cover-icon {
|
|
|
+ font-size: 40rpx;
|
|
|
+ opacity: 0.6;
|
|
|
+}
|
|
|
+
|
|
|
+.result-info {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.result-title {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #1f2937;
|
|
|
+ margin-bottom: 8rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.result-meta {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #9ca3af;
|
|
|
+}
|
|
|
+
|
|
|
+.no-result {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 200rpx 0;
|
|
|
+}
|
|
|
+
|
|
|
+.no-result-icon {
|
|
|
+ font-size: 80rpx;
|
|
|
+ margin-bottom: 24rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.no-result-text {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #6b7280;
|
|
|
+}
|
|
|
+
|
|
|
+.initial-state {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 200rpx 0;
|
|
|
+}
|
|
|
+
|
|
|
+.hint-text {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #9ca3af;
|
|
|
+}
|
|
|
+</style>
|