| <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栏 --> | |
|         <uv-tabs :list="tabs" :current="activeTab" keyName="title" @change="switchTab" :scrollable="true" | |
|             :lineColor="'#000'" :activeStyle="{ color: '#000', fontWeight: '900' }" | |
|             :inactiveStyle="{ color: '#606266' }"></uv-tabs> | |
| 
 | |
|         <!-- 轮播图 --> | |
|         <view v-if="activeTab == 0"> | |
|             <view class="swiper-container" :class="{ 'tablet-swiper': isTablet }"> | |
|                 <uv-swiper :list="bannerList" keyName="image" :height="swiperConfig.height" | |
|                     :radius="swiperConfig.radius" :previousMargin="swiperConfig.previousMargin" | |
|                     :nextMargin="swiperConfig.nextMargin" :displayMultipleItems="swiperConfig.displayMultipleItems" | |
|                     indicator indicatorInactiveColor="#fff" :loading="false" indicatorMode="dot" | |
|                     indicatorActiveColor="#F95A01" :autoplay="true" :interval="4000" :circular="true" | |
|                     @click="onBannerClick"></uv-swiper> | |
|             </view> | |
|  | |
|             <view> | |
|                 <articleList :articleList="articleList" /> | |
|             </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" v-if="labelData.labelInfo">{{ 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/default-image.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/play-icon.png" class="item-icon" /> | |
|                                     <text>{{ item.duration }}</text> | |
|                                 </view> | |
|                             </view> | |
|                         </view> | |
|                     </view> | |
|                 </scroll-view> --> | |
|  | |
|                 <!-- 第二个label:推荐书籍样式 --> | |
|                 <scroll-view v-if="labelIndex === 0" 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/default-image.png'" mode="aspectFill"></image> | |
|                                 <view class="book-overlay"> | |
|                                     <view class="book-duration" v-if="book.duration"> | |
|                                         <image src="/static/alarm-icon.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/default-image.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/play-icon.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="widthFix" class="recommend-image"></image> | |
|                     </view> | |
|                 </view> | |
|             </view> | |
|         </view> | |
| 
 | |
|         <view v-else> | |
|             <BookList :list="list" /> | |
|         </view> | |
| 
 | |
|         <!-- 视频播放弹窗 --> | |
|         <VideoPopup ref="videoPopup" /> | |
|     </view> | |
| </template> | |
| 
 | |
