修改语音播放等三个bug

This commit is contained in:
田岩
2025-09-08 20:40:49 +08:00
parent c020397e20
commit daf47b213d
11 changed files with 335 additions and 105 deletions
+25 -25
View File
@@ -80,7 +80,9 @@
<script setup>
import { computed, unref, ref, toRefs, watch, onMounted, nextTick } from 'vue';
import { onUnload } from '@dcloudio/uni-app';
import { useAudioStore } from '@/store/audioStore';
// import { useinnerAudio, useAudioFun } from '@/store/innerAudio';
import { useAudio } from './useAudio.js';
import {downloadFile} from '@/api/common.js'
const props = defineProps({
dataInfo: {
@@ -124,8 +126,9 @@
type: Boolean
}
});
const audioStore = useAudioStore();
const { dataInfo } = toRefs(props);
const innerAudio = useAudio();
const status = computed(() => props.status);
watch(
() => props.status,
@@ -142,7 +145,7 @@
return props.text || props.dataInfo.anserResult || '';
});
const isPlaying = ref(false);
const isPlaying = innerAudio.isPlaying;
const textLoading = ref(false);
const textShow = ref(props.defaultDisplayText); // 翻译文字展示状态
const voiceTime = ref('');
@@ -170,57 +173,54 @@
textShow.value = false;
}
};
const initVoice = () => {
innerAudioContext.onEnded(() => {
innerAudioContext.src = '';
});
innerAudioContext.onError(() => {
innerAudioContext.src = '';
});
};
const playVoice = () => {
// isPlaying.value = true;
console.log('点击播放');
audioStore.playAudio();
innerAudio.playAudio();
};
let unsubscribe = () => {};
onMounted(() => {
// 2. 监听 Store 中 isPlaying 的变化,实时同步
unsubscribe = audioStore.$subscribe((mutation, state) => {
isPlaying.value = state.isPlaying;
});
// unsubscribe = innerAudio.$subscribe((mutation, state) => {
// isPlaying.value = state.isPlaying;
// });
// 注册播放完成回调
audioStore.onAudioEnded(() => {
innerAudio.onAudioEnded(() => {
console.log('播放完成(通过回调)');
setTimeout(() => {
isPlaying.value = false;
}, 60);
});
// 注册播放错误回调
audioStore.onAudioError((error) => {
innerAudio.onAudioError((error) => {
console.error('播放错误(通过回调):', error);
});
// 声音加载完毕
audioStore.onAudioLoaded((e) => {
innerAudio.onAudioLoaded((e) => {
console.log('声音加载完毕', e.duration);
// 计算原始时长
const originalTime = Math.ceil(e.duration) || 1;
// 判断是否在118-123秒范围内
const displayTime = originalTime >= 118 && originalTime <= 124 ? 120 : originalTime;
// 赋值显示
console.log('displayTime', displayTime);
voiceTime.value = displayTime + 's';
console.log('displayTime', voiceTime.value);
});
watch(
() => props.dataInfo.voicePath,
(newVal) => {
console.log('newVal', newVal);
if(newVal === '' && props.dataInfo.ossKey) { // 去下载oss内容
console.log('去下载oss内容');
downloadFile(props.dataInfo.ossKey).then(res => {
audioStore.setAudioSrc(res.tempFilePath);
innerAudio.setAudioSrc(res.tempFilePath);
})
} else if(newVal === undefined){
innerAudio.stopAudio();
} else {
audioStore.setAudioSrc(newVal);
innerAudio.setAudioSrc(newVal);
}
},
{ deep: true, immediate: true }
@@ -228,9 +228,9 @@
});
onUnload(() => {
// 1. 停止当前播放(优先处理,避免音频继续播放)
audioStore.stopAudio();
innerAudio.stopAudio();
// 2. 移除当前组件注册的回调(避免内存泄漏)
audioStore.removeCallbacks();
innerAudio.removeCallbacks();
unsubscribe();
});
+3 -2
View File
@@ -78,7 +78,7 @@
<view
class="analysis_div"
v-if="!clearResult && ['study', 'mistake', 'settlement', 'practice'].includes(props.mode)"
v-if="!clearResult && ['study', 'mistake', 'settlement', 'practice', 'practice_record'].includes(props.mode)"
>
<view class="analysis_title" v-if="qnsTyp !== 'ES'">
<view class="success_answer">正确答案{{ correctResultLabel.join('') }}</view>
@@ -141,7 +141,8 @@
// 错题本: mistake
// 学习: study
// 考试结算页 settlement
return ['examination', 'practice', 'mistake', 'study', 'settlement'].includes(value);
// 练习记录页 practice_record
return ['examination', 'practice', 'mistake', 'study', 'settlement', 'practice_record'].includes(value);
}
},
// 是否预览模式
+220
View File
@@ -0,0 +1,220 @@
import {
computed,
reactive,
nextTick,
ref
} from 'vue';
// const innerAudioContext = uni.createInnerAudioContext();
// // 当前播放状态
const audioStopFun = ref(null)
export const useAudio = () => {
const audioInstance = uni.createInnerAudioContext();
const callbacks = reactive({
onEnded: null, // 播放完成回调
onError: null, // 播放错误回调
onLoaded: null, // 音频加载完成回调(可获取时长)
onStop: null
})
const isPlaying = ref(false)
// 加载状态
const isLoading = ref(false)
// 当前播放的音频地址
const currentSrc = ref('')
const errorMsg = ref('')
// 初始化音频实例(确保全局唯一)
const initAudio = () => {
bindAudioEvents();
}
const bindAudioEvents = () => {
if (!audioInstance) return;
// 播放开始事件
audioInstance.onPlay(() => {
isPlaying.value = true;
isLoading.value = false;
errorMsg.value = '';
});
audioInstance.onStop(() => {
console.log('音频停止事件');
isPlaying.value = false;
isLoading.value = false;
// 清空地址避免残留
// 触发自定义回调
if (callbacks.onStop && typeof callbacks.onStop === 'function') {
callbacks.onStop();
}
});
// 播放结束事件
audioInstance.onEnded(() => {
console.log('播放结束事件');
// 清空地址避免残留
// if (audioInstance) {
// audioInstance.src = '';
// }
// 触发自定义回调
if (callbacks.onEnded && typeof callbacks.onEnded === 'function') {
callbacks.onEnded();
}
});
// 音频可以播放时触发(此时通常能获取到时长)
audioInstance.onCanplay(() => {
// 尝试获取时长(部分环境需要延迟一点)
// console.log('尝试获取时长(部分环境需要延迟一点)');
isLoading.value = false;
setTimeout(() => {
const duration = audioInstance.duration || 0;
console.log('尝试获duration', duration);
if (duration > 0) {
// 触发加载完成回调(此时已获取时长)
if (callbacks.onLoaded && typeof callbacks.onLoaded === 'function') {
callbacks.onLoaded({
duration: duration
});
}
}
}, 100); // 短暂延迟确保时长已加载
});
// 错误事件
audioInstance.onError((err) => {
isPlaying.value = false;
isLoading.value = false;
console.error('音频错误:', err);
if (callbacks.onError && typeof callbacks.onError === 'function') {
callbacks.onError(err); // 把错误信息传给外部
}
});
}
// 新增:预设置音频地址并加载(此时不播放,仅获取时长)
const setAudioSrc = (src) => {
console.log('预设置音频地址并加载', src);
if (!src || src === currentSrc.value) return; // 地址不变则不重复加载
initAudio();
isLoading.value = true;
currentSrc.value = src;
errorMsg.value = '';
try {
// 先停止当前播放并清空旧地址
// audioInstance.stop();
// audioInstance.src = '';
// 设置新地址并加载(不自动播放)
audioInstance.src = src;
// audioInstance.load(); // 手动触发加载
} catch (err) {
isLoading.value = false;
errorMsg.value = `设置音频失败: ${err.message}`;
}
}
// 修改:播放当前已设置的音频(需先调用setAudioSrc
const playAudio = async (src) => {
console.log('播放当前已设置的音频', currentSrc.value);
if (!currentSrc.value) {
errorMsg.value = '请先设置音频地址';
return;
}
// 已在播放则暂停
console.log('已在播放则暂停', isPlaying.value);
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
if (isPlaying.value) {
pauseAudio();
return;
}
console.log('当前是是否暂停或停止状态', audioStopFun.value?.paused);
if (!audioStopFun.value?.paused) { // 当前是是否暂停或停止状态,true 表示暂停或停止,false 表示正在播放
audioStopFun.value?.stop();
wait(100)
}
// 执行播放
try {
// 哪个函数的实例正在播放,就把他挂在全局
audioStopFun.value = audioInstance
audioInstance.play();
isPlaying.value = true;
isLoading.value = false;
} catch (err) {
isLoading.value = false;
errorMsg.value = `播放失败: ${err.message}`;
}
}
// 暂停播放
const pauseAudio = () => {
if (audioInstance && isPlaying.value) {
console.log('暂停播放');
audioInstance.pause();
isPlaying.value = false;
}
}
// 停止播放并重置状态
const stopAudio = () => {
if (audioInstance && isPlaying.value) {
audioInstance.stop();
// todo
}
}
// 移除所有回调(组件卸载时调用)
const removeCallbacks = () => {
callbacks.onEnded = null;
callbacks.onError = null;
callbacks.onLoaded = null;
callbacks.onStop = null;
}
// 注册播放完成回调
const onAudioEnded = (callback) => {
if (typeof callback === 'function') {
callbacks.onEnded = callback;
}
}
// 注册播放错误回调
const onAudioError = (callback) => {
if (typeof callback === 'function') {
callbacks.onError = callback;
}
}
// 新增:注册音频加载完成回调(用于获取时长)
const onAudioLoaded = (callback) => {
if (typeof callback === 'function') {
callbacks.onLoaded = callback;
}
}
const onAudioStop = (callback) => {
if (typeof callback === 'function') {
callbacks.onStop = callback;
}
}
return {
isPlaying,
playAudio,
pauseAudio,
stopAudio,
setAudioSrc,
removeCallbacks,
onAudioEnded,
onAudioError,
onAudioLoaded,
onAudioStop
}
}