|
@@ -4,15 +4,36 @@
|
|
|
<view class="nav-bar">
|
|
<view class="nav-bar">
|
|
|
<view class="nav-content">
|
|
<view class="nav-content">
|
|
|
<text class="logo">AI有声书</text>
|
|
<text class="logo">AI有声书</text>
|
|
|
- <view class="nav-right" @click="handleUserClick">
|
|
|
|
|
- <image v-if="userStore.userInfo?.avatar" :src="userStore.userInfo.avatar" class="avatar" mode="aspectFill" />
|
|
|
|
|
- <view v-else class="avatar-placeholder">
|
|
|
|
|
- <text class="avatar-text">登</text>
|
|
|
|
|
|
|
+ <view class="nav-right">
|
|
|
|
|
+ <view class="search-icon" @click="goToSearch">
|
|
|
|
|
+ <text>🔍</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view @click="handleUserClick">
|
|
|
|
|
+ <image v-if="userStore.userInfo?.avatar" :src="userStore.userInfo.avatar" class="avatar" mode="aspectFill" :lazy-load="true" />
|
|
|
|
|
+ <view v-else class="avatar-placeholder">
|
|
|
|
|
+ <text class="avatar-text">登</text>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
|
|
+ <!-- 分类标签栏 -->
|
|
|
|
|
+ <scroll-view scroll-x class="category-bar">
|
|
|
|
|
+ <view class="category-list">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="cat in categories"
|
|
|
|
|
+ :key="cat.id"
|
|
|
|
|
+ class="category-item"
|
|
|
|
|
+ :class="{ active: selectedCategory === cat.id }"
|
|
|
|
|
+ @click="selectCategory(cat.id)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <text>{{ cat.icon }}</text>
|
|
|
|
|
+ <text class="category-name">{{ cat.name }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </scroll-view>
|
|
|
|
|
+
|
|
|
<!-- 骨架屏加载状态 -->
|
|
<!-- 骨架屏加载状态 -->
|
|
|
<scroll-view scroll-y class="audio-list" @scrolltolower="loadMore" v-if="loading && audioList.length === 0">
|
|
<scroll-view scroll-y class="audio-list" @scrolltolower="loadMore" v-if="loading && audioList.length === 0">
|
|
|
<view class="skeleton-grid">
|
|
<view class="skeleton-grid">
|
|
@@ -107,12 +128,35 @@ const total = ref(0);
|
|
|
const loading = ref(false);
|
|
const loading = ref(false);
|
|
|
const hasMore = ref(true);
|
|
const hasMore = ref(true);
|
|
|
|
|
|
|
|
|
|
+// 分类状态
|
|
|
|
|
+interface Category {
|
|
|
|
|
+ id: number;
|
|
|
|
|
+ name: string;
|
|
|
|
|
+ icon: string;
|
|
|
|
|
+}
|
|
|
|
|
+const categories = ref<Category[]>([
|
|
|
|
|
+ { id: 1, name: '全部', icon: '🎵' },
|
|
|
|
|
+ { id: 2, name: '故事', icon: '📖' },
|
|
|
|
|
+ { id: 3, name: '小说', icon: '📚' },
|
|
|
|
|
+ { id: 4, name: '儿童', icon: '👶' },
|
|
|
|
|
+ { id: 5, name: '知识', icon: '💡' },
|
|
|
|
|
+ { id: 6, name: '娱乐', icon: '🎮' },
|
|
|
|
|
+]);
|
|
|
|
|
+const selectedCategory = ref(1);
|
|
|
|
|
+
|
|
|
// 获取封面渐变色
|
|
// 获取封面渐变色
|
|
|
function getCoverGradient(voiceId: string): string {
|
|
function getCoverGradient(voiceId: string): string {
|
|
|
const colors = voiceColors[voiceId] || ['#667eea', '#764ba2'];
|
|
const colors = voiceColors[voiceId] || ['#667eea', '#764ba2'];
|
|
|
return `linear-gradient(135deg, ${colors[0]} 0%, ${colors[1]} 100%)`;
|
|
return `linear-gradient(135deg, ${colors[0]} 0%, ${colors[1]} 100%)`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 选择分类
|
|
|
|
|
+async function selectCategory(categoryId: number) {
|
|
|
|
|
+ selectedCategory.value = categoryId;
|
|
|
|
|
+ page.value = 1;
|
|
|
|
|
+ await fetchAudioList();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 获取音频列表
|
|
// 获取音频列表
|
|
|
async function fetchAudioList(isLoadMore = false) {
|
|
async function fetchAudioList(isLoadMore = false) {
|
|
|
if (loading.value) return;
|
|
if (loading.value) return;
|
|
@@ -122,11 +166,12 @@ async function fetchAudioList(isLoadMore = false) {
|
|
|
|
|
|
|
|
loading.value = true;
|
|
loading.value = true;
|
|
|
try {
|
|
try {
|
|
|
- const result = await get<{ list: AudioItem[]; total: number; totalPages: number }>('/audio/list', {
|
|
|
|
|
|
|
+ // 使用分类API
|
|
|
|
|
+ const result = await get<{ list: AudioItem[]; total: number; totalPages: number }>(`/categories/${selectedCategory.value}`, {
|
|
|
page: page.value,
|
|
page: page.value,
|
|
|
pageSize,
|
|
pageSize,
|
|
|
});
|
|
});
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (isLoadMore) {
|
|
if (isLoadMore) {
|
|
|
audioList.value.push(...result.list);
|
|
audioList.value.push(...result.list);
|
|
|
} else {
|
|
} else {
|
|
@@ -181,6 +226,11 @@ function handleUserClick() {
|
|
|
uni.navigateTo({ url: '/pages/mine/index' });
|
|
uni.navigateTo({ url: '/pages/mine/index' });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 跳转搜索页
|
|
|
|
|
+function goToSearch() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/search/index' });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 生成新音频(跳转到历史页面)
|
|
// 生成新音频(跳转到历史页面)
|
|
|
function handleGenerate() {
|
|
function handleGenerate() {
|
|
|
uni.navigateTo({ url: '/pages/history/index' });
|
|
uni.navigateTo({ url: '/pages/history/index' });
|
|
@@ -233,6 +283,51 @@ onShow(() => {
|
|
|
.nav-right {
|
|
.nav-right {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
|
|
+ gap: 24rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.search-icon {
|
|
|
|
|
+ width: 56rpx;
|
|
|
|
|
+ height: 56rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.category-bar {
|
|
|
|
|
+ position: sticky;
|
|
|
|
|
+ top: 88rpx;
|
|
|
|
|
+ background: #ffffff;
|
|
|
|
|
+ padding: 16rpx 0;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ z-index: 99;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.category-list {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 16rpx;
|
|
|
|
|
+ padding: 0 24rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.category-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6rpx;
|
|
|
|
|
+ padding: 12rpx 24rpx;
|
|
|
|
|
+ background: #f5f5f5;
|
|
|
|
|
+ border-radius: 32rpx;
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ color: #6b7280;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.category-item.active {
|
|
|
|
|
+ background: #4f46e5;
|
|
|
|
|
+ color: #ffffff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.category-name {
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.avatar {
|
|
.avatar {
|