| <template> | |
|     <view class="directory-container"> | |
|         <view class="book-container"> | |
|             <view class="book-info"> | |
|                 <view class="book-cover"> | |
|                     <image :src="bookInfo.booksImg" mode="aspectFill" :style="{ width: '100%', height: '100%' }"></image> | |
|                 </view> | |
|                 <view class="book-details"> | |
|                     <view class="book-title">{{ bookInfo.translate }}</view> | |
|                     <view class="book-subtitle">{{ bookInfo.booksName }}</view> | |
|                     <view class="book-author">{{ bookInfo.booksAuthor }}</view> | |
|                     <view class="book-level" :class="classMap[bookInfo.vipInfo.title]">{{ bookInfo.vipInfo.title }} | |
|                     </view> | |
|                 </view> | |
|             </view> | |
|             <view class="book-knowledge"> | |
|                 <view class="book-knowledge-title" v-if="bookInfo.vocabularyRange"> | |
|                     <text> | |
|                         适合词汇量 | |
|                     </text> | |
|                     <text class="book-knowledge-vocabulary"> | |
|                         {{ bookInfo.vocabularyRange }} | |
|                     </text> | |
|                 </view> | |
|                 <view class="border" /> | |
|                 <view class="book-knowledge-detail" v-if="bookInfo.knowledgePoints"> | |
|                     <view class="book-knowledge-detail-title"> | |
|                         知识收获 | |
|                     </view> | |
|                     <uv-parse :content="bookInfo.knowledgePoints"></uv-parse> | |
|                 </view> | |
|             </view> | |
|         </view> | |
| 
 | |
