You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							739 lines
						
					
					
						
							23 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							739 lines
						
					
					
						
							23 KiB
						
					
					
				| <template> | |
|   <view class="container"> | |
|     <!-- 顶部标题栏 --> | |
|     <!-- 主要内容区域 --> | |
|     <!-- 文本显示区域 --> | |
|     <scroll-view | |
|       class="content" | |
|       :show-scrollbar="false" | |
|       enhanced | |
|       scroll-y | |
|       :scroll-top="scrollTop" | |
|       scroll-with-animation | |
|       :style="{ height: scrollViewHeight + 'px' }" | |
|     > | |
|       <view class="content-wrapper"> | |
|         <view class="article-content"> | |
|           <text | |
|             v-for="(char, index) in currentChapter.characters" | |
|             :key="index" | |
|             class="character" | |
|             :class="{ active: index === activeCharIndex }" | |
|           > | |
|             {{ char.Text }} | |
|           </text> | |
|         </view> | |
|       </view> | |
|     </scroll-view> | |
|     <!-- 底部控制栏 --> | |
|     <view class="controls"> | |
|       <!-- 章节信息栏 --> | |
|       <view class="chapter-info-bar"> | |
|         <text class="chapter-title">{{ currentChapter.title }}</text> | |
|         <text class="chapter-count"> | |
|           {{ currentChapterIndex + 1 }}/{{ chapters.length }} | |
|         </text> | |
|       </view> | |
|  | |
|       <!-- 进度条区域 --> | |
|       <view class="progress-section"> | |
|         <text class="current-time">{{ formatTime(currentTime) }}</text> | |
|         <view class="progress-container"> | |
|           <view class="progress-bar" @click="seek"> | |
|             <view class="progress" :style="{ width: progress + '%' }"></view> | |
|             <view | |
|               class="progress-thumb" | |
|               :style="{ left: progress + '%' }" | |
|             ></view> | |
|           </view> | |
|         </view> | |
|         <text class="duration">{{ formatTime(duration) }}</text> | |
|       </view> | |
|  | |
|       <!-- 控制按钮区域 --> | |
|       <view class="control-buttons"> | |
|         <!-- 上一章按钮 --> | |
|         <button | |
|           class="control-btn prev-btn" | |
|           @click="prevChapter" | |
|           :disabled="currentChapterIndex === 0" | |
|         > | |
|           <text class="control-icon">⏮</text> | |
|         </button> | |
|  | |
|         <!-- 播放/暂停按钮 --> | |
|         <button class="play-btn" @click="togglePlay"> | |
|           <text class="play-icon">{{ playing ? "⏸️" : "▶️" }}</text> | |
|         </button> | |
|  | |
|         <!-- 下一章按钮 --> | |
|         <button | |
|           class="control-btn next-btn" | |
|           @click="nextChapter" | |
|           :disabled="currentChapterIndex === chapters.length - 1" | |
|         > | |
|           <text class="control-icon">⏭</text> | |
|         </button> | |
|       </view> | |
|     </view> | |
| 
 | |
|     <AudioPlayer | |
|       ref="audioPlayer" | |
|       :src="currentChapter.audioUrl" | |
|       @timeupdate="onTimeUpdate" | |
|       @ended="onEnded" | |
|     ></AudioPlayer> | |
|   </view> | |
| </template> | |
| 
 | |
| <script> | |
| // 由于uniapp不支持直接使用audio元素,我们使用innerAudioContext | |
| import AudioPlayer from "../components/AudioPlayer.vue"; | |
| 
 | |
