| <template> | |
|   <view class="home-container"> | |
|     <!-- 開動頁面組件 --> | |
|     <SplashScreen @close="onSplashClose" /> | |
|      | |
|     <!-- 状态栏安全区域 --> | |
|     <uv-status-bar></uv-status-bar> | |
|      | |
|     <!-- 顶部搜索栏 --> | |
|     <view class="header"> | |
|       <view class="search-container" @click="goSearch"> | |
|         <uv-search  | |
|           placeholder="请输入要查询的内容"  | |
|           :show-action="false" | |
|           shape="round" | |
|           bg-color="#f5f5f5" | |
|           color="#666" | |
|           height="38" | |
|           margin="0 200rpx 0 0" | |
|           placeholderColor="#c6c6c6" | |
|         ></uv-search> | |
|       </view> | |
| 
 | |
|     </view> | |
| 
 | |
|     <!-- Tab栏 --> | |
|     <view class="tab-container"> | |
|       <scroll-view show-scrollbar="false" class="tab-scroll" scroll-x="true" > | |
|         <view class="tab-list"> | |
|           <view  | |
|             v-for="(tab, index) in tabs"  | |
|             :key="index"  | |
|             class="tab-item"  | |
|             :class="{ active: activeTab === index }" | |
|             @click="switchTab(index)" | |
|           > | |
|             {{ tab.title }} | |
|           </view> | |
|         </view> | |
|       </scroll-view> | |
|     </view> | |
|  | |
|     <!-- 轮播图 --> | |
|     <view class="swiper-container"> | |
|       <uv-swiper  | |
|         :list="bannerList"  | |
|         keyName="image" | |
|         height="121" | |
|         radius="12" | |
|         indicator | |
|         ndicatorInactiveColor="#fff" | |
|         :loading="false" | |
|         indicatorMode="dot" | |
|         indicatorActiveColor="#F95A01" | |
|         @click="onBannerClick" | |
|       ></uv-swiper> | |
|     </view> | |
|  | |
|     <!-- 根据labelBooksData动态渲染书籍区块 --> | |
|     <view  | |
|       v-for="(labelData, labelIndex) in labelBooksData"  | |
|       :key="labelIndex"  | |
|       class="section" | |
|     > | |
|       <view class="section-header" @click="goLabel(labelData.labelInfo)"> | |
|         <text class="section-title">{{ labelData.labelInfo.title }}</text> | |
|         <view class="section-more"> | |
|           <text>更多</text> | |
|           <uv-icon name="arrow-right" size="14" color="#888"></uv-icon> | |
|         </view> | |
|       </view> | |
|        | |
|       <!-- 第一个label:今日更新样式 --> | |
|       <scroll-view  | |
|         v-if="labelIndex === 0"  | |
|         show-scrollbar="false"  | |
|         class="content-scroll"  | |
|         scroll-x="true" | |
|       > | |
|         <view class="content-list"> | |
|           <view  | |
|             v-for="(item, index) in labelData.books"  | |
|             :key="index"  | |
|             class="content-item" | |
|             @click="goBook(item)" | |
|           > | |
|             <view class="item-cover"> | |
|               <image :src="item.booksImg || '/static/默认图片.png'" mode="aspectFill"></image> | |
|             </view> | |
|             <view class="item-info"> | |
|               <text class="item-title">{{ item.booksName }}</text> | |
|               <text class="item-author">{{ item.booksAuthor }}</text> | |
|               <view class="item-duration"> | |
|                 <image src="/static/播放图标.png" class="item-icon" /> | |
|                 <text>{{ item.duration }}</text> | |
|               </view> | |
|             </view> | |
|           </view> | |
|         </view> | |
|       </scroll-view> | |
|        | |
|       <!-- 第二个label:推荐书籍样式 --> | |
|       <scroll-view  | |
|         v-else-if="labelIndex === 1"  | |
|         show-scrollbar="false"  | |
|         class="content-scroll"  | |
|         scroll-x="true" | |
|       > | |
|         <view class="book-list"> | |
|           <view  | |
|             v-for="(book, index) in labelData.books"  | |
|             :key="index"  | |
|             class="book-item" | |
|             @click="goBook(book)" | |
|           > | |
|             <view class="book-cover"> | |
|               <image :src="book.booksImg || '/static/默认图片.png'" mode="aspectFill"></image> | |
|               <view class="book-overlay"> | |
|                 <view class="book-duration"> | |
|                   <image src="/static/闹钟图标.png" class="book-duration-icon" /> | |
|                   <text class="book-duration-text">{{ book.duration }}</text> | |
|                 </view> | |
|                 <view class="book-title">{{ book.booksName }}</view> | |
|               </view> | |
|             </view> | |
|           </view> | |
|         </view> | |
|       </scroll-view> | |
|        | |
|       <!-- 第三个及以后的label:网格样式 --> | |
|       <view v-else class="book-grid"> | |
|         <view  | |
|           v-for="(book, index) in labelData.books"  | |
|           :key="index"  | |
|           class="book-grid-item" | |
|           @click="goBook(book)" | |
|         > | |
|           <view class="book-grid-cover"> | |
|             <image :src="book.booksImg || '/static/默认图片.png'" mode="aspectFill"></image> | |
|           </view> | |
|           <view class="book-grid-info"> | |
|             <text class="book-grid-title">{{ book.booksName }}</text> | |
|             <view class="book-grid-meta"> | |
|               <text class="book-grid-grade">{{ book.categoryName }}/</text> | |
|               <image src="/static/播放图标.png" class="book-grid-duration-icon" /> | |
|               <text class="book-grid-duration">{{ book.duration }}</text> | |
|             </view> | |
|           </view> | |
|         </view> | |
|       </view> | |
|     </view> | |
|  | |
|     <!-- 推荐内容列表 --> | |
|     <view class="section"> | |
|       <view class="recommend-list"> | |
|         <view  | |
|           @click="goPlan(item.id, item.type)" | |
|           v-for="(item, index) in recommendList"  | |
|           :key="index"  | |
|           class="recommend-item" | |
|         > | |
|           <image :src="item.img" mode="aspectFill" class="recommend-image"></image> | |
|         </view> | |
|       </view> | |
|     </view> | |
|   </view> | |
| </template> | |
| 
 | |
| <script> | |
| import SplashScreen from '../components/SplashScreen.vue' | |
| 
 | |
| export default { | |
|   components: { | |
|     SplashScreen | |
|   }, | |
|   data() { | |
|     return { | |
|       // Tab数据 | |
|       tabs: [ ], | |
|       activeTab: 0, | |
|        | |
|       // 轮播图数据 | |
|       bannerList: [ | |
| 
 | |
|       ], | |
| 
 | |
|       // 书籍分类 | |
|       labels: [ | |
|          | |
|       ], | |
|        | |
|       // 根据label获取的书籍数据(二维数组) | |
|       labelBooksData: [], | |
|        | |
| 
 | |
|         | |
|        // 推荐列表数据 | |
|        recommendList: [ | |
|           | |
|        ] | |
|     } | |
|   }, | |
|    | |
|   methods: { | |
|     // 開動頁面關閉處理 | |
|     onSplashClose() { | |
|       console.log('開動頁面已關閉') | |
|       // 可以在這裡添加其他邏輯,比如統計、初始化等 | |
|     }, | |
|      | |
|     // 切换Tab | |
|     async switchTab(index) { | |
|       this.activeTab = index | |
|       await this.getBooksByLabels() | |
|     }, | |
|      | |
|     // 轮播图点击事件 | |
|     onBannerClick(index) { | |
|       console.log('点击轮播图:', index) | |
|       // 这里可以添加跳转逻辑 | |
|     }, | |
|     // 跳转计划定制 | |
|     goPlan(id, type) { | |
|       uni.navigateTo({ | |
|         url: '/subPages/home/plan?id=' + id + '&type=' + type | |
|       }) | |
|     }, | |
|     goSearch() { | |
|       uni.navigateTo({ | |
|         url: '/subPages/home/search' | |
|       }) | |
|     }, | |
|     goBook(book) { | |
|       uni.navigateTo({ | |
|         url: '/subPages/home/directory?id=' + book.id | |
|       }) | |
|     }, | |
|     async getBanner() { | |
|       const bannerRes = await this.$api.home.getBanner() | |
|       if (bannerRes.code === 200){ | |
|         this.bannerList = bannerRes.result.map(item => ({ | |
|             image: item.img, | |
|             title: item.title    | |
|         })) | |
|       } | |
|     }, | |
|     async getSignup() { | |
|       const signupRes = await this.$api.home.getLink() | |
|       if (signupRes.code === 200){ | |
|         this.recommendList = signupRes.result.map(item => ({ | |
|           img: item.img, | |
|           id: item.id, | |
|           type: item.type | |
|         })) | |
|       } | |
|     }, | |
|     // 获取书籍分类 | |
|     async getCategory() { | |
|       const categoryRes = await this.$api.book.category() | |
|       if (categoryRes.code === 200){ | |
|         this.tabs = categoryRes.result.map(item => ({ | |
|             title:item.title, | |
|             id: item.id | |
|         })) | |
|       } | |
|     }, | |
|     // 获取书籍标签 | |
|     async getLabel() { | |
|       const labelRes = await this.$api.book.label() | |
|       if (labelRes.code === 200){ | |
|         this.labels = labelRes.result.map(item => ({ | |
|             title:item.lable, | |
|             id: item.id | |
|         })) | |
|       } | |
|     }, | |
| 
 | |
|      | |
|     // 根据label数组获取书籍数据 | |
|     async getBooksByLabels() { | |
|       if (!this.labels || this.labels.length === 0) { | |
|         console.log('labels数据为空,无法获取书籍') | |
|         return | |
|       } | |
|        | |
|       try { | |
|         // 创建请求数组,每个label发起一次请求 | |
|         const requests = this.labels.map(label =>  | |
|           this.$api.book.list({ | |
|             label: label.id, | |
|             pageNo: 1, | |
|             pageSize: 6, | |
|             category: this.tabs[this.activeTab].id | |
|           }, false) | |
|         ) | |
|          | |
|         // 并发执行所有请求 | |
|         const responses = await Promise.all(requests) | |
|          | |
|         // 创建二维数组存储结果 | |
|         this.labelBooksData = responses.map((response, index) => { | |
|           if (response.code === 200) { | |
|             return { | |
|               labelInfo: this.labels[index], | |
|               books: response.result.records || [] | |
|             } | |
|           } else { | |
|             console.error(`获取label ${this.labels[index].title} 的书籍失败:`, response) | |
|             return { | |
|               labelInfo: this.labels[index], | |
|               books: [] | |
|             } | |
|           } | |
|         }) | |
|          | |
|         console.log('根据label获取的书籍数据:', this.labelBooksData) | |
|       } catch (error) { | |
|         console.error('获取书籍数据失败:', error) | |
|         this.labelBooksData = [] | |
|       } | |
|     }, | |
|     goLabel(label){ | |
|       uni.navigateTo({ | |
|         url: '/subPages/home/search?label=' + label.id | |
|       }) | |
|     } | |
|   }, | |
| 
 | |
|   async onShow() { | |
|     // 先获取基础数据 | |
|     await Promise.all([this.getBanner(), this.getSignup(), this.getCategory(), this.getLabel()]) | |
|      | |
|     // 根据label数据获取对应的书籍 | |
|     await this.getBooksByLabels() | |
|   } | |
| } | |
| </script> | |
| 
 | |
| <style lang="scss" scoped> | |
| .home-container { | |
|   background: #fff; | |
|   min-height: 100vh; | |
|   padding-bottom: 80rpx; | |
| } | |
| 
 | |
| // 顶部搜索栏 | |
| .header { | |
| 
 | |
|   display: flex; | |
|   align-items: center; | |
|   padding: 6rpx 32rpx; | |
|   background: #fff; | |
|    | |
|   .search-container { | |
|     flex: 1; | |
|   } | |
| 
 | |
| } | |
| 
 | |
| // Tab栏 | |
| .tab-container { | |
|   background: #fff; | |
|   // border-bottom: 1px solid #f0f0f0; | |
|    | |
|   top: 0; | |
|   left: 0; | |
|   right: 0; | |
|   z-index: 999; | |
|   .tab-scroll { | |
|     white-space: nowrap; | |
|      | |
|     .tab-list { | |
|       display: flex; | |
|       padding: 0 20rpx; | |
|        | |
|       .tab-item { | |
|         flex-shrink: 0; | |
|         padding: 20rpx 20rpx; | |
|         font-size: 32rpx; | |
|         color: #666; | |
|         position: relative; | |
|          | |
|         &.active { | |
|           color: $primary-text-color; | |
|           font-weight: 700; | |
|            | |
|           &::after { | |
|             content: ''; | |
|             position: absolute; | |
|             bottom: 0; | |
|             left: 50%; | |
|             transform: translateX(-50%); | |
|             width: 22rpx; | |
|             height: 4rpx; | |
|             background: $primary-text-color; | |
|             border-radius: 2rpx; | |
|           } | |
|         } | |
|       } | |
|     } | |
|   } | |
| } | |
| 
 | |