| <script> | |
| import SplashScreen from '../components/SplashScreen.vue' | |
| import VideoPopup from '../components/VideoPopup.vue' | |
| import BookList from '@/components/BookList.vue' | |
| import articleList from '@/components/articleList.vue' | |
| import list from '@/mixins/list' | |
| export default { | |
|     components: { | |
|         SplashScreen, | |
|         VideoPopup, | |
|         BookList, | |
|         articleList, | |
|     }, | |
|     mixins: [list], | |
|     data() { | |
|         return { | |
|             // Tab数据 | |
|             tabs: [], | |
|             activeTab: 0, | |
|             // 轮播图数据 | |
|             bannerList: [ | |
| 
 | |
|             ], | |
|             // 书籍分类 | |
|             labels: [ | |
| 
 | |
|             ], | |
| 
 | |
|             // 根据label获取的书籍数据(二维数组) | |
|             labelBooksData: [], | |
|             // 推荐列表数据 | |
|             recommendList: [ | |
| 
 | |
|             ], | |
|             mixinListApi: 'book.list', | |
| 
 | |
|             // 设备类型检测 | |
|             isTablet: false, | |
| 
 | |
|             articleList: [] | |
|         } | |
|     }, | |
| 
 | |
|     computed: { | |
|         // 轮播图配置 | |
|         swiperConfig() { | |
|             if (this.isTablet) { | |
|                 // 平板设备使用卡片式轮播 | |
|                 return { | |
|                     height: "200", | |
|                     radius: "16", | |
|                     previousMargin: "60", | |
|                     nextMargin: "60", | |
|                     displayMultipleItems: 1.5 | |
|                 } | |
|             } else { | |
|                 // 手机设备使用普通轮播 | |
|                 return { | |
|                     height: "121", | |
|                     radius: "12", | |
|                     previousMargin: "0", | |
|                     nextMargin: "0", | |
|                     displayMultipleItems: 1 | |
|                 } | |
|             } | |
|         } | |
|     }, | |
|     methods: { | |
|         async getArticleList() { | |
|             try { | |
|                 const articleRes = await this.$api.home.getArticle() | |
|                 if (articleRes.code === 200) { | |
|                     this.articleList = articleRes.result || [] | |
|                     console.log('文章列表数据:', this.articleList) | |
|                 } | |
|             } catch (error) { | |
|                 console.error('获取文章列表失败:', error) | |
|                 this.articleList = [] | |
|             } | |
|         }, | |
|         mixinSetParams() { | |
|             return { | |
|                 category: this.tabs[this.activeTab]?.id | |
|             } | |
|         }, | |
|         // 检测设备类型 | |
|         detectDevice() { | |
|             // #ifdef H5 | |
|             const userAgent = navigator.userAgent | |
|             const screenWidth = window.innerWidth || document.documentElement.clientWidth | |
|             const screenHeight = window.innerHeight || document.documentElement.clientHeight | |
| 
 | |
|             // 判断是否为平板设备 | |
|             // 1. 屏幕宽度大于768px | |
|             // 2. 或者是iPad设备 | |
|             this.isTablet = screenWidth >= 768 || /iPad|Android.*(?=.*\b(tablet|pad)\b)/i.test(userAgent) | |
| 
 | |
|             console.log('设备检测结果:', { | |
|                 screenWidth, | |
|                 screenHeight, | |
|                 userAgent, | |
|                 isTablet: this.isTablet | |
|             }) | |
|             // #endif | |
|  | |
|             // #ifndef H5 | |
|             // 非H5环境,通过系统信息判断 | |
|             const systemInfo = uni.getSystemInfoSync() | |
|             const screenWidth = systemInfo.screenWidth | |
|             this.isTablet = screenWidth >= 768 | |
|             // #endif | |
|         }, | |
|         // 開動頁面關閉處理 | |
|         onSplashClose() { | |
|             console.log('開動頁面已關閉') | |
|             // 可以在這裡添加其他邏輯,比如統計、初始化等 | |
|         }, | |
| 
 | |
|         // 切换Tab | |
|         async switchTab(e) { | |
|             console.log('切换Tab:', e.index) | |
|             this.activeTab = e.index | |
|             if (e.index == 0) { | |
|                 this.getBooksByLabels() | |
|             } else { | |
|                 this.initPage() | |
|                 this.getList(true) | |
|             } | |
|         }, | |
| 
 | |
|         // 轮播图点击事件 | |
|         onBannerClick(index) { | |
|             console.log('点击轮播图:', index) | |
|             const bannerItem = this.bannerList[index] | |
|             if (!bannerItem) return | |
| 
 | |
|             // 根据 typ 字段判断跳转类型 | |
|             switch (bannerItem.typ) { | |
|                 case '1': // 课程详情 | |
|                     if (bannerItem.bookId) { | |
|                         uni.navigateTo({ | |
|                             url: '/subPages/home/directory?id=' + bannerItem.bookId | |
|                         }) | |
|                     } | |
|                     break | |
|                 case '2': // 视频播放 | |
|                     if (bannerItem.video) { | |
|                         this.$refs.videoPopup.open(bannerItem.video) | |
|                     } | |
|                     break | |
|                 case '0': // 富文本内容 | |
|                     if (bannerItem.content) { | |
|                         uni.navigateTo({ | |
|                             url: '/subPages/home/richtext?content=' + encodeURIComponent(bannerItem.content) | |
|                         }) | |
|                     } | |
|                     break | |
|                 default: | |
|                     console.log('未知的轮播图类型:', bannerItem.typ) | |
|             } | |
|         }, | |
|         // 跳转计划定制 | |
|         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, | |
|                     typ: item.typ, | |
|                     bookId: item.bookId, | |
|                     video: item.video, | |
|                     content: item.content, | |
|                     id: item.id | |
|                 })) | |
| 
 | |