| export default { | |
|   components: { | |
|     AudioPlayer, | |
|   }, | |
|   data() { | |
|     return { | |
|       chapters: [ | |
|         { | |
|           title: "第一章:人工智能的崛起", | |
|           audioUrl: "https://des.dayunyuanjian.cn/epicSoul/EpicSouls.mp3", | |
|           // 字符级别的时间同步数据 | |
|           characters: [ | |
|             {"BeginIndex": 0, "BeginTime": 0, "EndIndex": 1, "EndTime": 500, "Phoneme": "ren2", "Text": "人"}, | |
|             {"BeginIndex": 1, "BeginTime": 500, "EndIndex": 2, "EndTime": 1000, "Phoneme": "gong1", "Text": "工"}, | |
|             {"BeginIndex": 2, "BeginTime": 1000, "EndIndex": 3, "EndTime": 1500, "Phoneme": "zhi4", "Text": "智"}, | |
|             {"BeginIndex": 3, "BeginTime": 1500, "EndIndex": 4, "EndTime": 2000, "Phoneme": "neng2", "Text": "能"}, | |
|             {"BeginIndex": 4, "BeginTime": 2000, "EndIndex": 5, "EndTime": 2200, "Phoneme": "", "Text": "的"}, | |
|             {"BeginIndex": 5, "BeginTime": 2200, "EndIndex": 6, "EndTime": 2700, "Phoneme": "fa1", "Text": "发"}, | |
|             {"BeginIndex": 6, "BeginTime": 2700, "EndIndex": 7, "EndTime": 3200, "Phoneme": "zhan3", "Text": "展"}, | |
|             {"BeginIndex": 7, "BeginTime": 3200, "EndIndex": 8, "EndTime": 3700, "Phoneme": "li4", "Text": "历"}, | |
|             {"BeginIndex": 8, "BeginTime": 3700, "EndIndex": 9, "EndTime": 4200, "Phoneme": "cheng2", "Text": "程"}, | |
|             {"BeginIndex": 9, "BeginTime": 4200, "EndIndex": 10, "EndTime": 4400, "Phoneme": "", "Text": "。"}, | |
|             {"BeginIndex": 10, "BeginTime": 5500, "EndIndex": 11, "EndTime": 6000, "Phoneme": "cong2", "Text": "从"}, | |
|             {"BeginIndex": 11, "BeginTime": 6000, "EndIndex": 12, "EndTime": 6500, "Phoneme": "yi1", "Text": "1"}, | |
|             {"BeginIndex": 12, "BeginTime": 6500, "EndIndex": 13, "EndTime": 7000, "Phoneme": "jiu3", "Text": "9"}, | |
|             {"BeginIndex": 13, "BeginTime": 7000, "EndIndex": 14, "EndTime": 7500, "Phoneme": "wu3", "Text": "5"}, | |
|             {"BeginIndex": 14, "BeginTime": 7500, "EndIndex": 15, "EndTime": 8000, "Phoneme": "ling2", "Text": "0"}, | |
|             {"BeginIndex": 15, "BeginTime": 8000, "EndIndex": 16, "EndTime": 8500, "Phoneme": "nian2", "Text": "年"}, | |
|             {"BeginIndex": 16, "BeginTime": 8500, "EndIndex": 17, "EndTime": 9000, "Phoneme": "dai4", "Text": "代"}, | |
|             {"BeginIndex": 17, "BeginTime": 9000, "EndIndex": 18, "EndTime": 9500, "Phoneme": "kai1", "Text": "开"}, | |
|             {"BeginIndex": 18, "BeginTime": 9500, "EndIndex": 19, "EndTime": 10000, "Phoneme": "shi3", "Text": "始"}, | |
|             {"BeginIndex": 19, "BeginTime": 10000, "EndIndex": 20, "EndTime": 10200, "Phoneme": "", "Text": ","}, | |
|             {"BeginIndex": 20, "BeginTime": 10200, "EndIndex": 21, "EndTime": 10700, "Phoneme": "ren2", "Text": "人"}, | |
|             {"BeginIndex": 21, "BeginTime": 10700, "EndIndex": 22, "EndTime": 11200, "Phoneme": "gong1", "Text": "工"}, | |
|             {"BeginIndex": 22, "BeginTime": 11200, "EndIndex": 23, "EndTime": 11700, "Phoneme": "zhi4", "Text": "智"}, | |
|             {"BeginIndex": 23, "BeginTime": 11700, "EndIndex": 24, "EndTime": 12200, "Phoneme": "neng2", "Text": "能"}, | |
|             {"BeginIndex": 24, "BeginTime": 12200, "EndIndex": 25, "EndTime": 12700, "Phoneme": "zuo4", "Text": "作"}, | |
|             {"BeginIndex": 25, "BeginTime": 12700, "EndIndex": 26, "EndTime": 13200, "Phoneme": "wei2", "Text": "为"}, | |
|             {"BeginIndex": 26, "BeginTime": 13200, "EndIndex": 27, "EndTime": 13700, "Phoneme": "yi1", "Text": "一"}, | |
|             {"BeginIndex": 27, "BeginTime": 13700, "EndIndex": 28, "EndTime": 14200, "Phoneme": "men2", "Text": "门"}, | |
|             {"BeginIndex": 28, "BeginTime": 14200, "EndIndex": 29, "EndTime": 14700, "Phoneme": "xue2", "Text": "学"}, | |
|             {"BeginIndex": 29, "BeginTime": 14700, "EndIndex": 30, "EndTime": 15200, "Phoneme": "ke1", "Text": "科"}, | |
|             {"BeginIndex": 30, "BeginTime": 15200, "EndIndex": 31, "EndTime": 15700, "Phoneme": "zheng4", "Text": "正"}, | |
|             {"BeginIndex": 31, "BeginTime": 15700, "EndIndex": 32, "EndTime": 16200, "Phoneme": "shi4", "Text": "式"}, | |
|             {"BeginIndex": 32, "BeginTime": 16200, "EndIndex": 33, "EndTime": 16700, "Phoneme": "dan4", "Text": "诞"}, | |
|             {"BeginIndex": 33, "BeginTime": 16700, "EndIndex": 34, "EndTime": 17200, "Phoneme": "sheng1", "Text": "生"}, | |
|             {"BeginIndex": 34, "BeginTime": 17200, "EndIndex": 35, "EndTime": 17400, "Phoneme": "", "Text": "。"} | |
|           ], | |
|           content: `人工智能的发展历程。从1950年代开始,人工智能作为一门学科正式诞生。`, | |
|         }, | |
|         { | |
|           title: "第二章:科技与人文的融合", | |
|           audioUrl: "https://des.dayunyuanjian.cn/epicSoul/EpicSouls.mp3", | |
|           // 字符级别的时间同步数据 | |
|           characters: [ | |
|             {"BeginIndex": 0, "BeginTime": 0, "EndIndex": 1, "EndTime": 500, "Phoneme": "ke1", "Text": "科"}, | |
|             {"BeginIndex": 1, "BeginTime": 500, "EndIndex": 2, "EndTime": 1000, "Phoneme": "ji4", "Text": "技"}, | |
|             {"BeginIndex": 2, "BeginTime": 1000, "EndIndex": 3, "EndTime": 1500, "Phoneme": "fa1", "Text": "发"}, | |
|             {"BeginIndex": 3, "BeginTime": 1500, "EndIndex": 4, "EndTime": 2000, "Phoneme": "zhan3", "Text": "展"}, | |
|             {"BeginIndex": 4, "BeginTime": 2000, "EndIndex": 5, "EndTime": 2500, "Phoneme": "yu3", "Text": "与"}, | |
|             {"BeginIndex": 5, "BeginTime": 2500, "EndIndex": 6, "EndTime": 3000, "Phoneme": "ren2", "Text": "人"}, | |
|             {"BeginIndex": 6, "BeginTime": 3000, "EndIndex": 7, "EndTime": 3500, "Phoneme": "wen2", "Text": "文"}, | |
|             {"BeginIndex": 7, "BeginTime": 3500, "EndIndex": 8, "EndTime": 4000, "Phoneme": "guan1", "Text": "关"}, | |
|             {"BeginIndex": 8, "BeginTime": 4000, "EndIndex": 9, "EndTime": 4500, "Phoneme": "huai2", "Text": "怀"}, | |
|             {"BeginIndex": 9, "BeginTime": 4500, "EndIndex": 10, "EndTime": 5000, "Phoneme": "de5", "Text": "的"}, | |
|             {"BeginIndex": 10, "BeginTime": 5000, "EndIndex": 11, "EndTime": 5500, "Phoneme": "ping2", "Text": "平"}, | |
|             {"BeginIndex": 11, "BeginTime": 5500, "EndIndex": 12, "EndTime": 6000, "Phoneme": "heng2", "Text": "衡"}, | |
|             {"BeginIndex": 12, "BeginTime": 6000, "EndIndex": 13, "EndTime": 6200, "Phoneme": "", "Text": "。"} | |
|           ], | |
|           content: `科技发展与人文关怀的平衡。`, | |
|         }, | |
|         { | |
|           title: "第三章:未来社会的展望", | |
|           audioUrl: "https://des.dayunyuanjian.cn/epicSoul/EpicSouls.mp3", | |
|           // 字符级别的时间同步数据 | |
|           characters: [ | |
|             {"BeginIndex": 0, "BeginTime": 0, "EndIndex": 1, "EndTime": 500, "Phoneme": "zhan3", "Text": "展"}, | |
|             {"BeginIndex": 1, "BeginTime": 500, "EndIndex": 2, "EndTime": 1000, "Phoneme": "wang4", "Text": "望"}, | |
|             {"BeginIndex": 2, "BeginTime": 1000, "EndIndex": 3, "EndTime": 1500, "Phoneme": "wei4", "Text": "未"}, | |
|             {"BeginIndex": 3, "BeginTime": 1500, "EndIndex": 4, "EndTime": 2000, "Phoneme": "lai2", "Text": "来"}, | |
|             {"BeginIndex": 4, "BeginTime": 2000, "EndIndex": 5, "EndTime": 2500, "Phoneme": "she4", "Text": "社"}, | |
|             {"BeginIndex": 5, "BeginTime": 2500, "EndIndex": 6, "EndTime": 3000, "Phoneme": "hui4", "Text": "会"}, | |
|             {"BeginIndex": 6, "BeginTime": 3000, "EndIndex": 7, "EndTime": 3500, "Phoneme": "de5", "Text": "的"}, | |
|             {"BeginIndex": 7, "BeginTime": 3500, "EndIndex": 8, "EndTime": 4000, "Phoneme": "fa1", "Text": "发"}, | |
|             {"BeginIndex": 8, "BeginTime": 4000, "EndIndex": 9, "EndTime": 4500, "Phoneme": "zhan3", "Text": "展"}, | |
|             {"BeginIndex": 9, "BeginTime": 4500, "EndIndex": 10, "EndTime": 5000, "Phoneme": "qu1", "Text": "趋"}, | |
|             {"BeginIndex": 10, "BeginTime": 5000, "EndIndex": 11, "EndTime": 5500, "Phoneme": "shi4", "Text": "势"}, | |
|             {"BeginIndex": 11, "BeginTime": 5500, "EndIndex": 12, "EndTime": 5700, "Phoneme": "", "Text": "。"} | |
|           ], | |
|           content: `展望未来社会的发展趋势。`, | |
|         }, | |
|       ], | |
|       currentChapterIndex: 0, | |
|       playing: false, | |
|       currentTime: 0, | |
|       duration: 0, | |
|       activeCharIndex: -1, | |
|       scrollTop: 0, | |
|       scrollViewHeight: 0, | |
|     }; | |
|   }, | |
|   mounted() { | |
|     this.syncAudioState(); | |
|     this.calculateScrollViewHeight(); | |
|   }, | |
|   onShow() { | |
|     // 页面显示时同步音频状态 | |
|     this.syncAudioState(); | |
|   }, | |
|   onHide() { | |
|     // 页面隐藏时保持音频播放状态 | |
|     // 背景音频会自动继续播放 | |
|   }, | |
|   computed: { | |
|     currentChapter() { | |
|       const chapter = this.chapters[this.currentChapterIndex]; | |
|       // 使用字符级别的时间同步数据 | |
|       const characters = chapter.characters.map((char) => { | |
|         return { | |
|           ...char, | |
|           timeInSeconds: char.BeginTime / 1000, // 将毫秒转换为秒 | |
|         }; | |
|       }); | |
|       return { | |
|         ...chapter, | |
|         characters: characters, | |
|       }; | |
|     }, | |
|     progress() { | |
|       return (this.currentTime / this.duration) * 100 || 0; | |
|     }, | |
|     currentTimeFormatted() { | |
|       return this.formatTime(this.currentTime); | |
|     }, | |
|     durationFormatted() { | |
|       return this.formatTime(this.duration); | |
|     }, | |
|   }, | |
|   methods: { | |
|     togglePlay() { | |
|       if (this.playing) { | |
|         this.$refs.audioPlayer.pause(); | |
|         this.playing = false; | |
|       } else { | |
|         this.$refs.audioPlayer.play(); | |
|         this.playing = true; | |
|       } | |
|     }, | |
|     prevChapter() { | |
|       if (this.currentChapterIndex > 0) { | |
|         this.currentChapterIndex--; | |
|         this.resetPlayback(); | |
|       } | |
|     }, | |
|     nextChapter() { | |
|       if (this.currentChapterIndex < this.chapters.length - 1) { | |
|         this.currentChapterIndex++; | |
|         this.resetPlayback(); | |
|       } | |
|     }, | |
|     resetPlayback() { | |
|       this.playing = false; | |
|       this.currentTime = 0; | |
|       this.duration = 0; | |
|       this.activeCharIndex = -1; | |
|       this.scrollTop = 0; | |
| 
 | |
|       // 更新背景音频信息 | |
|       const currentChapter = this.chapters[this.currentChapterIndex]; | |
|       if ( | |
|         this.$refs.audioPlayer && | |
|         this.$refs.audioPlayer.backgroundAudioManager | |
|       ) { | |
|         const bgAudio = this.$refs.audioPlayer.backgroundAudioManager; | |
|         bgAudio.title = currentChapter.title; | |
|         bgAudio.epname = `第${this.currentChapterIndex + 1}章`; | |
|         bgAudio.src = currentChapter.audioUrl; | |
|         // 确保音频从头开始播放 | |
|         bgAudio.currentTime = 0; | |
|         // 如果音频播放器有seek方法,也调用它来确保重置 | |
|         if (this.$refs.audioPlayer.seek) { | |
|           this.$refs.audioPlayer.seek(0); | |
|         } | |
|       } | |
|     }, | |
|     onTimeUpdate(e) { | |
|       this.currentTime = e.currentTime; | |
|       this.duration = e.duration; | |
| 
 | |
|       // 同步播放状态 | |
|       this.playing = !e.paused; | |
| 
 | |
|       // 找到当前应该高亮的字符 | |
|       const characters = this.currentChapter.characters; | |
|       const currentTimeMs = this.currentTime * 1000; // 转换为毫秒 | |
|       for (let i = characters.length - 1; i >= 0; i--) { | |
|         if (currentTimeMs >= characters[i].BeginTime && currentTimeMs <= characters[i].EndTime) { | |
|           if (this.activeCharIndex !== i) { | |
|             this.activeCharIndex = i; | |
|             // 滚动到当前字符,这里简单实现:每个字符高度约30rpx,滚动到当前字符 | |
|             this.scrollTop = Math.floor(i / 20) * 30; // 假设每行约20个字符 | |
|           } | |
|           break; | |
|         } | |
|       } | |
|     }, | |
|     onEnded() { | |
|       this.playing = false; | |
|       // 自动播放下一章节 | |
|       if (this.currentChapterIndex < this.chapters.length - 1) { | |
|         this.nextChapter(); | |
|         setTimeout(() => { | |
|           this.$refs.audioPlayer.play(); | |
|           this.playing = true; | |
|         }, 500); | |
|       } | |
|     }, | |
|     seek(e) { | |
|       // 使用uni.createSelectorQuery获取进度条信息 | |
|       const query = uni.createSelectorQuery().in(this); | |
|       query | |
|         .select(".progress-bar") | |
|         .boundingClientRect((rect) => { | |
|           if (rect) { | |
|             const clickX = e.detail.x - rect.left; | |
|             const progressPercent = clickX / rect.width; | |
|             const seekTime = progressPercent * this.duration; | |
| 
 | |
|             if (seekTime >= 0 && seekTime <= this.duration) { | |
|               this.$refs.audioPlayer.seek(seekTime); | |
|             } | |
|           } | |
|         }) | |
|         .exec(); | |
|     }, | |
|     formatTime(seconds) { | |
|       const mins = Math.floor(seconds / 60); | |
|       const secs = Math.floor(seconds % 60); | |
|       return `${mins.toString().padStart(2, "0")}:${secs | |
|         .toString() | |
|         .padStart(2, "0")}`; | |
|     }, | |
|     syncAudioState() { | |
|       // 同步背景音频状态到界面 | |
|       if ( | |
|         this.$refs.audioPlayer && | |
|         this.$refs.audioPlayer.backgroundAudioManager | |
|       ) { | |
|         const bgAudio = this.$refs.audioPlayer.backgroundAudioManager; | |
| 
 | |
|         // 检查当前是否有音频在播放 | |
|         if (bgAudio.src) { | |
|           // 同步播放状态 - 注意:paused为true表示暂停,false表示播放 | |
|           this.playing = !bgAudio.paused; | |
| 
 | |
|           // 同步时间信息 | |
|           this.currentTime = bgAudio.currentTime || 0; | |
|           this.duration = bgAudio.duration || 0; | |
| 
 | |
|           // 如果有音频在播放,启动时间更新 | |
|           if (this.playing) { | |
|             this.$refs.audioPlayer.startTimeUpdate(); | |
|           } | |
|         } else { | |
|           // 没有音频源时重置状态 | |
|           this.playing = false; | |
|           this.currentTime = 0; | |
|           this.duration = 0; | |
|         } | |
|       } | |
|     }, | |
|     calculateScrollViewHeight() { | |
|       // 获取系统信息 | |
|       const systemInfo = uni.getSystemInfoSync(); | |
|       const windowHeight = systemInfo.windowHeight; | |
| 
 | |
|       // 计算控制栏高度(大约300rpx转换为px) | |
|       const controlsHeight = 300 * (systemInfo.windowWidth / 750); | |
| 
 | |
|       // 计算内容区域可用高度 | |
|       this.scrollViewHeight = windowHeight - controlsHeight - 60; // 60px为额外边距 | |
|     }, | |
|   }, | |
| }; | |
| </script> | |
| 
 | |
