2024-12-18 15:46:27 +08:00
|
|
|
|
<!-- 语音 -->
|
|
|
|
|
<template>
|
2025-01-08 21:01:16 +08:00
|
|
|
|
<view class="voice_box" @touchstart="sv.touchstartVoice" @touchmove.stop.prevent="sv.touchmoveVoice"
|
|
|
|
|
@touchend="sv.touchendVoice" @touchcancel="sv.touchcancelVoice">
|
2024-12-18 15:46:27 +08:00
|
|
|
|
<text class="voice_text c000">{{ voiceText }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 语音状态显示 -->
|
|
|
|
|
<template v-if="voiceFlg">
|
|
|
|
|
<!-- 录音UI效果 -->
|
|
|
|
|
<view class="record" :class="voiceFlg ? '' : 'hidden'">
|
|
|
|
|
<uni-icons :class="[voiceStop ? 'cancel' : 'ing']" :type="voiceStop ? 'micoff' : 'mic'" size="100" />
|
|
|
|
|
<view class="tis" :class="voiceStop ? 'change' : ''">{{ voiceTis }}</view>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2025-02-25 18:38:24 +08:00
|
|
|
|
import {
|
|
|
|
|
ref,
|
|
|
|
|
defineEmits
|
|
|
|
|
} from 'vue'
|
|
|
|
|
// 腾讯云聊天
|
|
|
|
|
import TencentCloudChat from '@tencentcloud/chat';
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
msg: {
|
|
|
|
|
type: Object
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
const emit = defineEmits(['send'])
|
|
|
|
|
//录音
|
|
|
|
|
// #ifdef APP-PLUS
|
|
|
|
|
const recorderManager = uni.getRecorderManager();
|
|
|
|
|
// #endif
|
|
|
|
|
// 录音时长
|
|
|
|
|
const voiceLength = ref(0);
|
|
|
|
|
// 录音定时器
|
|
|
|
|
const voiceTimer = ref(null);
|
|
|
|
|
// 录音文本
|
|
|
|
|
const voiceText = ref('按住 说话');
|
|
|
|
|
// 录音提示
|
|
|
|
|
const voiceTis = ref('手指上滑 取消发送');
|
|
|
|
|
// 录音图标显示
|
|
|
|
|
const voiceFlg = ref(false);
|
|
|
|
|
// 录音开始Y坐标
|
|
|
|
|
const voicePageY = ref(0);
|
|
|
|
|
// 录音结束
|
|
|
|
|
const voiceStop = ref(false);
|
|
|
|
|
const str = '';
|
|
|
|
|
// 录音相关
|
|
|
|
|
const sv = {
|
|
|
|
|
// 按下触发
|
|
|
|
|
touchstartVoice: (e) => {
|
|
|
|
|
voicePageY.value = (e.changedTouches[0].pageY).toFixed(2);
|
|
|
|
|
recorderManager.start({
|
|
|
|
|
duration: 60000, // 录音的时长,单位 ms,最大值 600000(10 分钟)
|
|
|
|
|
sampleRate: 44100, // 采样率
|
|
|
|
|
numberOfChannels: 1, // 录音通道数
|
|
|
|
|
encodeBitRate: 192000, // 编码码率
|
|
|
|
|
format: "mp3"
|
|
|
|
|
});
|
|
|
|
|
voiceLength.value = 0;
|
|
|
|
|
voiceFlg.value = true
|
|
|
|
|
console.log('recorder start success');
|
|
|
|
|
//录音开始,
|
|
|
|
|
voiceTimer.value = setInterval(() => {
|
|
|
|
|
voiceLength.value += 0.1;
|
|
|
|
|
}, 100);
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
console.log('touchstartVoice', voicePageY.value);
|
|
|
|
|
},
|
|
|
|
|
// 滑动触发
|
|
|
|
|
touchmoveVoice: (e) => {
|
|
|
|
|
// 没有展示UI不触发
|
|
|
|
|
if (!voiceFlg.value) {
|
|
|
|
|
return;
|
2024-12-18 15:46:27 +08:00
|
|
|
|
}
|
2025-02-25 18:38:24 +08:00
|
|
|
|
let numTemp = voicePageY.value - ((e.changedTouches[0].pageY).toFixed(2));
|
|
|
|
|
if (numTemp >= 60) {
|
|
|
|
|
console.log('取消发送');
|
|
|
|
|
voiceStop.value = true
|
|
|
|
|
voiceTis.value = '松开手指 取消发送'
|
|
|
|
|
} else {
|
|
|
|
|
console.log('继续发送');
|
|
|
|
|
voiceStop.value = false
|
|
|
|
|
voiceTis.value = '手指上滑 取消发送'
|
2024-12-18 15:46:27 +08:00
|
|
|
|
}
|
2025-02-25 18:38:24 +08:00
|
|
|
|
},
|
|
|
|
|
// 松开触发
|
|
|
|
|
touchendVoice: () => {
|
|
|
|
|
// 没有展示UI不触发
|
|
|
|
|
if (!voiceFlg.value) {
|
|
|
|
|
return;
|
2024-12-18 15:46:27 +08:00
|
|
|
|
}
|
2025-02-25 18:38:24 +08:00
|
|
|
|
clearInterval(voiceTimer.value);
|
|
|
|
|
voiceText.value = '按住 说话'
|
|
|
|
|
voiceTis.value = "手指上滑 取消发送"
|
|
|
|
|
console.log('touchendVoice');
|
|
|
|
|
sv.stop();
|
|
|
|
|
},
|
|
|
|
|
// 打断触发
|
|
|
|
|
touchcancelVoice: () => {
|
|
|
|
|
clearInterval(voiceTimer.value);
|
|
|
|
|
// 关闭UI
|
|
|
|
|
voiceText.value = '按住 说话'
|
|
|
|
|
voiceTis.value = "手指上滑 取消发送"
|
|
|
|
|
// 不发送语音
|
|
|
|
|
voiceStop.value = true
|
|
|
|
|
console.log('touchcancelVoice');
|
|
|
|
|
sv.stop();
|
|
|
|
|
},
|
|
|
|
|
stop: () => {
|
|
|
|
|
voiceTimer.value = null;
|
|
|
|
|
voiceFlg.value = false
|
|
|
|
|
recorderManager.stop(); // 录音结束
|
|
|
|
|
console.log('录音结束');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
// #ifdef APP-PLUS
|
|
|
|
|
// 监听录音停止事件
|
|
|
|
|
recorderManager.onStop((res) => {
|
|
|
|
|
// 被打断等情况不发送
|
|
|
|
|
if (voiceStop.value) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// 正常情况
|
|
|
|
|
if (voiceStop.value) {
|
|
|
|
|
uni.showToast({
|
|
|
|
|
icon: "none",
|
|
|
|
|
title: "取消发送",
|
|
|
|
|
duration: 2000
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (voiceLength.value < 1) {
|
|
|
|
|
uni.showToast({
|
|
|
|
|
icon: "none",
|
|
|
|
|
title: "语音时长过短",
|
|
|
|
|
duration: 2000
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (voiceLength.value > 60) {
|
|
|
|
|
uni.showToast({
|
|
|
|
|
icon: "none",
|
|
|
|
|
title: "语音时长过长",
|
|
|
|
|
duration: 2000
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
console.log('file', res)
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
let message = uni.$chat.createAudioMessage({
|
|
|
|
|
to: props.msg.id,
|
|
|
|
|
conversationType: props.msg.type,
|
|
|
|
|
payload: {
|
|
|
|
|
file: res
|
|
|
|
|
},
|
|
|
|
|
// 音频上传进度回调
|
|
|
|
|
onProgress: function (event) {
|
|
|
|
|
console.log('file uploading:', event)
|
|
|
|
|
}
|
|
|
|
|
})
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
let obj = {
|
|
|
|
|
query: {
|
|
|
|
|
toUserId: message.to,
|
|
|
|
|
formId: message.from,
|
|
|
|
|
msgType: message.type,
|
|
|
|
|
},
|
|
|
|
|
data: message
|
2024-12-18 15:46:27 +08:00
|
|
|
|
}
|
2025-02-25 18:38:24 +08:00
|
|
|
|
emit('send', obj)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.log('message catch', e)
|
|
|
|
|
}
|
|
|
|
|
//
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
})
|
|
|
|
|
// #endif
|
2024-12-18 15:46:27 +08:00
|
|
|
|
</script>
|
|
|
|
|
<style scoped lang="scss">
|
2025-02-25 18:38:24 +08:00
|
|
|
|
.voice_box {
|
|
|
|
|
padding: 20rpx 0;
|
|
|
|
|
margin: 0 20rpx;
|
|
|
|
|
border-radius: 50rpx;
|
|
|
|
|
background: #fff;
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
.hidden {
|
|
|
|
|
display: none !important;
|
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
.record {
|
|
|
|
|
width: 40vw;
|
|
|
|
|
height: 40vw;
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 55%;
|
|
|
|
|
left: 30%;
|
|
|
|
|
background-color: rgba(0, 0, 0, .6);
|
|
|
|
|
border-radius: 20rpx;
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
.ing {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 30vw;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
// 模拟录音音效动画
|
|
|
|
|
@keyframes volatility {
|
|
|
|
|
0% {
|
|
|
|
|
background-position: 0% 130%;
|
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
20% {
|
|
|
|
|
background-position: 0% 150%;
|
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
30% {
|
|
|
|
|
background-position: 0% 155%;
|
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
40% {
|
|
|
|
|
background-position: 0% 150%;
|
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
50% {
|
|
|
|
|
background-position: 0% 145%;
|
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
70% {
|
|
|
|
|
background-position: 0% 150%;
|
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
80% {
|
|
|
|
|
background-position: 0% 155%;
|
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
90% {
|
|
|
|
|
background-position: 0% 140%;
|
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
100% {
|
|
|
|
|
background-position: 0% 135%;
|
2024-12-18 15:46:27 +08:00
|
|
|
|
}
|
2025-02-25 18:38:24 +08:00
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
background-image: linear-gradient(to bottom, #f09b37, #fff 50%);
|
|
|
|
|
background-size: 100% 200%;
|
|
|
|
|
animation: volatility 1.5s ease-in-out -1.5s infinite alternate;
|
|
|
|
|
-webkit-background-clip: text;
|
|
|
|
|
-webkit-text-fill-color: transparent;
|
|
|
|
|
font-size: 150rpx;
|
|
|
|
|
color: #f09b37;
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
.cancel {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 30vw;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
color: #fff;
|
|
|
|
|
font-size: 150rpx;
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
.tis {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 10vw;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #fff;
|
2024-12-18 15:46:27 +08:00
|
|
|
|
|
2025-02-25 18:38:24 +08:00
|
|
|
|
&.change {
|
|
|
|
|
color: #f09b37;
|
2024-12-18 15:46:27 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-25 18:38:24 +08:00
|
|
|
|
}
|
2024-12-18 15:46:27 +08:00
|
|
|
|
</style>
|