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