3434 </div >
3535 </div >
3636
37- <!-- 中间对话主页面 -->
37+ <!-- 中间问答主页面 -->
3838 <div class =" main-content" :style =" { width: mainContentWidth + 'px' }" >
39- <ChatMain
40- :messages = " currentBranchMessages "
39+ <QuestionRenderer
40+ :current-question = " currentQuestion "
4141 :is-loading =" isLoading"
42- :streaming-node-id =" streamingNodeId"
4342 @send-message =" handleSendMessage"
43+ @submit-answer =" handleSubmitAnswer"
4444 />
4545 </div >
4646
7474
7575<script setup lang="ts">
7676import { ref , computed , onMounted , onUnmounted } from ' vue'
77- import ChatMain from ' ./ChatMain .vue'
77+ import QuestionRenderer from ' ./QuestionRenderer .vue'
7878import ChatTree from ' ./ChatTree.vue'
7979import MindMapTree from ' ./MindMapTree.vue'
8080import { startConversation , sendMessage , connectSSE , closeSSE , type MessageRequest , type MessageResponse , type ConversationSession } from ' @/services/conversationApi'
@@ -114,10 +114,6 @@ const isResizing = ref(false)
114114const startX = ref (0 )
115115const startRightWidth = ref (0 )
116116
117- // 添加流式消息状态管理
118- const streamingNodeId = ref <string >(' ' )
119- const streamingContent = ref <string >(' ' )
120-
121117// 会话状态
122118const session = ref <ConversationSession | null >(null )
123119const eventSource = ref <EventSource | null >(null )
@@ -129,6 +125,9 @@ const conversationTree = ref<Map<string, ConversationNode>>(new Map())
129125const currentNodeId = ref <string >(' ' )
130126const isLoading = ref (false )
131127
128+ // 问题状态管理
129+ const currentQuestion = ref <any >(null )
130+
132131// 初始化会话
133132const initializeSession = async () => {
134133 if (isInitializing .value ) return
@@ -184,70 +183,42 @@ const initializeSession = async () => {
184183const handleSSEMessage = (response : MessageResponse ) => {
185184 console .log (' 收到SSE消息:' , response )
186185
186+ // 根据消息类型处理
187187 switch (response .type ) {
188188 case ' AI_QUESTION' :
189- case ' AI_ANSWER' :
189+ // 尝试解析问题内容为问题对象
190+ try {
191+ const questionData = JSON .parse (response .content )
192+ if (questionData .type && [' input' , ' single' , ' multi' , ' form' ].includes (questionData .type )) {
193+ currentQuestion .value = questionData
194+ isLoading .value = false
195+ break
196+ }
197+ } catch (e ) {
198+ console .log (' 非JSON格式的问题,作为普通消息处理' )
199+ }
200+ // 如果不是问题格式,作为普通消息处理
190201 addAIMessage (response .nodeId , response .content )
191202 break
192- case ' AI_STREAM_START' :
193- // 开始流式响应
194- startStreamingMessage (response .nodeId )
203+ case ' AI_ANSWER' : // 添加对AI_ANSWER类型的处理
204+ addAIMessage (response .nodeId , response .content )
195205 break
196- case ' AI_STREAM_CHUNK' :
197- // 接收流式内容片段
198- appendStreamingContent (response .nodeId , response .content )
206+ case ' AI_SELECTION_QUESTION' :
207+ addAISelectionMessage (response .nodeId , response .content , response .options || [])
199208 break
200- case ' AI_STREAM_END' :
201- // 结束流式响应
202- finishStreamingMessage (response .nodeId )
209+ case ' USER_ANSWER' :
210+ // 用户消息确认,通常不需要特殊处理
211+ break
212+ case ' SYSTEM_INFO' :
213+ toast .info ({
214+ title: ' 系统消息' ,
215+ message: response .content ,
216+ duration: 3000
217+ })
218+ break
219+ default :
220+ console .warn (' 未知的消息类型:' , response .type , response )
203221 break
204- // ... 其他case保持不变
205- }
206- }
207-
208- // 开始流式消息
209- const startStreamingMessage = (nodeId : string ) => {
210- streamingNodeId .value = nodeId
211- streamingContent .value = ' '
212-
213- const aiNode: ConversationNode = {
214- id: nodeId ,
215- content: ' ' ,
216- type: ' assistant' ,
217- timestamp: new Date (),
218- parentId: currentNodeId .value ,
219- children: [],
220- isActive: true
221- }
222-
223- const currentNode = conversationTree .value .get (currentNodeId .value )
224- if (currentNode ) {
225- currentNode .children .push (nodeId )
226- }
227-
228- conversationTree .value .set (nodeId , aiNode )
229- currentNodeId .value = nodeId
230- isLoading .value = false
231- }
232-
233- // 追加流式内容
234- const appendStreamingContent = (nodeId : string , chunk : string ) => {
235- if (streamingNodeId .value === nodeId ) {
236- streamingContent .value += chunk
237-
238- // 更新节点内容
239- const node = conversationTree .value .get (nodeId )
240- if (node ) {
241- node .content = streamingContent .value
242- }
243- }
244- }
245-
246- // 完成流式消息
247- const finishStreamingMessage = (nodeId : string ) => {
248- if (streamingNodeId .value === nodeId ) {
249- streamingNodeId .value = ' '
250- streamingContent .value = ' '
251222 }
252223}
253224
@@ -275,13 +246,7 @@ const handleSSEError = (error: Event) => {
275246}
276247
277248// 添加AI消息到对话树
278- const addAIMessage = (nodeId : string , content : string , isStreaming = false ) => {
279- const existingNode = conversationTree .value .get (nodeId )
280- if (existingNode && isStreaming ) {
281- // 如果是流式更新,只更新内容
282- existingNode .content = content
283- return
284- }
249+ const addAIMessage = (nodeId : string , content : string ) => {
285250 const aiNode: ConversationNode = {
286251 id: nodeId ,
287252 content ,
@@ -429,6 +394,9 @@ const handleSendMessage = async (content: string) => {
429394 return
430395 }
431396
397+ // 重置当前问题状态,进入新的对话
398+ currentQuestion .value = null
399+
432400 const userNodeId = ` user_${Date .now ()} `
433401 const userNode: ConversationNode = {
434402 id: userNodeId ,
@@ -492,6 +460,82 @@ const handleSendMessage = async (content: string) => {
492460 }
493461}
494462
463+ // 处理答案提交
464+ const handleSubmitAnswer = async (answerData : any ) => {
465+ if (! session .value || ! isConnected .value || ! currentQuestion .value ) {
466+ toast .error ({
467+ title: ' 提交失败' ,
468+ message: ' 会话状态异常,请重新开始' ,
469+ duration: 3000
470+ })
471+ return
472+ }
473+
474+ isLoading .value = true
475+
476+ try {
477+ // 构建答案消息
478+ let answerContent = ' '
479+
480+ switch (currentQuestion .value .type ) {
481+ case ' input' :
482+ answerContent = ` 回答:${answerData } `
483+ break
484+ case ' single' :
485+ const selectedOption = currentQuestion .value .options .find ((opt : any ) => opt .id === answerData [0 ])
486+ answerContent = ` 选择:${selectedOption ? selectedOption .label : answerData [0 ]} `
487+ break
488+ case ' multi' :
489+ const selectedOptions = currentQuestion .value .options .filter ((opt : any ) => answerData .includes (opt .id ))
490+ answerContent = ` 选择:${selectedOptions .map ((opt : any ) => opt .label ).join (' 、' )} `
491+ break
492+ case ' form' :
493+ const formAnswers = answerData .map ((item : any ) => {
494+ const field = currentQuestion .value .fields .find ((f : any ) => f .id === item .id )
495+ if (field ) {
496+ if (field .type === ' input' ) {
497+ return ` ${field .question }:${item .value [0 ]} `
498+ } else {
499+ const selectedOpts = field .options ?.filter ((opt : any ) => item .value .includes (opt .id ))
500+ return ` ${field .question }:${selectedOpts ?.map ((opt : any ) => opt .label ).join (' 、' ) || item .value .join (' 、' )} `
501+ }
502+ }
503+ return ` ${item .id }:${item .value .join (' 、' )} `
504+ })
505+ answerContent = ` 表单回答:\n ${formAnswers .join (' \n ' )} `
506+ break
507+ }
508+
509+ // 发送答案到后端
510+ const messageRequest: MessageRequest = {
511+ sessionId: session .value .sessionId ,
512+ content: answerContent ,
513+ type: ' USER_ANSWER'
514+ }
515+
516+ await sendMessage (messageRequest )
517+
518+ // 清除当前问题状态
519+ currentQuestion .value = null
520+
521+ toast .success ({
522+ title: ' 提交成功' ,
523+ message: ' 答案已提交,等待AI回复' ,
524+ duration: 2000
525+ })
526+
527+ } catch (error : any ) {
528+ console .error (' 提交答案失败:' , error )
529+ isLoading .value = false
530+
531+ toast .error ({
532+ title: ' 提交失败' ,
533+ message: error .message || ' 答案提交失败,请重试' ,
534+ duration: 4000
535+ })
536+ }
537+ }
538+
495539const setNodeAndDescendantsInactive = (nodeId : string ) => {
496540 const node = conversationTree .value .get (nodeId )
497541 if (node ) {
0 commit comments