| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- <template>
- <view class="container">
- <!-- 顶部导航栏 -->
- <view class="header">
- <view class="back-btn" @click="goBack">
- <text class="back-icon">←</text>
- </view>
- <text class="header-title">排行榜</text>
- <view class="placeholder"></view>
- </view>
-
- <!-- 横向标签栏 -->
- <view class="tab-bar">
- <view
- class="tab-item"
- v-for="(tab, index) in rankingTabs"
- :key="index"
- :class="{ 'tab-active': activeTab === index }"
- @click="switchTab(index)"
- >
- <text class="tab-text" :class="{ 'tab-text-active': activeTab === index }">{{ tab }}</text>
- </view>
- </view>
-
- <!-- 主体内容区 -->
- <view class="main-content">
- <!-- 左侧分类边栏 -->
- <scroll-view class="sidebar" scroll-y>
- <view
- class="category-item"
- v-for="(category, index) in categories"
- :key="index"
- :class="{ 'category-active': activeCategory === index }"
- @click="switchCategory(index)"
- >
- <view class="category-indicator" v-if="activeCategory === index"></view>
- <text class="category-text" :class="{ 'category-text-active': activeCategory === index }">{{ category }}</text>
- </view>
- </scroll-view>
-
- <!-- 右侧书籍列表 -->
- <scroll-view class="book-list-container" scroll-y @scrolltolower="loadMore" :lower-threshold="100">
- <view
- class="book-item"
- v-for="(book, index) in bookList"
- :key="book.id || index"
- @click="goToBookDetail(book)"
- >
- <text class="rank-number">{{ index + 1 }}</text>
- <image
- class="book-cover"
- :src="book.cover"
- mode="aspectFill"
- :lazy-load="true"
- @error="handleImageError(index)"
- ></image>
- <view class="book-info">
- <text class="book-title">{{ book.title }}</text>
- <text class="book-author">{{ book.author }}</text>
- </view>
- <view class="divider-line"></view>
- </view>
-
- <!-- 加载中提示 -->
- <view class="load-more" v-if="isLoading">
- <text class="load-more-text">加载中...</text>
- </view>
- <!-- 空状态 -->
- <view class="load-more" v-else-if="!isLoading && bookList.length === 0">
- <text class="load-more-text">暂无数据</text>
- </view>
- </scroll-view>
- </view>
- </view>
- </template>
- <script>
- import { getAllRankingGroups, getRankingByCode, getAllCategories } from '../../utils/api.js'
-
- export default {
- data() {
- return {
- activeTab: 0, // 当前选中的标签索引
- activeCategory: 0, // 当前选中的分类索引
- hasMore: true,
- page: 1,
- isLoading: false,
- rankingTabs: [], // 从数据库获取的排行榜类型
- rankingGroups: [], // 排行榜组数据(包含id、name、code)
- categories: [], // 从数据库获取的分类列表
- categoryList: [], // 分类数据(包含id、name)
- bookList: [] // 书籍列表(从数据库获取)
- }
- },
- onLoad() {
- // 页面加载时初始化数据
- this.initData();
- },
- methods: {
- // 初始化数据:加载排行榜类型和分类
- async initData() {
- try {
- this.isLoading = true;
- // 并行加载排行榜类型和分类
- await Promise.all([
- this.loadRankingGroups(),
- this.loadCategories()
- ]);
- // 加载完成后,加载书籍列表
- await this.loadBookList();
- } catch (err) {
- console.error('初始化数据失败:', err);
- uni.showToast({
- title: '加载失败,请重试',
- icon: 'none'
- });
- } finally {
- this.isLoading = false;
- }
- },
- // 加载排行榜类型
- async loadRankingGroups() {
- try {
- const res = await getAllRankingGroups();
- if (res && res.code === 200 && res.data) {
- this.rankingGroups = res.data;
- this.rankingTabs = res.data.map(group => group.name);
- console.log('排行榜类型加载成功:', this.rankingTabs);
- } else {
- console.warn('排行榜类型数据为空,使用默认数据');
- // 如果数据库没有数据,使用默认值
- this.rankingTabs = ['畅销榜', '热门榜', '口碑榜', '好评榜', '新书榜'];
- this.rankingGroups = [
- { id: 1, name: '畅销榜', code: 'bestseller' },
- { id: 2, name: '热门榜', code: 'popular' },
- { id: 3, name: '口碑榜', code: 'reputation' },
- { id: 4, name: '好评榜', code: 'good_reviews' },
- { id: 5, name: '新书榜', code: 'new_books' }
- ];
- }
- } catch (err) {
- console.error('加载排行榜类型失败:', err);
- // 使用默认值
- this.rankingTabs = ['畅销榜', '热门榜', '口碑榜', '好评榜', '新书榜'];
- this.rankingGroups = [
- { id: 1, name: '畅销榜', code: 'bestseller' },
- { id: 2, name: '热门榜', code: 'popular' },
- { id: 3, name: '口碑榜', code: 'reputation' },
- { id: 4, name: '好评榜', code: 'good_reviews' },
- { id: 5, name: '新书榜', code: 'new_books' }
- ];
- }
- },
- // 加载分类列表
- async loadCategories() {
- try {
- const res = await getAllCategories();
- if (res && res.code === 200 && res.data) {
- this.categoryList = res.data;
- // 添加"全部分类"选项
- this.categories = ['全部分类', ...res.data.map(cat => cat.name)];
- console.log('分类列表加载成功:', this.categories);
- } else {
- console.warn('分类数据为空,使用默认数据');
- // 如果数据库没有数据,使用默认值
- this.categories = ['全部分类', '文艺', '历史', '人文', '科学', '教育', '生活', '外语', '商业', '养生', '职场', '少儿'];
- this.categoryList = [
- { id: 1, name: '文艺' },
- { id: 2, name: '历史' },
- { id: 3, name: '人文' },
- { id: 4, name: '科学' },
- { id: 5, name: '教育' },
- { id: 6, name: '生活' },
- { id: 7, name: '外语' },
- { id: 8, name: '商业' },
- { id: 9, name: '养生' },
- { id: 10, name: '职场' },
- { id: 11, name: '少儿' }
- ];
- }
- } catch (err) {
- console.error('加载分类失败:', err);
- // 使用默认值
- this.categories = ['全部分类', '文艺', '历史', '人文', '科学', '教育', '生活', '外语', '商业', '养生', '职场', '少儿'];
- this.categoryList = [
- { id: 1, name: '文艺' },
- { id: 2, name: '历史' },
- { id: 3, name: '人文' },
- { id: 4, name: '科学' },
- { id: 5, name: '教育' },
- { id: 6, name: '生活' },
- { id: 7, name: '外语' },
- { id: 8, name: '商业' },
- { id: 9, name: '养生' },
- { id: 10, name: '职场' },
- { id: 11, name: '少儿' }
- ];
- }
- },
- goBack() {
- uni.navigateBack({
- delta: 1
- });
- },
- switchTab(index) {
- if (this.activeTab !== index) {
- this.activeTab = index;
- this.page = 1;
- this.hasMore = true;
- this.loadBookList();
- }
- },
- switchCategory(index) {
- if (this.activeCategory !== index) {
- this.activeCategory = index;
- this.page = 1;
- this.hasMore = true;
- this.loadBookList();
- }
- },
- goToBookDetail(book) {
- if (!book || !book.id) {
- uni.showToast({
- title: '书籍信息不完整',
- icon: 'none'
- })
- return
- }
- uni.navigateTo({
- url: `/pages/book-detail/book-detail?bookId=${book.id}`
- });
- },
- handleImageError(index) {
- // 图片加载失败时使用备用图片
- if (this.bookList[index]) {
- this.bookList[index].cover = `https://picsum.photos/seed/fallback${index}/200/300`;
- }
- },
- // 从后端加载书籍列表
- async loadBookList() {
- try {
- this.isLoading = true;
-
- // 获取当前选中的排行榜类型
- if (!this.rankingGroups || this.rankingGroups.length === 0) {
- console.warn('排行榜类型未加载');
- this.bookList = [];
- return;
- }
-
- const currentGroup = this.rankingGroups[this.activeTab];
- if (!currentGroup || !currentGroup.code) {
- console.warn('当前排行榜类型无效');
- this.bookList = [];
- return;
- }
-
- // 获取当前选中的分类ID
- let categoryId = null;
- if (this.activeCategory > 0 && this.categoryList && this.categoryList.length > 0) {
- // activeCategory为0表示"全部分类",从1开始才是实际分类
- const categoryIndex = this.activeCategory - 1;
- if (categoryIndex >= 0 && categoryIndex < this.categoryList.length) {
- categoryId = this.categoryList[categoryIndex].id;
- }
- }
-
- // 调用API获取排行榜书籍
- const res = await getRankingByCode(currentGroup.code, categoryId);
-
- if (res && res.code === 200 && res.data) {
- // 转换数据格式
- this.bookList = res.data.map((book, index) => ({
- id: book.id,
- title: book.title || '未知书名',
- author: book.author || '未知作者',
- cover: book.cover || book.image || `https://picsum.photos/seed/book${book.id}/200/300`,
- rank: index + 1
- }));
- console.log('排行榜书籍加载成功,共', this.bookList.length, '本');
- } else {
- console.warn('排行榜书籍数据为空');
- this.bookList = [];
- }
- } catch (err) {
- console.error('加载排行榜书籍失败:', err);
- this.bookList = [];
- uni.showToast({
- title: '加载失败,请重试',
- icon: 'none'
- });
- } finally {
- this.isLoading = false;
- this.hasMore = false; // 排行榜数据不分页,一次性加载
- }
- },
- // 加载更多(排行榜数据一次性加载,此方法保留但不使用)
- loadMore() {
- // 排行榜数据一次性加载,不需要分页
- // 此方法保留用于兼容,但不执行任何操作
- if (this.hasMore) {
- this.hasMore = false;
- }
- }
- }
- }
- </script>
- <style scoped>
- .container {
- width: 100%;
- height: 100vh;
- background-color: #FFFFFF;
- display: flex;
- flex-direction: column;
- }
-
- /* 顶部导航栏 */
- .header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 20rpx 30rpx;
- background-color: #4DB6AC;
- position: relative;
- }
-
- .back-btn {
- width: 60rpx;
- height: 60rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .back-icon {
- font-size: 40rpx;
- color: #FFFFFF;
- font-weight: bold;
- }
-
- .header-title {
- font-size: 36rpx;
- font-weight: bold;
- color: #FFFFFF;
- position: absolute;
- left: 50%;
- transform: translateX(-50%);
- }
-
- .placeholder {
- width: 60rpx;
- }
-
- /* 横向标签栏 */
- .tab-bar {
- display: flex;
- align-items: center;
- background-color: #66CCC2;
- padding: 0 20rpx;
- overflow-x: auto;
- white-space: nowrap;
- }
-
- .tab-item {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 24rpx 20rpx;
- min-width: 120rpx;
- }
-
- .tab-text {
- font-size: 28rpx;
- color: #FFFFFF;
- transition: color 0.3s ease;
- }
-
- .tab-text-active {
- font-size: 30rpx;
- font-weight: bold;
- color: #2E7D32;
- }
-
- .tab-active {
- border-bottom: 4rpx solid #2E7D32;
- }
-
- /* 主体内容区 */
- .main-content {
- flex: 1;
- display: flex;
- width: 100%;
- height: 0;
- overflow: hidden;
- }
-
- /* 左侧分类边栏 */
- .sidebar {
- width: 160rpx;
- height: 100%;
- background-color: #FFFFFF;
- border-right: 1rpx solid #E5E5E5;
- }
-
- .category-item {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 30rpx 20rpx;
- min-height: 80rpx;
- box-sizing: border-box;
- }
-
- .category-active {
- background-color: #F5F5F5;
- }
-
- .category-indicator {
- position: absolute;
- left: 0;
- top: 0;
- bottom: 0;
- width: 6rpx;
- background-color: #4DB6AC;
- }
-
- .category-text {
- font-size: 28rpx;
- color: #999999;
- transition: color 0.3s ease;
- }
-
- .category-text-active {
- color: #4DB6AC;
- font-weight: bold;
- }
-
- /* 右侧书籍列表 */
- .book-list-container {
- flex: 1;
- height: 100%;
- background-color: #FFFFFF;
- padding: 0 30rpx;
- }
-
- .book-item {
- display: flex;
- align-items: center;
- padding: 30rpx 0;
- position: relative;
- }
-
- .rank-number {
- font-size: 48rpx;
- font-weight: bold;
- color: #CCCCCC;
- width: 80rpx;
- text-align: center;
- flex-shrink: 0;
- }
-
- .book-cover {
- width: 120rpx;
- height: 160rpx;
- border-radius: 8rpx;
- margin-right: 24rpx;
- flex-shrink: 0;
- background-color: #F5F5F5;
- }
-
- .book-info {
- flex: 1;
- display: flex;
- flex-direction: column;
- justify-content: center;
- min-width: 0;
- }
-
- .book-title {
- font-size: 32rpx;
- font-weight: bold;
- color: #000000;
- margin-bottom: 12rpx;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- .book-author {
- font-size: 26rpx;
- color: #999999;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- .divider-line {
- position: absolute;
- bottom: 0;
- left: 80rpx;
- right: 0;
- height: 1rpx;
- background-color: #E5E5E5;
- }
-
- /* 加载更多 */
- .load-more {
- width: 100%;
- height: 80rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-top: 20rpx;
- margin-bottom: 40rpx;
- }
-
- .load-more-text {
- font-size: 28rpx;
- color: #999999;
- }
- </style>
|