|         <!-- 课程和简介容器 --> | |
|         <view class="content-container"> | |
|             <!-- 课程部分 --> | |
|             <view class="course-section"> | |
|                 <view class="course-header"> | |
|                     <view class="course-title">课程</view> | |
|                 </view> | |
|                 <view class="course-list"> | |
|                     <!-- 限制在做多五個課程 --> | |
|                     <view v-for="(course, index) in computedList" :key="index" class="course-item" | |
|                         @click="startLearning(course.id)"> | |
|                         <view class="course-number">{{ String(index + 1).padStart(2, '0') }}</view> | |
|                         <view class="course-content"> | |
|                             <view class="course-name">{{ course.english }}</view> | |
|                             <view class="course-subtitle">{{ course.chinese }}</view> | |
|                         </view> | |
|                     </view> | |
|                 </view> | |
|                 <view class="course-footer"> | |
|                     <view class="course-total" @click="showAllCoursePopup">全部课程 · {{ courseList.total }}节</view> | |
|                     <uv-icon name="arrow-right" size="24rpx" color="#999"></uv-icon> | |
|                 </view> | |
|             </view> | |
|  | |
|             <!-- 简介部分 --> | |
|             <view class="intro-section"> | |
|                 <view class="intro-title">简介</view> | |
|                 <uv-parse :content="bookInfo.booksIntro" class="intro-content"></uv-parse> | |
|             </view> | |
|  | |
|             <!-- 作者部分 --> | |
|             <view class="author-section"> | |
|                 <view class="author-title">作者</view> | |
|                 <view class="author-info"> | |
|                     <view class="author-avatar"> | |
|                         <image :src="bookInfo.aouthorImg" mode="aspectFill"></image> | |
|                         <view> | |
|                             <view class="author-name">{{ bookInfo.enAuthor }}</view> | |
|                             <view class="author-subtitle">{{ bookInfo.booksAuthor }}</view> | |
|                         </view> | |
|                     </view> | |
|                     <view class="author-details"> | |
|                         <view class="author-description"> | |
|                             {{ bookInfo.aouthorIntro }} | |
|                         </view> | |
|                     </view> | |
|                 </view> | |
|             </view> | |
|         </view> | |
|  | |
|         <!-- 底部固定操作栏 --> | |
|         <view class="bottom-action-bar"> | |
|             <view class="bottom-action-container"> | |
|                 <view class="action-button secondary" @click="joinCourse"> | |
|                     <image src="/static/course-icon.png" class="button-icon" mode="aspectFill"></image> | |
|                     <text>加入课程</text> | |
|                 </view> | |
|                 <view class="action-button primary" @click="startLearning(courseList.records[0].id)"> | |
|                     <image src="/static/content-icon.png" class="button-icon"></image> | |
|                     <text>内容朗读</text> | |
|                 </view> | |
|                 <uv-button @click="startLearning(courseList.records[0].id)" type="primary" :custom-style="{ | |
|                     width: '400rpx', | |
|                     height: '80rpx', | |
|                     borderRadius: '198rpx', | |
|                     background: '#06DADC', | |
|                     fontSize: '28rpx', | |
|                     fontWeight: '600', | |
|                 }">开始学习</uv-button> | |
|             </view> | |
|             <uv-safe-bottom></uv-safe-bottom> | |
|         </view> | |
|  | |
|         <!-- 全部課程彈出窗 --> | |
|         <uv-popup mode="bottom" ref="allCoursePopup" round="32rpx" bg-color="#f8f8f8"> | |
|             <view class="course-popup"> | |
|                 <view class="popup-header"> | |
|                     <view @click="closeAllCoursePopup"> | |
|                         <uv-icon name="arrow-down" color="black" size="20"></uv-icon> | |
|                     </view> | |
|                     <view class="popup-title">全部课程</view> | |
|                     <view class="popup-title" @click="toggleCourseSort"> | |
|                         倒序 | |
|                     </view> | |
|                 </view> | |
|                 <view class="course-list"> | |
|                     <view v-for="(course, index) in displayAllCourseList" :key="course.id" class="course-item" | |
|                         @click="startLearning(course.id)"> | |
|                         <view class="course-number">{{ String(course.index || index + 1).padStart(2, '0') }}</view> | |
|                         <view class="course-content"> | |
|                             <view class="course-english">{{ course.english }}</view> | |
|                             <view class="course-chinese">{{ course.chinese }}</view> | |
|                         </view> | |
|                     </view> | |
|                 </view> | |
|             </view> | |
|         </uv-popup> | |
|  | |
|     </view> | |
| </template> | |
|  | |
| <script> | |
| export default { | |
|     data() { | |
|         return { | |
|             classMap: { | |
|                 '朵蕾会员': 'book-level-1', | |
|                 '萌芽会员': 'book-level-2', | |
|                 '盛放会员': 'book-level-3', | |
|             }, | |
|             bookInfo: { | |
|  | |
|             }, | |
|             id: '', | |
|             courseList: [ | |
|  | |
|             ], | |
|             allCourseList: [], // 全部課程列表 | |
|             isCourseSortReversed: false, // 課程排序是否倒序 | |
|         } | |
|     }, | |
|     computed: { | |
|         // 顯示的全部課程列表(支持倒序) | |
|         displayAllCourseList() { | |
|             const list = this.allCourseList.length > 0 ? this.allCourseList : this.courseList.records || []; | |
|             return this.isCourseSortReversed ? [...list].reverse() : list; | |
|         }, | |
|         computedList() { | |
|             return this.courseList.records?.slice(0, 5) || [] | |
|         } | |
|     }, | |
|     methods: { | |
|         goBack() { | |
|             uni.navigateBack() | |
|         }, | |
|         // 加入课程 | |
|         async joinCourse() { | |
|             const joinRes = await this.$api.book.addStand({ | |
|                 id: this.id | |
|             }) | |
|             if (joinRes.code === 200) { | |
|                 uni.showToast({ | |
|                     title: '加入成功', | |
|                     icon: 'success', | |
|                     duration: 2000 | |
|                 }) | |
|             } | |
|         }, | |
|         // 开始学习 | |
|         startLearning(id) { | |
|             // 默认学第一堂课 | |
|             uni.navigateTo({ | |
|                 url: '/subPages/home/book?courseId=' + id + '&bookId=' + this.id + '&memberId=' + this.bookInfo.vip | |
|             }) | |
|         }, | |
|  | |
|         scroll() { | |
|             console.log('被点击了'); | |
|  | |
|             this.$scrollTo('testRef') | |
|         }, | |
|         // 获取书籍详情 | |
|         async getDetail() { | |
|             const detailRes = await this.$api.book.detail({ | |
|                 id: this.id | |
|             }) | |
|             if (detailRes.code === 200) { | |
|                 this.bookInfo = detailRes.result | |
|             } | |
|         }, | |
|         // 获取书籍的课程 | |
|         async getCourse() { | |
|             const courseRes = await this.$api.book.course({ | |
|                 id: this.id, | |
|                 pageNo: 1, | |
|                 pageSize: 999 | |
|             }) | |
|             if (courseRes.code === 200) { | |
|                 this.courseList = courseRes.result | |
|                 // 同時設置全部課程列表 | |
|                 this.allCourseList = courseRes.result.records || [] | |
|             } | |
|         }, | |
|  | |
|         // 顯示全部課程彈出窗 | |
|         showAllCoursePopup() { | |
|             this.$refs.allCoursePopup.open() | |
|         }, | |
|  | |
|         // 關閉全部課程彈出窗 | |
|         closeAllCoursePopup() { | |
|             this.$refs.allCoursePopup.close() | |
|         }, | |
|  | |
|         // 切換課程排序 | |
|         toggleCourseSort() { | |
|             this.isCourseSortReversed = !this.isCourseSortReversed | |
|         } | |
|     }, | |
|     onLoad(options) { | |
|         if (options.id) { | |
|             this.id = options.id | |
|             Promise.all([ | |
|                 this.getDetail(), | |
|                 this.getCourse() | |
|             ]) | |
|         } | |
|     } | |
| } | |
| </script> | |
|  | |
| <style scoped lang="scss"> | |
| .directory-container { | |
|     min-height: 100vh; | |
|     background-color: #264C8F; | |
| 
 | |
| 
 | |
| } | |
| 
 | |
