chore:新增AI智能问题是否合并回答/单独回答
This commit is contained in:
@@ -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()
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user