miniprogramme/components/voice-recorder/voice-recorder.js
2025-09-12 16:08:17 +08:00

562 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 🎤 语音录制组件逻辑
const voiceMessageManager = require('../../utils/voice-message-manager.js');
Component({
properties: {
// 是否显示录音界面
visible: {
type: Boolean,
value: false,
observer: 'onVisibleChange'
},
// 最大录音时长(毫秒)
maxDuration: {
type: Number,
value: 60000
},
// 最小录音时长(毫秒)
minDuration: {
type: Number,
value: 1000
}
},
data: {
// 录音状态idle, recording, paused, completed, error
recordingState: 'idle',
// 状态文本
statusText: '准备录音',
// 录音时长
recordingDuration: 0,
// 实时波形数据
realtimeWaveform: [],
// 最终波形数据
finalWaveform: [],
// 录音文件信息
tempFilePath: '',
fileSize: 0,
// 播放状态
isPlaying: false,
// 错误信息
errorMessage: '',
// 权限引导
showPermissionGuide: false,
// 定时器
durationTimer: null,
waveformTimer: null
},
lifetimes: {
attached() {
console.log('🎤 语音录制组件加载');
this.initComponent();
},
detached() {
console.log('🎤 语音录制组件卸载');
this.cleanup();
}
},
methods: {
// 事件阻断占位
noop() {},
// 初始化组件
initComponent() {
// 注册语音管理器事件
this.registerVoiceEvents();
// 初始化波形数据
this.initWaveform();
},
// 可见性变化处理
onVisibleChange(visible) {
if (visible) {
this.resetRecorder();
} else {
this.cleanup();
}
},
// 注册语音管理器事件
registerVoiceEvents() {
// 录音开始事件
voiceMessageManager.on('recordStart', () => {
this.setData({
recordingState: 'recording',
statusText: '正在录音...',
recordingDuration: 0
});
this.startDurationTimer();
this.startWaveformAnimation();
});
// 录音停止事件
voiceMessageManager.on('recordStop', (data) => {
this.setData({
recordingState: 'completed',
statusText: '录音完成',
recordingDuration: data.duration,
tempFilePath: data.tempFilePath,
fileSize: data.fileSize
});
this.stopTimers();
this.generateFinalWaveform();
});
// 录音暂停事件
voiceMessageManager.on('recordPause', () => {
this.setData({
recordingState: 'paused',
statusText: '录音已暂停'
});
this.stopTimers();
});
// 录音恢复事件
voiceMessageManager.on('recordResume', () => {
this.setData({
recordingState: 'recording',
statusText: '正在录音...'
});
this.startDurationTimer();
this.startWaveformAnimation();
});
// 录音错误事件
voiceMessageManager.on('recordError', (error) => {
console.error('🎤 录音错误:', error);
this.setData({
recordingState: 'error',
statusText: '录音失败',
errorMessage: this.getErrorMessage(error)
});
this.stopTimers();
});
// 录音帧数据事件
voiceMessageManager.on('recordFrame', (data) => {
this.updateRealtimeWaveform(data.frameBuffer);
});
},
// 🎤 ===== 录音控制 =====
// 开始录音
async startRecording() {
console.log('🎤 开始录音');
try {
await voiceMessageManager.startRecording({
duration: this.properties.maxDuration,
format: 'mp3'
});
} catch (error) {
console.error('🎤 开始录音失败:', error);
if (error.message.includes('权限')) {
this.setData({
showPermissionGuide: true
});
} else {
this.setData({
recordingState: 'error',
statusText: '录音失败',
errorMessage: this.getErrorMessage(error)
});
}
}
},
// 停止录音
stopRecording() {
console.log('🎤 停止录音');
try {
// 检查最小录音时长
if (this.data.recordingDuration < this.properties.minDuration) {
wx.showToast({
title: `录音时长不能少于${this.properties.minDuration / 1000}`,
icon: 'none'
});
return;
}
voiceMessageManager.stopRecording();
} catch (error) {
console.error('🎤 停止录音失败:', error);
this.setData({
recordingState: 'error',
statusText: '停止录音失败',
errorMessage: this.getErrorMessage(error)
});
}
},
// 暂停录音
pauseRecording() {
console.log('🎤 暂停录音');
try {
voiceMessageManager.pauseRecording();
} catch (error) {
console.error('🎤 暂停录音失败:', error);
}
},
// 恢复录音
resumeRecording() {
console.log('🎤 恢复录音');
try {
voiceMessageManager.resumeRecording();
} catch (error) {
console.error('🎤 恢复录音失败:', error);
}
},
// 取消录音
cancelRecording() {
console.log('🎤 取消录音');
try {
voiceMessageManager.cancelRecording();
this.resetRecorder();
} catch (error) {
console.error('🎤 取消录音失败:', error);
}
},
// 丢弃录音
discardRecording() {
console.log('🎤 丢弃录音');
this.resetRecorder();
},
// 🔊 ===== 播放控制 =====
// 播放预览
async playPreview() {
if (!this.data.tempFilePath) {
return;
}
try {
if (this.data.isPlaying) {
voiceMessageManager.stopPlaying();
this.setData({ isPlaying: false });
} else {
await voiceMessageManager.playVoiceMessage(this.data.tempFilePath);
this.setData({ isPlaying: true });
// 监听播放结束
const onPlayEnd = () => {
this.setData({ isPlaying: false });
voiceMessageManager.off('playEnd', onPlayEnd);
};
voiceMessageManager.on('playEnd', onPlayEnd);
}
} catch (error) {
console.error('🎤 播放预览失败:', error);
wx.showToast({
title: '播放失败',
icon: 'none'
});
}
},
// 📤 ===== 发送录音 =====
// 发送录音
async sendRecording() {
if (!this.data.tempFilePath || !this.data.recordingDuration) {
return;
}
console.log('📤 发送录音');
try {
wx.showLoading({
title: '上传中...',
mask: true
});
// 上传语音文件
const uploadResult = await voiceMessageManager.uploadVoiceFile(
this.data.tempFilePath,
this.data.recordingDuration
);
wx.hideLoading();
if (uploadResult.success) {
// 触发发送事件
this.triggerEvent('send', {
type: 'voice',
url: uploadResult.url,
duration: uploadResult.duration,
size: uploadResult.size,
tempFilePath: this.data.tempFilePath
});
// 关闭录音界面
this.closeRecorder();
wx.showToast({
title: '发送成功',
icon: 'success'
});
} else {
throw new Error('上传失败');
}
} catch (error) {
wx.hideLoading();
console.error('📤 发送录音失败:', error);
wx.showToast({
title: '发送失败',
icon: 'none'
});
}
},
// 🎨 ===== 界面控制 =====
// 关闭录音界面
closeRecorder() {
console.log('❌ 关闭录音界面');
// 如果正在录音,先停止
if (this.data.recordingState === 'recording') {
this.cancelRecording();
}
// 如果正在播放,先停止
if (this.data.isPlaying) {
voiceMessageManager.stopPlaying();
}
// 先自隐,再通知父级,提升关闭成功率
this.setData({ visible: false });
this.triggerEvent('close');
},
// 遮罩点击
onOverlayTap() {
// 点击遮罩关闭
this.closeRecorder();
},
// 🔐 ===== 权限处理 =====
// 取消权限申请
cancelPermission() {
this.setData({
showPermissionGuide: false
});
},
// 打开设置页面
openSettings() {
wx.openSetting({
success: (res) => {
if (res.authSetting['scope.record']) {
this.setData({
showPermissionGuide: false
});
wx.showToast({
title: '权限已开启',
icon: 'success'
});
}
}
});
},
// 🔧 ===== 工具方法 =====
// 重置录音器
resetRecorder() {
this.setData({
recordingState: 'idle',
statusText: '准备录音',
recordingDuration: 0,
tempFilePath: '',
fileSize: 0,
isPlaying: false,
errorMessage: '',
showPermissionGuide: false
});
this.stopTimers();
this.initWaveform();
},
// 初始化波形
initWaveform() {
const waveform = Array(20).fill(0).map(() => Math.random() * 30 + 10);
this.setData({
realtimeWaveform: waveform,
finalWaveform: []
});
},
// 开始时长计时器
startDurationTimer() {
this.stopTimers();
this.data.durationTimer = setInterval(() => {
const duration = this.data.recordingDuration + 100;
this.setData({
recordingDuration: duration
});
// 检查最大时长
if (duration >= this.properties.maxDuration) {
this.stopRecording();
}
}, 100);
},
// 开始波形动画
startWaveformAnimation() {
this.data.waveformTimer = setInterval(() => {
const waveform = Array(20).fill(0).map(() => Math.random() * 80 + 20);
this.setData({
realtimeWaveform: waveform
});
}, 150);
},
// 停止定时器
stopTimers() {
if (this.data.durationTimer) {
clearInterval(this.data.durationTimer);
this.data.durationTimer = null;
}
if (this.data.waveformTimer) {
clearInterval(this.data.waveformTimer);
this.data.waveformTimer = null;
}
},
// 更新实时波形
updateRealtimeWaveform(frameBuffer) {
if (!frameBuffer) return;
// 简化的波形数据处理
const waveform = Array(20).fill(0).map(() => Math.random() * 80 + 20);
this.setData({
realtimeWaveform: waveform
});
},
// 生成最终波形
generateFinalWaveform() {
const duration = this.data.recordingDuration;
const barCount = Math.min(Math.max(Math.floor(duration / 200), 15), 40);
const waveform = Array(barCount).fill(0).map(() => Math.random() * 70 + 15);
this.setData({
finalWaveform: waveform
});
},
// 格式化时长
formatDuration(duration) {
if (!duration || duration <= 0) return '00:00';
const totalSeconds = Math.floor(duration / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
},
// 获取文件大小文本
getFileSizeText(fileSize) {
if (!fileSize || fileSize <= 0) return '';
if (fileSize < 1024) {
return `${fileSize}B`;
} else if (fileSize < 1024 * 1024) {
return `${(fileSize / 1024).toFixed(1)}KB`;
} else {
return `${(fileSize / (1024 * 1024)).toFixed(1)}MB`;
}
},
// 获取质量文本
getQualityText(duration) {
if (!duration || duration <= 0) return '';
const seconds = Math.floor(duration / 1000);
if (seconds < 3) return '音质:一般';
if (seconds < 10) return '音质:良好';
return '音质:优秀';
},
// 获取错误消息
getErrorMessage(error) {
if (error.message) {
if (error.message.includes('权限')) {
return '需要录音权限';
} else if (error.message.includes('timeout')) {
return '录音超时';
} else if (error.message.includes('fail')) {
return '录音失败';
}
return error.message;
}
return '未知错误';
},
// 清理资源
cleanup() {
this.stopTimers();
// 如果正在录音,取消录音
if (this.data.recordingState === 'recording') {
voiceMessageManager.cancelRecording();
}
// 如果正在播放,停止播放
if (this.data.isPlaying) {
voiceMessageManager.stopPlaying();
}
// 移除事件监听器
voiceMessageManager.off('recordStart');
voiceMessageManager.off('recordStop');
voiceMessageManager.off('recordPause');
voiceMessageManager.off('recordResume');
voiceMessageManager.off('recordError');
voiceMessageManager.off('recordFrame');
}
}
});