From 2452af761a9bc655ebf07733719b0e4869a9a935 Mon Sep 17 00:00:00 2001 From: xueyinfei <1207092115@qq.com> Date: Tue, 7 Oct 2025 00:24:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A7=E6=A8=A1=E5=9E=8Bbug=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/aichat/aichat.vue | 140 +++++++++--------- 1 file changed, 72 insertions(+), 68 deletions(-) diff --git a/vue-fastapi-frontend/src/views/aichat/aichat.vue b/vue-fastapi-frontend/src/views/aichat/aichat.vue index d8026ff..5f5983e 100644 --- a/vue-fastapi-frontend/src/views/aichat/aichat.vue +++ b/vue-fastapi-frontend/src/views/aichat/aichat.vue @@ -150,7 +150,7 @@ - + { + // 修复点1:将tempResult改为局部变量,避免递归状态污染 let tempResult = ''; - /** - * 处理流式数据 - * @param {done, value} obj - 包含 done 和 value 属性的对象 - */ - const write_stream = ({ done,value }) => { - try { - if (done) { - return + + const write_stream = ({ done, value }) => { + if (done) return; + const decoder = new TextDecoder('utf-8'); + let str = decoder.decode(value, { stream: true }); + console.log(str) + tempResult += str; + + // 修复点2:使用非贪婪匹配确保精确分块 + const split = tempResult.match(/data:.*?}\r\n/g); + + if (split) { + // 修复点3:按顺序处理每个匹配块,避免遗漏 + for (let i = 0; i < split.length; i++) { + const chunkStr = split[i]; + tempResult = tempResult.replace(chunkStr, '', 1); // 修复点4:单次替换避免残留 + + try { + const chunk = JSON.parse(chunkStr.replace('data:', '').trim()); + processChunk(chunk); + } catch (e) { + console.error('解析错误:', e, chunkStr); + } } - const decoder = new TextDecoder('utf-8'); - let str = decoder.decode(value, { stream: true }); - // 解释:数据流返回的 chunk 可能不完整,因此我们需要累积并拆分它们。 - tempResult += str; - console.log(tempResult) - const split = tempResult.match(/data:.*}\r\n/g); - if (split) { - str = split.join(''); - tempResult = tempResult.replace(str, ''); + + // 递归处理剩余数据 + return reader.read().then(write_stream); + } else { + // 无匹配块时继续累积数据 + return reader.read().then(write_stream); + } + }; + + const processChunk = (chunk) => { + const lastMsg = chatList.value[chatList.value.length - 1]; + + // 修复点5:统一处理所有类型的数据块 + if (chunk.docs?.length) { + lastMsg.content.push({ content: chunk.docs[0], type: "docs" }); + } else if (chunk.G6_ER?.length) { + lastMsg.content.push({ content: chunk.G6_ER, type: "G6_ER" }); + } else if (chunk.html_image?.length) { + lastMsg.content.push({ content: chunk.html_image, type: "html_image" }); + } else if (chunk.table?.length) { + lastMsg.content.push({ content: chunk.table, type: "table" }); + } else if (chunk.choices?.length) { + // 修复点6:纯文本处理增加防重复校验 + // const text = chunk.choices[0].delta.content.replace(/\n/g, "\n\n"); + // 智能换行处理 + const content = chunk.choices[0].delta.content; + const isNewParagraph = content.startsWith('\n\n'); + // 仅当需要时添加换行 + const text = isNewParagraph + ? content.replace(/\n{2,}/g, '\n\n') + : content.replace(/\n/g, ' '); + const lastContent = lastMsg.content[lastMsg.content.length - 1]; + console.log(lastContent) + if (lastContent?.type === "text") { + lastContent.content += text; } else { - return reader.read().then(write_stream); - } - // 如果 str 存在且以 "data:" 开头,则处理数据块。 - if (str && str.startsWith('data:')) { - split.forEach((chunkStr) => { - const chunk = JSON.parse(chunkStr.replace('data:', '').trim()); - if (chunk.docs && chunk.docs[0].length > 0){ - //超链接 - chatList.value[chatList.value.length - 1].content.push({"content":chunk.docs[0],"type":"docs"}) - }else if (chunk.G6_ER && chunk.G6_ER.length > 0){ - //说明是ER图 - chatList.value[chatList.value.length - 1].content.push({"content":chunk.G6_ER,"type":"G6_ER"}) - }else if (chunk.html_image && chunk.html_image.length > 0){ - //说明是html的echarts图 - chatList.value[chatList.value.length - 1].content.push({"content":chunk.html_image,"type":"html_image"}) - }else if (chunk.table && chunk.table.length > 0){ - //说明是 表格 - chatList.value[chatList.value.length - 1].content.push({"content":chunk.table,"type":"table"}) - }else { - // 纯文本 - let last_answer = chatList.value[chatList.value.length - 1].content - chunk.choices[0].delta.content = chunk.choices[0].delta.content.replace("\n","\n\n") - if (last_answer.length > 0) { - if(last_answer[last_answer.length - 1].type === 'text'){ - chatList.value[chatList.value.length - 1].content[last_answer.length - 1].content += chunk.choices[0].delta.content - }else{ - chatList.value[chatList.value.length - 1].content.push({"content":chunk.choices[0].delta.content,"type":"text"}) - } - }else{ - chatList.value[chatList.value.length - 1].content.push({"content":chunk.choices[0].delta.content,"type":"text"}) - } - } - nextTick(() => { - // 将滚动条滚动到最下面 - scrollDiv.value.setScrollTop(getMaxHeight()) - }) - if (chunk.isEnd || chunk.is_end){ - chatList.value[chatList.value.length - 1].isEnd = true - chatList.value[chatList.value.length - 1].time = formatDate(new Date()) - nextTick(() => { - // 将滚动条滚动到最下面 - scrollDiv.value.setScrollTop(getMaxHeight()) - }) - return Promise.resolve(); - } - }); + lastMsg.content.push({ content: text, type: "text" }); } - } catch (e) { - return Promise.reject(e); } - return reader.read().then(write_stream); + + // 修复点7:统一处理结束标志 + if (chunk.isEnd || chunk.is_end) { + lastMsg.isEnd = true; + console.log(lastMsg) + lastMsg.time = formatDate(new Date()); + } + + nextTick(() => scrollDiv.value.setScrollTop(getMaxHeight())); }; - return write_stream + + return write_stream; }; const stopChat = (index) => {