|             } | |
|         }, | |
|         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 | |
|                 })) | |
|                 this.tabs.unshift({ | |
|                     title: '为您推荐', | |
|                 }) | |
|             } | |
|         }, | |
|         // 获取书籍标签 | |
|         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, index) => | |
|                     this.$api.book.list({ | |
|                         label: label.id, | |
|                         pageNo: 1, | |
|                         pageSize: 6, | |
|                         // category: this.tabs[this.activeTab].id | |
|                     }, false) | |
|                         .then(res => { | |
|                             if (res.code === 200) { | |
|                                 this.$set(this.labelBooksData, index, { | |
|                                     labelInfo: label, | |
|                                     books: res.result.records || [] | |
|                                 }) | |
|                             } else { | |
|                                 console.error(`获取label ${label.title} 的书籍失败:`, res) | |
|                                 this.$set(this.labelBooksData, index, { | |
|                                     labelInfo: label, | |
|                                     books: [] | |
|                                 }) | |
|                             } | |
|                         }) | |
|                 ) | |
| 
 | |
|                 // 并发执行所有请求 | |
|                 // 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() { | |
|         // 检测设备类型 | |
|         this.detectDevice() | |
| 
 | |
|         this.getArticleList() | |
|          | |
|         if (uni.getStorageSync('token')) { | |
|             this.$store.dispatch('getUserInfo'); | |
|         } | |
| 
 | |
|         // 先获取基础数据 | |
|         await Promise.all([this.getBanner(), this.getSignup(), this.getCategory(), this.getLabel()]) | |
| 
 | |
|         // 根据label数据获取对应的书籍 | |
|         await this.getBooksByLabels() | |
|     }, | |
| 
 | |
|     mounted() { | |
|         // 页面挂载时也检测一次设备类型 | |
|         this.detectDevice() | |
| 
 | |
|         // #ifdef H5 | |
|         // 监听窗口大小变化 | |
|         window.addEventListener('resize', this.detectDevice) | |
|         // #endif | |
|     }, | |
| 
 | |
|     beforeDestroy() { | |
|         // #ifdef H5 | |
|         // 移除事件监听 | |
|         window.removeEventListener('resize', this.detectDevice) | |
|         // #endif | |
|     } | |
| } | |
| </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; | |
|     } | |
| 
 | |
| } | |
| 
 | |
| // 轮播图容器 | |
| .swiper-container { | |
|     margin: 20rpx; | |
|     border-radius: 12rpx; | |
|     overflow: hidden; | |
| 
 | |
|     // 平板设备的轮播图样式 | |
|     &.tablet-swiper { | |
|         margin: 30rpx 0; | |
|         border-radius: 16rpx; | |
| 
 | |
|         // 卡片式轮播的额外样式 | |
|         :deep(.uv-swiper) { | |
|             .swiper-slide { | |
|                 transition: all 0.3s ease; | |
|                 transform-origin: center; | |
| 
 | |
|                 // 非激活状态的卡片 | |
|                 &:not(.swiper-slide-active) { | |
|                     transform: scale(0.9); | |
|                     opacity: 0.7; | |
|                 } | |
| 
 | |
|                 // 激活状态的卡片 | |
|                 &.swiper-slide-active { | |
|                     transform: scale(1); | |
|                     opacity: 1; | |
|                     z-index: 2; | |
|                 } | |
|             } | |
| 
 | |
|             // 调整指示器位置 | |
|             .uv-swiper__indicator { | |
|                 bottom: -40rpx; | |
|             } | |
|         } | |
|     } | |
| } | |
| 
 | |
