chore:新增AI智能问题是否合并回答/单独回答

This commit is contained in:
2026-09-23 16:25:39 +08:00
parent 158750b933
commit a42ad4ebb3

View File

@@ -703,6 +703,10 @@ export default {
streamInterval: null,
streamContent: '',
isStreaming: false,
// PR-CHAT-PARALLEL-CONFIRM (2026-09-23): AI 正在回答时又发新问题, 用户选"单独处理"
// → 该消息排队, 等本轮流式回答结束后自动发送
// (避免后端把"上一轮未完成的请求"和"新问题"并行/合并处理, 导致答非所问串题)
pendingSendAfterStream: false,
// 本地用户头像(优先使用缓存,无缓存则用默认图)
localUserAvatar: '',
showAvatarSelector: false, // 头像选择弹窗控制
@@ -1376,6 +1380,37 @@ async loadChatHistoryIfExist() {
}
},
// PR-CHAT-PARALLEL-CONFIRM (2026-09-23): AI 还在回答上一个问题时又发新消息的"是否合并"确认
// 背景: AI 是"并行处理"的智能体, 上一轮未完成的任务 + 新问题一起丢给它时,
// 它会自作主张把两件事并行处理(思考流里会出现 "do these in parallel"),
// 结果就是答非所问/串题(例如问"王维怎么玩"却回答上一个公司的信息)。
// 策略: 发出去之前先问用户 —— 合并处理(立即发, 与当前回答并行) / 单独处理(排队, 本轮结束后自动发)
_confirmParallelMerge() {
return new Promise((resolve) => {
uni.showModal({
title: 'AI 正在回答上一个问题',
content: '是否把这条新消息与当前回答合并处理?\n· 合并处理:立即发送,AI 会并行处理两个问题\n· 单独处理:等当前回答结束后自动发送',
confirmText: '合并处理',
cancelText: '单独处理',
success: (res) => resolve(!!res.confirm),
fail: () => resolve(false)
})
})
},
// PR-CHAT-PARALLEL-CONFIRM (2026-09-23): 本轮回答结束后, 自动发送排队中的消息
// 多个结束路径(正常结束/报错/降级)都会调, 用标记位保证只触发一次
_flushQueuedSend() {
if (!this.pendingSendAfterStream) return
this.pendingSendAfterStream = false
// 排队期间用户可能把输入框清空了/删掉了附件 → 没有可发内容就放弃, 不弹空消息
const hasText = this.inputText && this.inputText.trim() !== ''
const hasAttach = (this.pendingImages && this.pendingImages.length) || (this.pendingFiles && this.pendingFiles.length)
if (!hasText && !hasAttach) return
uni.showToast({ title: '正在发送排队中的消息', icon: 'none' })
this.$nextTick(() => { this.sendMessage() })
},
// 发送消息
// PR-FRONTEND-MEDIA-UPLOAD-FIX (2026-09-16): pendingImages 数组版
// 原 pendingImage 单数, 改支持 1-9 张图一起发
@@ -1401,6 +1436,18 @@ async loadChatHistoryIfExist() {
if (!hasContent && !hasEmoji && !allPending.length) return
// PR-CHAT-PARALLEL-CONFIRM (2026-09-23): AI 还在回答时又发新消息 → 先问用户是否合并处理
// 合并: 立即发出(与当前回答并行, 可能出现串题) 单独: 排队, 本轮流式回答结束后自动发送
if (this.isStreaming) {
const mergeNow = await this._confirmParallelMerge()
if (!mergeNow) {
// 输入框内容与待发附件原样保留, 等本轮回答结束由 _flushQueuedSend 自动发出
this.pendingSendAfterStream = true
uni.showToast({ title: '已排队,本轮回答结束后自动发送', icon: 'none' })
return
}
}
// PR-FIX (2026-09-20): 手动发送消息后清掉欢迎气泡上的快捷问题 chips
// 原来只有点击 chip 才摘除, 用户手动打字发消息后 chips 一直挂着,
// 与"引导一次性"的语义不符
@@ -1720,12 +1767,16 @@ async sendStreamMessage(userMessage, attachments = []) {
}
this.$nextTick(() => this.scrollToBottom())
this.$emit('ai-response', msg)
// PR-CHAT-PARALLEL-CONFIRM: 本轮结束 → 若有排队消息, 自动发出
this._flushQueuedSend()
break
}
case 'error':
msg.isStreaming = false
this.isStreaming = false
msg.content = (msg.content || '') + '\n[错误] ' + (data.message || '未知错误')
// PR-CHAT-PARALLEL-CONFIRM: 报错也算本轮结束, 排队消息照常发出
this._flushQueuedSend()
break
case 'attachments':
if (!msg.attachments) this.$set(msg, 'attachments', [])
@@ -1748,6 +1799,8 @@ async sendStreamMessage(userMessage, attachments = []) {
this.isStreaming = false
this.$nextTick(() => this.scrollToBottom())
this.$emit('ai-response', msg)
// PR-CHAT-PARALLEL-CONFIRM: 本轮结束 → 若有排队消息, 自动发出
this._flushQueuedSend()
}
try {
@@ -1777,6 +1830,8 @@ async sendStreamMessage(userMessage, attachments = []) {
}
this.messages.push(errorMsg)
this.$emit('ai-response', errorMsg)
// PR-CHAT-PARALLEL-CONFIRM: 请求失败也算本轮结束, 排队消息照常发出
this._flushQueuedSend()
}
},