// utils/audioManager.js export default { player: null, initPlayer() { if (!this.player) { this.player = uni.createInnerAudioContext() this.player.obeyMuteSwitch = false // iOS静音模式设置 } return this.player }, // 从单词数据中提取所有音频信息 extractAudioInfo(wordData) { const audioInfo = { main: null, syllables: [], frequencies: [] } // 主音频 if (wordData.yinpin) { audioInfo.main = { id: `word_${wordData.id}_main`, url: wordData.yinpin, type: 'main', text: wordData.name } } // 音节音频 if (wordData.yinjie && Array.isArray(wordData.yinjie)) { audioInfo.syllables = wordData.yinjie.map((item, index) => ({ id: `word_${wordData.id}_syllable_${index}`, url: item.yinpin, type: 'syllable', text: item.cigen, index })).filter(item => item.url) } // 频度音频 if (wordData.pindu && Array.isArray(wordData.pindu)) { audioInfo.frequencies = wordData.pindu.map((item, index) => ({ id: `word_${wordData.id}_frequency_${index}`, url: item.yinpin, type: 'frequency', text: item.cigen, index })).filter(item => item.url) } return audioInfo }, // 缓存单词的所有音频 async cacheWordAudios(wordData) { try { // 1. 提取音频信息 const { main, syllables, frequencies } = this.extractAudioInfo(wordData) // 2. 准备所有需要缓存的音频 const allAudios = [] if (main) allAudios.push(main) allAudios.push(...syllables) allAudios.push(...frequencies) // 3. 缓存所有音频 await this.cacheAllAudios(allAudios) // 4. 保存音频信息到统一存储 this.saveAudioInfoToStorage(wordData.id, { main, syllables, frequencies }) return true } catch (error) { console.error('音频缓存失败:', error) return false } }, // 保存音频信息到storage(结构化存储) saveAudioInfoToStorage(wordId, audioInfo) { const cacheKey = `word_audio_${wordId}` const cachedData = { timestamp: Date.now(), ...audioInfo } uni.setStorageSync(cacheKey, cachedData) }, // 从storage获取音频信息 getAudioInfoFromStorage(wordId) { const cacheKey = `word_audio_${wordId}` return uni.getStorageSync(cacheKey) || null }, // 缓存所有音频 async cacheAllAudios(audioList) { const cachePromises = audioList.map(audio => { return this.cacheSingleAudio(audio.id, audio.url).catch(e => { console.warn(`音频 ${audio.id} 预加载失败:`, e) return null }) }) await Promise.all(cachePromises) }, // 缓存单个音频 async cacheSingleAudio(id, url) { return new Promise((resolve, reject) => { uni.downloadFile({ url, success: (res) => { if (res.statusCode === 200) { // 存储音频文件路径 uni.setStorageSync(`audio_file_${id}`, res.tempFilePath) resolve() } else { reject(new Error(`下载失败,状态码: ${res.statusCode}`)) } }, fail: reject }) }) }, // 播放音频 playAudio(audioInfo) { if (!audioInfo || !audioInfo.url) return const player = this.initPlayer() player.stop() // 尝试从缓存获取 const cachedPath = uni.getStorageSync(`audio_file_${audioInfo.id}`) if (cachedPath) { // 有缓存,直接播放 player.src = cachedPath player.play() } else { // 无缓存,从网络播放并后台缓存 console.warn(`音频 ${audioInfo.id} 未缓存,从网络加载`) player.src = audioInfo.url player.play() // 后台缓存 this.cacheSingleAudio(audioInfo.id, audioInfo.url).catch(console.error) } player.onError((err) => { console.error('播放失败:', err) uni.showToast({ title: '播放失败', icon: 'none' }) }) return player }, // 清理指定单词的缓存 clearWordCache(wordId) { // 1. 清理音频信息 uni.removeStorageSync(`word_audio_${wordId}`) // 2. 清理所有相关音频文件 const prefix = `word_${wordId}_` const keys = uni.getStorageInfoSync().keys keys.filter(key => key.startsWith('audio_file_') && key.includes(prefix)) .forEach(key => uni.removeStorageSync(key)) }, // 清理所有缓存 clearAllCache() { // 1. 清理所有音频信息 const keys = uni.getStorageInfoSync().keys keys.filter(key => key.startsWith('word_audio_')) .forEach(key => uni.removeStorageSync(key)) // 2. 清理所有音频文件 keys.filter(key => key.startsWith('audio_file_')) .forEach(key => uni.removeStorageSync(key)) } }