| // 轮播图容器 | |
| .swiper-container { | |
|   margin: 20rpx; | |
|   border-radius: 12rpx; | |
|   overflow: hidden; | |
| } | |
| 
 | |
| // 内容区块 | |
| .section { | |
|   margin-top: 40rpx; | |
|    | |
|   .section-header { | |
|     display: flex; | |
|     align-items: center; | |
|     justify-content: space-between; | |
|     padding: 0 30rpx ; | |
|     margin-bottom: 24rpx; | |
|     .section-title { | |
|       font-size: 36rpx; | |
|       // font-weight: 600; | |
|       color: $primary-text-color; | |
|     } | |
|      | |
|     .section-more { | |
|       display: flex; | |
|       align-items: center; | |
|       gap: 4rpx; | |
|        | |
|       text { | |
|         font-size: 24rpx; | |
|         color: $secondary-text-color; | |
|       } | |
|     } | |
|   } | |
|    | |
|   .content-scroll { | |
|     white-space: nowrap; | |
|   } | |
| } | |
| 
 | |
| // 今日更新列表 | |
| .content-list { | |
|   display: flex; | |
|   padding: 0 30rpx; | |
|   gap: 32rpx; | |
|    | |
|   .content-item { | |
|     flex-shrink: 0; | |
|     width: 602rpx; | |
|     height: 212rpx; | |
|     display: flex; | |
|     align-items: center; | |
|     background: #F8F8F8; | |
|     padding: 16rpx; | |
|     border-radius: 16rpx; | |
|     gap: 16rpx; | |
|     .item-cover { | |
|       width: 136rpx; | |
|       height: 200rpx; | |
|       border-radius: 16rpx; | |
|       // overflow: hidden; | |
|        | |
|       image { | |
|         width: 136rpx; | |
|       height: 200rpx; | |
|       } | |
|     } | |
|      | |
|     .item-info { | |
|       // padding-top: 20rpx; | |
|       gap: 16rpx; | |
|       display: flex; | |
|       flex-direction: column; | |
|       .item-title { | |
|          | |
|         font-size: 32rpx; | |
|         font-weight: 700; | |
|         color: $primary-text-color; | |
|         letter-spacing: 0; | |
|         line-height: 48rpx; | |
|         // margin-bottom: 12rpx; | |
|         overflow: hidden; | |
|         text-overflow: ellipsis; | |
|         white-space: nowrap; | |
|       } | |
|        | |
|       .item-author { | |
|          | |
|         font-size: 24rpx; | |
|         color: $secondary-text-color; | |
|         // margin-bottom: 8rpx; | |
|         letter-spacing: 0; | |
|         overflow: hidden; | |
|         text-overflow: ellipsis; | |
|         white-space: nowrap; | |
|       } | |
|        | |
|       .item-duration { | |
|         gap: 12rpx; | |
|         display: flex; | |
|         align-items: center; | |
|         font-size: 22rpx; | |
|         letter-spacing: 0; | |
|         color: $secondary-text-color; | |
|         .item-icon{ | |
|           width: 22rpx; | |
|           height: 25rpx; | |
|         } | |
|       } | |
|     } | |
|   } | |
| } | |
| 
 | |
