From 5fd6100fddb9170e97d8eaa4a9f6fc59833baeef Mon Sep 17 00:00:00 2001 From: hflllll Date: Tue, 21 Oct 2025 22:18:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=9F=B3=E9=A2=91=E6=8E=A7=E5=88=B6):=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=9F=B3=E9=A2=91=E6=92=AD=E6=94=BE=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E6=94=AF=E6=8C=81=E8=B7=B3=E8=BF=87=E5=AF=BC?= =?UTF-8?q?=E8=AF=AD=E9=9F=B3=E9=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 缩短滚动安全超时时间从2秒到1.5秒 - 添加findFirstNonLeadAudioIndex方法查找非导语音频 - 修改音频播放逻辑,自动跳过导语音频 - 改进滚动位置估算算法,使用智能高度计算 - 更新音频加载条件判断,使用shouldLoadAudio替代isTextPage --- subPages/home/AudioControls.vue | 96 +++++++++++++++++++++++++++++++++++------ subPages/home/book.vue | 19 +++++--- 2 files changed, 95 insertions(+), 20 deletions(-) diff --git a/subPages/home/AudioControls.vue b/subPages/home/AudioControls.vue index f60c24f..205fdda 100644 --- a/subPages/home/AudioControls.vue +++ b/subPages/home/AudioControls.vue @@ -6,13 +6,13 @@ - + 第{{currentPage}}页音频加载中,请稍等... - + @@ -274,6 +274,16 @@ export default { } }, methods: { + // 查找第一个非导语音频的索引 + findFirstNonLeadAudioIndex() { + for (let i = 0; i < this.currentPageAudios.length; i++) { + if (!this.currentPageAudios[i].isLead) { + return i; + } + } + return -1; // 如果所有音频都是导语,返回-1 + }, + // 创建HTML5 Audio实例并包装为uni-app兼容接口 createHTML5Audio() { const audio = new Audio(); @@ -426,7 +436,9 @@ export default { this.totalTime = cachedAudio.totalDuration || 0; this.hasAudioData = true; this.isAudioLoading = false; - this.currentAudioIndex = 0; + // 设置初始音频索引为第一个非导语音频 + const firstNonLeadIndex = this.findFirstNonLeadAudioIndex(); + this.currentAudioIndex = firstNonLeadIndex >= 0 ? firstNonLeadIndex : 0; this.currentTime = 0; this.currentHighlightIndex = -1; @@ -768,9 +780,27 @@ export default { } if (currentPageData) { - // 收集所有text类型的元素 - const textItems = currentPageData.filter(item => item.type === 'text'); - console.log(`🎵 getCurrentPageAudio: 找到${textItems.length}个文本项:`, textItems.map(item => item.content?.substring(0, 50) + '...')); + // 收集所有text类型的元素,但排除导语(isLead为true的元素) + // 对于重点卡片页面,还需要检查language属性 + const textItems = currentPageData.filter(item => { + if (item.type !== 'text' || item.isLead) { + return false; + } + + // 检查当前页面类型 + const currentPageType = this.bookPages && this.bookPages[this.currentPage - 1] + ? (this.bookPages[this.currentPage - 1].some(pageItem => pageItem.type === 'image') ? '1' : '0') + : '0'; + + // 如果是重点卡片页面,需要有language属性 + if (currentPageType === '1') { + return item.content && (item.language === 'en' || item.language === 'zh'); + } + + // 普通文本页面,只需要有content + return item.content; + }); + console.log(`🎵 getCurrentPageAudio: 找到${textItems.length}个文本项(已排除导语):`, textItems.map(item => item.content?.substring(0, 50) + '...')); if (textItems.length > 0) { let firstAudioPlayed = false; // 标记是否已播放第一个音频 @@ -811,7 +841,8 @@ export default { endIndex: segment.endIndex, segmentIndex: segment.segmentIndex, originalTextIndex: index, // 标记属于哪个原始文本项 - isSegmented: batchResult.audioSegments.length > 1 // 标记是否为分段音频 + isSegmented: batchResult.audioSegments.length > 1, // 标记是否为分段音频 + isLead: item.isLead || false // 标记是否为导语 }; this.currentPageAudios.push(audioData); loadedAudiosCount++; @@ -822,7 +853,9 @@ export default { if (!firstAudioPlayed && this.currentPageAudios.length > 0) { firstAudioPlayed = true; this.hasAudioData = true; - this.currentAudioIndex = 0; + // 设置初始音频索引为第一个非导语音频 + const firstNonLeadIndex = this.findFirstNonLeadAudioIndex(); + this.currentAudioIndex = firstNonLeadIndex >= 0 ? firstNonLeadIndex : 0; @@ -1828,6 +1861,13 @@ export default { return; } + // 如果当前音频是导语,不进行高亮 + if (currentAudio.isLead) { + this.currentHighlightIndex = -1; + this.emitHighlightChange(-1); + return; + } + // 检查音频数据是否属于当前页面,防止页面切换时的数据错乱 const audioCacheKey = `${this.courseId}_${this.currentPage}_${this.localVoiceId}`; const currentPageCache = this.audioCache[audioCacheKey]; @@ -1921,9 +1961,24 @@ export default { // 音频播放结束处理 onAudioEnded() { if (this.currentAudioIndex < this.currentPageAudios.length - 1) { - // 播放下一个音频 - this.currentAudioIndex++; - this.playAudio(); + // 查找下一个非导语音频 + let nextIndex = this.currentAudioIndex + 1; + while (nextIndex < this.currentPageAudios.length && this.currentPageAudios[nextIndex].isLead) { + console.log(`🎵 跳过导语音频: ${this.currentPageAudios[nextIndex].text}`); + nextIndex++; + } + + if (nextIndex < this.currentPageAudios.length) { + // 找到下一个非导语音频,播放它 + this.currentAudioIndex = nextIndex; + this.playAudio(); + } else { + // 没有更多非导语音频,结束播放 + this.isPlaying = false; + this.currentTime = this.totalTime; + this.currentHighlightIndex = -1; + this.$emit('highlight-change', -1); + } // 滚动到下一段音频对应的文字 // setTimeout(() => { @@ -1933,9 +1988,22 @@ export default { } else { // 所有音频播放完毕 if (this.isLoop) { - // 循环播放 - this.currentAudioIndex = 0; - this.playAudio(); + // 循环播放,从第一个非导语音频开始 + let firstNonLeadIndex = 0; + while (firstNonLeadIndex < this.currentPageAudios.length && this.currentPageAudios[firstNonLeadIndex].isLead) { + firstNonLeadIndex++; + } + + if (firstNonLeadIndex < this.currentPageAudios.length) { + this.currentAudioIndex = firstNonLeadIndex; + this.playAudio(); + } else { + // 所有音频都是导语,停止播放 + this.isPlaying = false; + this.currentTime = this.totalTime; + this.currentHighlightIndex = -1; + this.$emit('highlight-change', -1); + } // 滚动到第一段音频对应的文字 // setTimeout(() => { diff --git a/subPages/home/book.vue b/subPages/home/book.vue index 473d00b..4a84d38 100644 --- a/subPages/home/book.vue +++ b/subPages/home/book.vue @@ -795,7 +795,7 @@ export default { console.warn('⚠️ 滚动状态安全超时,强制重置'); resetScrollingState(); } - }, 2000); // 2秒安全超时 + }, 1500); // 缩短安全超时时间到1.5秒 if (!scrollData || typeof scrollData.highlightIndex !== 'number' || scrollData.highlightIndex < 0) { console.warn('滚动数据无效:', scrollData); @@ -827,13 +827,14 @@ export default { selector = `#text-segment-${scrollData.segmentIndex}`; } else { // 普通音频:需要找到对应的文本元素 - // originalTextIndex是指向原始页面数据中的索引,需要映射到实际的DOM元素 + // highlightIndex是文本元素的序号,需要映射到页面数组中的实际索引 const targetItemIndex = this.findTextItemIndex(scrollData.highlightIndex); if (targetItemIndex !== -1) { selector = `#text-${targetItemIndex}`; } else { console.warn('无法找到对应的文本元素索引:', scrollData.highlightIndex); - selector = `#text-${scrollData.highlightIndex}`; // 备用方案 + // 备用方案:尝试直接使用highlightIndex + selector = `#text-${scrollData.highlightIndex}`; } } @@ -910,11 +911,17 @@ export default { highlightIndex: scrollData.highlightIndex }); - // 尝试备用方案:直接滚动到页面顶部附近 + // 尝试备用方案:智能估算滚动位置 if (!targetRect) { console.log('🔄 尝试备用滚动方案'); - const fallbackScrollTop = scrollData.highlightIndex * 100; // 简单估算位置 - this.$set(this.scrollTops, this.currentPage - 1, fallbackScrollTop); + // 根据高亮索引和页面内容智能估算位置 + const currentPageData = this.bookPages[this.currentPage - 1]; + if (currentPageData && Array.isArray(currentPageData)) { + // 计算每个文本元素的大概高度 + const estimatedItemHeight = 80; // 估算每个内容项的高度 + const fallbackScrollTop = scrollData.highlightIndex * estimatedItemHeight; + this.$set(this.scrollTops, this.currentPage - 1, Math.max(0, fallbackScrollTop)); + } setTimeout(() => { resetScrollingState(); }, 300);