diff --git a/src/components/touch-btn/touch-btn.ts b/src/components/touch-btn/touch-btn.ts index 4344bf47..15e83df1 100644 --- a/src/components/touch-btn/touch-btn.ts +++ b/src/components/touch-btn/touch-btn.ts @@ -98,7 +98,6 @@ export const vibrateLight = () : void => { if (IsAndEnv) { uni.vibrateShort({ success: function () { - console.log('success'); } }); } else { diff --git a/src/components/touch-btn/touch-btn.vue b/src/components/touch-btn/touch-btn.vue index b5998e9f..f2aed84a 100644 --- a/src/components/touch-btn/touch-btn.vue +++ b/src/components/touch-btn/touch-btn.vue @@ -159,6 +159,16 @@ type: Number, default: 500 }, + // 最小录音时长 + minRecordDuration: { + type: Number, + default: 1000 + }, + // 最大录音时长 + maxRecordDuration: { + type: Number, + default: 60000 + }, adjustPosition: { default: false, type: Boolean @@ -213,6 +223,8 @@ const isInputVisible = ref(true); // true=显示录音按钮,false=显示输入框 const recordStartTime = ref(0); // 录音开始时间戳 let pressTimer = null; + // 时长自动关闭定时器 + let durationTimer = null; // 计算属性:判断是否仅语音/仅文字模式(简化模板逻辑) const isVoiceOnlyMode = computed(() => workMode.value === 'voice-only'); const isTextOnlyMode = computed(() => workMode.value === 'text-only'); @@ -241,6 +253,7 @@ // 开启语音 const startRecord = async (obj: { touches: any[] }) => { if (props.isDisabled) return; + if (props.isLoading) return common.msg(props.loadingText); if (pressTimer || isRecording.value) return; if (props.canAnswer) return common.msg(props.canAnswerText); try { @@ -271,7 +284,7 @@ } ws.value = socketTask; wsStatus.value = true; - console.log('WS连接成功'); + console.log('WS连接成功', isRecording.value, ); ws.value.onMessage((res: { data: Uint8Array | ArrayBuffer }) => { try { const { msgType, body } = ProtocolCodec.unpack(res.data); @@ -289,6 +302,10 @@ // 如果不等于init说明已经超时,被超时函数处理完了,这里即使返回也不做处理 if (status.value === 'voice_to_text' && voice_to_text_status.value !== 'init') return; if (status.value === 'send_over') return; + // 时间太短,关掉的时候就处理了,这里不处理 + const recordDurationMs = Date.now() - recordStartTime.value; + if (recordDurationMs <= props.minRecordDuration) return; + status.value = 'send_over'; // 设置成发送完成 if (voiceTimer) clearTimeout(voiceTimer); // 设置识别结果 @@ -320,11 +337,13 @@ console.log('网络出现问题'); }); pressTimer = setTimeout(() => { - if(!isRecording.value) { + if (!isRecording.value) { console.log('定时器被取消还是进入了'); - return + return; } + pressTimer = null; status.value = 'voice_recording'; + console.log('触感震动'); vibrateLight(); // 触感震动 // 重设按下位置 const changedTouche = obj.touches[0]; @@ -333,7 +352,7 @@ // 记录开始时间戳(毫秒) recordStartTime.value = Date.now(); // 弹出录音界面 - popupRef.value.open(); + startRecordingTimer(true) // 启动超长定时器 startAudioRecord({ onFrame: (pcmData: any) => { if (wsStatus.value && ws.value) { @@ -343,6 +362,7 @@ onStart: (res: any) => { console.log('启动', res); recordId.value = res.recordId ?? ''; + popupRef.value.open(); }, onStop: () => { console.log('关闭'); @@ -354,11 +374,12 @@ } } }); - }, 100); + }, 140); }; // 结束录音 const closePopup = () => { + startRecordingTimer(false) isRecording.value = false; popupRef.value.close(); }; @@ -382,6 +403,11 @@ clearTimeout(pressTimer); console.log('取消一个存在的定时器'); pressTimer = null; + isRecording.value = false; + setTimeout(() => { + status.value = 'default' + }, 50); + return; } if (!isRecording.value) return; // 未处于录音状态 if (status.value !== 'voice_recording') { @@ -389,35 +415,38 @@ closePopup(); return; } - + // 核心:计算录音总时长(毫秒转秒,保留1位小数) + const recordDurationMs = Date.now() - recordStartTime.value; + console.log('recordDurationMs', recordDurationMs); // 拖动到转文字按钮上抬手或者仅文字模式的发送也强行算他转文字 const _isOutStatus = workMode.value === 'text-only' && isOutStatus.value === '01' ? '03' : isOutStatus.value; - if (_isOutStatus === '03') { + // 时间太短03也直接关了 + if (_isOutStatus === '03' && !(recordDurationMs <= props.minRecordDuration)) { + console.log('时间不短进来了'); status.value = 'voice_to_text'; //主状态设置为转文字 voice_to_text_status.value = 'init'; if (voiceTimer) clearTimeout(voiceTimer); voiceTimer = setTimeout(() => { voiceTimer = null; + console.log('3号定时器', voice_to_text_status.value, status.value); if (voice_to_text_status.value !== 'init') return; + if (status.value === 'send_over') return; voice_to_text_status.value = 'networkError'; status.value = 'send_over'; // 设置成发送完成 - emit('submitVoice', { ossKey: '', text: '', recordId: recordId.value }); - }, 5000); + }, 4000); } else if (_isOutStatus === '01') { status.value = 'voice_send_ing'; //主状态设置为语音发送中 } - const recordDurationMs = Date.now() - recordStartTime.value; - // 核心:计算录音总时长(毫秒转秒,保留1位小数) - if (recordDurationMs < 540) { + if ((_isOutStatus !== '03' || recordDurationMs <= props.minRecordDuration) && recordDurationMs < 640) { await new Promise((resolve) => { setTimeout(() => { resolve(); - }, 540 - recordDurationMs); + }, 640 - recordDurationMs); }); } - + // 取消的时候自己主动关闭websocket,其他的时候需要等待后端的消息关闭 if (_isOutStatus === '02' || errrStatus === 'networkError') { closeWS(); @@ -431,19 +460,26 @@ } stopAudioRecord(); // 关掉录音 isRecording.value = false; - if (_isOutStatus !== '03' || errrStatus === 'networkError') { + console.log('_isOutStatus', _isOutStatus); + if (_isOutStatus !== '03' || errrStatus === 'networkError' || recordDurationMs <= props.minRecordDuration) { // 转文字时候不关遮罩层,网络错误必须关遮罩层 + console.log('转文字时候不关遮罩层,网络错误必须关遮罩层'); closePopup(); + // 关掉弹窗后的提示 - if (errrStatus === 'networkError') { + if (errrStatus === 'networkError' && _isOutStatus === '01') { status.value = 'send_over'; // 设置成发送完成 - if (_isOutStatus !== '03') { - emit('submitVoice', { ossKey: '', text: '', recordId: recordId.value }); + emit('submitVoice', { ossKey: '', text: '', recordId: recordId.value }); + return; + } else if (recordDurationMs <= props.minRecordDuration) { + console.log('进入这里了', status.value); + if (_isOutStatus !== '02') { + showErrorTip(ErrorCode.ShortRecord); } - // return showErrorTip(ErrorCode.NetworkError); - } else if (_isOutStatus === '01' && recordDurationMs <= 1000) { - status.value = 'send_over'; // 设置成发送完成 - return showErrorTip(ErrorCode.ShortRecord); + setTimeout(() => { + status.value = 'send_over'; // 设置成发送完成 + }, 200); + return; } } if (_isOutStatus === '01' && status.value == 'voice_send_ing') { @@ -451,13 +487,14 @@ // 如果socket长时间没有返回,这里兜底一下 if (voiceTimer) clearTimeout(voiceTimer); voiceTimer = setTimeout(() => { + console.log('1号定时器', voice_to_text_status.value, status.value); voiceTimer = null; if (workMode.value === 'voice-only') { if (status.value === 'send_over') return; status.value = 'send_over'; // 恢复成默认状态 emit('submitVoice', { ossKey: '', text: '', recordId: recordId.value }); } - }, 5000); + }, 4000); } }; @@ -523,7 +560,24 @@ const _height = Math.max(height - 100, 0); pageBoxMarginBottom.value = `${_height}px`; }); - + const startRecordingTimer = (status: boolean) => { + const clearRecordingTimer = () => { + if (durationTimer) { + clearTimeout(durationTimer); + durationTimer = null; // 重置定时器变量,避免内存泄漏 + console.log('录音时长定时器已清除'); + } + }; + clearRecordingTimer(); + if (!status) return; + const maxDuration = props.maxRecordDuration; + durationTimer = setTimeout(() => { + console.log(`录音达到最大时长 ${maxDuration} 秒,自动关闭`); + // 执行关闭录音的逻辑 + stopRecord(); + clearRecordingTimer(); + }, maxDuration); + }; // 外面调用放弃这次 const give_up = () => { console.log('调用结束语音回答'); diff --git a/src/pages/competition/pk.vue b/src/pages/competition/pk.vue index 472cd15d..11791144 100644 --- a/src/pages/competition/pk.vue +++ b/src/pages/competition/pk.vue @@ -92,7 +92,7 @@ - --> + @@ -121,7 +130,7 @@ import Subject from '@/components/examination/subject.vue'; import CharactersSubject from '@/components/examination/characters-subject.vue'; // import TouchBtn from '@/components/examination/touchBtn.vue'; - import TouchBtn from '@/pages/course/components/touchBtn.vue'; + // import TouchBtn from '@/pages/course/components/touchBtn.vue'; import common from '@/common/common'; import { setPageCache, formatSeconds, getPageCache, setupKeyboardHeightListener, formatName } from '@/common/common'; import { commonUploadVoiceFile } from '@/api/common.js'; @@ -326,7 +335,44 @@ }); }); }; - + + // 录音组件提交中转圈 + const sendLoading = () => { + const topic = topicList[questionNumber.value]; + console.log('录音组件提交中转圈', topic.es_status); + if (topic.es_status === '00') return; + topic.es_status = '00'; // 00转圈 + } + + // 新录音组件语音提交 + const submitVoice = ({ossKey,text}) => { + console.log('新录音组件语音提交', ossKey,text); + const topic = topicList[questionNumber.value]; + if(!ossKey) { + topic.es_status = ''; // 取消转圈 + common.msg('系统异常,请联系管理员'); + } else if (text === '') { + topic.es_status = ''; // 取消转圈 + return common.msg('未识别到文字'); + } else { + topic.es_status = '02'; + topic.my_answer = { + anserResult: text, + ossKey: ossKey + }; + } + + } + // 新录音组件文本提交 + const submitAnswer = (answerText) => { + const topic = topicList[questionNumber.value]; + touchBtnRef.value?.clearInputText(); // 清除发送框数据 + topic.es_status = '01'; + topic.my_answer = { + anserResult: answerText // 正确答案文字内容 + }; + } + // 录音组件提交 const touchBtnSubmit = (type, submitObj) => { const topic = topicList[questionNumber.value]; diff --git a/src/pages/examination/index.vue b/src/pages/examination/index.vue index 08983e6b..65955efd 100644 --- a/src/pages/examination/index.vue +++ b/src/pages/examination/index.vue @@ -96,7 +96,7 @@ - --> + @@ -174,9 +184,8 @@ import Feedback from './components/feedback.vue'; import Dialog from './components/dialog.vue'; import Subject from '@/components/examination/subject.vue'; - // import TouchBtn from '@/components/examination/touchBtn.vue'; - import TouchBtn from '@/pages/course/components/touchBtn.vue'; - import TouchBtnOnlyVoice from '@/pages/course/components/touchBtnOnlyVoice.vue'; + // import TouchBtn from '@/pages/course/components/touchBtn.vue'; + // import TouchBtnOnlyVoice from '@/pages/course/components/touchBtnOnlyVoice.vue'; import { ref, reactive, computed, onMounted, nextTick } from 'vue'; import { onLoad, onBackPress } from '@dcloudio/uni-app'; import common from '@/common/common'; @@ -321,7 +330,48 @@ } } }; - + + + + // 录音组件提交中转圈 + const sendLoading = () => { + const topic = topicList[questionNumber.value]; + console.log('录音组件提交中转圈', topic.es_status); + if (topic.es_status === '00') return; + topic.es_status = '00'; // 00转圈 + } + + // 新录音组件语音提交 + const submitVoice = ({ossKey,text}) => { + console.log('新录音组件语音提交', ossKey,text); + const topic = topicList[questionNumber.value]; + if(!ossKey) { + topic.es_status = ''; // 取消转圈 + common.msg('系统异常,请联系管理员'); + } else if (text === '') { + topic.es_status = ''; // 取消转圈 + return common.msg('未识别到文字'); + } else { + topic.es_status = '02'; + topic.my_answer = { + anserResult: text, + ossKey: ossKey + }; + hand_in_paper_cache(); + } + } + // 新录音组件文本提交 + const submitAnswer = (answerText) => { + const topic = topicList[questionNumber.value]; + touchBtnRef.value?.clearInputText(); // 清除发送框数据 + topic.es_status = '01'; + topic.my_answer = { + anserResult: answerText // 正确答案文字内容 + }; + hand_in_paper_cache(); + } + + // 录音按钮提交 const touchBtnSubmit = (type, submitObj) => { const topic = topicList[questionNumber.value]; diff --git a/src/pages/wrongQuestionRecord/correct.vue b/src/pages/wrongQuestionRecord/correct.vue index 999ede61..72398620 100644 --- a/src/pages/wrongQuestionRecord/correct.vue +++ b/src/pages/wrongQuestionRecord/correct.vue @@ -62,7 +62,7 @@ - --> + @@ -88,7 +97,7 @@ import Dialog from '@/pages/examination/components/dialog.vue'; import Subject from '@/components/examination/subject.vue'; // import TouchBtn from '@/components/examination/touchBtn.vue'; - import TouchBtn from '@/pages/course/components/touchBtn.vue'; + // import TouchBtn from '@/pages/course/components/touchBtn.vue'; import common from '@/common/common'; import { getPageCache, setupKeyboardHeightListener } from '@/common/common'; import { commonUploadVoiceFile } from '@/api/common.js'; @@ -285,7 +294,44 @@ return Promise.reject(error); }); }; - + + // 录音组件提交中转圈 + const sendLoading = () => { + const topic = topicList[questionNumber.value]; + console.log('录音组件提交中转圈', topic.es_status); + if (topic.es_status === '00') return; + topic.es_status = '00'; // 00转圈 + } + + // 新录音组件语音提交 + const submitVoice = ({ossKey,text}) => { + console.log('新录音组件语音提交', ossKey,text); + const topic = topicList[questionNumber.value]; + if(!ossKey) { + topic.es_status = ''; // 取消转圈 + common.msg('系统异常,请联系管理员'); + } else if (text === '') { + topic.es_status = ''; // 取消转圈 + return common.msg('未识别到文字'); + } else { + topic.es_status = '02'; + topic.my_answer = { + anserResult: text, + ossKey: ossKey + }; + } + + } + // 新录音组件文本提交 + const submitAnswer = (answerText) => { + const topic = topicList[questionNumber.value]; + touchBtnRef.value?.clearInputText(); // 清除发送框数据 + topic.es_status = '01'; + topic.my_answer = { + anserResult: answerText // 正确答案文字内容 + }; + } + // 录音组件提交 const touchBtnSubmit = (type, submitObj) => { const topic = topicList[questionNumber.value];