| <style> | |
| .container { | |
|   height: 100vh; | |
|   background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
|   font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; | |
|   padding: 30rpx; | |
|   position: relative; | |
| } | |
| 
 | |
| /* 顶部标题栏样式 */ | |
| .header { | |
|   display: flex; | |
|   justify-content: space-between; | |
|   align-items: center; | |
|   padding: 40rpx 48rpx; | |
|   background: rgba(255, 255, 255, 0.95); | |
|   backdrop-filter: blur(20rpx); | |
|   border-bottom: 2rpx solid rgba(255, 255, 255, 0.2); | |
|   box-shadow: 0 4rpx 40rpx rgba(0, 0, 0, 0.1); | |
| } | |
| 
 | |
| .header-content { | |
|   flex: 1; | |
| } | |
| 
 | |
| .title { | |
|   font-size: 48rpx; | |
|   font-weight: 700; | |
|   color: #2d3748; | |
|   margin-bottom: 8rpx; | |
| } | |
| 
 | |
| .subtitle { | |
|   font-size: 32rpx; | |
|   color: #718096; | |
|   font-weight: 500; | |
| } | |
| 
 | |
| .chapter-info { | |
|   background: linear-gradient(135deg, #667eea, #764ba2); | |
|   padding: 16rpx 32rpx; | |
|   border-radius: 40rpx; | |
|   box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3); | |
| } | |
| 
 | |
