Commit 567853a3 authored by 水玉婷's avatar 水玉婷
Browse files

feat:添加sse断开后消息重发

parent 82acfe7e
......@@ -73,6 +73,15 @@
</div>
</template>
</div>
<!-- 失败状态重发按钮 -->
<div v-if="msg.status === -1" class="message-failed-wrapper">
<div class="failed-indicator">
<span class="failed-text">发送失败</span>
<a-button class="retry-button" @click="retrySendMessage(index)" title="重新发送">
<reload-outlined />
</a-button>
</div>
</div>
<!-- 操作按钮 -->
<!-- <div class="operation-box" v-if="msg.recordId">
<p>
......@@ -110,7 +119,7 @@
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
import dayjs from 'dayjs';
import { markdownTemplate, isLastBlockMarkdown, getLastMarkdownBlockIndex, mergeMarkdownContent } from './utils/markdownTemplate';
import { SendOutlined, UserOutlined, UpOutlined, DownOutlined } from '@ant-design/icons-vue';
import { SendOutlined, UserOutlined, UpOutlined, DownOutlined, ReloadOutlined } from '@ant-design/icons-vue';
import defaultAvatar from '@/assets/logo.png';
import ChartComponent from './ChartComponent.vue'; // 导入独立的图表组件
import VoiceRecognition from './VoiceRecognition.vue'; // 导入语音识别组件
......@@ -379,6 +388,9 @@ const sendMessage = async (type: MessageType = 'text', params: MessageParams = {
totalTokens: 0,
date: dayjs().format('HH:mm'),
contentBlocks: [] as any[],
status: 1, // 发送中状态
originalContent: messageContent, // 保存原始内容用于重发
originalMessageType: type // 保存消息类型用于重发
};
switch (type) {
......@@ -417,22 +429,6 @@ const sendMessage = async (type: MessageType = 'text', params: MessageParams = {
await nextTick();
scrollToBottom();
// 检查SSE连接状态,如果连接断开或连接中则触发重连
const connectionState = sseService.getConnectionState();
console.log('SSE连接状态:', connectionState);
// EventSource readyState: 0=CONNECTING, 1=OPEN, 2=CLOSED
if (connectionState === 2) { // 连接已关闭
sseService.reconnectSSE(dialogSessionId.value);
const simulatedMessage = {
message: '检测到SSE连接错误,触发重连',
status: -1, // 错误状态
type: 0 // 错误类型
};
// 调用handleSSEMessage处理模拟消息
return handleSSEMessage(simulatedMessage, true);
}
try {
// 调用外部传入的消息发送函数
if (props.onMessageSend) {
......@@ -465,15 +461,174 @@ const sendMessage = async (type: MessageType = 'text', params: MessageParams = {
const data = await response.json();
if (data.code === 0) {
console.log(`发送成功`);
// 发送成功,更新消息状态为已发送
if (messageData) {
messageData.status = 2;
}
}else if(data.code === -100){
// 处理-100错误码,触发重连
sseService.reconnectSSE(dialogSessionId.value);
// 设置当前消息为失败状态
if (messageData) {
messageData.status = -1;
}
const simulatedMessage = {
message: '检测到SSE连接错误,触发重连,请重新发送消息',
status: -1, // 错误状态
type: 0 // 错误类型
};
// 调用handleSSEMessage处理模拟消息
return handleSSEMessage(simulatedMessage, true);
}
}
} catch (e) {
loading.value = false;
console.error(`发送失败:`, e);
} finally {
loading.value = false;
}
};
// 重发消息
const retrySendMessage = async (messageIndex: number) => {
const originalMessage = messages.value[messageIndex];
if (!originalMessage || originalMessage.status !== -1) {
console.log('消息不存在或不是失败状态,无需重发');
return;
}
console.log('重发消息:', originalMessage.originalContent, originalMessage.originalMessageType);
try {
// 创建新消息进行重发,保留原失败消息
// const messageContent = '出差标准'
const messageContent = originalMessage.originalContent;
const messageType = originalMessage.originalMessageType;
originalMessage.status = 0; // 重置原失败消息状态为正常
loading.value = true;
isAIResponding.value = false;
isInThinkingMode.value = false;
currentAIResponse.value = null;
// 创建新消息
const newMessageData = {
messageType: 'sent',
avatar: '',
recordId: '',
promptTokens: 0,
completionTokens: 0,
totalTokens: 0,
date: dayjs().format('HH:mm'),
contentBlocks: [] as any[],
status: 1, // 发送中状态
originalContent: messageContent, // 保存原始内容用于重发
originalMessageType: messageType // 保存消息类型用于重发
};
switch (messageType) {
case 'text':
newMessageData.contentBlocks.push({
content: templateService.generateTextTemplate(messageContent),
thinkContent: '',
hasThinkBox: false,
thinkBoxExpanded: false,
});
break;
case 'audio':
newMessageData.contentBlocks.push({
audioData: {
src: messageContent,
durationTime: 0
},
thinkContent: '',
hasThinkBox: false,
thinkBoxExpanded: false,
});
break;
default:
loading.value = false;
return;
}
// 添加新消息到聊天记录
messages.value.push(newMessageData);
await nextTick();
scrollToBottom();
// 调用外部传入的消息发送函数
if (props.onMessageSend) {
const sendContent = messageType === 'audio' ? messageContent : messageContent;
console.log(`调用外部发送函数重发消息`, sendContent);
await props.onMessageSend(sendContent);
// 发送成功后更新新消息状态为已发送,并重置原失败消息状态为正常
newMessageData.status = 2;
} else {
// 默认的API调用逻辑
console.log(`默认API调用逻辑重发消息`, dialogSessionId.value);
const requestData = messageType === 'audio' ? {
questionLocalAudioFilePath: messageContent,
audioDuration: 0,
...props.params,
} : {
question: messageContent,
...props.params,
};
const response = await fetch(`${import.meta.env.VITE_SSE_PATH}/ask/app/${props.params?.appId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'token': props.token,
'x-session-id': props?.token,
'x-app-code': props.appCode || ''
},
body: JSON.stringify(requestData)
});
const data = await response.json();
if (data.code === 0) {
console.log(`重发成功`);
// 发送成功,更新新消息状态为已发送,并重置原失败消息状态为正常
newMessageData.status = 2;
originalMessage.status = 0; // 重置原失败消息状态为正常
} else if (data.code === -100) {
// 处理-100错误码,触发重连
sseService.reconnectSSE(dialogSessionId.value);
// 设置新消息为失败状态
newMessageData.status = -1;
const simulatedMessage = {
message: '检测到SSE连接错误,触发重连,请重新发送消息',
status: -1, // 错误状态
type: 0 // 错误类型
};
// 调用handleSSEMessage处理模拟消息
return handleSSEMessage(simulatedMessage, true);
} else {
// 其他错误码,设置新消息为失败状态
newMessageData.status = -1;
console.error(`重发失败,错误码: ${data.code}`);
}
}
} catch (error) {
console.error('重发消息失败:', error);
// 如果创建了新消息但发送失败,可以将其状态设置为失败
if (newMessageData) {
newMessageData.status = -1;
}
} finally {
loading.value = false;
}
};
// 重新连接SSE
const reconnectSSE = (newDialogSessionId: string) => {
console.log('开始重连SSE,新的dialogSessionId:', newDialogSessionId);
......
......@@ -264,7 +264,6 @@ const renderCellContent = (header: string, value: any, record: any) => {
.message-chart {
width: 100%;
max-width: 100%;
margin: 0 0 16px;
}
.table-title {
......
......@@ -301,6 +301,43 @@ li {
margin-top: 16px;
}
// 失败消息样式
.message-failed-wrapper {
margin-top: 8px;
.failed-indicator {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
font-size: 12px;
color: @error-color;
.failed-text {
color: @error-color;
}
.retry-button {
background: @error-bg;
border: 1px solid @error-border;
color: @error-color;
padding: 4px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
&:hover {
background: @error-color;
color: white;
}
}
}
}
// 错误消息样式
:deep(.message-error) {
line-height: 1.5;
......@@ -915,6 +952,9 @@ li {
color: @gray-9;
background: none;
white-space: initial;
br{
display: contents;
}
// 标题样式
h1,
......
......@@ -48,6 +48,12 @@ export interface Message {
date: string;
customClass?: string;
contentBlocks: MessageBlock[];
// 消息状态:-1=失败,0=正常,1=发送中,2=已发送
status?: number;
// 原始消息内容(用于重发)
originalContent?: string;
// 消息类型(用于重发)
originalMessageType?: 'text' | 'audio';
}
// SSE数据类型定义
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment