This commit is contained in:
hy
2026-08-03 17:09:22 +08:00
parent 85ecfc4c5b
commit 9965f2772d
2 changed files with 154 additions and 40 deletions

View File

@@ -733,11 +733,61 @@
const isThinking = ref(false)
const isSelfSent = ref(false) // 是否当前设备发起的 AI 对话(用于区分弹窗/仅锁输入)
const programInputLocked = ref(false)
const isUploading = ref(false)
const isLoadingMessages = ref(false) // 消息加载中状态(仅会话变化时显示)
const uploadedFiles = ref([])
const streamingMessageId = ref(null) // 流式消息的临时 ID用于实时更新 AI 回复
const ensureStreamingBubble = () => {
if (streamingMessageId.value) return
streamingMessageId.value = 'streaming-' + Date.now()
allmessages.value = [...allmessages.value, {
role: 'assistant',
content: '',
_streaming: true,
_id: streamingMessageId.value
}]
}
const removeStreamingBubble = () => {
if (!streamingMessageId.value) return
allmessages.value = allmessages.value.filter(m => m._id !== streamingMessageId.value)
streamingMessageId.value = null
}
const beginAiTurn = (selfSent = false) => {
programInputLocked.value = false
isThinking.value = true
socketStore.messageString = ''
socketStore.isThinking = true
isSelfSent.value = selfSent
ensureStreamingBubble()
}
const cleanupAiTurn = async (options = {}) => {
programInputLocked.value = false
isThinking.value = false
isSelfSent.value = false
removeStreamingBubble()
if (options.refresh !== false) {
await takeConversationMessages()
}
}
const finishAiTurn = async (options = {}) => {
await cleanupAiTurn(options)
socketStore.isThinking = false
}
const keepProgramInputLocked = () => {
programInputLocked.value = true
isThinking.value = true
socketStore.isThinking = true
isSelfSent.value = false
ensureStreamingBubble()
}
// 消息详情弹窗
const showMessageModal = ref(false)
const detailLinks = ref([])
@@ -904,6 +954,9 @@
const sendMessage = async () => {
if (isUploading.value) return
if (ChatType.value === 0 && isThinking.value) return
const message = textMessage.value.trim()
if (!message && previewFileArray.value.length === 0) return
@@ -1064,27 +1117,18 @@
"conversation_id": currentSessionId.value,
"content": messageContent
}
socketStore.send(data)
isThinking.value = true
isSelfSent.value = true
beginAiTurn(true)
await socketStore.send(data)
// // 立即添加用户消息到列表(使用完整的 messageContent包含文件信息
// allmessages.value = [...allmessages.value, {
// role: 'user',
// content: messageContent
// }]
// 添加流式消息气泡(加载中状态)
streamingMessageId.value = 'streaming-' + Date.now()
allmessages.value = [...allmessages.value, {
role: 'assistant',
content: '',
_streaming: true,
_id: streamingMessageId.value
}]
textMessage.value = ''
uploadedFiles.value = []
scrollToBottom()
} catch (error) {
isThinking.value = false
await finishAiTurn({ refresh: false })
uni.showToast({
title: `用户发送消息失败${error}`,
icon: 'error'
@@ -1104,16 +1148,7 @@
} catch (err) {
console.error('中断指令发送失败:', err)
}
// 移除流式消息占位
if (streamingMessageId.value) {
allmessages.value = allmessages.value.filter(m => m._id !== streamingMessageId.value)
streamingMessageId.value = null
}
// 立即关闭弹窗,清除超时
// clearTimeout(aiResponseTimer)
isThinking.value = false
socketStore.isThinking = false
isSelfSent.value = false
await finishAiTurn({ refresh: false })
textMessage.value = ''
uploadedFiles.value = []
// takeConversationMessages 由后端中断确认驱动(见 handleBusinessMessage
@@ -1132,28 +1167,14 @@
console.log('AI开始回复跨设备同步锁定输入框');
isThinking.value = true
// 非本设备发起时,也需要添加流式气泡
if (!streamingMessageId.value) {
streamingMessageId.value = 'streaming-' + Date.now()
allmessages.value = [...allmessages.value, {
role: 'assistant',
content: '',
_streaming: true,
_id: streamingMessageId.value
}]
}
ensureStreamingBubble()
return
}
// true → falseAI 回复结束或中断,解锁输入框并刷新消息列表
if (oldVal && !newVal) {
console.log('AI回复结束正常/中断),解锁并刷新消息列表');
// 移除流式消息气泡,从服务器加载完整消息
if (streamingMessageId.value) {
allmessages.value = allmessages.value.filter(m => m._id !== streamingMessageId.value)
streamingMessageId.value = null
}
takeConversationMessages()
isThinking.value = false
isSelfSent.value = false
cleanupAiTurn()
textMessage.value = ''
uploadedFiles.value = []
}
@@ -1832,4 +1853,4 @@
color: #fff;
font-weight: 500;
}
</style>
</style>