diff --git a/zhiyin-app/src/pages/interview/interview.vue b/zhiyin-app/src/pages/interview/interview.vue index 7427d7d..fb95bb1 100644 --- a/zhiyin-app/src/pages/interview/interview.vue +++ b/zhiyin-app/src/pages/interview/interview.vue @@ -146,7 +146,8 @@ const aiAmplitudeData = ref([]) const isSpeaking = ref(false) const dhRef = ref(null) const isRecording = ref(false) - let recorder = null +let recorder = null +let recordingStarted = false // 标记 recorder.start() 是否已实际调用,避免 stopRecord 在 start 前触发 // 录音初始化(一次性创建 recorder 并设置事件) function initRecorder() { @@ -154,15 +155,18 @@ function initRecorder() { if (recorder) return // 已初始化 recorder = uni.getRecorderManager() recorder.onStart(() => { + recordingStarted = true console.log('[Recorder] start recording') }) recorder.onError((err) => { console.error('[Recorder] error:', err) isRecording.value = false + recordingStarted = false uni.showToast({ title: '录音失败,请重试', icon: 'none' }) }) recorder.onStop(async (res) => { isRecording.value = false + recordingStarted = false console.log('[Recorder] stop, tempFilePath:', res.tempFilePath) if (!res.tempFilePath) { uni.showToast({ title: '录音文件为空', icon: 'none' }) @@ -207,7 +211,7 @@ onLoad((options) => { if (options?.position) { const pos = decodeURIComponent(options.position) position.value = pos - messages.value = [{ role: 'ai', content: `你好!我是你的专属 ${pos} 面试官,准备好了就开始吧!` }] + messages.value = [{ role: 'ai', content: `你好!我是你的专属 ${pos} 面试官。准备好后发送任意消息,我会立即开始面试并给出第一个问题。` }] } }) @@ -229,8 +233,7 @@ const loadPositions = async () => { const selectPosition = (pos) => { position.value = pos.name showPositionPicker.value = false - messages.value = [{ role: 'ai', content: `你好!我是你的专属 ${pos.name} 面试官,准备好了就开始吧!` }] - startInterview() + messages.value = [{ role: 'ai', content: `你好!我是你的专属 ${pos.name} 面试官。准备好后发送任意消息,我会立即开始面试并给出第一个问题。` }] } onMounted(() => { @@ -239,8 +242,6 @@ onMounted(() => { if (!position.value) { loadPositions() showPositionPicker.value = true - } else if (token()) { - startInterview() } }) @@ -270,9 +271,13 @@ const startInterview = async () => { }) if (res.statusCode >= 200 && res.statusCode < 300 && res.data) { interviewId.value = res.data.id - messages.value = res.data.messages || messages.value answeredCount.value = res.data.questionCount || 0 if (res.data.totalQuestions) MAX_QUESTIONS = res.data.totalQuestions + // 将后端返回的 AI 消息追加入对话,不替换已有消息(保留用户已发的准备消息) + if (res.data.messages?.length) { + const aiMsgs = res.data.messages.filter(m => m.role === 'ai') + if (aiMsgs.length > 0) messages.value.push(...aiMsgs) + } // Speak first question in avatar mode if (avatarMode.value && res.data.messages?.length) { const last = res.data.messages[res.data.messages.length - 1] @@ -301,13 +306,18 @@ const startInterview = async () => { const sendAnswer = async () => { if (!inputText.value.trim() || aiLoading.value || isComplete.value) return if (!token()) { checkLogin(); return } - // 确保面试已创建(startInterview 是 async,必须 await) + const answer = inputText.value.trim() + + // 首次发送:不把用户消息当答案提交,而是先创建面试获取第一个问题 if (!interviewId.value) { + messages.value.push({ role: 'user', content: answer }) + inputText.value = '' + scrollToBottom() await startInterview() - if (!interviewId.value) return // 创建失败,不丢弃用户输入 + // startInterview 成功后已经把第一个问题追加到 messages 了 + return } - const answer = inputText.value.trim() messages.value.push({ role: 'user', content: answer }) inputText.value = '' scrollToBottom() @@ -402,7 +412,7 @@ function startRecord() { if (aiLoading.value || isComplete.value) return // #ifdef MP-WEIXIN if (isRecording.value) return - // 先标记正在录音,避免 touchend 在 authorize 回调前就触发 stopRecord + recordingStarted = false isRecording.value = true uni.authorize({ scope: 'scope.record', @@ -413,7 +423,6 @@ function startRecord() { uni.showToast({ title: '录音初始化失败', icon: 'none' }) return } - // 确保在授权通过后才开始录音,避免 stopRecord 先于 startRecord 被调用 recorder.start({ format: 'wav', sampleRate: 16000, @@ -442,6 +451,8 @@ function startRecord() { function stopRecord() { if (!isRecording.value) return isRecording.value = false + // 松手太快,recorder.start() 还没实际执行(authorize 异步或录音尚未开始),直接跳过 + if (!recordingStarted) return if (!recorder) return recorder.stop() }