| .chapter-count { | |
|   color: white; | |
|   font-size: 28rpx; | |
|   font-weight: 600; | |
| } | |
| 
 | |
| .content { | |
|   width: 690rpx; | |
|   background: rgba(255, 255, 255, 0.9); | |
|   backdrop-filter: blur(20rpx); | |
|   border-radius: 32rpx; | |
|   margin: 0 auto; | |
|   margin-bottom: 20rpx; | |
| } | |
| 
 | |
| .content-wrapper { | |
|   padding: 48rpx; | |
| } | |
| 
 | |
| .article-content { | |
|   line-height: 1.8; | |
|   font-size: 32rpx; | |
|   color: #2d3748; | |
|   text-align: justify; | |
| } | |
| 
 | |
| .character { | |
|   transition: all 0.3s ease; | |
|   padding: 2rpx 4rpx; | |
|   border-radius: 6rpx; | |
|   display: inline; | |
|   margin: 0 1rpx; | |
| } | |
| 
 | |
| .character.active { | |
|   background: linear-gradient( | |
|     135deg, | |
|     rgba(102, 126, 234, 0.25), | |
|     rgba(118, 75, 162, 0.25) | |
|   ); | |
|   color: #667eea; | |
|   font-weight: 700; | |
|   box-shadow: 0 2rpx 8rpx rgba(102, 126, 234, 0.3); | |
|   transform: scale(1.1); | |
| } | |
| 
 | |
