diff --git a/vue-fastapi-frontend/src/main.js b/vue-fastapi-frontend/src/main.js
index a772047..1e38b0d 100644
--- a/vue-fastapi-frontend/src/main.js
+++ b/vue-fastapi-frontend/src/main.js
@@ -52,8 +52,7 @@ const app = createApp(App)
// 全局方法挂载
app.config.globalProperties.useDict = useDict
app.config.globalProperties.download = download
-app.config.globalProperties.download = download
-app.config.globalProperties.formatDateTime = formatDateTime
+app.config.globalProperties.formatDateTime = formatDateTime
app.config.globalProperties.resetForm = resetForm
app.config.globalProperties.handleTree = handleTree
app.config.globalProperties.addDateRange = addDateRange
diff --git a/vue-fastapi-frontend/src/views/aichat/MdRenderer.vue b/vue-fastapi-frontend/src/views/aichat/MdRenderer.vue
index b93546d..c747797 100644
--- a/vue-fastapi-frontend/src/views/aichat/MdRenderer.vue
+++ b/vue-fastapi-frontend/src/views/aichat/MdRenderer.vue
@@ -15,6 +15,11 @@
+
+
+ {{ doc.file_name }}
+
+
@@ -29,7 +34,7 @@ import chatTable from './chatTable.vue'
import htmlCharts from './htmlCharts.vue'
import {Download, FullScreen} from "@element-plus/icons-vue";
import { ref, watch} from 'vue'
-
+const { proxy } = getCurrentInstance();
const props = defineProps({
source: Array,
is_large: Boolean,
@@ -41,6 +46,13 @@ function fullscreenG6(data){
emit('fullscreenG6',data)
}
+function downLoadFile(doc){
+ let data = {file:doc.file_name,bucket: doc.bucket,sessionId: doc.session_id}
+ proxy.download("/default-api/aichat/file/download", {
+ ...data,
+ }, doc.file_name);
+}
+
function downLoadTable(data){
const worksheet = XLSX.utils.json_to_sheet(data);
// 创建一个新的工作簿并将工作表添加到工作簿中
diff --git a/vue-fastapi-frontend/src/views/aichat/aichat.vue b/vue-fastapi-frontend/src/views/aichat/aichat.vue
index d8026ff..976a817 100644
--- a/vue-fastapi-frontend/src/views/aichat/aichat.vue
+++ b/vue-fastapi-frontend/src/views/aichat/aichat.vue
@@ -150,7 +150,7 @@
-
+
import { ref, nextTick, computed, watch, reactive, onMounted } from 'vue'
import { useRoute } from 'vue-router'
-import antvg6 from './antv-g6.vue'
import OperationButton from './OperationButton.vue'
import MdRenderer from '@/views/aichat/MdRenderer.vue'
import fullscreenG6 from '@/views/aichat/fullscreenG6.vue'
@@ -485,79 +484,83 @@ watch(
}
)
const getWrite = (reader) => {
+ // 修复点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, 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) => {