| .book-container { | |
|     // position: sticky; | |
|     // left: 0; | |
|     // right: 0; | |
|     // top: 0; | |
|     padding: 30rpx; | |
|     // z-index: 1; | |
| } | |
| 
 | |
| .book-info { | |
|     display: flex; | |
|     align-items: start; | |
| 
 | |
|     gap: 32rpx; | |
| 
 | |
|     .book-cover { | |
|         width: 208rpx; | |
|         height: 292rpx; | |
|         border-radius: 16rpx; | |
|     } | |
| 
 | |
|     .book-details { | |
|         color: white; | |
|         display: flex; | |
|         flex-direction: column; | |
|         gap: 16rpx; | |
| 
 | |
|         .book-title { | |
|             font-weight: 500; | |
|             font-size: 40rpx; | |
|         } | |
| 
 | |
|         .book-subtitle { | |
|             font-weight: 500; | |
|             font-size: 30rpx; | |
|         } | |
| 
 | |
|         .book-author { | |
|             font-size: 24rpx; | |
|         } | |
| 
 | |
|         .book-level { | |
|             font-size: 24rpx; | |
|             width: 124rpx; | |
|             height: 38rpx; | |
|             border-radius: 8rpx; | |
|             text-align: center; | |
|             line-height: 38rpx; | |
|             color: #080D21; | |
|             background: #E9F1FF; | |
|             border: 2rpx solid #C4DAFF | |
|         } | |
| 
 | |
|         .book-level-1 { | |
|             background: #E9F1FF; | |
|             border: 2rpx solid #C4DAFF | |
|         } | |
| 
 | |
|         .book-level-2 { | |
|             background: #FFE9E9; | |
|             border: 2rpx solid #FFDBC4 | |
|         } | |
| 
 | |
|         .book-level-3 { | |
|             background: #FFF4E9; | |
|             border: 2rpx solid #FFE2C4 | |
|         } | |
|     } | |
| } | |
| 
 | |
| .book-knowledge { | |
|     box-shadow: 0px 1px 5px 0px #103577; | |
|     background: #234684; | |
|     color: #fff; | |
|     margin-top: 32rpx; | |
|     border: 2rpx solid #FFFFFF3B; | |
|     border-radius: 32rpx; | |
|     padding-top: 32rpx; | |
|     padding-right: 40rpx; | |
|     padding-bottom: 32rpx; | |
|     padding-left: 40rpx; | |
|     gap: 24rpx; | |
|     display: flex; | |
|     flex-direction: column; | |
|     gap: 22rpx; | |
| 
 | |
|     .book-knowledge-title { | |
|         font-size: 32rpx; | |
|         font-weight: 600; | |
|         display: flex; | |
|         justify-content: space-between; | |
| 
 | |
|         .book-knowledge-vocabulary { | |
|             font-size: 40rpx; | |
|             color: #06DADC; | |
|         } | |
|     } | |
| 
 | |
|     .border { | |
|         width: 100%; | |
|         border: 2rpx solid; | |
|         border-image-source: linear-gradient(90deg, rgba(233, 181, 123, 0) 0%, rgba(255, 255, 255, 0.79) 50.48%, rgba(233, 181, 123, 0) 100%); | |
|         border-image-slice: 1; | |
|     } | |
| 
 | |
|     .book-knowledge-detail-title { | |
|         font-size: 32rpx; | |
|         font-weight: 600; | |
|         margin-bottom: 16rpx; | |
|     } | |
| 
 | |
| } | |
| 
 | |