| /* 底部控制栏样式 */ | |
| .controls { | |
|   background: rgba(255, 255, 255, 0.98); | |
|   backdrop-filter: blur(40rpx); | |
|   border-top: 2rpx solid rgba(255, 255, 255, 0.3); | |
|   box-shadow: 0 -16rpx 64rpx rgba(0, 0, 0, 0.12), | |
|     0 -4rpx 16rpx rgba(0, 0, 0, 0.08); | |
|   border-radius: 48rpx 48rpx 0 0; | |
|   padding: 32rpx 40rpx 40rpx; | |
|   display: flex; | |
|   flex-direction: column; | |
|   gap: 24rpx; | |
|   position: fixed; | |
|   bottom: 0; | |
|   left: 0; | |
|   right: 0; | |
|   z-index: 1000; | |
| } | |
| 
 | |
| /* 章节信息栏 */ | |
| .chapter-info-bar { | |
|   display: flex; | |
|   justify-content: space-between; | |
|   align-items: center; | |
|   padding: 0 8rpx; | |
| } | |
| 
 | |
| .chapter-title { | |
|   font-size: 28rpx; | |
|   font-weight: 600; | |
|   color: #2d3748; | |
|   flex: 1; | |
|   overflow: hidden; | |
|   text-overflow: ellipsis; | |
|   white-space: nowrap; | |
|   margin-right: 16rpx; | |
| } | |
| 
 | |
