Browse Source

feat(音频控制): 增强音频播放管理和单词释义功能

1. 在CustomTabbar和book页面添加isWordAudioPlaying状态管理,防止单词音频与句子音频同时播放
2. 优化MeaningPopup图片显示模式为aspectFit
3. 在book页面添加当前页面标题显示
4. 重构handleTextClick和playWordAudio方法,添加详细日志和错误处理
5. 改进音频播放冲突处理,确保单词音频优先
6. 增强AudioControls组件,添加单词音频状态检查和停止功能
7. 优化音频匹配逻辑,支持多种匹配方式并添加实时TTS生成功能
8. 简化倍速播放检测逻辑,默认启用并优化设置流程
9. 添加页面卸载和隐藏时的资源清理逻辑
hfll
hflllll 2 weeks ago
parent
commit
eccc9e475b
4 changed files with 930 additions and 212 deletions
  1. +598
    -164
      subPages/home/AudioControls.vue
  2. +326
    -47
      subPages/home/book.vue
  3. +5
    -0
      subPages/home/components/CustomTabbar.vue
  4. +1
    -1
      subPages/home/components/MeaningPopup.vue

+ 598
- 164
subPages/home/AudioControls.vue View File

@ -110,6 +110,10 @@ export default {
pagePay: {
type: Array,
default: () => []
},
isWordAudioPlaying: {
type: Boolean,
default: false
}
},
data() {
@ -152,6 +156,8 @@ export default {
shouldCancelRequest: false, //
// IDprop
localVoiceId: '', // IDprop
//
lastSpeedCheckTime: -1, //
}
},
computed: {
@ -433,9 +439,9 @@ export default {
});
}
// 400ms
// 200ms
if (i < segments.length - 1) {
await new Promise(resolve => setTimeout(resolve, 400));
await new Promise(resolve => setTimeout(resolve, 200));
}
}
@ -450,6 +456,34 @@ export default {
//
async getCurrentPageAudio(autoPlay = false) {
// 🎯 ID
if (!this.localVoiceId || this.localVoiceId === '' || this.localVoiceId === null || this.localVoiceId === undefined) {
console.log('⚠️ 音色ID未加载,无法获取音频');
console.log(' localVoiceId:', this.localVoiceId);
console.log(' 类型:', typeof this.localVoiceId);
//
this.isAudioLoading = false;
this.audioLoadFailed = true;
this.hasAudioData = false;
//
this.$emit('audio-state-change', {
hasAudioData: false,
isLoading: false,
currentHighlightIndex: -1
});
uni.showToast({
title: '音色未加载,请稍后重试',
icon: 'none',
duration: 2000
});
return;
}
console.log('✅ 音色ID验证通过:', this.localVoiceId);
//
if (this.isPageChanging) {
console.log('页面切换中,跳过音频加载');
@ -1005,6 +1039,9 @@ export default {
if (this.currentPageAudios.length === 0) return;
// 🎯
this.stopWordAudio();
//
if (!this.currentAudio || this.currentAudio.src !== this.currentPageAudios[this.currentAudioIndex].url) {
this.createAudioInstance();
@ -1021,6 +1058,10 @@ export default {
this.currentAudio.play();
// isPlayingonPlay
this.updateHighlightIndex();
//
setTimeout(() => {
this.applyPlaybackRate(this.currentAudio);
}, 100);
}
},
@ -1036,13 +1077,124 @@ export default {
this.emitHighlightChange(-1);
},
// -
normalizeText(text) {
if (!text || typeof text !== 'string') return '';
return text
.trim()
.replace(/\s+/g, ' ') //
.replace(/[,。!?;:""''()【】《》]/g, '') //
.replace(/[,.!?;:"'()\[\]<>]/g, '') //
.toLowerCase(); //
},
// 使 TTS API
async playTextWithTTS(textContent) {
try {
console.log('🔊 使用 TTS 实时生成音频:', textContent);
//
if (this.currentAudio) {
this.currentAudio.pause();
this.currentAudio.destroy();
this.currentAudio = null;
}
//
uni.showLoading({
title: '正在生成音频...'
});
// TTS API
const audioRes = await this.$api.music.textToVoice({
text: textContent,
voiceType: this.voiceId || 1 // 使1
});
uni.hideLoading();
console.log('🔊 TTS API 响应:', audioRes);
if (audioRes && audioRes.result && audioRes.result.url) {
const audioUrl = audioRes.result.url;
//
const audio = uni.createInnerAudioContext();
audio.src = audioUrl;
audio.onPlay(() => {
console.log('🔊 TTS 音频开始播放');
this.isPlaying = true;
});
audio.onEnded(() => {
console.log('🔊 TTS 音频播放完成');
this.isPlaying = false;
audio.destroy();
if (this.currentAudio === audio) {
this.currentAudio = null;
}
});
audio.onError((error) => {
console.error('🔊 TTS 音频播放失败:', error);
this.isPlaying = false;
audio.destroy();
if (this.currentAudio === audio) {
this.currentAudio = null;
}
uni.showToast({
title: '音频播放失败',
icon: 'none'
});
});
//
this.currentAudio = audio;
audio.play();
console.log('✅ TTS 音频播放成功');
return true;
} else {
console.error('❌ TTS API 请求失败:', audioRes);
uni.showToast({
title: '音频生成失败',
icon: 'none'
});
return false;
}
} catch (error) {
uni.hideLoading();
console.error('❌ TTS 音频生成异常:', error);
uni.showToast({
title: '音频生成失败',
icon: 'none'
});
return false;
}
},
//
playSpecificAudio(textContent) {
console.log('尝试播放指定音频段落:', textContent);
console.log('🎯 尝试播放指定音频段落:', textContent);
console.log('📊 当前页面音频数组长度:', this.currentPageAudios.length);
console.log('📋 音频加载状态:', this.isAudioLoading);
//
console.log('🎵 检查单词音频播放状态:', this.isWordAudioPlaying);
if (this.isWordAudioPlaying) {
console.log('⚠️ 单词音频正在播放,阻止句子音频播放');
uni.showToast({
title: '请等待单词音频播放完成',
icon: 'none'
});
return false;
}
// textContent
if (!textContent || typeof textContent !== 'string') {
console.log('无效的文本内容:', textContent);
console.error('无效的文本内容:', textContent);
uni.showToast({
title: '无效的文本内容',
icon: 'none'
@ -1050,37 +1202,170 @@ export default {
return false;
}
//
let audioIndex = this.currentPageAudios.findIndex(audio =>
audio.text && audio.text.trim() === textContent.trim()
);
//
if (this.currentPageAudios.length === 0) {
console.warn('⚠️ 当前页面音频数据为空,可能还在加载中');
uni.showToast({
title: '音频正在加载中,请稍后再试',
icon: 'none'
});
return false;
}
//
const normalizedTarget = this.normalizeText(textContent);
console.log('🔍 标准化后的目标文本:', normalizedTarget);
//
if (audioIndex === -1) {
console.log('直接匹配失败,尝试匹配分段音频');
//
console.log('📝 当前页面所有音频文本:');
this.currentPageAudios.forEach((audio, index) => {
console.log(` [${index}] 原始文本: "${audio.text}"`);
console.log(` [${index}] 标准化文本: "${this.normalizeText(audio.text)}"`);
if (audio.originalText) {
console.log(` [${index}] 原始完整文本: "${audio.originalText}"`);
}
});
let audioIndex = -1;
//
audioIndex = this.currentPageAudios.findIndex(audio => {
if (!audio.text) return false;
const normalizedAudio = this.normalizeText(audio.text);
return normalizedAudio === normalizedTarget;
});
if (audioIndex !== -1) {
console.log('✅ 精确匹配成功,索引:', audioIndex);
} else {
console.log('❌ 精确匹配失败,尝试模糊匹配');
//
//
audioIndex = this.currentPageAudios.findIndex(audio => {
if (!audio.text) return false;
const normalizedAudio = this.normalizeText(audio.text);
//
if (audio.isSegmented && audio.originalText) {
return audio.originalText.trim() === textContent.trim();
}
//
const audioText = audio.text.trim();
const targetText = textContent.trim();
return audioText.includes(targetText) || targetText.includes(audioText);
//
return normalizedAudio.includes(normalizedTarget) || normalizedTarget.includes(normalizedAudio);
});
if (audioIndex !== -1) {
console.log('找到分段音频匹配,索引:', audioIndex);
console.log('✅ 包含匹配成功,索引:', audioIndex);
} else {
console.log('❌ 包含匹配失败,尝试分段音频匹配');
//
audioIndex = this.currentPageAudios.findIndex(audio => {
if (!audio.text) return false;
//
if (audio.isSegmented && audio.originalText) {
const normalizedOriginal = this.normalizeText(audio.originalText);
return normalizedOriginal === normalizedTarget ||
normalizedOriginal.includes(normalizedTarget) ||
normalizedTarget.includes(normalizedOriginal);
}
return false;
});
if (audioIndex !== -1) {
console.log('✅ 分段音频匹配成功,索引:', audioIndex);
} else {
console.log('❌ 所有匹配方式都失败');
//
console.log('❌ 尝试句子分割匹配...');
//
const targetSentences = normalizedTarget.split(/[,。!?;:,!?;:]/).filter(s => s.trim().length > 0);
console.log('🔪 目标句子分割结果:', targetSentences);
if (targetSentences.length > 1) {
//
for (let i = 0; i < targetSentences.length; i++) {
const sentence = targetSentences[i].trim();
if (sentence.length < 3) continue; //
audioIndex = this.currentPageAudios.findIndex(audio => {
if (!audio.text) return false;
const normalizedAudio = this.normalizeText(audio.text);
return normalizedAudio.includes(sentence) || sentence.includes(normalizedAudio);
});
if (audioIndex !== -1) {
console.log(`✅ 句子片段匹配成功,片段: "${sentence}", 索引: ${audioIndex}`);
break;
}
}
}
if (audioIndex === -1) {
console.log('❌ 句子分割匹配失败,尝试关键词匹配...');
//
const keywords = normalizedTarget.split(/\s+/).filter(word => word.length > 2);
console.log('🔑 提取的关键词:', keywords);
let bestKeywordMatch = -1;
let bestKeywordCount = 0;
this.currentPageAudios.forEach((audio, index) => {
if (!audio.text) return;
const normalizedAudio = this.normalizeText(audio.text);
//
const matchedKeywords = keywords.filter(keyword => normalizedAudio.includes(keyword));
const matchCount = matchedKeywords.length;
if (matchCount > bestKeywordCount && matchCount >= Math.min(2, keywords.length)) {
bestKeywordCount = matchCount;
bestKeywordMatch = index;
console.log(` [${index}] 关键词匹配: ${matchCount}/${keywords.length}, 匹配词: [${matchedKeywords.join(', ')}]`);
}
});
if (bestKeywordMatch !== -1) {
audioIndex = bestKeywordMatch;
console.log(`✅ 关键词匹配成功,索引: ${audioIndex}, 匹配数: ${bestKeywordCount}/${keywords.length}`);
} else {
console.log('❌ 关键词匹配失败,尝试相似度匹配...');
//
let bestMatch = -1;
let bestSimilarity = 0;
this.currentPageAudios.forEach((audio, index) => {
if (!audio.text) return;
const normalizedAudio = this.normalizeText(audio.text);
// /
const commonChars = [...normalizedTarget].filter(char => normalizedAudio.includes(char)).length;
const maxLength = Math.max(normalizedTarget.length, normalizedAudio.length);
const similarity = maxLength > 0 ? commonChars / maxLength : 0;
console.log(` [${index}] 相似度: ${similarity.toFixed(2)}, 文本: "${audio.text}"`);
if (similarity > bestSimilarity && similarity > 0.5) { // 50%
bestSimilarity = similarity;
bestMatch = index;
}
});
if (bestMatch !== -1) {
audioIndex = bestMatch;
console.log(`✅ 相似度匹配成功,索引: ${audioIndex}, 相似度: ${bestSimilarity.toFixed(2)}`);
}
}
}
}
}
}
if (audioIndex !== -1) {
console.log('找到匹配的音频,索引:', audioIndex);
console.log('🎵 找到匹配的音频,开始播放,索引:', audioIndex);
console.log('🎵 匹配的音频文本:', this.currentPageAudios[audioIndex].text);
//
if (this.currentAudio) {
@ -1108,18 +1393,19 @@ export default {
setTimeout(() => {
if (this.currentAudio) {
this.currentAudio.play();
console.log('开始播放指定音频段落');
console.log('🎵 开始播放指定音频段落');
} else {
console.error('❌ 音频实例创建失败');
}
}, 100);
return true; //
} else {
console.log('未找到匹配的音频段落:', textContent);
uni.showToast({
title: '未找到对应的音频',
icon: 'none'
});
return false; //
console.error('❌ 未找到匹配的音频段落:', textContent);
console.log('💡 尝试使用备用方案:实时生成音频');
// 使 textToVoice API
return this.playTextWithTTS(textContent);
}
},
@ -1141,8 +1427,14 @@ export default {
console.log('使用微信原生音頻API');
audio = wx.createInnerAudioContext();
} else {
console.log('使用uni-app音頻API');
audio = uni.createInnerAudioContext();
// H5使HTML5 Audio API
if (typeof window !== 'undefined' && window.Audio) {
console.log('使用原生HTML5 Audio API以支持倍速');
audio = this.createHTML5Audio();
} else {
console.log('使用uni-app音頻API');
audio = uni.createInnerAudioContext();
}
}
const audioUrl = this.currentPageAudios[this.currentAudioIndex].url;
audio.src = audioUrl;
@ -1154,43 +1446,47 @@ export default {
// playbackRate
audio.onCanplay(() => {
console.log('🎵 音頻可以播放,開始檢測playbackRate支持');
this.checkPlaybackRateSupport(audio);
console.log('🎵 音頻可以播放事件觸發,開始檢測playbackRate支持');
console.log('🔍 音頻實例基本信息:', {
src: audio.src,
duration: audio.duration,
readyState: audio.readyState || '未知',
constructor: audio.constructor.name
});
//
setTimeout(() => {
if (this.playbackRateSupported) {
console.log('設置播放速度:', this.playSpeed);
audio.playbackRate = this.playSpeed;
//
setTimeout(() => {
console.log('最終播放速度:', audio.playbackRate);
if (Math.abs(audio.playbackRate - this.playSpeed) > 0.01) {
console.log('⚠️ 播放速度設置可能未生效');
} else {
console.log('✅ 播放速度設置成功');
}
}, 50);
} else {
console.log('❌ 當前環境不支持播放速度控制');
}
}, 50);
this.checkPlaybackRateSupport(audio);
});
//
audio.onPlay(() => {
console.log('音频开始播放');
console.log('🎵 音频开始播放');
this.isPlaying = true;
//
this.applyPlaybackRate(audio);
});
audio.onPause(() => {
console.log('音频暂停');
console.log('⏸️ 音频暂停');
console.log('📊 暫停時倍速狀態:', {
當前播放速度: audio.playbackRate + 'x',
期望播放速度: this.playSpeed + 'x'
});
this.isPlaying = false;
});
audio.onTimeUpdate(() => {
this.updateCurrentTime();
// 5
if (Math.floor(audio.currentTime) % 5 === 0 && Math.floor(audio.currentTime) !== this.lastSpeedCheckTime) {
this.lastSpeedCheckTime = Math.floor(audio.currentTime);
const rateDifference = Math.abs(audio.playbackRate - this.playSpeed);
if (rateDifference > 0.01) {
console.log('⚠️ 检测到倍速偏差,重新应用倍速设置');
this.applyPlaybackRate(audio);
}
}
});
audio.onEnded(() => {
@ -1317,14 +1613,18 @@ export default {
},
toggleSpeed() {
//
console.log('🎛️ 用户点击倍速切换按钮...');
//
console.log('🔍 检查倍速支持状态:', {
playbackRateSupported: this.playbackRateSupported,
当前状态: this.playbackRateSupported ? '✅ 支持' : '⚠️ 受限'
});
// Android 4.x
if (!this.playbackRateSupported) {
uni.showToast({
title: '當前設備不支持播放速度控制',
icon: 'none',
duration: 2000
});
return;
console.log('⚠️ 倍速功能受限,但仍尝试切换');
//
}
const currentIndex = this.speedOptions.indexOf(this.playSpeed);
@ -1332,30 +1632,64 @@ export default {
const oldSpeed = this.playSpeed;
this.playSpeed = this.speedOptions[nextIndex];
console.log(`播放速度切換: ${oldSpeed}x -> ${this.playSpeed}x`);
console.log('⚡ 倍速切换详情:', {
可用选项: this.speedOptions,
当前索引: currentIndex,
下一个索引: nextIndex,
旧速度: oldSpeed + 'x',
新速度: this.playSpeed + 'x',
切换时间: new Date().toLocaleTimeString()
});
//
console.log('🎵 检查音频实例状态:', {
音频实例存在: !!this.currentAudio,
正在播放: this.isPlaying,
音频src: this.currentAudio ? this.currentAudio.src : '无'
});
if (this.currentAudio) {
const wasPlaying = this.isPlaying;
const currentTime = this.currentAudio.currentTime;
//
this.currentAudio.playbackRate = this.playSpeed;
console.log('🔧 准备更新音频播放速度:', {
播放状态: wasPlaying ? '正在播放' : '已暂停',
当前时间: currentTime + 's',
目标速度: this.playSpeed + 'x'
});
// 使
this.applyPlaybackRate(this.currentAudio);
// 使
// 使
if (wasPlaying) {
console.log('🔄 重启播放以应用新速度...');
this.currentAudio.pause();
setTimeout(() => {
this.currentAudio.seek(currentTime);
// 使seek
this.currentAudio.play();
}, 50);
console.log('▶️ 播放已重启,新速度已生效');
}, 100);
}
console.log('音頻實例播放速度已更新為:', this.currentAudio.playbackRate);
console.log('📊 最终音频状态:', {
播放速度: this.currentAudio.playbackRate + 'x',
播放时间: this.currentAudio.currentTime + 's',
播放状态: this.isPlaying ? '播放中' : '已暂停'
});
//
//
uni.showToast({
title: `播放速度: ${this.playSpeed}x`,
title: `🎵 播放速度: ${this.playSpeed}x`,
icon: 'none',
duration: 1000
});
console.log('🎉 倍速切换完成!新速度:', this.playSpeed + 'x');
} else {
console.log('⚠️ 没有音频实例,仅更新速度设置');
uni.showToast({
title: `⚡ 速度设为: ${this.playSpeed}x`,
icon: 'none',
duration: 1000
});
@ -1506,123 +1840,161 @@ export default {
}
},
//
//
checkInitialPlaybackRateSupport() {
console.log('🚀 开始初始倍速播放支持检测...');
try {
const systemInfo = uni.getSystemInfoSync();
console.log('初始檢測 - 系統信息:', systemInfo);
// - playbackRate2.11.0
const SDKVersion = systemInfo.SDKVersion || '0.0.0';
const versionArray = SDKVersion.split('.').map(v => parseInt(v));
const isVersionSupported = versionArray[0] > 2 ||
(versionArray[0] === 2 && versionArray[1] > 11) ||
(versionArray[0] === 2 && versionArray[1] === 11 && versionArray[2] >= 0);
if (!isVersionSupported) {
this.playbackRateSupported = false;
console.log(`初始檢測 - 基礎庫版本過低 (${SDKVersion}),需要2.11.0及以上才支持播放速度控制`);
return;
}
console.log('📱 系统信息:', {
platform: systemInfo.platform,
system: systemInfo.system,
SDKVersion: systemInfo.SDKVersion,
brand: systemInfo.brand,
model: systemInfo.model
});
//
//
this.playbackRateSupported = true;
// Android 6
// AndroidAndroid 4.x
if (systemInfo.platform === 'android') {
const androidVersion = systemInfo.system.match(/Android (\d+)/);
if (androidVersion && parseInt(androidVersion[1]) < 6) {
if (androidVersion && parseInt(androidVersion[1]) < 5) {
this.playbackRateSupported = false;
console.log('初始檢測 - Android版本過低,需要Android 6及以上才支持播放速度控制');
console.log(`⚠️ Android版本过低 (${androidVersion[1]}),禁用倍速功能`);
uni.showToast({
title: `Android ${androidVersion[1]} 不支持倍速`,
icon: 'none',
duration: 2000
});
return;
}
}
// API
if (typeof wx === 'undefined' || !wx.createInnerAudioContext) {
console.log('初始檢測 - 微信原生API不可用,可能影響playbackRate支持');
} else {
console.log('初始檢測 - 微信原生API可用');
}
console.log('✅ 倍速功能已启用!');
console.log('📊 可用倍速选项:', this.speedOptions);
console.log('⚡ 当前播放速度:', this.playSpeed + 'x');
//
this.playbackRateSupported = true;
console.log('初始檢測 - 基本條件滿足,等待音頻實例檢測');
//
uni.showToast({
title: '✅ 倍速功能可用',
icon: 'none',
duration: 1500
});
} catch (error) {
console.error('初始檢測播放速度支持時出錯:', error);
this.playbackRateSupported = false;
console.error('💥 检测播放速度支持时出错:', error);
// 使
this.playbackRateSupported = true;
console.log('⚠️ 检测出错,但仍启用倍速功能');
}
},
//
applyPlaybackRate(audio) {
if (!audio) return;
console.log('⚙️ 开始应用播放速度设置...');
console.log('📊 当前状态检查:', {
playbackRateSupported: this.playbackRateSupported,
期望速度: this.playSpeed + 'x',
音频当前速度: audio.playbackRate + 'x',
音频播放状态: this.isPlaying ? '播放中' : '未播放'
});
if (this.playbackRateSupported) {
try {
//
const maxAttempts = 3;
let attempt = 0;
const trySetRate = () => {
attempt++;
audio.playbackRate = this.playSpeed;
setTimeout(() => {
const actualRate = audio.playbackRate;
const rateDifference = Math.abs(actualRate - this.playSpeed);
console.log(`🔍 倍速设置尝试 ${attempt}/${maxAttempts}:`, {
期望速度: this.playSpeed,
实际速度: actualRate,
差值: rateDifference,
设置成功: rateDifference < 0.01
});
if (rateDifference >= 0.01 && attempt < maxAttempts) {
console.log(`⚠️ 倍速设置未完全生效,进行第 ${attempt + 1} 次尝试...`);
setTimeout(trySetRate, 100);
} else if (rateDifference < 0.01) {
console.log('✅ 倍速设置成功!');
} else {
console.log('⚠️ 倍速设置可能不完全支持,但已尽力尝试');
}
}, 50);
};
trySetRate();
} catch (error) {
console.log('💥 设置播放速度时出错:', error);
}
} else {
console.log('❌ 当前环境不支持播放速度控制');
}
},
//
//
checkPlaybackRateSupport(audio) {
console.log('🎵 开始音频实例倍速支持检测...');
try {
//
const systemInfo = uni.getSystemInfoSync();
console.log('系統信息:', systemInfo);
console.log('平台:', systemInfo.platform);
console.log('基礎庫版本:', systemInfo.SDKVersion);
console.log('系統版本:', systemInfo.system);
// uni-app2.11.0+playbackRate
const SDKVersion = systemInfo.SDKVersion || '0.0.0';
const versionArray = SDKVersion.split('.').map(v => parseInt(v));
const isVersionSupported = versionArray[0] > 2 ||
(versionArray[0] === 2 && versionArray[1] > 11) ||
(versionArray[0] === 2 && versionArray[1] === 11 && versionArray[2] >= 0);
console.log('基礎庫版本檢查:', {
version: SDKVersion,
parsed: versionArray,
supported: isVersionSupported
});
if (!isVersionSupported) {
this.playbackRateSupported = false;
console.log(`❌ 基礎庫版本不支持 (${SDKVersion}),微信小程序需要2.11.0+才支持playbackRate`);
//
if (!this.playbackRateSupported) {
console.log('⚠️ 初始检测已禁用倍速功能,跳过音频实例检测');
return;
}
// Android6.0+
if (systemInfo.platform === 'android') {
const androidVersion = systemInfo.system.match(/Android (\d+)/);
console.log('Android版本檢查:', androidVersion);
if (androidVersion && parseInt(androidVersion[1]) < 6) {
this.playbackRateSupported = false;
console.log(`❌ Android版本不支持 (${androidVersion[1]}),需要Android 6+才支持playbackRate`);
return;
}
}
// playbackRate
console.log('🔍 音頻實例檢查:');
console.log('- playbackRate初始值:', audio.playbackRate);
console.log('- playbackRate類型:', typeof audio.playbackRate);
//
const testRate = 1.25;
const originalRate = audio.playbackRate || 1.0;
console.log('🎧 音频实例信息:', {
音频对象存在: !!audio,
音频对象类型: typeof audio,
音频src: audio ? audio.src : '无'
});
try {
audio.playbackRate = testRate;
console.log('- 設置測試速度:', testRate);
console.log('- 設置後的值:', audio.playbackRate);
// playbackRate
if (audio && typeof audio.playbackRate !== 'undefined') {
console.log('✅ 音频实例支持playbackRate属性');
//
if (Math.abs(audio.playbackRate - testRate) < 0.01) {
this.playbackRateSupported = true;
console.log('✅ playbackRate功能正常');
} else {
this.playbackRateSupported = false;
console.log('❌ playbackRate設置無效,可能不被支持');
//
try {
const currentSpeed = this.playSpeed || 1.0;
audio.playbackRate = currentSpeed;
console.log(`🔧 设置播放速度为 ${currentSpeed}x`);
//
setTimeout(() => {
const actualRate = audio.playbackRate;
console.log('🔍 播放速度验证:', {
期望值: currentSpeed,
实际值: actualRate,
设置成功: Math.abs(actualRate - currentSpeed) < 0.1
});
}, 50);
} catch (error) {
console.log('⚠️ 设置播放速度时出现错误,但继续保持倍速功能启用:', error.message);
}
} catch (error) {
this.playbackRateSupported = false;
console.log('❌ playbackRate設置出錯:', error);
} else {
console.log('⚠️ 音频实例不支持playbackRate属性,但保持倍速功能启用');
}
//
console.log('✅ 音频实例检测完成,倍速功能保持启用');
} catch (error) {
console.error('檢查播放速度支持時出錯:', error);
this.playbackRateSupported = false;
console.error('💥 检查播放速度支持时出错:', error);
// 使
console.log('⚠️ 检测出错,但保持倍速功能启用');
}
},
@ -1680,12 +2052,64 @@ export default {
//
destroyAudio() {
console.log('🧹 AudioControls: 开始清理音频资源');
// 1.
if (this.currentAudio) {
this.currentAudio.destroy();
console.log('🧹 销毁当前音频实例');
try {
//
if (this.isPlaying) {
this.currentAudio.pause();
}
this.currentAudio.destroy();
} catch (error) {
console.error('销毁音频实例失败:', error);
}
this.currentAudio = null;
}
// 2.
this.isPlaying = false;
this.currentTime = 0;
this.sliderValue = 0;
this.currentHighlightIndex = -1;
// 3.
this.clearAudioCache();
// 4.
this.shouldCancelRequest = true;
// 5.
this.isAudioLoading = false;
this.isVoiceChanging = false;
this.audioLoadFailed = false;
console.log('✅ AudioControls: 音频资源清理完成');
},
//
stopWordAudio() {
try {
// 访book
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
if (currentPage && currentPage.$vm) {
const bookVm = currentPage.$vm;
if (bookVm.currentWordAudio) {
console.log('🔄 AudioControls: 停止单词音频播放');
bookVm.currentWordAudio.pause();
bookVm.currentWordAudio.destroy();
bookVm.currentWordAudio = null;
bookVm.isWordAudioPlaying = false;
console.log('✅ AudioControls: 单词音频已停止');
}
}
} catch (error) {
console.log('⚠️ AudioControls: 停止单词音频时出现异常:', error);
}
},
//
@ -1990,7 +2414,7 @@ export default {
console.log(`预加载进度: ${this.preloadProgress}%`);
//
await new Promise(resolve => setTimeout(resolve, 600));
await new Promise(resolve => setTimeout(resolve, 300));
} catch (error) {
console.error(`预加载第${pageInfo.pageIndex + 1}页音频失败:`, error);
@ -2108,9 +2532,9 @@ export default {
console.error(`${pageIndex + 1}页第${i + 1}个文本项处理异常:`, error);
}
// 600ms
// 300ms
if (i < textItems.length - 1) {
await new Promise(resolve => setTimeout(resolve, 600));
await new Promise(resolve => setTimeout(resolve, 300));
}
}
@ -2186,8 +2610,18 @@ export default {
}
},
mounted() {
console.log('🎛️ AudioControls組件初始化開始');
console.log('⚙️ 初始倍速配置:', {
默認播放速度: this.playSpeed + 'x',
可選速度選項: this.speedOptions.map(s => s + 'x'),
初始支持狀態: this.playbackRateSupported
});
//
console.log('🔍 開始檢測播放速度支持...');
this.checkInitialPlaybackRateSupport();
console.log('✅ AudioControls組件初始化完成');
},
//


+ 326
- 47
subPages/home/book.vue View File

@ -30,7 +30,7 @@
>
<scroll-view scroll-y style="height: 100vh;">
<view scroll-y class="content-area" @click="toggleNavbar">
<view class="title">{{ currentPageTitle }}</view>
<!-- 会员限制页面 -->
<view v-if="!isMember && pagePay[index] === 'Y'" class="member-content" >
<text class="member-title">{{ pageTitles[index] }}</text>
@ -47,13 +47,14 @@
</view>
<view v-for="(item, itemIndex) in page" :key="itemIndex">
<image class="card-image" v-if="item && item.type === 'image'" :src="item.imageUrl" mode="aspectFill"></image>
<view class="english-text-container clickable-text" v-else-if="item && item.type === 'text' && item.language === 'en' && item.content" @click.stop="handleTextClick(item.content, item, index)">
<view class="english-text-container clickable-text" v-else-if="item && item.type === 'text' && item.language === 'en' && item.content" @click.stop="handleTextClick(item.content, item, index)" >
<text
v-for="(token, tokenIndex) in splitEnglishSentence(item.content)"
:key="tokenIndex"
:class="['english-token', { 'clickable-word': token.isWord && findWordDefinition(token.text) }]"
@click.stop="token.isWord && findWordDefinition(token.text) ? handleWordClick(token.text) : null"
user-select
:style="item.style"
>{{ token.text }}</text>
</view>
<view v-else-if="item && item.type === 'text' && item.language === 'zh' && item.content" @click.stop="handleTextClick(item.content, item, index)">
@ -72,7 +73,7 @@
<view v-for="(item, itemIndex) in page" :key="itemIndex">
<!-- 文本页面 -->
<view v-if="item && item.type === 'text' && item.content" class="text-content" >
<view :class="{ 'text-highlight': isTextHighlighted(page, itemIndex) }" @click.stop="handleTextClick(item.content, item, index)">
<view :class="{ 'lead-text': isTextHighlighted(page, itemIndex) }" @click.stop="handleTextClick(item.content, item, index)">
<text
v-if="!item.isLead"
class="content-text clickable-text"
@ -81,7 +82,7 @@
>
{{ item.content }}
</text>
<text v-else-if="item.isLead" class="content-text clickable-text lead-text" :style="item.style" user-select>{{ item.content }}</text>
<view v-else-if="item.isLead" class="content-text clickable-text lead-text" :style="item.style" user-select>{{ item.content }}</view>
</view>
</view>
@ -128,6 +129,7 @@
:is-member="isMember"
:current-page-requires-member="currentPageRequiresMember"
:page-pay="pagePay"
:is-word-audio-playing="isWordAudioPlaying"
@toggle-course-popup="toggleCoursePopup"
@toggle-sound="toggleSound"
@go-to-page="goToPage"
@ -191,6 +193,7 @@ export default {
currentHighlightIndex: -1, //
wordAudioCache: {}, //
currentWordAudio: null, //
isWordAudioPlaying: false, //
//
isAudioLoading: false, //
@ -361,25 +364,42 @@ export default {
//
handleTextClick(textContent, item, pageIndex) {
// console.log(':', textContent);
// console.log('textContent:', typeof textContent);
// console.log('textContentundefined:', textContent === undefined);
// console.log('item:', item);
// console.log('item.content:', item ? item.content : 'item');
// console.log(':', this.currentPage);
// console.log(':', pageIndex);
// console.log(':', this.bookPages[this.currentPage - 1]);
// console.log(':', this.bookPages[this.currentPage - 1] ? this.bookPages[this.currentPage - 1].length : '');
console.log('🎯 ===== 文本点击事件开始 =====');
console.log('📝 点击文本:', textContent);
console.log('📄 textContent类型:', typeof textContent);
console.log('❓ textContent是否为undefined:', textContent === undefined);
console.log('📦 完整item对象:', item);
console.log('📝 item.content:', item ? item.content : 'item为空');
console.log('📖 当前页面索引:', this.currentPage);
console.log('👆 点击的页面索引:', pageIndex);
console.log('📊 当前页面类型:', this.currentPageType);
console.log('📄 是否为文本页面:', this.isTextPage);
console.log('📋 当前页面数据:', this.bookPages[this.currentPage - 1]);
console.log('📏 页面数据长度:', this.bookPages[this.currentPage - 1] ? this.bookPages[this.currentPage - 1].length : '页面不存在');
//
console.log('🎵 ===== 音频状态检查 =====');
console.log(' isWordAudioPlaying:', this.isWordAudioPlaying);
console.log(' currentWordAudio存在:', !!this.currentWordAudio);
console.log(' currentWordMeaning存在:', !!this.currentWordMeaning);
if (this.isWordAudioPlaying) {
console.log('⚠️ 检测到单词音频正在播放状态,这可能会阻止句子音频播放');
console.log('🔄 尝试重置音频播放状态...');
this.isWordAudioPlaying = false;
console.log('✅ 音频播放状态已重置');
}
//
if (pageIndex !== undefined && pageIndex !== this.currentPage - 1) {
console.log('点击的不是当前页面,忽略点击事件');
console.warn('⚠️ 点击的不是当前页面,忽略点击事件');
console.log(` 期望页面: ${this.currentPage - 1}, 点击页面: ${pageIndex}`);
return;
}
//
if (!item) {
console.error('handleTextClick: item参数为空');
console.error('handleTextClick: item参数为空');
uni.showToast({
title: '数据错误,请刷新页面',
icon: 'none'
@ -390,12 +410,16 @@ export default {
// textContentundefineditem
if (!textContent && item && item.content) {
textContent = item.content;
console.log('从item中获取到content:', textContent);
console.log('🔄 从item中获取到content:', textContent);
}
// textContent
if (!textContent || typeof textContent !== 'string' || textContent.trim() === '') {
console.error('handleTextClick: 无效的文本内容', textContent);
console.error('❌ handleTextClick: 无效的文本内容', textContent);
console.log(' textContent:', textContent);
console.log(' 类型:', typeof textContent);
console.log(' 是否为空字符串:', textContent === '');
console.log(' trim后是否为空:', textContent && textContent.trim() === '');
uni.showToast({
title: '文本内容无效',
icon: 'none'
@ -403,9 +427,27 @@ export default {
return;
}
console.log('✅ 文本内容验证通过:', textContent);
console.log(' 文本长度:', textContent.length);
console.log(' 文本预览:', textContent.substring(0, 50) + (textContent.length > 50 ? '...' : ''));
//
if (!this.$refs.customTabbar || !this.$refs.customTabbar.$refs.audioControls) {
console.log('音频控制组件未找到');
console.log('🔍 检查音频控制组件引用...');
console.log(' customTabbar存在:', !!this.$refs.customTabbar);
if (!this.$refs.customTabbar) {
console.error('❌ customTabbar引用不存在');
uni.showToast({
title: '音频控制组件未准备好',
icon: 'none'
});
return;
}
console.log(' audioControls存在:', !!this.$refs.customTabbar.$refs.audioControls);
if (!this.$refs.customTabbar.$refs.audioControls) {
console.error('❌ audioControls引用不存在');
uni.showToast({
title: '音频控制组件未准备好',
icon: 'none'
@ -415,8 +457,13 @@ export default {
//
// type'1'
console.log('🔍 检查页面类型支持...');
console.log(' isTextPage:', this.isTextPage);
console.log(' currentPageType:', this.currentPageType);
if (!this.isTextPage && this.currentPageType !== '1') {
console.log('当前页面不是文本页面或卡片页面');
console.warn('⚠️ 当前页面不是文本页面或卡片页面');
console.log(` isTextPage: ${this.isTextPage}, currentPageType: ${this.currentPageType}`);
uni.showToast({
title: '当前页面不支持音频播放',
icon: 'none'
@ -424,14 +471,35 @@ export default {
return;
}
console.log('✅ 页面类型检查通过,准备播放音频');
//
const audioControls = this.$refs.customTabbar.$refs.audioControls;
console.log('🎵 音频控制组件状态:');
console.log(' currentPageAudios存在:', !!audioControls.currentPageAudios);
console.log(' currentPageAudios长度:', audioControls.currentPageAudios ? audioControls.currentPageAudios.length : 0);
console.log(' isAudioLoading:', audioControls.isAudioLoading);
console.log(' currentAudioIndex:', audioControls.currentAudioIndex);
console.log(' isPlaying:', audioControls.isPlaying);
// AudioControls
const success = this.$refs.customTabbar.$refs.audioControls.playSpecificAudio(textContent);
console.log('🚀 调用 playSpecificAudio...');
const success = audioControls.playSpecificAudio(textContent);
console.log('🎵 playSpecificAudio 返回结果:', success);
if (success) {
console.log('成功播放指定音频段落');
console.log('成功播放指定音频段落');
} else {
console.log('播放指定音频段落失败');
console.error('❌ 播放指定音频段落失败');
console.log('💡 失败可能原因:');
console.log(' 1. 文本内容与音频数据不匹配');
console.log(' 2. 音频数据尚未加载完成');
console.log(' 3. 音频文件路径错误或文件损坏');
console.log(' 4. 网络连接问题');
}
console.log('🎯 ===== 文本点击事件结束 =====');
},
onHighlightChange(highlightData) {
@ -498,7 +566,35 @@ export default {
}
},
closeMeaningPopup() {
this.currentWordMeaning = null
console.log('🔄 ===== 关闭重点单词弹窗 =====');
console.log('📱 清理弹窗数据...');
this.currentWordMeaning = null;
//
console.log('🎵 检查音频播放状态...');
console.log(' 当前 isWordAudioPlaying:', this.isWordAudioPlaying);
console.log(' 当前 currentWordAudio:', !!this.currentWordAudio);
if (this.isWordAudioPlaying) {
console.log('🛑 重置音频播放状态');
this.isWordAudioPlaying = false;
//
if (this.currentWordAudio) {
console.log('🛑 停止当前播放的单词音频');
try {
this.currentWordAudio.pause();
this.currentWordAudio.destroy();
console.log('✅ 单词音频已停止并销毁');
} catch (error) {
console.log('⚠️ 停止单词音频时出现异常:', error);
}
this.currentWordAudio = null;
}
}
console.log('✅ 弹窗关闭完成,音频状态已重置');
console.log('🏁 ===== 关闭重点单词弹窗结束 =====');
},
//
@ -600,50 +696,133 @@ export default {
async playWordAudio(word) {
try {
console.log('開始播放單詞語音:', word);
console.log('🎵 ===== 开始播放单词语音 =====');
console.log('📝 目标单词:', word);
console.log('🎧 当前音频实例状态:', this.currentWordAudio ? '存在' : '不存在');
console.log('🔊 voiceId:', this.voiceId, '类型:', typeof this.voiceId);
//
if (this.isWordAudioPlaying) {
console.log('⚠️ 有音频正在播放,将停止当前播放');
this.isWordAudioPlaying = false;
}
// 🎯
if (this.$refs.customTabbar && this.$refs.customTabbar.$refs.audioControls) {
const audioControls = this.$refs.customTabbar.$refs.audioControls;
if (audioControls.isPlaying) {
console.log('🔄 停止主音频播放,准备播放单词音频');
audioControls.pauseAudio();
}
}
//
if (this.currentWordAudio) {
this.currentWordAudio.destroy();
console.log('🛑 停止当前播放的音频');
try {
//
this.currentWordAudio.pause();
this.currentWordAudio.destroy();
console.log('✅ 音频已暂停并销毁');
} catch (error) {
console.log('⚠️ 清理音频时出现异常:', error);
}
this.currentWordAudio = null;
this.isWordAudioPlaying = false;
console.log('✅ currentWordAudio 已重置,播放状态已清除');
} else {
console.log('ℹ️ 没有正在播放的音频需要停止');
}
// 🎯 ID
if (!this.voiceId || this.voiceId === '' || this.voiceId === null || this.voiceId === undefined) {
console.log('⚠️ 音色ID未加载,无法获取单词音频');
console.log(' voiceId:', this.voiceId);
console.log(' 类型:', typeof this.voiceId);
uni.showToast({
title: '音色未加载,请稍后重试',
icon: 'none',
duration: 2000
});
return;
}
console.log('✅ 单词音频音色ID验证通过:', this.voiceId);
// 調API
console.log('playWordAudio - voiceId值:', this.voiceId, '类型:', typeof this.voiceId);
console.log('🌐 准备调用 textToVoice API');
console.log('📋 API参数:', { text: word, voiceType: this.voiceId });
const audioRes = await this.$api.music.textToVoice({
text: word,
voiceType: this.voiceId
});
console.log('單詞語音API響應:', audioRes);
console.log('📡 API响应:', audioRes);
console.log('✅ API调用完成');
//
if (audioRes && audioRes.result && audioRes.result.url) {
console.log('🎵 API响应有效,准备播放音频');
// 使url
const audioUrl = audioRes.result.url;
console.log('🔗 音频URL:', audioUrl);
//
console.log('🎧 创建音频实例');
const audio = uni.createInnerAudioContext();
audio.src = audioUrl;
console.log('✅ 音频实例创建完成,设置URL');
//
audio.onCanplay(() => {
console.log('🎵 音频已加载完成,可以播放');
});
audio.onPlay(() => {
console.log('開始播放單詞語音:', word);
console.log('🎵 音频开始播放:', word);
console.log('🎧 当前音频实例已激活');
this.isWordAudioPlaying = true;
console.log('✅ 播放状态已设置为 true');
});
audio.onEnded(() => {
console.log('單詞語音播放完成:', word);
console.log('✅ 音频播放完成:', word);
console.log('🧹 准备清理音频实例');
this.isWordAudioPlaying = false;
console.log('🔄 播放状态已重置为 false');
audio.destroy();
if (this.currentWordAudio === audio) {
this.currentWordAudio = null;
console.log('🔄 currentWordAudio 已重置为 null');
} else {
console.log('⚠️ 音频实例不匹配,可能已被替换');
}
});
audio.onError((error) => {
console.error('單詞語音播放失敗:', error);
audio.destroy();
console.error('❌ 音频播放失败:', error);
console.log('🔍 错误详情:', JSON.stringify(error));
console.log('🧹 准备清理失败的音频实例');
this.isWordAudioPlaying = false;
console.log('🔄 播放状态已重置为 false (错误)');
try {
audio.destroy();
console.log('✅ 失败的音频实例已销毁');
} catch (destroyError) {
console.error('⚠️ 销毁音频实例时出错:', destroyError);
}
if (this.currentWordAudio === audio) {
this.currentWordAudio = null;
console.log('🔄 currentWordAudio 已重置为 null (错误)');
} else {
console.log('⚠️ 音频实例不匹配,可能已被替换');
}
uni.showToast({
title: '語音播放失敗',
icon: 'none'
@ -651,22 +830,48 @@ export default {
});
//
console.log('💾 保存音频实例到 currentWordAudio');
this.currentWordAudio = audio;
audio.play();
//
console.log('⏱️ 等待音频实例准备...');
setTimeout(() => {
if (this.currentWordAudio === audio) {
console.log('🚀 开始播放音频');
try {
audio.play();
console.log('✅ 音频播放命令已发送');
} catch (playError) {
console.error('❌ 播放命令失败:', playError);
uni.showToast({
title: '音频播放失败',
icon: 'none'
});
}
} else {
console.log('⚠️ 音频实例已被替换,取消播放');
}
}, 100);
} else {
console.error('單詞語音請求失敗:', audioRes);
console.error('❌ API响应无效:', audioRes);
console.log('🔍 响应结构检查:');
console.log(' audioRes存在:', !!audioRes);
console.log(' audioRes.result存在:', !!(audioRes && audioRes.result));
console.log(' audioRes.result.url存在:', !!(audioRes && audioRes.result && audioRes.result.url));
uni.showToast({
title: '語音播放失敗',
icon: 'none'
});
}
} catch (error) {
console.error('播放單詞語音異常:', error);
console.error('❌ 播放单词语音异常:', error);
console.log('🔍 异常详情:', error.message || error);
uni.showToast({
title: '語音播放失敗',
icon: 'none'
});
}
console.log('🏁 ===== playWordAudio 方法结束 =====');
},
@ -685,38 +890,54 @@ export default {
//
handleWordClick(word) {
console.log('🎯 ===== 单词点击事件开始 =====');
console.log('📝 点击的单词:', word);
console.log('🔍 开始查找单词释义...');
const definition = this.findWordDefinition(word);
console.log('查找单词:', word, '释义:', definition);
console.log('📖 查找结果:', definition ? '找到释义' : '未找到释义');
console.log('📋 完整释义数据:', definition);
if (definition) {
console.log('✅ 找到单词释义,准备设置弹窗数据');
this.currentWordMeaning = {
word: definition.word,
phonetic: definition.soundmark || '',
partOfSpeech: '', //
meaning: definition.paraphrase || '',
knowledgeGain: definition.knowledge || ''
knowledgeGain: definition.knowledge || '',
image: definition.image || ''
};
console.log('📋 弹窗数据已设置:', this.currentWordMeaning);
//
const combinedText = `${word}${definition.paraphrase || ''}`;
console.log('播放合并文本:', combinedText);
console.log('🎵 准备播放合并文本:', combinedText);
this.playWordAudio(combinedText);
console.log('📱 显示单词释义弹窗');
this.showWordMeaning();
} else {
console.log('未找到单词释义:', word);
console.log('⚠️ 未找到单词释义:', word);
console.log('🎵 只播放单词本身');
//
if (word) {
this.playWordAudio(word);
} else {
console.log('❌ 单词为空,无法播放');
}
}
console.log('🏁 ===== handleWordClick 方法结束 =====');
},
//
handleChineseKeywordClick(keywordData) {
console.log('点击中文重点词汇:', keywordData.word, '释义:', keywordData);
console.log('🎯 ===== 中文重点词汇点击事件开始 =====');
console.log('📝 点击的词汇数据:', keywordData);
console.log('📖 词汇:', keywordData ? keywordData.word : '无');
if (keywordData) {
console.log('✅ 词汇数据有效,准备设置弹窗数据');
this.currentWordMeaning = {
word: keywordData.word,
phonetic: keywordData.soundmark || '',
@ -724,16 +945,19 @@ export default {
meaning: keywordData.paraphrase || '',
knowledgeGain: keywordData.knowledge || ''
};
console.log('📋 弹窗数据已设置:', this.currentWordMeaning);
//
const combinedText = `${keywordData.word}${keywordData.paraphrase || ''}`;
console.log('播放中文合并文本:', combinedText);
console.log('🎵 准备播放中文合并文本:', combinedText);
this.playWordAudio(combinedText);
console.log('📱 显示词汇释义弹窗');
this.showWordMeaning();
} else {
console.log('未找到中文词汇释义');
console.log('未找到中文词汇释义数据');
}
console.log('🏁 ===== handleChineseKeywordClick 方法结束 =====');
},
//
@ -1745,27 +1969,71 @@ export default {
},
//
onUnload() {
console.log('📱 页面卸载:开始清理所有音频资源');
uni.$off('selectVoice')
//
// 1.
if (this.currentWordAudio) {
this.currentWordAudio.destroy();
console.log('🧹 清理单词音频实例');
try {
this.currentWordAudio.destroy();
} catch (error) {
console.error('销毁单词音频实例失败:', error);
}
this.currentWordAudio = null;
}
//
// 2.
this.clearWordAudioCache();
// AudioControls
// 3.
this.isWordAudioPlaying = false;
// 4. AudioControls
if (this.$refs.customTabbar && this.$refs.customTabbar.$refs.audioControls) {
console.log('🧹 通知AudioControls清理音频资源');
this.$refs.customTabbar.$refs.audioControls.destroyAudio();
}
// 5.
try {
//
if (typeof wx !== 'undefined' && wx.getBackgroundAudioManager) {
const bgAudio = wx.getBackgroundAudioManager();
if (bgAudio) {
bgAudio.stop();
}
}
} catch (error) {
console.error('清理背景音频失败:', error);
}
console.log('✅ 页面卸载:音频资源清理完成');
},
//
onHide() {
// AudioControls
console.log('📱 页面隐藏:暂停所有音频播放');
// 1.
if (this.currentWordAudio && this.isWordAudioPlaying) {
console.log('⏸️ 暂停单词音频播放');
try {
this.currentWordAudio.pause();
this.isWordAudioPlaying = false;
} catch (error) {
console.error('暂停单词音频失败:', error);
}
}
// 2. AudioControls
if (this.$refs.customTabbar && this.$refs.customTabbar.$refs.audioControls) {
console.log('⏸️ 通知AudioControls暂停音频');
this.$refs.customTabbar.$refs.audioControls.pauseOnHide();
}
console.log('✅ 页面隐藏:音频暂停完成');
}
}
</script>
@ -1837,6 +2105,7 @@ export default {
.swiper-item {
min-height: 100vh;
// background-color: red;
}
.content-area {
@ -1850,6 +2119,15 @@ export default {
min-height: 100%;
box-sizing: border-box;
overflow-y: auto;
.title{
font-family: PingFang SC;
font-weight: 500;
font-size: 34rpx;
text-align: center;
color: #181818;
line-height: 48rpx;
margin-bottom: 32rpx;
}
.content-image{
width: 100%;
height: auto;
@ -2082,6 +2360,7 @@ export default {
background: #fffbe6; /* 柔和的提示背景 */
border: 1px solid #ffe58f;
border-radius: 8px;
padding: 10rpx 20rpx;
}
.text-highlight {


+ 5
- 0
subPages/home/components/CustomTabbar.vue View File

@ -11,6 +11,7 @@
:is-member="isMember"
:current-page-requires-member="currentPageRequiresMember"
:page-pay="pagePay"
:is-word-audio-playing="isWordAudioPlaying"
@previous-page="previousPage"
@next-page="nextPage"
@audio-state-change="onAudioStateChange"
@ -102,6 +103,10 @@ export default {
pagePay: {
type: Array,
default: () => []
},
isWordAudioPlaying: {
type: Boolean,
default: false
}
},
methods: {


+ 1
- 1
subPages/home/components/MeaningPopup.vue View File

@ -21,7 +21,7 @@
v-if="currentWordMeaning.image"
class="meaning-image"
:src="currentWordMeaning.image"
mode="aspectFill"
mode="aspectFit"
/>
<view class="word-info">


Loading…
Cancel
Save