You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

295 lines
8.0 KiB

<template>
<div class="voice-record">
<div class="record-btn" :ref="refNode">
<i></i>
<span>{{ text }}</span>
<div class="mask" :ref="refNode" @touchstart.prevent.stop="handlerTouchstart" @touchmove="handlerTouchmove"
@touchend="handlerTouchend">
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch, getCurrentInstance, onBeforeUnmount } from 'vue'
import { showToast, showLoadingToast, closeToast } from 'vant'
import { openWebSocket } from '@/utils/websocket.js'
import { recorderInit, recorderStart, recorderStop, recorderClose, mergeWav } from '@/utils/record.js'
import '@/assets/images/fill-microphone.png'
const instance = getCurrentInstance()
const store = instance?.appContext?.config?.globalProperties?.$store
const props = defineProps({
content: { type: String, default: '' },
refNode: { type: String, default: '' },
isEdit: { type: Boolean, default: true },
asrData: { type: Object, default: () => ({}) }
})
const emit = defineEmits(['handleUpdateContent', 'isClose', 'isRecorderStart', 'clickFinish'])
const talkList = ref([])
const isRecording = ref(false)
const connectTimes = ref(0)
const connectStatus = ref(null)
const trainWebsocket = ref(null)
const activeCloseFlag = ref(false)
const identifyingTimer = ref(null)
const lastContent = ref('')
const voiceContent = ref('')
const timeOutEvent = ref('')
const mouse = ref({ x: 0, y: 0 })
const text = ref('长按开始录音')
const isShowClose = ref(true)
const recorderBlob = ref(null)
const asrMsg = ref('')
const longTimeNoSay = ref(false)
const touchTimeStart = ref(0)
const touchTimeEnd = ref(0)
const recordRight = ref(true)
const recorderEnded = ref(true)
const oneMinLoading = ref(null)
const oneMinLimit = ref(null)
const sendMsgTimeout = ref(null)
const asrLoading = ref(null)
const asrLoadingState = ref(false)
const asrTransLoading = ref(null)
const sceneType = ref('123')
const practiceId = ref('')
const reconnectFlag = ref(false)
const userId = ref(localStorage.getItem('userId') || '')
const manualService = ref('1')
watch(talkList, (newVal) => {
if (newVal) {
const length = newVal.length
if (newVal[length - 1] !== undefined) {
asrMsg.value += newVal[length - 1]
}
}
}, { deep: true })
watch(connectStatus, (newVal) => {
if (newVal === 'close') {
emit('isClose', false)
}
})
function handleClick() {
if (!props.isEdit) {
showToast('不可编辑,禁止语音识别!')
return
}
const tts = store?.getters?.tts
if (tts && tts._uid === instance?.uid) {
recorderClose.call(instance?.proxy || {})
tts.isShowClose = true
trainWebsocket.value?.onclose?.()
store?.commit?.('set_tts', '')
return
}
if (tts) {
if (tts._uid !== instance?.uid) {
tts.isShowClose = false
}
recorderClose.call(instance?.proxy || {})
tts.trainWebsocket.onclose()
}
lastContent.value = props.content
openWebSocket.call(instance?.proxy || {}, props.asrData, connectTimes.value, sceneType.value)
asrLoading.value = showLoadingToast({ message: '语音连接中...', duration: 0, loadingType: 'spinner', forbidClick: true })
asrLoadingState.value = true
setTimeout(() => {
if (trainWebsocket.value?.readyState !== 1 && asrLoadingState.value) {
closeToast()
asrLoadingState.value = false
recorderClose.call(instance?.proxy || {})
isShowClose.value = true
trainWebsocket.value?.close?.()
store?.commit?.('set_tts', '')
showToast('语音连接失败,请重试')
}
}, 10000)
const time = setTimeout(() => {
recorderInit.call(instance?.proxy || {})
const isRecorderStart = recorderStart.call(instance?.proxy || {})
store?.commit?.('set_tts', instance?.proxy || {})
emit('isRecorderStart', isRecorderStart)
clearTimeout(time)
}, 500)
}
function handlerTouchstart(event) {
asrMsg.value = ''
recorderEnded.value = false
longTimeNoSay.value = false
recordRight.value = true
touchTimeStart.value = new Date().getTime()
talkList.value = []
if (!props.isEdit) {
showToast('不可编辑,禁止语音转文字!')
return
}
const clientX = event.targetTouches[0].clientX
const clientY = event.targetTouches[0].clientY
mouse.value.x = clientX
mouse.value.y = clientY
activeCloseFlag.value = false
if (!trainWebsocket.value) {
openWebSocket.call(instance?.proxy || {}, props.asrData, connectTimes.value, sceneType.value)
} else {
trainWebsocket.value.onclose()
}
asrLoading.value = showLoadingToast({ message: '语音连接中...', duration: 0, loadingType: 'spinner', forbidClick: true })
asrLoadingState.value = true
setTimeout(() => {
if (trainWebsocket.value?.readyState !== 1 && asrLoadingState.value) {
closeToast()
asrLoadingState.value = false
recorderClose.call(instance?.proxy || {})
isShowClose.value = true
trainWebsocket.value?.close?.()
store?.commit?.('set_tts', '')
showToast('语音连接失败,请重试')
}
}, 10000)
const time = setTimeout(() => {
recorderInit.call(instance?.proxy || {})
recorderStart.call(instance?.proxy || {})
store?.commit?.('set_tts', instance?.proxy || {})
clearTimeout(time)
}, 500)
}
function handlerTouchmove(event) {
if (!props.isEdit) return
const clientX = event.targetTouches[0].clientX
const clientY = event.targetTouches[0].clientY
let x = Math.abs(mouse.value.x - clientX)
let y = Math.abs(mouse.value.y - clientY)
if (x > 10 || y > 10) {
cancelTimeoutEvent()
}
}
function handlerTouchend() {
text.value = '长按开始录音'
if (recorderEnded.value) return
touchTimeEnd.value = new Date().getTime()
recorderEnded.value = true
mergeWav.call(instance?.proxy || {})
if (touchTimeEnd.value - touchTimeStart.value < 500) {
trainWebsocket.value?.close?.()
recorderClose.call(instance?.proxy || {})
showToast('录音时间过短,请长按')
recordRight.value = false
return
}
if (!props.isEdit) return
clearTimeout(oneMinLimit.value)
oneMinLoading.value?.close?.()
cancelTimeoutEvent()
if (!longTimeNoSay.value) {
asrTransLoading.value = showLoadingToast({ message: '语音识别中...', duration: 0, loadingType: 'spinner', forbidClick: true })
}
const sec = longTimeNoSay.value ? 10 : 8000
sendMsgTimeout.value = setTimeout(() => {
closeAndSendMsg()
}, sec)
emit('clickFinish', 2)
}
function cancelTimeoutEvent() {
if (timeOutEvent.value) {
clearTimeout(timeOutEvent.value)
timeOutEvent.value = null
}
}
function stop() {
recorderClose.call(instance?.proxy || {})
}
function closeAndSendMsg() {
trainWebsocket.value?.close?.()
recorderStop.call(instance?.proxy || {})
stop()
asrTransLoading.value?.close?.()
if (!asrMsg.value) return
let voiceNo = new Date().getTime()
let voiceContent = { voiceData: recorderBlob.value, id: voiceNo }
store?.commit?.('set_VoiceData', voiceContent)
let sendParmas = {
reqAndResType: '5',
msgText: asrMsg.value,
voiceNo: voiceNo,
msgSource: manualService.value,
voiceData: recorderBlob.value,
}
store?.dispatch?.('addUpLoadVoice', sendParmas).then(() => {
store?.commit?.('set_isHumanChat', true)
}).catch(err => console.log(err))
}
onBeforeUnmount(() => {
if (trainWebsocket.value) {
recorderClose.call(instance?.proxy || {})
isShowClose.value = true
trainWebsocket.value.onclose?.()
store?.commit?.('set_tts', '')
}
})
</script>
<style lang="scss" scoped>
.voice-record {
.record {
border-radius: 8px;
padding: 12px;
background: #f7f8fa;
}
:deep(.van-field__word-limit) {
line-height: 22px;
font-size: 12px;
}
.record-btn {
position: relative;
display: flex;
align-items: center;
font-size: 12px;
color: #7e849f;
i {
width: 22px;
height: 22px;
border-radius: 50%;
background-size: 100%;
margin-right: 8px;
}
span {
font-size: 18px;
}
.mask {
width: 174px;
height: 44px;
position: absolute;
left: 0;
top: 0;
z-index: 1;
background-color: transparent;
}
}
}
</style>