| .chapter-count { | |
|   font-size: 24rpx; | |
|   color: #718096; | |
|   font-weight: 500; | |
|   background: rgba(139, 92, 246, 0.1); | |
|   padding: 8rpx 16rpx; | |
|   border-radius: 20rpx; | |
|   backdrop-filter: blur(10rpx); | |
| } | |
| 
 | |
| /* 进度条区域 */ | |
| .progress-section { | |
|   display: flex; | |
|   align-items: center; | |
|   gap: 20rpx; | |
| } | |
| 
 | |
| .current-time, | |
| .duration { | |
|   font-size: 24rpx; | |
|   color: #718096; | |
|   font-weight: 500; | |
|   font-family: "Courier New", monospace; | |
|   min-width: 80rpx; | |
|   text-align: center; | |
| } | |
| 
 | |
| /* 控制按钮区域 */ | |
| .control-buttons { | |
|   display: flex; | |
|   align-items: center; | |
|   justify-content: center; | |
|   gap: 32rpx; | |
| } | |
| 
 | |
| /* 播放按钮 */ | |
| .play-btn { | |
|   width: 96rpx; | |
|   height: 96rpx; | |
|   border-radius: 50%; | |
|   border: none; | |
|   background: linear-gradient(135deg, #8b5cf6, #a855f7); | |
|   color: white; | |
|   display: flex; | |
|   align-items: center; | |
|   justify-content: center; | |
|   cursor: pointer; | |
|   transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
|   box-shadow: 0 12rpx 40rpx rgba(139, 92, 246, 0.4), | |
|     0 6rpx 20rpx rgba(0, 0, 0, 0.15); | |
|   position: relative; | |
|   overflow: hidden; | |
|   z-index: 2; | |
| } | |
| 
 | |
| /* 控制按钮(上一章/下一章) */ | |
| .control-btn { | |
|   width: 88rpx; | |
|   height: 88rpx; | |
|   border-radius: 50%; | |
|   border: none; | |
|   background: rgba(139, 92, 246, 0.08); | |
|   color: #8b5cf6; | |
|   display: flex; | |
|   align-items: center; | |
|   justify-content: center; | |
|   cursor: pointer; | |
|   transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
|   box-shadow: 0 4rpx 16rpx rgba(139, 92, 246, 0.15); | |
|   backdrop-filter: blur(20rpx); | |
|   position: relative; | |
|   overflow: hidden; | |
| } | |
| 
 | |
| .play-btn::before { | |
|   content: ""; | |
|   position: absolute; | |
|   top: 0; | |
|   left: 0; | |
|   right: 0; | |
|   bottom: 0; | |
|   background: linear-gradient( | |
|     135deg, | |
|     rgba(255, 255, 255, 0.2), | |
|     rgba(255, 255, 255, 0.05) | |
|   ); | |
|   border-radius: 50%; | |
|   opacity: 0; | |
|   transition: opacity 0.3s ease; | |
| } | |
| 
 | |
| .play-btn:hover { | |
|   transform: scale(1.08); | |
|   box-shadow: 0 18rpx 56rpx rgba(139, 92, 246, 0.5), | |
|     0 9rpx 28rpx rgba(0, 0, 0, 0.2); | |
| } | |
| 
 | |
| .play-btn:hover::before { | |
|   opacity: 1; | |
| } | |
| 
 | |
| .play-btn:active { | |
|   transform: scale(0.96); | |
|   transition: transform 0.1s ease; | |
| } | |
| 
 | |
| /* 进度条容器 */ | |
| .progress-container { | |
|   flex: 1; | |
|   position: relative; | |
| } | |
| 
 | |
| .progress-bar { | |
|   height: 12rpx; | |
|   background: rgba(139, 92, 246, 0.12); | |
|   border-radius: 6rpx; | |
|   position: relative; | |
|   cursor: pointer; | |
|   overflow: hidden; | |
|   backdrop-filter: blur(20rpx); | |
|   box-shadow: inset 0 2rpx 6rpx rgba(0, 0, 0, 0.1); | |
| } | |
| 
 | |
| .progress { | |
|   height: 100%; | |
|   background: linear-gradient(90deg, #8b5cf6 0%, #a855f7 50%, #c084fc 100%); | |
|   border-radius: 6rpx; | |
|   transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1); | |
|   box-shadow: 0 4rpx 16rpx rgba(139, 92, 246, 0.3); | |
|   position: relative; | |
| } | |
| 
 | |
| .progress::after { | |
|   content: ""; | |
|   position: absolute; | |
|   top: 0; | |
|   left: 0; | |
|   right: 0; | |
|   bottom: 0; | |
|   background: linear-gradient( | |
|     90deg, | |
|     rgba(255, 255, 255, 0.3) 0%, | |
|     rgba(255, 255, 255, 0.1) 100% | |
|   ); | |
|   border-radius: 6rpx; | |
| } | |
| 
 | |
| .progress-thumb { | |
|   position: absolute; | |
|   top: 50%; | |
|   width: 28rpx; | |
|   height: 28rpx; | |
|   background: white; | |
|   border: 4rpx solid #8b5cf6; | |
|   border-radius: 50%; | |
|   transform: translate(-50%, -50%); | |
|   box-shadow: 0 6rpx 20rpx rgba(139, 92, 246, 0.4), | |
|     0 3rpx 6rpx rgba(0, 0, 0, 0.1); | |
|   transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); | |
|   cursor: pointer; | |
| } | |
| 
 | |