| // 推荐书籍列表 | |
| .book-list { | |
|   display: flex; | |
|   padding: 0 30rpx; | |
|   gap: 32rpx; | |
|    | |
|   .book-item { | |
|     flex-shrink: 0; | |
|     width: 270rpx; | |
|     // border-radius: 16rpx; | |
|     .book-cover { | |
|       width: 100%; | |
|       height: 360rpx; | |
|       border-radius: 16rpx; | |
|       overflow: hidden; | |
|       position: relative; | |
|        | |
|       image { | |
|         width: 100%; | |
|         height: 100%; | |
|       } | |
|        | |
| 
 | |
|       .book-overlay { | |
|         position: absolute; | |
|         bottom: 0; | |
|         left: 0; | |
|         right: 0; | |
|         width: 100%; | |
|         height: 140rpx; | |
|         padding-top: 4rpx; | |
|         padding-right: 16rpx; | |
|         padding-bottom: 8rpx; | |
|         padding-left: 16rpx; | |
|         backdrop-filter: blur(5px); | |
|         box-sizing: border-box; | |
|         background: #00000066; | |
|         padding: 20rpx 16rpx 8rpx; | |
|         // gap: 26rpx; | |
|  | |
|         .book-duration{ | |
|           display: flex; | |
|           gap: 8rpx; | |
|            | |
|           &-icon{ | |
|             width: 24rpx; | |
|             height: 24rpx; | |
|           } | |
|           &-text{ | |
|             font-size: 20rpx; | |
|             color: #DCDCDC; | |
|           } | |
|         } | |
| 
 | |
|         .book-title { | |
|           margin-top: 10rpx; | |
|           max-width: 220rpx; | |
|           font-size: 24rpx; | |
|           line-height: 1.4; | |
|           color: #fff; | |
| 
 | |
|           // max-height: 68rpx;        /* = line-height * 2(34rpx * 2)作为保险 */ | |
|           display: -webkit-box; | |
|           -webkit-box-orient: vertical; | |
|           -webkit-line-clamp: 2; /* 关键:显示两行,超过两行才省略 */ | |
|           overflow: hidden; | |
|           word-break: break-word; /* 长单词也能断行 */ | |
|           white-space: normal;      /* 允许换行 */ | |
|         } | |
|          | |
|       } | |
|     } | |
|      | |
|   } | |
| } | |
| 
 | |
| // 书籍网格布局 | |
| .book-grid { | |
|   display: flex; | |
|   flex-wrap: wrap; | |
|   padding: 0 30rpx; | |
|   gap: 32rpx; | |
|    | |
|   .book-grid-item { | |
|     width: 208rpx; | |
|     display: flex; | |
|     flex-direction: column; | |
|      | |
|     .book-grid-cover { | |
|       box-shadow: 0px 4px 4px 0px #C0BCBA75; | |
| 
 | |
|       width: 100%; | |
|       height: 278rpx; | |
|       border-radius: 16rpx; | |
|       overflow: hidden; | |
|       margin-bottom: 16rpx; | |
|        | |
|       image { | |
|         width: 100%; | |
|         height: 100%; | |
|       } | |
|     } | |
|      | |
|     .book-grid-info { | |
|       padding: 6rpx; | |
|       .book-grid-title { | |
|         font-size: 28rpx; | |
|         font-weight: 700; | |
|         color: $primary-text-color; | |
|         margin-bottom: 14rpx; | |
|         overflow: hidden; | |
|         text-overflow: ellipsis; | |
|         white-space: nowrap; | |
|       } | |
|        | |
|       .book-grid-meta { | |
|         display: flex; | |
|         align-items: center; | |
|         // gap: 16rpx; | |
|         .book-grid-duration-icon { | |
|           width: 24rpx; | |
|           height: 24rpx; | |
|           margin-right: 12rpx; | |
|         } | |
|         .book-grid-grade { | |
|           font-size: 24rpx; | |
|           color: $secondary-text-color; | |
|           margin-right: 8rpx; | |
|         } | |
|          | |
|         .book-grid-duration { | |
|           font-size: 24rpx; | |
|           color: $secondary-text-color; | |
|         } | |
|       } | |
|     } | |
|   } | |
| } | |
| 
 | |
| // 推荐列表样式 | |
| .recommend-list { | |
|   padding: 0 30rpx; | |
|    | |
|   .recommend-item { | |
|     width: 100%; | |
|     height: 200rpx; | |
|     margin-bottom: 48rpx; | |
|     border-radius: 32rpx; | |
|     overflow: hidden; | |
|      | |
|     &:last-child { | |
|       margin-bottom: 0; | |
|     } | |
|      | |
|     .recommend-image { | |
|       width: 100%; | |
|       height: 100%; | |
|     } | |
|   } | |
| } | |
| </style>
 |