From 430d3fb7f1c588572463ddfbf216de5e7f18f2ff Mon Sep 17 00:00:00 2001 From: zhangyahui Date: Fri, 13 Mar 2026 17:23:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8A=A5=E5=91=8A=E5=88=9D=E6=AD=A5?= =?UTF-8?q?=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/home/agent/components/Agent.vue | 194 +++++++++++++----- src/views/home/agent/components/Item.vue | 31 ++- src/views/home/agent/components/Preview.vue | 63 +++++- .../home/agent/components/ReportCard.vue | 13 +- src/views/home/agent/index.vue | 32 ++- 5 files changed, 256 insertions(+), 77 deletions(-) diff --git a/src/views/home/agent/components/Agent.vue b/src/views/home/agent/components/Agent.vue index c075d81..3ae97ad 100644 --- a/src/views/home/agent/components/Agent.vue +++ b/src/views/home/agent/components/Agent.vue @@ -33,7 +33,10 @@ const agentStore = useAgentStore() const projectStore = useProjectStore() - const emits = defineEmits(['update:sketchList']) + const reportsContent = ref('') + const isGeneratingReport = ref(false) + + const emits = defineEmits(['update:sketchList', 'setTitle']) const props = withDefaults( defineProps<{ title: string @@ -206,6 +209,7 @@ // 流式响应处理 let contentBody = '' let buffer = '' + const webAddressList = [] const reader = response.body?.getReader() if (!reader) throw new Error('无法获取流读取器') @@ -230,18 +234,43 @@ let events = buffer.split(/\n\n/) buffer = events.pop() // 保留不完整块 + let previousEventName = '' // 记录上一个事件名称 + let hasReportStarted = false // 标记 report 是否已经开始 + for (let event of events) { if (!event.trim()) continue - // debugger - // 过滤掉 id: 等字段,只取 data: - let isNodeIdEvent = false - if (event.includes('nodeId')) { - isNodeIdEvent = true - // continue + // 解析事件名称(从 event:xxx 行) + const eventName = + event + .split(/\n/) + .find((line) => line.startsWith('event:')) + ?.replace(/^event:\s*/, '') + ?.trim() || '' + + if (!hasReportStarted && eventName === 'report') { + console.log('开始生成报告--------') + + isGeneratingReport.value = true + contentBody += `123` + hasReportStarted = true } - console.log('event', event) - if (event.includes('error')) { + + if ( + previousEventName === 'report' && + eventName !== 'report' && + reportsContent.value + ) { + isGeneratingReport.value = false + localStorage.setItem('reportsContent', reportsContent.value) + } + + previousEventName = eventName + + // console.log('eventName:', eventName, 'event:', event) + + // 根据事件名称精确判断,而不是用 includes + if (eventName === 'error') { aiMessage.text = '出现错误,请重试' aiMessage.streaming = false aiMessage.loading = false @@ -249,27 +278,29 @@ flag = false break } - // TODO: 暂时不处理webAddress - if (event.includes('todo')) { + if (eventName === 'todo') { break } - let hasSketch = false - if (event.includes('sketchIDAndUrl')) { - hasSketch = true - } + + let isNodeIdEvent = eventName === 'nodeId' + + let hasSketch = eventName === 'sketch' const dataLines = event .split(/\n/) .filter((line) => line.startsWith('data:')) - .map((line) => line.replace(/^data:\s*/, '').trim()) + .map((line) => line.replace(/^data:\s*/, '')) .filter((content) => content.startsWith('{') || content.startsWith('[')) - console.log('dataLInes', dataLines) + // console.log('dataLInes', dataLines) if (isNodeIdEvent) { params.versionID = dataLines[0] projectStore.setProject({ nodeId: dataLines[0] }) } + if (eventName === 'webAddress') { + console.log('webAddress-----', eventName, dataLines) + } - if (event.includes('tool')) { + if (eventName === 'tool') { MyEvent.emit('loading-sketch') } @@ -278,21 +309,38 @@ try { const jsonData = JSON.parse(jsonText) - console.log('jsonData', jsonData) + // console.log('jsonData', jsonData) + if (jsonData.webAddress) { + console.log('webAddress-----', jsonData) + } + if (jsonData.title) { + emits('setTitle', jsonData.title) + } if (hasSketch) { sketchList.value.push({ [Object.keys(jsonData)[0]]: jsonData[Object.keys(jsonData)[0]] }) } - if ( - jsonData.content && - jsonData.content.length > 0 && - jsonData.type !== 'end' - ) { - contentBody += jsonData.content - aiMessage.text = contentBody - aiMessage.loading = false + if (eventName === 'report') { + reportsContent.value += jsonData.content + } else { + if (jsonData.reasoning) { + aiMessage.thinking = true + aiMessage.loading = false + aiMessage.thinkingText += jsonData.reasoning + } else { + aiMessage.thinking = false + if ( + jsonData.content && + jsonData.content.length > 0 && + jsonData.type !== 'end' + ) { + contentBody += jsonData.content + aiMessage.text = contentBody + aiMessage.loading = false + } + } } if (jsonData.type === 'end') { aiMessage.streaming = false @@ -319,6 +367,9 @@ console.warn('⚠️ JSON 格式错误,跳过:', jsonText) } } + + // 更新上一个事件名称 + previousEventName = eventName } } } catch (error) { @@ -378,7 +429,7 @@ } // 处理对话列表,将连续的 assistant 消息合并为一条 - const processDialogue = (dialogue, startIndex, existingImgList) => { + const processDialogue = (dialogue, startIndex, existingImgList, sessionId) => { if (!dialogue || dialogue.length === 0) return [] const result = [] @@ -393,26 +444,30 @@ ...item, text: item.content, isUser: true, - id: result.length + 1 + id: result.length + 1, + sessionId: sessionId }) i++ } else if (item.role === 'assistant') { // assistant 角色,拼接直到下一个 user let combinedContent = item.content || '' - + let combinedThinkingText = item.reasoning || '' // 继续往后找连续的 assistant 消息 let j = i + 1 while (j < dialogue.length && dialogue[j].role === 'assistant') { combinedContent += dialogue[j].content || '' + combinedThinkingText += dialogue[j].reasoning || '' j++ } result.push({ ...item, content: combinedContent, + thinkingText: combinedThinkingText, text: combinedContent, isUser: false, - id: result.length + 1 + id: result.length + 1, + sessionId: sessionId }) i = j @@ -422,7 +477,8 @@ ...item, text: item.content, isUser: item.role === 'user', - id: result.length + 1 + id: result.length + 1, + sessionId: sessionId }) i++ } @@ -430,6 +486,44 @@ return result } + const processSession = (session, imgList, ancestorsList, idCounterRef) => { + if (!session) return + + // 1. 在 dialogue 第一个 report 有值的项的 content 中插入报告卡片 + if (session.dialogue) { + for (let i = 0; i < session.dialogue.length; i++) { + if (session.dialogue[i].report) { + session.dialogue[i].content = + `123` + + (session.dialogue[i].content || '') + break + } + } + } + + // 2. 收集 report 内容并保存到 localStorage + let reportStr = '' + session.dialogue?.forEach((item) => { + if (item.report) { + reportStr += item.report + } + }) + if (reportStr && session.id) { + localStorage.setItem(`reportsContent_${session.id}`, reportStr) + } + + // 3. 收集 sketchIDAndUrl 到 imgList + if (session.sketchIDAndUrl) { + imgList.push(...session.sketchIDAndUrl) + } + + // 4. 处理 dialogue + const list = processDialogue(session.dialogue, 0, imgList, session.id) + list.forEach((el) => { + el.id = idCounterRef.value++ + }) + ancestorsList.push(...list) + } const setChatInfo = (info) => { const initialData = agentStore.getInitialProjectData @@ -454,37 +548,25 @@ messageList.value = [] return } - const { ancestors, current } = data - let imgList = [] - const ancestorsList = [] - let ancestorsIdCounter = 1 - if (ancestors) { - ancestors.forEach((item) => { - if (item.sketchIDAndUrl) { - imgList = imgList.concat(item.sketchIDAndUrl) - } - const list = processDialogue(item.dialogue, 0, imgList) - // 重新设置 id - list.forEach((el) => { - el.id = ancestorsIdCounter++ - }) - ancestorsList.push(...list) - }) - } - const currentList = processDialogue(current?.dialogue, 0, imgList) - if (current.sketchIDAndUrl) { - imgList = imgList.concat(current.sketchIDAndUrl) - } + // 处理单个会话(ancestor 或 current) - currentList.forEach((el, index) => { - el.id = index + 1 + ancestorsList.length + const imgList = [] + const ancestorsList = [] + const idCounterRef = { value: 1 } + + // 处理所有 ancestors + ancestors?.forEach((item) => { + processSession(item, imgList, ancestorsList, idCounterRef) }) + // 处理 current + processSession(current, imgList, ancestorsList, idCounterRef) + // 延迟设置新数据,确保 UI 有时间响应清空操作 nextTick(() => { - messageList.value = [...ancestorsList, ...currentList] + messageList.value = [...ancestorsList] params.versionID = current?.id sketchList.value = imgList }) diff --git a/src/views/home/agent/components/Item.vue b/src/views/home/agent/components/Item.vue index e604da0..fa08495 100644 --- a/src/views/home/agent/components/Item.vue +++ b/src/views/home/agent/components/Item.vue @@ -16,7 +16,7 @@ class="img-item" /> -
+
-