| /* 课程和简介容器 */ | |
| .content-container { | |
|     padding: 40rpx 32rpx 240rpx; | |
|     border-radius: 40rpx 40rpx 0 0; | |
|     overflow: hidden; | |
|     background: #fff; | |
|     display: flex; | |
|     gap: 24rpx; | |
|     flex-direction: column; | |
|     position: relative; | |
| 
 | |
| } | |
| 
 | |
| /* 课程部分 */ | |
| .course-section { | |
|     background: #F8F8F8; | |
|     border-radius: 32rpx; | |
|     border-radius: 32rpx; | |
|     padding-top: 36rpx; | |
|     padding-right: 32rpx; | |
|     padding-bottom: 36rpx; | |
|     padding-left: 32rpx; | |
|     gap: 36rpx; | |
|     display: flex; | |
|     flex-direction: column; | |
| } | |
| 
 | |
| 
 | |
| 
 | |
| .course-title { | |
|     font-size: 32rpx; | |
|     font-weight: 600; | |
|     color: #3B3D3D; | |
| } | |
| 
 | |
| .course-list { | |
|     // margin-bottom: 32rpx; | |
|     display: flex; | |
|     flex-direction: column; | |
|     gap: 24rpx; | |
| } | |
| 
 | |
| .course-item { | |
|     display: flex; | |
|     align-items: center; | |
|     // background: red; | |
|     border-bottom: 2rpx solid #EEEEEE; | |
|     padding-bottom: 20rpx; | |
|     gap: 36rpx; | |
| } | |
| 
 | |
| .course-item:last-child { | |
|     border-bottom: none; | |
| } | |
| 
 | |
| .course-number { | |
|     font-size: 36rpx; | |
|     color: #999; | |
| 
 | |
| 
 | |
| } | |
| 
 | |
| .course-content { | |
|     flex: 1; | |
| } | |
| 
 | |
| .course-name { | |
|     font-size: 32rpx; | |
|     font-weight: 600; | |
|     color: #3B3D3D; | |
|     margin-bottom: 8rpx; | |
| } | |
| 
 | |
| .course-subtitle { | |
|     font-size: 28rpx; | |
|     color: #3B3D3D; | |
| } | |
| 
 | |
| .course-footer { | |
|     display: flex; | |
|     align-items: center; | |
|     // justify-content: space-between; | |
| } | |
| 
 | |
| .course-total { | |
|     font-size: 24rpx; | |
|     color: #999; | |
|     cursor: pointer; | |
| } | |
| 
 | |
| /* 课程弹出窗样式 */ | |
| .course-popup { | |
|     padding: 0 32rpx; | |
|     max-height: 80vh; | |
|     .popup-header { | |
|         display: flex; | |
|         justify-content: space-between; | |
|         align-items: center; | |
|         padding: 20rpx 0; | |
|         border-bottom: 2rpx solid #EEEEEE // margin-bottom: 40rpx; | |
|     } | |
| 
 | |
|     .popup-title { | |
|         font-family: PingFang SC; | |
|         font-weight: 500; | |
|         font-size: 34rpx; | |
|         color: #181818; | |
|     } | |
| 
 | |
| 
 | |
|     .course-list { | |
|         max-height: 60vh; | |
|         overflow-y: auto; | |
|     } | |
| 
 | |
|     .course-item { | |
|         display: flex; | |
|         align-items: center; | |
|         gap: 24rpx; | |
|         padding-top: 24rpx; | |
|         padding-right: 8rpx; | |
|         padding-bottom: 24rpx; | |
|         padding-left: 8rpx; | |
| 
 | |
|         border-bottom: 1px solid #EEEEEE; | |
|         cursor: pointer; | |
| 
 | |
|         &:last-child { | |
|             border-bottom: none; | |
|         } | |
|     } | |
| 
 | |
|     .course-number { | |
|         width: 80rpx; | |
|         font-family: PingFang SC; | |
|         // font-weight: 400; | |
|         font-size: 36rpx; | |
|         color: #999; | |
| 
 | |
|         &.highlight { | |
|             color: $primary-color; | |
|         } | |
| 
 | |
|         // margin-right: 24rpx; | |
|     } | |
| 
 | |
|     .course-content { | |
|         flex: 1; | |
|     } | |
| 
 | |
|     .course-english { | |
|         font-family: PingFang SC; | |
|         font-weight: 600; | |
|         font-size: 36rpx; | |
|         line-height: 44rpx; | |
|         color: #252545; | |
|         margin-bottom: 8rpx; | |
| 
 | |
|         &.highlight { | |
|             color: $primary-color; | |
|         } | |
|     } | |
| 
 | |
|     .course-chinese { | |
|         font-size: 28rpx; | |
|         line-height: 48rpx; | |
|         color: #3B3D3D; | |
| 
 | |
|         &.highlight { | |
|             color: $primary-color; | |
|         } | |
|     } | |
| } | |
| 
 | |
