JLBA202505130001_关于吉林银行新建AI智能培训系统的需求_替换新录音组件,练习考试错题本,Pk

This commit is contained in:
田岩
2026-03-05 19:52:34 +08:00
parent 344b07bbf5
commit 6b6bd90642
5 changed files with 231 additions and 36 deletions
-1
View File
@@ -98,7 +98,6 @@ export const vibrateLight = () : void => {
if (IsAndEnv) {
uni.vibrateShort({
success: function () {
console.log('success');
}
});
} else {
+78 -24
View File
@@ -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<void>((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('调用结束语音回答');
+49 -3
View File
@@ -92,7 +92,7 @@
</view>
<view class="fixed-box font_pf" :style="{ marginBottom: popup_input_bottom }">
<view class="touch-btn-div" :class="{ show: showTouchBtn }">
<TouchBtn
<!-- <TouchBtn
class="talk-btns"
ref="touchBtnRef"
@uploadVoice="touchBtnSubmit('voice',$event)"
@@ -101,6 +101,15 @@
loadingText="只能进行一条回答"
:adjustPosition="false"
:isDisabled="false"
/> -->
<touch-btn
class="talk-btns"
ref="touchBtnRef"
@sendLoading="sendLoading"
@submitVoice="submitVoice"
@submitAnswer="submitAnswer"
:isLoading="touchIsDisabled"
loadingText="只能进行一条回答"
/>
</view>
</view>
@@ -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];
+55 -5
View File
@@ -96,7 +96,7 @@
</view>
</view>
<view class="touch-btn-div" :class="{ show: showTouchBtn }">
<TouchBtnOnlyVoice
<!-- <TouchBtnOnlyVoice
v-if="answerMethod === '03'"
class="talk-btns"
ref="touchBtnRef"
@@ -120,6 +120,16 @@
:adjustPosition="false"
:isDisabled="false"
:answerMethod="answerMethod"
/> -->
<touch-btn
class="talk-btns"
ref="touchBtnRef"
@sendLoading="sendLoading"
@submitVoice="submitVoice"
@submitAnswer="submitAnswer"
:isLoading="touchIsDisabled"
loadingText="只能进行一条回答"
:answerMethod="answerMethod"
/>
</view>
</view>
@@ -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];
+49 -3
View File
@@ -62,7 +62,7 @@
</view>
<view class="fixed-box font_pf" :style="{ marginBottom: popup_input_bottom }">
<view class="touch-btn-div" :class="{ show: showTouchBtn }">
<TouchBtn
<!-- <TouchBtn
class="talk-btns"
ref="touchBtnRef"
@uploadVoice="touchBtnSubmit('voice',$event)"
@@ -71,6 +71,15 @@
loadingText="只能进行一条回答"
:adjustPosition="false"
:isDisabled="false"
/> -->
<touch-btn
class="talk-btns"
ref="touchBtnRef"
@sendLoading="sendLoading"
@submitVoice="submitVoice"
@submitAnswer="submitAnswer"
:isLoading="touchIsDisabled"
loadingText="只能进行一条回答"
/>
</view>
</view>
@@ -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];