feat: 优化

This commit is contained in:
2026-02-22 21:36:47 +08:00
parent 227dd4f78d
commit ff11f04b43
12 changed files with 364 additions and 134 deletions

View File

@@ -70,8 +70,8 @@
<!-- 加载中 -->
<div v-if="loading" class="message-item message-item--assistant">
<div class="agent-avatar-small msg-avatar">
<RobotOutlined v-if="!agent?.avatar" class="avatar-icon" />
<img v-else :src="agent?.avatar" :alt="agent?.name" />
<img v-if="agent?.avatar" :src="agent?.avatar" :alt="agent?.name" />
<RobotOutlined v-else class="avatar-icon" />
</div>
<div class="message-bubble message-bubble--assistant">
<div class="typing-indicator">
@@ -135,6 +135,7 @@ import {
ThunderboltFilled
} from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
import { sendChatStream } from '@/api/agent'
const props = defineProps({
visible: {
@@ -156,9 +157,16 @@ const loading = ref(false)
const messages = ref([])
const messagesRef = ref(null)
const userAvatar = ref('王')
const conversationId = ref(null) // 会话ID用于连续对话
const abortController = ref(null) // 用于取消请求
// 方法
const handleClose = () => {
// 取消正在进行的请求
if (abortController.value) {
abortController.value.abort()
abortController.value = null
}
emit('update:visible', false)
}
@@ -181,24 +189,74 @@ const handleSend = async () => {
await scrollToBottom()
// 模拟 AI 响应
setTimeout(() => {
const assistantMessage = {
role: 'assistant',
content: generateMockResponse(question),
isPro: modelMode.value === 'pro',
actions: true
}
messages.value.push(assistantMessage)
loading.value = false
nextTick(() => scrollToBottom())
}, 1500)
// 创建 AI 消息占位
const assistantMessage = {
role: 'assistant',
content: '',
isPro: modelMode.value === 'pro',
actions: false
}
messages.value.push(assistantMessage)
emit('send', {
agentId: props.agent?.id,
content: question,
modelMode: modelMode.value
})
// 创建取消控制器
abortController.value = new AbortController()
// 调用流式对话 API
try {
await sendChatStream({
agentId: props.agent?.id,
content: question,
conversationId: conversationId.value,
ctrl: abortController.value,
onMessage: (result) => {
if (result.event === 'message' && result.content) {
// 追加消息内容
assistantMessage.content += result.content
scrollToBottom()
} else if (result.event === 'done') {
// 对话完成
conversationId.value = result.conversationId
assistantMessage.actions = true
} else if (result.event === 'error') {
// 错误处理
message.error(result.errorMessage || '对话出错')
}
},
onError: (error) => {
console.error('发送消息失败:', error)
message.error('发送消息失败,请重试')
// 移除失败的 AI 消息
const lastMsg = messages.value[messages.value.length - 1]
if (lastMsg?.role === 'assistant' && !lastMsg.content) {
messages.value.pop()
}
},
onClose: () => {
loading.value = false
abortController.value = null
nextTick(() => scrollToBottom())
}
})
emit('send', {
agentId: props.agent?.id,
content: question,
modelMode: modelMode.value
})
} catch (error) {
// 用户取消不需要提示
if (error.name !== 'AbortError') {
console.error('发送消息失败:', error)
message.error('发送消息失败,请重试')
// 移除失败的 AI 消息
const lastMsg = messages.value[messages.value.length - 1]
if (lastMsg?.role === 'assistant' && !lastMsg.content) {
messages.value.pop()
}
}
} finally {
loading.value = false
}
}
const handleKeyDown = (e) => {
@@ -213,9 +271,14 @@ const handleCopy = (content) => {
message.success('已复制到剪贴板')
}
const handleRegenerate = (index) => {
// TODO: 重新生成消息
message.info('重新生成中...')
const handleRegenerate = async (index) => {
// 重新生成:移除当前消息,重新发送上一条用户消息
if (index > 0 && messages.value[index - 1]?.role === 'user') {
const userMsg = messages.value[index - 1]
messages.value.splice(index - 1, 2) // 移除用户消息和 AI 回复
inputText.value = userMsg.content
await handleSend()
}
}
const scrollToBottom = async () => {
@@ -225,20 +288,13 @@ const scrollToBottom = async () => {
}
}
const generateMockResponse = (question) => {
const responses = [
'当夜深人静的时候,我们卸下了一天的铠甲,那才是真实的自己。成年人的世界里,连崩溃都要调成静音模式。',
'根据您的需求,我为您生成以下内容:这是一个经过精心设计的文案,结合了情感共鸣和产品卖点。',
'让我帮您分析一下这个问题。首先,我们需要考虑目标受众的需求和痛点...'
]
return responses[Math.floor(Math.random() * responses.length)]
}
// 监听 visible 变化,重置状态
watch(() => props.visible, (newVal) => {
if (newVal) {
messages.value = []
inputText.value = ''
conversationId.value = null // 重置会话ID
abortController.value = null
nextTick(() => scrollToBottom())
}
})