| // 内容区块 | |
| .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; | |
|         transition: transform 0.3s ease, box-shadow 0.3s ease; | |
| 
 | |
|         &:active { | |
|             transform: scale(0.98); | |
|         } | |
| 
 | |
|         .book-cover { | |
|             width: 100%; | |
|             height: 360rpx; | |
|             border-radius: 16rpx; | |
|             overflow: hidden; | |
|             position: relative; | |
|             box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15); | |
|             transition: box-shadow 0.3s ease, transform 0.3s ease; | |
| 
 | |
|             &:active { | |
|                 box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.25); | |
|                 transform: translateY(-2rpx); | |
|             } | |
| 
 | |
|             image { | |
|                 width: 100%; | |
|                 height: 100%; | |
|                 transition: transform 0.3s ease; | |
|             } | |
| 
 | |
| 
 | |
|             .book-overlay { | |
|                 position: absolute; | |
|                 bottom: 0; | |
|                 left: 0; | |
|                 right: 0; | |
|                 width: 100%; | |
|                 height: 140rpx; | |
|                 padding: 20rpx 16rpx 12rpx; | |
|                 box-sizing: border-box; | |
| 
 | |
|                 /* 优化的渐变遮罩效果 */ | |
|                 background: linear-gradient(180deg, | |
|                         rgba(0, 0, 0, 0) 0%, | |
|                         rgba(0, 0, 0, 0.3) 30%, | |
|                         rgba(0, 0, 0, 0.7) 70%, | |
|                         rgba(0, 0, 0, 0.85) 100%); | |
| 
 | |
|                 /* 增强的毛玻璃效果 */ | |
|                 backdrop-filter: blur(8px) saturate(1.2); | |
|                 -webkit-backdrop-filter: blur(8px) saturate(1.2); | |
| 
 | |
|                 /* 添加微妙的边框 */ | |
|                 border-top: 1px solid rgba(255, 255, 255, 0.1); | |
| 
 | |
|                 /* 平滑过渡效果 */ | |
|                 transition: all 0.3s ease; | |
| 
 | |
|                 .book-duration { | |
|                     display: flex; | |
|                     align-items: center; | |
|                     gap: 6rpx; | |
|                     margin-bottom: 8rpx; | |
| 
 | |
|                     &-icon { | |
|                         width: 22rpx; | |
|                         height: 22rpx; | |
|                         opacity: 0.9; | |
|                     } | |
| 
 | |
|                     &-text { | |
|                         font-size: 20rpx; | |
|                         font-weight: 500; | |
|                         color: rgba(255, 255, 255, 0.9); | |
|                         text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); | |
|                     } | |
|                 } | |
| 
 | |
|                 .book-title { | |
|                     max-width: 220rpx; | |
|                     font-size: 24rpx; | |
|                     font-weight: 600; | |
|                     line-height: 1.3; | |
|                     color: #ffffff; | |
|                     text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5); | |
| 
 | |
|                     /* 文本截断优化 */ | |
|                     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; | |
| 
 | |
|         // backdrop-filter: red; | |
|         .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 { | |
|             width: 208rpx; | |
|             padding: 6rpx; | |
|             overflow: hidden; | |
|             text-overflow: ellipsis; | |
|             white-space: nowrap; | |
| 
 | |
|             .book-grid-title { | |
|                 font-size: 28rpx; | |
|                 font-weight: 700; | |
|                 color: $primary-text-color; | |
|                 margin-bottom: 14rpx; | |
| 
 | |
|             } | |
| 
 | |
|             .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%; | |
|         margin-bottom: 48rpx; | |
|         border-radius: 32rpx; | |
|         overflow: hidden; | |
| 
 | |
|         &:last-child { | |
|             margin-bottom: 0; | |
|         } | |
| 
 | |
|         .recommend-image { | |
|             width: 100%; | |
|             height: 200rpx; | |
|         } | |
|     } | |
| } | |
| </style>
 |