| .progress-thumb:hover { | |
|   transform: translate(-50%, -50%) scale(1.2); | |
|   box-shadow: 0 8rpx 24rpx rgba(139, 92, 246, 0.5), | |
|     0 4rpx 8rpx rgba(0, 0, 0, 0.15); | |
| } | |
| 
 | |
| /* 控制按钮交互效果 */ | |
| .control-btn::before { | |
|   content: ""; | |
|   position: absolute; | |
|   top: 0; | |
|   left: 0; | |
|   right: 0; | |
|   bottom: 0; | |
|   background: linear-gradient( | |
|     135deg, | |
|     rgba(139, 92, 246, 0.1), | |
|     rgba(168, 85, 247, 0.05) | |
|   ); | |
|   border-radius: 50%; | |
|   opacity: 0; | |
|   transition: opacity 0.3s ease; | |
| } | |
| 
 | |
| .control-btn:hover { | |
|   background: rgba(139, 92, 246, 0.15); | |
|   transform: scale(1.1); | |
|   box-shadow: 0 8rpx 32rpx rgba(139, 92, 246, 0.25); | |
| } | |
| 
 | |
| .control-btn:hover::before { | |
|   opacity: 1; | |
| } | |
| 
 | |
| .control-btn:active { | |
|   transform: scale(0.95); | |
|   transition: transform 0.1s ease; | |
| } | |
| 
 | |
| .control-btn:disabled { | |
|   opacity: 0.3; | |
|   cursor: not-allowed; | |
|   transform: none; | |
|   box-shadow: 0 2rpx 6rpx rgba(139, 92, 246, 0.1); | |
| } | |
| 
 | |
| .control-btn:disabled:hover { | |
|   background: rgba(139, 92, 246, 0.08); | |
|   transform: none; | |
|   box-shadow: 0 2rpx 6rpx rgba(139, 92, 246, 0.1); | |
| } | |
| 
 | |
| .control-btn:disabled::before { | |
|   opacity: 0; | |
| } | |
| 
 | |
| /* 控制图标样式 */ | |
| .control-icon { | |
|   font-size: 32rpx; | |
|   line-height: 1; | |
|   filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.1)); | |
| } | |
| 
 | |
| .play-icon { | |
|   font-size: 42rpx; | |
|   line-height: 1; | |
|   margin-left: 3rpx; | |
|   filter: drop-shadow(0 3rpx 6rpx rgba(0, 0, 0, 0.2)); | |
| } | |
| </style>
 | |
| 
 |