|
|
|
@ -27,10 +27,10 @@ |
|
|
|
<view class="content-area" @click="toggleNavbar"> |
|
|
|
|
|
|
|
<!-- 会员限制页面 --> |
|
|
|
<view v-if="!isMember && pagePay[index] === 'Y'" class="member-content"> |
|
|
|
<view v-if="!isMember && pagePay[index] === 'Y'" class="member-content" > |
|
|
|
<text class="member-title">{{ pageTitles[index] }}</text> |
|
|
|
<view class="member-button" @click="unlockBook"> |
|
|
|
<text class="member-button-text">升級會員解鎖</text> |
|
|
|
<view class="member-button" @click.stop="unlockBook"> |
|
|
|
<text class="member-button-text">升级会员解锁</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
@ -99,10 +99,15 @@ |
|
|
|
:voice-id="voiceId" |
|
|
|
:book-pages="bookPages" |
|
|
|
:is-text-page="isTextPage" |
|
|
|
:is-member="isMember" |
|
|
|
:current-page-requires-member="currentPageRequiresMember" |
|
|
|
:page-pay="pagePay" |
|
|
|
@previous-page="previousPage" |
|
|
|
@next-page="nextPage" |
|
|
|
@audio-state-change="onAudioStateChange" |
|
|
|
@highlight-change="onHighlightChange" |
|
|
|
@voice-change-complete="onVoiceChangeComplete" |
|
|
|
@voice-change-error="onVoiceChangeError" |
|
|
|
ref="audioControls" |
|
|
|
/> |
|
|
|
|
|
|
|
@ -194,7 +199,7 @@ |
|
|
|
</view> |
|
|
|
|
|
|
|
<view class="meaning-content"> |
|
|
|
<image class="meaning-image" src="/static/默认图片.png" mode="aspectFill"></image> |
|
|
|
<image v-if="currentWordMeaning.imag" class="meaning-image" :src="currentWordMeaning.image" mode="aspectFill"></image> |
|
|
|
|
|
|
|
<view class="word-info"> |
|
|
|
<view class="word-main"> |
|
|
|
@ -251,6 +256,11 @@ export default { |
|
|
|
currentHighlightIndex: -1, // 当前高亮的文本索引 |
|
|
|
wordAudioCache: {}, // 單詞語音緩存 |
|
|
|
currentWordAudio: null, // 當前播放的單詞音頻實例 |
|
|
|
|
|
|
|
// 音频状态相关 |
|
|
|
isAudioLoading: false, // 音频是否正在加载 |
|
|
|
hasAudioData: false, // 是否有音频数据 |
|
|
|
audioLoadFailed: false, // 音频加载是否失败 |
|
|
|
courseIdList: [], |
|
|
|
bookTitle: '', |
|
|
|
courseList: [ |
|
|
|
@ -301,6 +311,11 @@ export default { |
|
|
|
// 当前页面的单词释义数据 |
|
|
|
currentPageWords() { |
|
|
|
return this.pageWords[this.currentPage - 1] || []; |
|
|
|
}, |
|
|
|
|
|
|
|
// 当前页面是否需要会员 |
|
|
|
currentPageRequiresMember() { |
|
|
|
return this.pagePay[this.currentPage - 1] === 'Y'; |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
@ -318,6 +333,54 @@ export default { |
|
|
|
onAudioStateChange(audioState) { |
|
|
|
// 更新高亮状态 |
|
|
|
this.currentHighlightIndex = audioState.currentHighlightIndex; |
|
|
|
|
|
|
|
// 更新音频加载状态(用于控制UI显示) |
|
|
|
if (audioState.hasOwnProperty('isLoading')) { |
|
|
|
this.isAudioLoading = audioState.isLoading; |
|
|
|
} |
|
|
|
|
|
|
|
// 更新音频数据状态 |
|
|
|
if (audioState.hasOwnProperty('hasAudioData')) { |
|
|
|
this.hasAudioData = audioState.hasAudioData; |
|
|
|
} |
|
|
|
|
|
|
|
// 更新音频加载失败状态 |
|
|
|
if (audioState.hasOwnProperty('audioLoadFailed')) { |
|
|
|
this.audioLoadFailed = audioState.audioLoadFailed; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 处理音色切换完成事件 |
|
|
|
onVoiceChangeComplete(data) { |
|
|
|
console.log('音色切换完成:', data); |
|
|
|
// 可以在这里添加一些UI反馈,比如显示切换成功的提示 |
|
|
|
if (data.hasAudioData) { |
|
|
|
console.log('新音色当前页面音频已加载完成'); |
|
|
|
} else { |
|
|
|
console.log('当前页面没有音频数据'); |
|
|
|
} |
|
|
|
|
|
|
|
// 如果启用了预加载所有页面 |
|
|
|
if (data.preloadAllPages) { |
|
|
|
console.log('正在后台预加载所有页面的新音色音频...'); |
|
|
|
// 可以显示一个提示,告诉用户正在后台加载 |
|
|
|
uni.showToast({ |
|
|
|
title: '正在加载新音色...', |
|
|
|
icon: 'loading', |
|
|
|
duration: 2000 |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 处理音色切换错误事件 |
|
|
|
onVoiceChangeError(error) { |
|
|
|
console.error('音色切换失败:', error); |
|
|
|
// 可以在这里显示错误提示给用户 |
|
|
|
uni.showToast({ |
|
|
|
title: '音色切换失败,请重试', |
|
|
|
icon: 'none', |
|
|
|
duration: 2000 |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 处理文本点击事件 |
|
|
|
@ -375,7 +438,14 @@ export default { |
|
|
|
} |
|
|
|
|
|
|
|
// 检查当前页面是否为文本页面 |
|
|
|
// 对于卡片页面(type为'1'),不显示错误提示,因为单词点击会单独处理 |
|
|
|
if (!this.isTextPage) { |
|
|
|
// 如果是卡片页面,静默返回,不显示错误提示 |
|
|
|
if (this.currentPageType === '1') { |
|
|
|
console.log('卡片页面的文本点击,单词播放由handleWordClick处理'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
console.log('当前页面不是文本页面'); |
|
|
|
uni.showToast({ |
|
|
|
title: '当前页面不支持音频播放', |
|
|
|
@ -424,8 +494,10 @@ export default { |
|
|
|
async getVoiceList() { |
|
|
|
const voiceRes = await this.$api.music.list() |
|
|
|
if(voiceRes.code === 200){ |
|
|
|
this.voiceId = voiceRes.result[0].voiceType |
|
|
|
console.log('获取默认音色ID:', this.voiceId); |
|
|
|
console.log('音色列表API返回:', voiceRes.result); |
|
|
|
console.log('第一个音色数据:', voiceRes.result[0]); |
|
|
|
this.voiceId = Number(voiceRes.result[0].voiceType) |
|
|
|
console.log('获取默认音色ID:', this.voiceId, '类型:', typeof this.voiceId); |
|
|
|
} |
|
|
|
}, |
|
|
|
toggleNavbar() { |
|
|
|
@ -495,8 +567,9 @@ export default { |
|
|
|
this.currentWordAudio.destroy(); |
|
|
|
this.currentWordAudio = null; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 調用語音轉換API |
|
|
|
console.log('playWordAudio - voiceId值:', this.voiceId, '类型:', typeof this.voiceId); |
|
|
|
const audioRes = await this.$api.music.textToVoice({ |
|
|
|
text: word, |
|
|
|
voiceType: this.voiceId |
|
|
|
@ -1257,11 +1330,6 @@ export default { |
|
|
|
if (this.courseIdList[this.currentPage - 1] && this.bookPages[this.currentPage - 1].length === 0) { |
|
|
|
await this.getBookPages(this.courseIdList[this.currentPage - 1]); |
|
|
|
} |
|
|
|
|
|
|
|
// 通知音频控制组件重置状态 |
|
|
|
if (this.$refs.audioControls) { |
|
|
|
this.$refs.audioControls.resetAudioState(); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
async nextPage() { |
|
|
|
@ -1271,14 +1339,29 @@ export default { |
|
|
|
if (this.courseIdList[this.currentPage - 1] && this.bookPages[this.currentPage - 1].length === 0) { |
|
|
|
await this.getBookPages(this.courseIdList[this.currentPage - 1]); |
|
|
|
} |
|
|
|
|
|
|
|
// 通知音频控制组件重置状态 |
|
|
|
if (this.$refs.audioControls) { |
|
|
|
this.$refs.audioControls.resetAudioState(); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
toggleSound() { |
|
|
|
// 检查是否正在加载音频,如果是则阻止音色切换 |
|
|
|
if (this.isAudioLoading) { |
|
|
|
uni.showToast({ |
|
|
|
title: '音频加载中,请稍后再试', |
|
|
|
icon: 'none', |
|
|
|
duration: 2000 |
|
|
|
}); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 检查AudioControls组件是否正在加载音频 |
|
|
|
if (this.$refs.audioControls && this.$refs.audioControls.isAudioLoading) { |
|
|
|
uni.showToast({ |
|
|
|
title: '音频加载中,请稍后再试', |
|
|
|
icon: 'none', |
|
|
|
duration: 2000 |
|
|
|
}); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
console.log('音色切换') |
|
|
|
uni.navigateTo({ |
|
|
|
url: '/subPages/home/music?voiceId=' + this.voiceId |
|
|
|
@ -1298,21 +1381,6 @@ export default { |
|
|
|
if (this.courseIdList[this.currentPage - 1] && this.bookPages[this.currentPage - 1].length === 0) { |
|
|
|
await this.getBookPages(this.courseIdList[this.currentPage - 1]); |
|
|
|
} |
|
|
|
|
|
|
|
// 通知音频控制组件处理页面切换 |
|
|
|
if (this.$refs.audioControls) { |
|
|
|
// 检查当前页面是否有预加载的音频缓存 |
|
|
|
const hasPreloadedAudio = this.$refs.audioControls.checkAudioCache(this.currentPage); |
|
|
|
|
|
|
|
if (hasPreloadedAudio) { |
|
|
|
// 如果有预加载的音频,直接播放 |
|
|
|
console.log(`第${this.currentPage}页音频已预加载,自动播放`); |
|
|
|
this.$refs.audioControls.autoPlayCachedAudio(); |
|
|
|
} else { |
|
|
|
// 如果没有预加载的音频,重置状态 |
|
|
|
this.$refs.audioControls.resetAudioState(); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
async onSwiperChange(e) { |
|
|
|
this.currentPage = e.detail.current + 1 |
|
|
|
@ -1320,27 +1388,28 @@ export default { |
|
|
|
if (this.courseIdList[this.currentPage - 1] && this.bookPages[this.currentPage - 1].length === 0) { |
|
|
|
await this.getBookPages(this.courseIdList[this.currentPage - 1]); |
|
|
|
} |
|
|
|
|
|
|
|
// 通知音频控制组件处理页面切换 |
|
|
|
if (this.$refs.audioControls) { |
|
|
|
// 检查当前页面是否有预加载的音频缓存 |
|
|
|
const hasPreloadedAudio = this.$refs.audioControls.checkAudioCache(this.currentPage); |
|
|
|
|
|
|
|
if (hasPreloadedAudio) { |
|
|
|
// 如果有预加载的音频,直接播放 |
|
|
|
console.log(`第${this.currentPage}页音频已预加载,自动播放`); |
|
|
|
this.$refs.audioControls.autoPlayCachedAudio(); |
|
|
|
} else { |
|
|
|
// 如果没有预加载的音频,重置状态 |
|
|
|
this.$refs.audioControls.resetAudioState(); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
async getCourseList(id) { |
|
|
|
const res = await this.$api.book.coursePage({ |
|
|
|
id: id |
|
|
|
}) |
|
|
|
if (res.code === 200) { |
|
|
|
// 课程切换时,先清理音频控制组件的所有数据 |
|
|
|
if (this.$refs.audioControls) { |
|
|
|
this.$refs.audioControls.resetForCourseChange(); |
|
|
|
} |
|
|
|
|
|
|
|
// 清空当前页面相关数据 |
|
|
|
this.currentPage = 1; // 重置到第一页 |
|
|
|
this.currentCourse = 1; // 重置当前课程索引 |
|
|
|
this.currentWordMeaning = null; // 清空单词释义 |
|
|
|
this.currentWordAudio = null; // 清空单词音频 |
|
|
|
this.currentHighlightIndex = -1; // 清空高亮索引 |
|
|
|
|
|
|
|
// 清理单词音频缓存 |
|
|
|
this.clearWordAudioCache(); |
|
|
|
|
|
|
|
// 重新初始化课程数据 |
|
|
|
this.courseIdList = res.result.map(item => item.id) |
|
|
|
// 初始化二维数组 换一种方式 |
|
|
|
this.bookPages = this.courseIdList.map(() => []) |
|
|
|
@ -1350,13 +1419,30 @@ export default { |
|
|
|
this.pageTypes = this.courseIdList.map(() => '') |
|
|
|
// 初始化页面单词数组 |
|
|
|
this.pageWords = this.courseIdList.map(() => []) |
|
|
|
// 通知音频控制组件重置状态 |
|
|
|
if (this.$refs.audioControls) { |
|
|
|
this.$refs.audioControls.resetAudioState(); |
|
|
|
} |
|
|
|
|
|
|
|
console.log('课程切换完成,courseId:', id, '总页数:', this.courseIdList.length); |
|
|
|
|
|
|
|
// 初始化第一页 |
|
|
|
if (this.courseIdList.length > 0) { |
|
|
|
await this.getBookPages(this.courseIdList[0]) |
|
|
|
|
|
|
|
// 课程切换后,确保音频控件能正确加载新课程的音频 |
|
|
|
console.log('课程切换完成,准备加载音频,当前页面类型:', this.isTextPage); |
|
|
|
|
|
|
|
// 使用$nextTick确保DOM和数据都已更新 |
|
|
|
this.$nextTick(async () => { |
|
|
|
if (this.$refs.audioControls) { |
|
|
|
console.log('开始自动加载课程音频'); |
|
|
|
try { |
|
|
|
// 直接调用getCurrentPageAudio方法,更可靠 |
|
|
|
await this.$refs.audioControls.getCurrentPageAudio(); |
|
|
|
console.log('课程切换后音频加载完成'); |
|
|
|
} catch (error) { |
|
|
|
console.error('课程切换后音频加载失败:', error); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 预加载后续几页的内容(异步执行,不阻塞当前页面显示) |
|
|
|
this.preloadNextPages() |
|
|
|
} |
|
|
|
@ -1528,8 +1614,24 @@ export default { |
|
|
|
}, |
|
|
|
async onLoad(args) { |
|
|
|
// 监听音色切换事件,传递给AudioControls组件处理 |
|
|
|
uni.$on('selectVoice', (voiceId) => { |
|
|
|
console.log('音色切換:', this.voiceId, '->', voiceId); |
|
|
|
uni.$on('selectVoice', async (voiceId) => { |
|
|
|
if (this.voiceId === voiceId) { |
|
|
|
console.log('音色未變化,跳過處理'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 检查是否正在加载音频,如果是则阻止音色切换 |
|
|
|
if (this.isAudioLoading || (this.$refs.audioControls && this.$refs.audioControls.isAudioLoading)) { |
|
|
|
console.log('音频正在加载中,阻止音色切换'); |
|
|
|
uni.showToast({ |
|
|
|
title: '音频加载中,请稍后再试', |
|
|
|
icon: 'none', |
|
|
|
duration: 2000 |
|
|
|
}); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 更新本地音色ID |
|
|
|
this.voiceId = voiceId; |
|
|
|
|
|
|
|
// 清理單詞語音資源 |
|
|
|
@ -1538,24 +1640,36 @@ export default { |
|
|
|
this.currentWordAudio.destroy(); |
|
|
|
this.currentWordAudio = null; |
|
|
|
} |
|
|
|
|
|
|
|
// 通知AudioControls组件处理音色切换 |
|
|
|
if (this.$refs.audioControls) { |
|
|
|
try { |
|
|
|
console.log('开始处理音色切换,清理所有音频缓存并重新获取所有页面音频'); |
|
|
|
// 传入选项:preloadAllPages: true 表示要预加载所有页面的音频 |
|
|
|
await this.$refs.audioControls.handleVoiceChange(voiceId, { |
|
|
|
preloadAllPages: true |
|
|
|
}); |
|
|
|
console.log('音色切换处理完成'); |
|
|
|
} catch (error) { |
|
|
|
console.error('音色切换处理失败:', error); |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
this.courseId = args.courseId |
|
|
|
this.memberId = args.memberId |
|
|
|
|
|
|
|
// 先获取点进来的课程的页面列表 |
|
|
|
await Promise.all([this.getCourseList(this.courseId), this.getCoursePageList(args.bookId), this.getMemberInfo(), this.getVoiceList()]) |
|
|
|
await Promise.all([this.getVoiceList(),this.getMemberInfo(),this.getCourseList(this.courseId), this.getCoursePageList(args.bookId)]) |
|
|
|
|
|
|
|
// 页面加载完成后,通知AudioControls组件自动加载第一页音频 |
|
|
|
this.$nextTick(() => { |
|
|
|
if (this.$refs.audioControls) { |
|
|
|
this.$refs.audioControls.autoLoadAndPlayFirstPage(); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 页面加载完成后,通知AudioControls组件自动加载第一页音频 |
|
|
|
this.$nextTick(() => { |
|
|
|
if (this.$refs.audioControls) { |
|
|
|
this.$refs.audioControls.autoLoadAndPlayFirstPage(); |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 页面卸载时清理资源 |
|
|
|
onUnload() { |
|
|
|
uni.$off('selectVoice') |
|
|
|
|