DELIVERED消息回调函数不能立即删除,需要考虑消息重发

This commit is contained in:
bob
2025-03-20 17:50:12 +08:00
parent c8bfd3e070
commit c7634c1c0a
2 changed files with 184 additions and 1 deletions

View File

@@ -0,0 +1,180 @@
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { Play, Pause } from '@element-plus/icons-vue'
const props = defineProps({
audioUrl: {
type: String,
required: true
}
})
const isPlaying = ref(false)
const currentTime = ref(0)
const duration = ref(0)
const audioContext = ref(null)
const audioBuffer = ref(null)
const analyser = ref(null)
const canvas = ref(null)
const canvasCtx = ref(null)
const animationFrame = ref(null)
const audio = ref(null)
// 格式化时间
const formatTime = (time) => {
const minutes = Math.floor(time / 60)
const seconds = Math.floor(time % 60)
return `${minutes}:${seconds.toString().padStart(2, '0')}`
}
// 初始化音频上下文和分析器
const initAudioContext = async () => {
audioContext.value = new (window.AudioContext || window.webkitAudioContext)()
analyser.value = audioContext.value.createAnalyser()
analyser.value.fftSize = 256
try {
const response = await fetch(props.audioUrl)
const arrayBuffer = await response.arrayBuffer()
audioBuffer.value = await audioContext.value.decodeAudioData(arrayBuffer)
duration.value = audioBuffer.value.duration
} catch (error) {
console.error('音频加载失败:', error)
}
}
// 绘制音频波形
const drawWaveform = () => {
if (!isPlaying.value || !canvas.value || !analyser.value) return
const bufferLength = analyser.value.frequencyBinCount
const dataArray = new Uint8Array(bufferLength)
analyser.value.getByteFrequencyData(dataArray)
canvasCtx.value.fillStyle = '#f5f5f5'
canvasCtx.value.fillRect(0, 0, canvas.value.width, canvas.value.height)
const barWidth = (canvas.value.width / bufferLength) * 2.5
let barHeight
let x = 0
for (let i = 0; i < bufferLength; i++) {
barHeight = (dataArray[i] / 255) * canvas.value.height
canvasCtx.value.fillStyle = '#409eff'
canvasCtx.value.fillRect(x, canvas.value.height - barHeight, barWidth, barHeight)
x += barWidth + 1
}
animationFrame.value = requestAnimationFrame(drawWaveform)
}
// 播放/暂停音频
const togglePlay = async () => {
if (!audioContext.value) {
await initAudioContext()
}
if (isPlaying.value) {
audioContext.value.suspend()
cancelAnimationFrame(animationFrame.value)
} else {
audioContext.value.resume()
drawWaveform()
}
isPlaying.value = !isPlaying.value
}
// 更新进度
const updateProgress = (e) => {
if (!audioBuffer.value) return
const time = e.target.value
currentTime.value = time
audio.value.currentTime = time
}
// 组件卸载时清理资源
onUnmounted(() => {
if (audioContext.value) {
audioContext.value.close()
}
if (animationFrame.value) {
cancelAnimationFrame(animationFrame.value)
}
})
onMounted(() => {
initAudioContext()
})
</script>
<template>
<div class="audio-player">
<el-button class="play-button" :icon="isPlaying ? Pause : Play" circle @click="togglePlay" />
<div class="progress-container">
<canvas ref="canvas" class="waveform" width="300" height="40"></canvas>
<el-slider
v-model="currentTime"
:max="duration"
:step="0.1"
@change="updateProgress"
class="progress-slider"
/>
</div>
<span class="time">{{ formatTime(currentTime) }} / {{ formatTime(duration) }}</span>
</div>
</template>
<style scoped>
.audio-player {
display: flex;
align-items: center;
gap: 12px;
padding: 8px;
background-color: #f5f5f5;
border-radius: 4px;
}
.play-button {
flex-shrink: 0;
}
.progress-container {
flex: 1;
position: relative;
height: 40px;
}
.waveform {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.progress-slider {
position: relative;
z-index: 2;
}
.time {
flex-shrink: 0;
font-size: 14px;
color: #606266;
min-width: 100px;
text-align: right;
}
:deep(.el-slider__runway) {
background-color: transparent;
}
:deep(.el-slider__bar) {
background-color: rgba(64, 158, 255, 0.3);
}
:deep(.el-slider__button-wrapper) {
z-index: 3;
}
</style>

View File

@@ -105,7 +105,10 @@ class WsConnect {
},
[MsgType.DELIVERED]: (deliveredMsg) => {
this.msgIdRefillCallback[deliveredMsg.body.seq](deliveredMsg.body.msgId)
delete this.msgIdRefillCallback[deliveredMsg.body.seq]
setTimeout(() => {
// 不能立即删除,因为有可能重发消息还会用到
delete this.msgIdRefillCallback[deliveredMsg.body.seq]
}, 30000)
},
[MsgType.HEART_BEAT]: () => {
if (this.heartBeat.healthPoint > 0) this.heartBeat.healthPoint--