|
|
@@ -79,7 +79,37 @@
|
|
|
<template v-if="message.type === 'user'">
|
|
|
<div class="flex-grow-1"></div>
|
|
|
<div class="message-wrapper user-message-wrapper">
|
|
|
- <div class="ask-message">{{ message.parsedContent }}</div>
|
|
|
+ <!-- <div class="ask-message" v-if="message.message_files && message.message_files.length > 0" @click="showPreview()">
|
|
|
+ <div class="img-preview" v-if="askFileType == 'image'">
|
|
|
+ <img :src="askFile.source_url" alt="" />
|
|
|
+ </div>
|
|
|
+ <div class="file-preview" v-else-if="askFileType !== 'image' && askFileType != ''">
|
|
|
+ <div class="file-info"> 📄{{ askFile.name }} (大小:{{ (askFile.size / 1024).toFixed(2) }}KB) </div>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ {{ message.parsedContent }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="ask-message" v-else @click="showPreview()">
|
|
|
+ <div>
|
|
|
+ {{ message.parsedContent }}
|
|
|
+ </div>
|
|
|
+ </div> -->
|
|
|
+ <div class="ask-message">
|
|
|
+ <!-- 渲染当前消息的文件 -->
|
|
|
+ <div v-if="message.message_files && message.message_files.length > 0" @click="showPreview(message.message_files)">
|
|
|
+ <div v-for="(file, fIdx) in message.message_files" :key="fIdx">
|
|
|
+ <div class="img-preview" v-if="file.mime_type?.toLowerCase().includes('image')">
|
|
|
+ <img :src="file.source_url ? file.source_url : file.url" alt="" />
|
|
|
+ </div>
|
|
|
+ <div class="file-preview" v-else>
|
|
|
+ <div class="file-info"> 📄{{ file.name }} (大小:{{ (file.size / 1024).toFixed(2) }}KB) </div></div
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 消息内容 -->
|
|
|
+ <div>{{ message.parsedContent }}</div>
|
|
|
+ </div>
|
|
|
<div class="copy-icon-container">
|
|
|
<CopyOutlined class="copy-icon" @click="copyToClipboard(message.parsedContent)" title="复制消息" />
|
|
|
<EditOutlined class="copy-icon" @click="editAsk(message.parsedContent)" title="重新编辑" />
|
|
|
@@ -99,6 +129,12 @@
|
|
|
<div v-show="message.isShowThink" class="color-gray font-size-12px" v-html="formatMessage(message.parsedContentR1)"></div>
|
|
|
</div>
|
|
|
<div v-if="message.parsedContent" v-html="formatMessage(message.parsedContent)"> </div>
|
|
|
+ <div
|
|
|
+ id="chart-target"
|
|
|
+ class="echarts-container"
|
|
|
+ style="width: 400px; height: 400px"
|
|
|
+ v-if="message.echartsOption && Object.keys(echartsOption).length > 0"
|
|
|
+ ></div>
|
|
|
</div>
|
|
|
<div class="copy-icon-container">
|
|
|
<CopyOutlined class="copy-icon" @click="copyToClipboard(message.parsedContent)" title="复制消息" />
|
|
|
@@ -115,13 +151,22 @@
|
|
|
</div>
|
|
|
<!-- 底部输入区 -->
|
|
|
<div class="input-area">
|
|
|
- <!-- <div class="file-preview" v-if="currentFile">
|
|
|
+ <div class="img-preview" v-if="uploadfileType == 'image'">
|
|
|
+ <img :src="uploadedFiles.source_url" alt="" />
|
|
|
+ </div>
|
|
|
+ <div class="file-preview" v-else-if="uploadedFiles.name && uploadedFiles.size && uploadfileType !== 'image'">
|
|
|
<div class="file-info">
|
|
|
- 📄 已选择文件:{{ currentFile.name }} (大小:{{ (currentFile.size / 1024).toFixed(2) }}KB)
|
|
|
- <button @click="clearFile" class="clear-btn">×</button>
|
|
|
+ 📄{{ uploadedFiles.name }} (大小:{{ (uploadedFiles.size / 1024).toFixed(2) }}KB)
|
|
|
+ <DeleteOutlined @click="deleteFile()"></DeleteOutlined>
|
|
|
</div>
|
|
|
- </div> -->
|
|
|
- <a-textarea v-model:value="inputText" placeholder="请输入你的问题" @keyup.enter="handleSend(inputText)" class="ant-input" auto-size />
|
|
|
+ </div>
|
|
|
+ <a-textarea
|
|
|
+ auto-size
|
|
|
+ v-model:value="inputText"
|
|
|
+ placeholder="请输入你的问题"
|
|
|
+ @keyup.enter="handleSend(inputText, uploadedFiles)"
|
|
|
+ class="ant-input"
|
|
|
+ />
|
|
|
<div class="ctrl-btn">
|
|
|
<div class="input-controls">
|
|
|
<button class="control-btn" :class="{ active: isThinking }" @click="toggleThinking">深度思考</button>
|
|
|
@@ -134,7 +179,7 @@
|
|
|
<SvgIcon name="send-file" />
|
|
|
</template>
|
|
|
</Button>
|
|
|
- <Button class="control-btn2" size="small" @click="handleSend(inputText)">
|
|
|
+ <Button class="control-btn2" size="small" @click="handleSend(inputText, uploadedFiles)">
|
|
|
<template #icon>
|
|
|
<SvgIcon name="send" />
|
|
|
</template>
|
|
|
@@ -152,7 +197,6 @@
|
|
|
:before-upload="handleBeforeUpload"
|
|
|
:file-list="fileList"
|
|
|
:remove="handleRemove"
|
|
|
- accept=".pdf,.docx,.xlsx,.xls"
|
|
|
>
|
|
|
<a-button class="upload-btn">
|
|
|
<UploadOutlined></UploadOutlined>
|
|
|
@@ -165,18 +209,6 @@
|
|
|
</div>
|
|
|
<div class="doc" v-if="isShowDoc">
|
|
|
<div class="close"> <button class="closeBtn" @click="close">关闭</button></div>
|
|
|
- <!-- 已上传文件列表 -->
|
|
|
- <div class="file-list" v-if="uploadedFiles.length">
|
|
|
- <div class="file-item" v-for="file in uploadedFiles" :key="file.id">
|
|
|
- <div class="file-info">
|
|
|
- <div class="file-name" :title="file.name">{{ file.name }}</div>
|
|
|
- </div>
|
|
|
- <div class="file-actions">
|
|
|
- <button class="btn btn-preview" @click="previewFile(file)"> <i class="fas fa-eye"></i> 预览 </button>
|
|
|
- <button class="btn btn-delete" @click="deleteFile(file.id)"> <i class="fas fa-trash"></i> 删除 </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
<!-- 预览内容 -->
|
|
|
<div class="pre-container">
|
|
|
<!-- PDF预览 -->
|
|
|
@@ -191,6 +223,14 @@
|
|
|
@rendered="handleRendered"
|
|
|
@error="handleError"
|
|
|
/>
|
|
|
+ <img
|
|
|
+ class="img-container"
|
|
|
+ v-else-if="
|
|
|
+ fileType === 'jpg' || fileType === 'jpeg' || fileType === 'png' || fileType === 'gjf' || fileType === 'webp' || fileType === 'svg'
|
|
|
+ "
|
|
|
+ :src="fileUrl"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
<!-- 不支持的文件类型 -->
|
|
|
<div v-else class="unsupported">
|
|
|
<p>不支持预览该文件类型: {{ fileType }}</p>
|
|
|
@@ -208,7 +248,7 @@
|
|
|
import VueOfficePdf from '@vue-office/pdf/lib/v3/vue-office-pdf.mjs';
|
|
|
import VueOfficeDocx from '@vue-office/docx/lib/v3/vue-office-docx.mjs';
|
|
|
import VueOfficeExcel from '@vue-office/excel/lib/v3/vue-office-excel.mjs';
|
|
|
-import { ref, onMounted, nextTick } from 'vue';
|
|
|
+import { ref, onMounted, nextTick, watch, onUnmounted } from 'vue';
|
|
|
import { SvgIcon } from '../Icon';
|
|
|
import { Space, Button, Modal, Input, message } from 'ant-design-vue';
|
|
|
// import AIChat from './index.vue';
|
|
|
@@ -227,6 +267,7 @@ import { createVNode } from 'vue';
|
|
|
import { marked } from 'marked';
|
|
|
import katex from 'katex';
|
|
|
import 'katex/dist/katex.min.css';
|
|
|
+import * as echarts from 'echarts';
|
|
|
const TextArea = Input.TextArea; // 直接导入TextArea组件使用时打包报错
|
|
|
const inputText = ref(''); // 输入框内容
|
|
|
const refreshText = ref(''); //重新生成文本
|
|
|
@@ -257,17 +298,69 @@ interface Message {
|
|
|
parsedContentR1: String; // 解析后的思考过程 HTML
|
|
|
timestamp: number; // 排序依据
|
|
|
isShowThink: boolean; //深度思考展示
|
|
|
+ message_files?: Array<[]>; //文件信息
|
|
|
+ echartsOption?: Object; //echart图表
|
|
|
}
|
|
|
// 定义消息历史数组类型
|
|
|
const messageHistory = ref<Message[]>([]);
|
|
|
const isFold = ref(true); // 是否折叠
|
|
|
const userid = useUserStore().getUserInfo.id as string;
|
|
|
-const filePath = ref(''); // 绑定输入框值
|
|
|
-const uploadedFiles = ref([]);
|
|
|
-const showConfirmBtn = ref(false); // 控制确认按钮显示状态
|
|
|
+const uploadedFiles = ref({}); //接口获取文件信息
|
|
|
+const askFile = ref({});
|
|
|
+const uploadfileType = ref(''); //提问框文件展示
|
|
|
+const askFileType = ref(''); //回答区域文件展示
|
|
|
+const message_files = ref([]);
|
|
|
const fileList = ref([]);
|
|
|
const suggestList = ref([]); //建议列表
|
|
|
const loading = ref(false);
|
|
|
+// const chartRef = ref(null);
|
|
|
+const validEchartsOption = ref(null);
|
|
|
+const optionTextRef = ref(null);
|
|
|
+const echartsOption = ref({});
|
|
|
+let myChart: echarts.EChartsType | null = null;
|
|
|
+const chartDom = ref<HTMLDivElement | null>(null);
|
|
|
+// 初始化图表
|
|
|
+const initChart = () => {
|
|
|
+ const targetNode = document.getElementById('chart-target');
|
|
|
+ if (!targetNode) {
|
|
|
+ console.error('目标插入节点不存在!');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 创建 ECharts 容器(带唯一 id,方便后续清理)
|
|
|
+ const chartContainer = document.createElement('div');
|
|
|
+ chartContainer.id = 'dynamic-echarts-container';
|
|
|
+ // 必须设置宽高(继承目标节点宽高,或自定义)
|
|
|
+ chartContainer.style.width = '100%';
|
|
|
+ chartContainer.style.height = '100%';
|
|
|
+ chartContainer.style.border = '1px solid #eee';
|
|
|
+
|
|
|
+ // 3. 初始化 ECharts 实例到新建容器
|
|
|
+ myChart = echarts.init(chartContainer);
|
|
|
+ myChart.setOption(echartsOption.value);
|
|
|
+
|
|
|
+ // 4. 将容器插入目标节点(清空原有内容,可选)
|
|
|
+ targetNode.innerHTML = ''; // 清空目标节点原有内容(如需保留可注释)
|
|
|
+ targetNode.appendChild(chartContainer);
|
|
|
+
|
|
|
+ // 5. 监听窗口resize,自适应图表
|
|
|
+ window.addEventListener('resize', handleResize);
|
|
|
+};
|
|
|
+// 图表自适应方法
|
|
|
+const handleResize = () => {
|
|
|
+ myChart?.resize();
|
|
|
+};
|
|
|
+// 监听配置变化,确保DOM更新后初始化
|
|
|
+watch(
|
|
|
+ () => echartsOption.value,
|
|
|
+ async (newOption) => {
|
|
|
+ if (!newOption || Object.keys(newOption).length === 0) return;
|
|
|
+ await nextTick(); // 等待 DOM 渲染
|
|
|
+ initChart();
|
|
|
+ },
|
|
|
+ { deep: true, immediate: true }
|
|
|
+);
|
|
|
+
|
|
|
// 文件预览
|
|
|
const handleRendered = () => {
|
|
|
loading.value = false;
|
|
|
@@ -277,21 +370,15 @@ const handleError = (error) => {
|
|
|
loading.value = false;
|
|
|
console.error('文件预览错误:', error);
|
|
|
};
|
|
|
-function previewFile(data) {
|
|
|
- fileType.value = data.name.split('.').pop().toLowerCase();
|
|
|
- fileUrl.value = data.source_url;
|
|
|
-}
|
|
|
-function deleteFile(fileId) {
|
|
|
- // 确认删除
|
|
|
- if (confirm('确定要删除这个文件吗?')) {
|
|
|
- uploadedFiles.value = uploadedFiles.value.filter((file) => file.id !== fileId);
|
|
|
- }
|
|
|
-}
|
|
|
+// function previewFile(data) {
|
|
|
+// fileType.value = data.extension;
|
|
|
+// fileUrl.value = data.source_url;
|
|
|
+// }
|
|
|
//启用深度思考
|
|
|
const toggleThinking = () => {
|
|
|
isThinking.value = !isThinking.value;
|
|
|
if (isThinking.value) {
|
|
|
- APIKEY.value = 'Bearer app-kprgsFKtySM4Wjxs0ZGzaNFN';
|
|
|
+ APIKEY.value = 'Bearer app-7iLQR9T77YtwDYUYGPk1PFbi';
|
|
|
} else {
|
|
|
APIKEY.value = 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd';
|
|
|
}
|
|
|
@@ -360,7 +447,7 @@ const initMarked = () => {
|
|
|
breaks: false, // 禁用换行符转换
|
|
|
});
|
|
|
};
|
|
|
-// LaTeX 公式渲染(内部使用)
|
|
|
+// LaTeX 公式渲染
|
|
|
const renderLatexInHtml = (html) => {
|
|
|
// 匹配块级公式($$...$$)
|
|
|
const blockRegex = /\$\$(.*?)\$\$/gs;
|
|
|
@@ -397,6 +484,21 @@ const parseMarkdownWithLatex = (mdStr) => {
|
|
|
return mdStr; // 降级显示原始字符串
|
|
|
}
|
|
|
};
|
|
|
+// echart 数据解析
|
|
|
+const parseEchart = (data) => {
|
|
|
+ const reg = /```[\s\S]*?\n([\s\S]*?)```/;
|
|
|
+ const match = data.match(reg);
|
|
|
+
|
|
|
+ if (match && match[1]) {
|
|
|
+ // 提取匹配到的JSON部分并解析
|
|
|
+ echartsOption.value = JSON.parse(match[1].trim());
|
|
|
+ }
|
|
|
+ return match;
|
|
|
+};
|
|
|
+const triggerChart = async () => {
|
|
|
+ await nextTick();
|
|
|
+ initChart();
|
|
|
+};
|
|
|
//重新生成
|
|
|
const refresh = () => {
|
|
|
handleSend(refreshText.value);
|
|
|
@@ -406,11 +508,17 @@ const editAsk = (data) => {
|
|
|
inputText.value = data;
|
|
|
};
|
|
|
//获取消息列表
|
|
|
-async function handleSend(data) {
|
|
|
+async function handleSend(data, files?) {
|
|
|
refreshText.value = data;
|
|
|
inputText.value = '';
|
|
|
+ if (files) {
|
|
|
+ askFileType.value = files?.mime_type?.startsWith('image') ? 'image' : 'document';
|
|
|
+ }
|
|
|
+ // message_files.value.push(files);
|
|
|
+ const fileList = files ? [files] : [];
|
|
|
+ uploadfileType.value = '';
|
|
|
if (isThinking) {
|
|
|
- messageHistory.value.push({
|
|
|
+ const newMessage = {
|
|
|
id: `user_${Date.now()}`,
|
|
|
type: 'user',
|
|
|
content: '',
|
|
|
@@ -419,9 +527,12 @@ async function handleSend(data) {
|
|
|
parsedContentR1: '',
|
|
|
timestamp: Date.now(),
|
|
|
isShowThink: true,
|
|
|
- });
|
|
|
+ message_files: fileList,
|
|
|
+ };
|
|
|
+ messageHistory.value.push(newMessage);
|
|
|
+ message_files.value.push(files);
|
|
|
} else {
|
|
|
- messageHistory.value.push({
|
|
|
+ const newMessage = {
|
|
|
id: `user_${Date.now()}`,
|
|
|
type: 'user',
|
|
|
content: '',
|
|
|
@@ -430,7 +541,10 @@ async function handleSend(data) {
|
|
|
parsedContentR1: '',
|
|
|
timestamp: Date.now(),
|
|
|
isShowThink: false,
|
|
|
- });
|
|
|
+ message_files: fileList,
|
|
|
+ };
|
|
|
+ messageHistory.value.push(newMessage);
|
|
|
+ message_files.value.push(files);
|
|
|
}
|
|
|
try {
|
|
|
const response = await fetch('http://39.97.59.228:8000/v1/chat-messages', {
|
|
|
@@ -442,16 +556,27 @@ async function handleSend(data) {
|
|
|
body: JSON.stringify({
|
|
|
conversation_id: currentSessionID.value ? currentSessionID.value : '',
|
|
|
query: data,
|
|
|
+ ...(askFile.value?.id
|
|
|
+ ? {
|
|
|
+ files: [
|
|
|
+ {
|
|
|
+ type: askFileType.value == 'image' ? 'image' : 'document',
|
|
|
+ transfer_method: 'local_file',
|
|
|
+ upload_file_id: askFile.value.id,
|
|
|
+ url: '',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ }
|
|
|
+ : {}),
|
|
|
response_mode: 'streaming',
|
|
|
user: userid,
|
|
|
inputs: {},
|
|
|
}),
|
|
|
});
|
|
|
-
|
|
|
if (!response.ok) {
|
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
|
}
|
|
|
-
|
|
|
+ uploadedFiles.value = {};
|
|
|
const decoder = new TextDecoder('utf-8');
|
|
|
const reader = response.body.getReader();
|
|
|
let textBuffer = ''; // 使用字符串缓冲区来累积数据
|
|
|
@@ -513,7 +638,6 @@ async function handleSend(data) {
|
|
|
case 'message':
|
|
|
if (data.answer) {
|
|
|
const targetMessage = messageHistory.value.find((msg) => msg.id === currentProcessingMessage.value.id);
|
|
|
-
|
|
|
if (!targetMessage) break;
|
|
|
let currentChunk = data.answer;
|
|
|
|
|
|
@@ -603,6 +727,15 @@ const showModal = (data) => {
|
|
|
const close = () => {
|
|
|
isShowDoc.value = false;
|
|
|
};
|
|
|
+const showPreview = (data) => {
|
|
|
+ fileType.value = data[0].extension ? data[0].extension : data[0].filename.split('.').pop();
|
|
|
+ fileUrl.value = data[0].source_url ? data[0].source_url : data[0].url;
|
|
|
+ isShowDoc.value = true;
|
|
|
+};
|
|
|
+//删除文件
|
|
|
+const deleteFile = () => {
|
|
|
+ uploadedFiles.value = {};
|
|
|
+};
|
|
|
// 上传文件
|
|
|
const handleBeforeUpload = async (file) => {
|
|
|
const formData = new FormData();
|
|
|
@@ -620,11 +753,10 @@ const handleBeforeUpload = async (file) => {
|
|
|
|
|
|
const result = await response.json();
|
|
|
if (response.ok) {
|
|
|
- const linkText = `[${result.name}](${result.source_url})`;
|
|
|
- // inputText.value += `\n${linkText}`;
|
|
|
- uploadedFiles.value.push(result);
|
|
|
- isShowDoc.value = true;
|
|
|
- previewFile(result);
|
|
|
+ uploadedFiles.value = result;
|
|
|
+ askFile.value = result;
|
|
|
+ uploadfileType.value = result.mime_type.startsWith('image') ? 'image' : 'document';
|
|
|
+ // previewFile(result);
|
|
|
} else {
|
|
|
message.error(`上传失败: ${result.message || '网络错误'}`);
|
|
|
}
|
|
|
@@ -663,7 +795,7 @@ async function stopReq() {
|
|
|
}
|
|
|
//获取具体会话记录
|
|
|
async function sessionsHistory(id: string, retryCount = 0) {
|
|
|
- const API_KEYS = ['Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd', 'Bearer app-kprgsFKtySM4Wjxs0ZGzaNFN'];
|
|
|
+ const API_KEYS = ['Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd', 'Bearer app-7iLQR9T77YtwDYUYGPk1PFbi'];
|
|
|
// 最大重试次数
|
|
|
const maxRetries = API_KEYS.length - 1;
|
|
|
try {
|
|
|
@@ -689,6 +821,7 @@ async function sessionsHistory(id: string, retryCount = 0) {
|
|
|
parsedContent: item.query,
|
|
|
contentR1: '',
|
|
|
parsedContentR1: '',
|
|
|
+ message_files: item.message_files,
|
|
|
timestamp: Date.now(),
|
|
|
isShowThink: false,
|
|
|
});
|
|
|
@@ -696,6 +829,7 @@ async function sessionsHistory(id: string, retryCount = 0) {
|
|
|
// 查找<think>
|
|
|
const startIndex = answer.indexOf('<think>');
|
|
|
const endIndex = answer.indexOf('</think>');
|
|
|
+ const isChart = answer.indexOf('echarts');
|
|
|
let content = '';
|
|
|
let contentR1 = '';
|
|
|
// 根据标签判断
|
|
|
@@ -714,17 +848,33 @@ async function sessionsHistory(id: string, retryCount = 0) {
|
|
|
// 没有标签
|
|
|
content = answer;
|
|
|
}
|
|
|
- // 添加到消息历史
|
|
|
- messageHistory.value.push({
|
|
|
- id: `system_${Date.now()}`,
|
|
|
- type: 'system',
|
|
|
- content: '',
|
|
|
- parsedContent: parseMarkdownWithLatex(content.trim()),
|
|
|
- contentR1: '',
|
|
|
- parsedContentR1: parseMarkdownWithLatex(contentR1.trim()),
|
|
|
- timestamp: Date.now(),
|
|
|
- isShowThink: contentR1.length > 0, // 如果有思考内容则显示
|
|
|
- });
|
|
|
+ if (isChart != -1) {
|
|
|
+ // 添加到消息历史
|
|
|
+ messageHistory.value.push({
|
|
|
+ id: `system_${Date.now()}`,
|
|
|
+ type: 'system',
|
|
|
+ content: '',
|
|
|
+ parsedContent: '',
|
|
|
+ contentR1: '',
|
|
|
+ parsedContentR1: parseMarkdownWithLatex(contentR1.trim()),
|
|
|
+ timestamp: Date.now(),
|
|
|
+ isShowThink: contentR1.length > 0, // 如果有思考内容则显示
|
|
|
+ echartsOption: parseEchart(content.trim()),
|
|
|
+ });
|
|
|
+ triggerChart();
|
|
|
+ } else {
|
|
|
+ // 添加到消息历史
|
|
|
+ messageHistory.value.push({
|
|
|
+ id: `system_${Date.now()}`,
|
|
|
+ type: 'system',
|
|
|
+ content: '',
|
|
|
+ parsedContent: parseMarkdownWithLatex(content.trim()),
|
|
|
+ contentR1: '',
|
|
|
+ parsedContentR1: parseMarkdownWithLatex(contentR1.trim()),
|
|
|
+ timestamp: Date.now(),
|
|
|
+ isShowThink: contentR1.length > 0, // 如果有思考内容则显示
|
|
|
+ });
|
|
|
+ }
|
|
|
});
|
|
|
}
|
|
|
} catch (error) {
|
|
|
@@ -742,7 +892,6 @@ async function sessionsHistory(id: string, retryCount = 0) {
|
|
|
//获取下一轮建议问题列表
|
|
|
//停止响应
|
|
|
async function getNextSuggest() {
|
|
|
- console.log(messageID.value, '123');
|
|
|
try {
|
|
|
let response = await fetch(`http://39.97.59.228:8000/v1/messages/${messageID.value}/suggested?user=${userid}`, {
|
|
|
method: 'GET',
|
|
|
@@ -832,45 +981,47 @@ const fold = () => {
|
|
|
async function getHistoryList() {
|
|
|
sessionHistory.value = [];
|
|
|
try {
|
|
|
- // 并行请求两个接口,提升效率
|
|
|
- const [response1, response2] = await Promise.all([
|
|
|
- // 第一个请求
|
|
|
- fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
|
|
|
- method: 'get',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json',
|
|
|
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
- },
|
|
|
- }),
|
|
|
- // 第二个请求
|
|
|
- fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
|
|
|
- method: 'get',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json',
|
|
|
- Authorization: 'Bearer app-kprgsFKtySM4Wjxs0ZGzaNFN',
|
|
|
- },
|
|
|
- }),
|
|
|
- ]);
|
|
|
-
|
|
|
- // 检查响应是否成功
|
|
|
- if (!response1.ok || !response2.ok) {
|
|
|
- throw new Error('接口请求失败');
|
|
|
- }
|
|
|
+ const fetch1 = fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
|
|
|
+ method: 'get',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
+ },
|
|
|
+ })
|
|
|
+ .then((res) => {
|
|
|
+ return res.json();
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.error('第一个请求错误:', err);
|
|
|
+ return { data: [] }; // 失败时返回空数组
|
|
|
+ });
|
|
|
|
|
|
- // 解析两个响应的JSON数据
|
|
|
- const [data1, data2] = await Promise.all([response1.json(), response2.json()]);
|
|
|
+ const fetch2 = fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
|
|
|
+ method: 'get',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: 'Bearer app-7iLQR9T77YtwDYUYGPk1PFbi',
|
|
|
+ },
|
|
|
+ })
|
|
|
+ .then((res) => {
|
|
|
+ return res.json();
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.error('第二个请求错误:', err);
|
|
|
+ return { data: [] }; // 失败时返回空数组
|
|
|
+ });
|
|
|
|
|
|
- // 合并两个数组(核心修改:用扩展运算符合并)
|
|
|
- // 确保 data1.data 和 data2.data 都是数组(防止接口返回异常)
|
|
|
+ // 等待两个请求完成
|
|
|
+ const [data1, data2] = await Promise.all([fetch1, fetch2]);
|
|
|
+ // 合并两个数组(确保是数组)
|
|
|
const list1 = Array.isArray(data1.data) ? data1.data : [];
|
|
|
const list2 = Array.isArray(data2.data) ? data2.data : [];
|
|
|
-
|
|
|
// 合并数组并赋值给 sessionHistory
|
|
|
sessionHistory.value = [...list1, ...list2];
|
|
|
- sessionHistory.value.forEach((item) => {
|
|
|
- currentSessionID.value = item.id;
|
|
|
- });
|
|
|
- console.log(sessionHistory.value, '历史数据');
|
|
|
+ // 设置当前会话ID(如果有数据)
|
|
|
+ if (sessionHistory.value.length > 0) {
|
|
|
+ currentSessionID.value = sessionHistory.value[0].id;
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
console.error('获取历史数据失败:', error);
|
|
|
// 错误处理(比如提示用户)
|
|
|
@@ -899,6 +1050,19 @@ onMounted(() => {
|
|
|
getHistoryList();
|
|
|
// 初始化(仅执行一次)
|
|
|
initMarked();
|
|
|
+ triggerChart();
|
|
|
+});
|
|
|
+onUnmounted(() => {
|
|
|
+ // 卸载时销毁实例,清理 DOM
|
|
|
+ if (myChart) {
|
|
|
+ myChart.dispose();
|
|
|
+ myChart = null;
|
|
|
+ }
|
|
|
+ const chartContainer = document.getElementById('dynamic-echarts-container');
|
|
|
+ if (chartContainer && chartContainer.parentNode) {
|
|
|
+ chartContainer.parentNode.removeChild(chartContainer);
|
|
|
+ }
|
|
|
+ window.removeEventListener('resize', handleResize);
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
@@ -1141,24 +1305,39 @@ onMounted(() => {
|
|
|
}
|
|
|
|
|
|
.input-area {
|
|
|
- margin: 10px 10px 20px 10px;
|
|
|
- padding: 10px;
|
|
|
+ padding: 5px;
|
|
|
background-color: #043256;
|
|
|
border: 1px solid #2cb6ff;
|
|
|
border-radius: 5px;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- justify-content: space-between;
|
|
|
+ justify-content: space-around;
|
|
|
gap: 5px;
|
|
|
- height: 25%;
|
|
|
+ min-height: 170px;
|
|
|
+ height: auto;
|
|
|
+ margin: 5px;
|
|
|
+ position: relative;
|
|
|
}
|
|
|
/* 文件展示区域 */
|
|
|
+ .img-preview {
|
|
|
+ position: relative;
|
|
|
+ width: 50px;
|
|
|
+ height: 50px;
|
|
|
+ }
|
|
|
+ .img-preview img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: contain;
|
|
|
+ display: block;
|
|
|
+ }
|
|
|
.file-preview {
|
|
|
+ position: relative;
|
|
|
margin-bottom: 10px;
|
|
|
padding: 8px;
|
|
|
border: 1px solid #e5e7eb;
|
|
|
border-radius: 4px;
|
|
|
background: #f9fafb;
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
|
.file-info {
|
|
|
display: flex;
|
|
|
@@ -1481,6 +1660,8 @@ onMounted(() => {
|
|
|
height: 100%;
|
|
|
overflow: auto;
|
|
|
}
|
|
|
+.img-container {
|
|
|
+}
|
|
|
.vue-office-excel {
|
|
|
background: #fff !important;
|
|
|
}
|