| /* 简介部分 */ | |
| .intro-section { | |
|     background: #F8F8F8; | |
| 
 | |
|     border-radius: 32rpx; | |
|     padding: 32rpx; | |
| 
 | |
| } | |
| 
 | |
| .intro-title { | |
|     font-size: 32rpx; | |
|     font-weight: 600; | |
|     color: #3B3D3D; | |
|     margin-bottom: 24rpx; | |
| } | |
| 
 | |
| .intro-content { | |
|     font-size: 28rpx; | |
|     line-height: 48rpx; | |
|     color: #4F4F4F; | |
| } | |
| 
 | |
| /* 作者部分 */ | |
| .author-section { | |
|     background: #F8F8F8; | |
|     border-radius: 32rpx; | |
|     padding: 32rpx; | |
| 
 | |
|     .author-title { | |
|         font-size: 32rpx; | |
|         font-weight: 600; | |
|         color: #3B3D3D; | |
|         margin-bottom: 24rpx; | |
|     } | |
| 
 | |
|     .author-info { | |
|         display: flex; | |
|         gap: 24rpx; | |
|         align-items: flex-start; | |
|         flex-direction: column; | |
| 
 | |
|         .author-avatar { | |
| 
 | |
|             display: flex; | |
| 
 | |
|             align-items: center; | |
|             gap: 16rpx; | |
| 
 | |
|             image { | |
|                 width: 80rpx; | |
|                 height: 80rpx; | |
|                 border-radius: 50%; | |
|                 overflow: hidden; | |
|                 flex-shrink: 0; | |
| 
 | |
|             } | |
| 
 | |
|             .author-name { | |
|                 font-size: 36rpx; | |
|                 font-weight: 600; | |
|                 color: #252545; | |
|                 margin-bottom: 12rpx; | |
|             } | |
| 
 | |
|             .author-subtitle { | |
|                 font-size: 28rpx; | |
|                 color: #3B3D3D; | |
|                 // margin-bottom: 16rpx; | |
|             } | |
| 
 | |
|         } | |
| 
 | |
|         .author-details { | |
|             flex: 1; | |
| 
 | |
| 
 | |
|             .author-description { | |
|                 font-size: 28rpx; | |
|                 line-height: 48rpx; | |
|                 color: #4F4F4F; | |
|             } | |
|         } | |
|     } | |
| } | |
| 
 | |
| /* 底部固定操作栏 */ | |
| .bottom-action-bar { | |
|     position: fixed; | |
|     bottom: 0; | |
|     left: 0; | |
|     right: 0; | |
|     background: #fff; | |
|     padding: 24rpx 32rpx 0; | |
|     box-shadow: 0rpx -2rpx 0rpx 0rpx #0000001A; | |
|     z-index: 99; | |
| 
 | |
|     .bottom-action-container { | |
|         display: flex; | |
|         align-items: center; | |
|         gap: 20rpx; | |
| 
 | |
|         .action-button { | |
|             display: flex; | |
|             flex-direction: column; | |
|             align-items: center; | |
|             justify-content: center; | |
|             padding: 16rpx 0rpx; | |
|             border-radius: 16rpx; | |
|             min-width: 120rpx; | |
|             gap: 8rpx; | |
| 
 | |
|             .button-icon { | |
|                 width: 44rpx; | |
|                 height: 44rpx; | |
|             } | |
| 
 | |
|             text { | |
|                 font-size: 24rpx; | |
|                 color: #999999; | |
|             } | |
| 
 | |
|         } | |
| 
 | |
|     } | |
| } | |
| </style> |