Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
水玉婷
ai-wechat
Commits
5d8d6101
Commit
5d8d6101
authored
Mar 19, 2026
by
水玉婷
Browse files
feat:sse消息从长链接改为ask触发,调整数据结构
parent
d9bbddfb
Changes
3
Hide whitespace changes
Inline
Side-by-side
src/views/History.vue
View file @
5d8d6101
...
...
@@ -93,7 +93,7 @@
<!-- 没有更多数据提示 -->
<
div v-if="!hasMore && recentSessions.length > 0" class="no-more-data">
<
span>没有更多
历史记录
了
</span>
<
span>没有更多
数据
了
</span>
</div>
<!-- 空状态 -->
...
...
@@ -140,7 +140,6 @@ import defaultAvatar from '@/assets/logo2.png';
import
menuIcon
from
'
@/assets/sidebar-unfold-line.svg
'
import
newChatIcon
from
'
@/assets/chat-ai-line.svg
'
import
{
PlusOutlined
,
SearchOutlined
,
DeleteOutlined
,
FileSearchOutlined
...
...
src/views/components/AiChat.vue
View file @
5d8d6101
...
...
@@ -14,8 +14,8 @@
<
div class="chat-intro-center" v-if="!props?.dialogSessionId && !hasStartedConversation">
<
div class="intro-content">
<
img :src="defaultAvatar" alt="avatar" class="avatar-image" />
<
h3>嗨,我是
国械小智
</h3>
<
p>
我可以帮您搜索数据、查找流程制度,
<
br/>请把问题交给我吧
</p>
<
h3>嗨,我是
{{
appData
?.
app_name
||
'
国械小智
'
}}
</h3>
<
p>
{{
appData
?.
ai_app_prop
?.
prologue
}}
</p>
</div>
</div>
...
...
@@ -126,14 +126,13 @@
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
import dayjs from 'dayjs';
import { markdownTemplate, isLastBlockMarkdown, getLastMarkdownBlockIndex, mergeMarkdownContent } from './utils/markdownTemplate';
import { SendOutlined,
UserOutlined,
ReloadOutlined, CopyOutlined, LikeOutlined, DislikeOutlined } from '@ant-design/icons-vue';
import { SendOutlined, ReloadOutlined, CopyOutlined, LikeOutlined, DislikeOutlined } from '@ant-design/icons-vue';
import { message as antdMessage } from 'ant-design-vue';
import defaultAvatar from '@/assets/logo.png';
import rightIcon from '@/assets/right.svg'
import thinkIcon from '@/assets/think.svg'
import ChartComponent from './ChartComponent.vue'; // 导入独立的图表组件
import VoiceRecognitionText from './VoiceRecognitionText.vue'; // 导入语音识别组件
import { createSSEService, type SSEData } from './utils/sseService'; // 导入SSE服务
import { createContentTemplateService, type Message } from './utils/contentTemplateService'; // 导入模板服务
// 定义组件属性接口
...
...
@@ -175,6 +174,12 @@ const props = withDefaults(defineProps<Props>(), {
})
});
// 定义SSE数据接口
export interface SSEData {
message: any;
status: number | string;
type: number | string;
}
// 响应式数据
const messageText = ref('');
...
...
@@ -272,37 +277,21 @@ const handleSSEMessage = (data: SSEData, isSimulated: boolean = false) => {
dialogSessionId.value = result.newDialogSessionId;
}
// 在收到status: 29(消息结束)时以及status: -1(错误)时,设置loading为false
if (data.status === 29 || data.status === -1) {
loading.value = false;
}
nextTick(() => {
scrollToBottom();
});
} catch (error) {
console.error('处理SSE消息时出错:', error);
// 错误时也设置loading为false
loading.value = false;
}
};
// 创建SSE服务实例
const sseService = createSSEService({
apiBaseUrl: props.apiBaseUrl,
appCode: props.appCode,
token: props.token,
params: props.params
}, {
onMessage: handleSSEMessage,
onError: (error) => {
console.error('SSE error:', error);
isAIResponding.value = false;
isInThinkingMode.value = false;
closeSSE();
},
onOpen: (event) => {
console.log('SSE连接已建立', event);
},
onReconnect: (newDialogSessionId) => {
console.log('SSE重连成功,新的dialogSessionId:', newDialogSessionId);
dialogSessionId.value = newDialogSessionId;
}
});
// 创建模板服务实例
const templateService = createContentTemplateService();
...
...
@@ -452,13 +441,15 @@ const sendMessage = async (type: MessageType = 'text', params: MessageParams = {
audioDuration: durationTime,
...props.params,
dialogSessionId: dialogSessionId.value,
appId: props.params?.appId,
} : {
question: messageContent,
...props.params,
dialogSessionId: dialogSessionId.value,
appId: props.params?.appId,
};
const response = await fetch(`${import.meta.env.VITE_SSE_PATH}/
ask/app/${props.params?.appId}
`, {
const response = await fetch(`${import.meta.env.VITE_SSE_PATH}/
sse/ask
`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...
...
@@ -469,17 +460,17 @@ const sendMessage = async (type: MessageType = 'text', params: MessageParams = {
body: JSON.stringify(requestData)
});
const data = await response.json();
if (data.code === 0) {
console.log(`发送成功`);
// 检查响应是否为SSE流式响应
if (response.ok && response.headers.get('content-type')?.includes('text/event-stream')) {
// 处理SSE流式响应
await processSSEStreamResponse(response.body);
console.log(`发送成功`)
// 发送成功,更新消息状态为已发送
if (messageData) {
messageData.status = 2;
}
}else if(data.code === -100){
// 处理-100错误码,触发重连
sseService.reconnectSSE(dialogSessionId.value);
}else{
loading.value = false;
// 设置当前消息为失败状态
if (messageData) {
messageData.status = -1;
...
...
@@ -502,7 +493,6 @@ const retrySendMessage = async (messageIndex: number) => {
return;
}
console.log('重发消息:', originalMessage.originalContent, originalMessage.originalMessageType);
// 重置原失败消息状态为正常
originalMessage.status = 0;
...
...
@@ -564,29 +554,51 @@ const handleMessageClick = (message: Message, block: any) => {
}
};
// 重新连接SSE
const reconnectSSE = (newDialogSessionId: string) => {
console.log('开始重连SSE,新的dialogSessionId:', newDialogSessionId);
dialogSessionId.value = newDialogSessionId;
sseService.reconnectSSE(newDialogSessionId);
};
// 关闭SSE连接
const closeSSE = () => {
sseService.closeSSE();
};
// 处理SSE流式响应
const processSSEStreamResponse = async (stream: ReadableStream | null) => {
if (!stream) {
console.warn('SSE流为空');
return;
}
// 初始化SSE连接
const initSSE = () => {
sseService.initSSE(dialogSessionId.value);
const reader = stream.getReader();
const decoder = new TextDecoder();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.slice(6)); // 移除'data: '前缀
// 使用现有的SSE消息处理函数
handleSSEMessage(data);
} catch (e) {
loading.value = false;
console.warn('解析SSE数据失败:');
}
}
}
}
} catch (error) {
loading.value = false;
console.error('处理SSE流失败:', error);
} finally {
reader.releaseLock();
}
};
// 组件卸载时清理所有资源
onBeforeUnmount(() => {
closeSSE();
isAIResponding.value = false;
isInThinkingMode.value = false;
sseService.destroy()
;
currentBlockIndex.value = -1
;
});
// 处理历史记录数据
...
...
@@ -627,6 +639,28 @@ const getChatRecord = async (dialogSessionId: string) => {
}
};
// 获取应用基本信息
const appData = ref<any>({});
const getAppInfo = async () => {
if(!props.params?.appId) {
return;
}
const response = await fetch(`${import.meta.env.VITE_SSE_PATH}/apps/getAppInfoById/${props.params?.appId}`, {
method: 'GET',
headers: {
'token': props.token,
'x-session-id': props.token,
'x-app-code': props.appCode || ''
}
});
const data = await response.json();
if (data.code === 0) {
appData.value = data.data || {};
} else {
console.error('获取应用基本信息失败:', data);
}
};
// 思考框切换
const toggleThinkBox = (messageIndex: number, blockIndex: number) => {
if (messages.value[messageIndex] && messages.value[messageIndex].contentBlocks[blockIndex]) {
...
...
@@ -724,11 +758,6 @@ const scrollToBottom = () => {
// 暴露组件方法给外部使用
defineExpose({
// SSE相关方法
initSSE, // 初始化SSE连接
closeSSE, // 关闭SSE连接
reconnectSSE, // 重新连接SSE
// 消息处理方法
processHistoryData, // 处理历史记录数据
...
...
@@ -747,15 +776,11 @@ defineExpose({
// 生命周期
onMounted(() => {
console.log('组件挂载,初始 dialogSessionId:', props.dialogSessionId);
initSSE();
scrollToBottom();
if (props.dialogSessionId) {
getChatRecord(props.dialogSessionId);
}
});
onBeforeUnmount(() => {
closeSSE();
getAppInfo();
});
// 模拟折线图消息
...
...
src/views/components/style.less
View file @
5d8d6101
...
...
@@ -122,6 +122,7 @@ li {
color: @gray-6;
line-height: 1.5;
margin-bottom: 30px;
word-break: keep-all;
}
.start-chat-btn {
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment