|
|
@@ -8,6 +8,23 @@
|
|
|
<div v-if="isShowChatBroad" class="mini-chat">
|
|
|
<!-- 左侧折叠区域 -->
|
|
|
<div class="left-side" :class="{ collapsed: isFold }" id="leftSide">
|
|
|
+ <div
|
|
|
+ class="addBtn"
|
|
|
+ :style="{
|
|
|
+ backgroundImage: `url(${isFold ? '/src/assets/images/vent/home/add.svg' : ''})`,
|
|
|
+ backgroundColor: isFold ? '' : '#2cb6ff',
|
|
|
+ width: isFold ? '20px' : 'auto',
|
|
|
+ }"
|
|
|
+ @click="addNew"
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ class="btn-text-bg"
|
|
|
+ :style="{
|
|
|
+ backgroundImage: `url(${!isFold ? '/src/assets/images/vent/home/addB.svg' : ''})`,
|
|
|
+ }"
|
|
|
+ ></span>
|
|
|
+ <span v-if="!isFold" class="btn-text">添加新对话</span>
|
|
|
+ </div>
|
|
|
<div
|
|
|
v-if="isFold"
|
|
|
class="historyBtn"
|
|
|
@@ -81,23 +98,35 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
- <SvgIcon size="80" class="ml-2px mr-2px" name="ai-logo" />
|
|
|
+ <SvgIcon size="40" class="answerIcon" name="ai-logo" />
|
|
|
<div class="message-wrapper ai-message-wrapper">
|
|
|
<div class="answer-message">
|
|
|
- <div v-if="message.contentR1" class="color-gray font-size-12px" v-html="message.contentR1"> </div>
|
|
|
- <div v-else v-html="message.content"> </div>
|
|
|
+ <div v-if="message.contentR1" class="thinking-section">
|
|
|
+ <div class="thinking-header" @click="isShow(message)">
|
|
|
+ <span class="thinking-title"
|
|
|
+ >思考过程:<RightOutlined v-if="!message.isShowThink" /> <DownOutlined v-if="message.isShowThink"
|
|
|
+ /></span>
|
|
|
+ </div>
|
|
|
+ <div v-show="message.isShowThink" class="color-gray font-size-12px" v-html="formatMessage(message.contentR1)"></div>
|
|
|
+ </div>
|
|
|
+ <div v-if="message.content" v-html="formatMessage(message.content)"> </div>
|
|
|
</div>
|
|
|
- <CopyOutlined class="copy-icon" @click="copyToClipboard(message.contentR1 || message.content)" title="复制消息" />
|
|
|
+ <CopyOutlined class="copy-icon" @click="copyToClipboard(message.content)" title="复制消息" />
|
|
|
</div>
|
|
|
</template>
|
|
|
</div>
|
|
|
+ <!-- 建议信息 -->
|
|
|
+ <div v-for="(item, index) in suggestList" :key="index" class="suggestion-item" @click="handleSuggestClick(item)">
|
|
|
+ <span class="suggestion-text">{{ item }}</span>
|
|
|
+ <a-icon type="right" class="suggestion-arrow" />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<!-- 底部输入区 -->
|
|
|
<div class="input-area">
|
|
|
- <a-input v-model:value="inputText" placeholder="请输入你的问题" @keyup.enter="handleSend(inputText)" class="ant-input" />
|
|
|
+ <a-textarea v-model:value="inputText" placeholder="请输入你的问题" @keyup.enter="handleSend(inputText)" class="ant-input" auto-size />
|
|
|
<div class="ctrl-btn">
|
|
|
<div class="input-controls">
|
|
|
- <button class="control-btn">深度学习</button>
|
|
|
+ <button class="control-btn" :class="{ active: isThinking }" @click="toggleThinking">深度学习</button>
|
|
|
<button class="control-btn" @click="stopReq()">停止响应</button>
|
|
|
</div>
|
|
|
<div class="action-bar">
|
|
|
@@ -117,15 +146,16 @@
|
|
|
</div>
|
|
|
<!-- 右侧文件上传区 -->
|
|
|
<div v-if="open" class="file-upload">
|
|
|
- <!-- 输入框区域,包含确认按钮 -->
|
|
|
- <div class="input-container">
|
|
|
- <a-input v-model:value="filePath" placeholder="输入文件连接" class="file-input" @pressEnter="handlePathConfirm" />
|
|
|
- <button class="confirm-btn" @click="handlePathConfirm">确认</button>
|
|
|
- </div>
|
|
|
<!-- 上传按钮 -->
|
|
|
- <!-- <a-upload> <button class="upload-btn" @click="customUpload">从本地上传</button></a-upload> -->
|
|
|
- <a-upload class="custom-upload" name="file" :multiple="false">
|
|
|
- <a-button class="upload-btn" @click="customUpload">
|
|
|
+ <a-upload
|
|
|
+ class="custom-upload"
|
|
|
+ name="file"
|
|
|
+ :multiple="false"
|
|
|
+ :before-upload="handleBeforeUpload"
|
|
|
+ :file-list="fileList"
|
|
|
+ :remove="handleRemove"
|
|
|
+ >
|
|
|
+ <a-button class="upload-btn">
|
|
|
<UploadOutlined></UploadOutlined>
|
|
|
从本地上传
|
|
|
</a-button>
|
|
|
@@ -142,7 +172,7 @@ import { SvgIcon } from '../Icon';
|
|
|
import { Space, Button, Modal, Input, message } from 'ant-design-vue';
|
|
|
// import AIChat from './index.vue';
|
|
|
import { useUserStore } from '/@/store/modules/user';
|
|
|
-import { EditOutlined, DeleteOutlined, UploadOutlined, CopyOutlined } from '@ant-design/icons-vue';
|
|
|
+import { EditOutlined, DeleteOutlined, UploadOutlined, CopyOutlined, RightOutlined, DownOutlined } from '@ant-design/icons-vue';
|
|
|
import { createVNode } from 'vue';
|
|
|
const TextArea = Input.TextArea; // 直接导入TextArea组件使用时打包报错
|
|
|
const inputText = ref(''); // 输入框内容
|
|
|
@@ -152,7 +182,11 @@ const editingId = ref<number | null>(null);
|
|
|
const editText = ref('');
|
|
|
const currentSessionID = ref('');
|
|
|
const taskID = ref('');
|
|
|
+const messageID = ref('');
|
|
|
const open = ref<boolean>(false);
|
|
|
+const isThinking = ref(false); //深度思考是否开启
|
|
|
+const Thinking = ref(false);
|
|
|
+const APIKEY = ref('Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd');
|
|
|
interface ListItem {
|
|
|
id: number;
|
|
|
name?: string;
|
|
|
@@ -164,6 +198,7 @@ interface Message {
|
|
|
/** 深度思考时的文本 */
|
|
|
contentR1: string;
|
|
|
timestamp: number; // 排序依据
|
|
|
+ isShowThink: boolean; //深度思考展示
|
|
|
}
|
|
|
// 定义消息历史数组类型
|
|
|
const messageHistory = ref<Message[]>([]);
|
|
|
@@ -172,89 +207,29 @@ const userid = useUserStore().getUserInfo.id as string;
|
|
|
const filePath = ref(''); // 绑定输入框值
|
|
|
const showConfirmBtn = ref(false); // 控制确认按钮显示状态
|
|
|
const fileList = ref([]);
|
|
|
+const suggestList = ref([]); //建议列表
|
|
|
+//启用深度思考
|
|
|
+const toggleThinking = () => {
|
|
|
+ isThinking.value = !isThinking.value;
|
|
|
+ if (isThinking.value) {
|
|
|
+ APIKEY.value = 'Bearer app-kprgsFKtySM4Wjxs0ZGzaNFN';
|
|
|
+ } else {
|
|
|
+ APIKEY.value = 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd';
|
|
|
+ }
|
|
|
+};
|
|
|
+// 折叠思考过程
|
|
|
+const isShow = (message) => {
|
|
|
+ message.isShowThink = !message.isShowThink;
|
|
|
+};
|
|
|
+// 点击建议项时的处理函数
|
|
|
+const handleSuggestClick = (text) => {
|
|
|
+ // 将选中的建议填充到文本框
|
|
|
+ inputText.value = text;
|
|
|
+};
|
|
|
function showAIChat() {
|
|
|
isShowChatBroad.value = !isShowChatBroad.value;
|
|
|
}
|
|
|
-//获取消息列表
|
|
|
-// async function handleSend(data) {
|
|
|
-// messageHistory.value.push({
|
|
|
-// id: `user_${Date.now()}`,
|
|
|
-// type: 'user',
|
|
|
-// content: data,
|
|
|
-// contentR1: '',
|
|
|
-// timestamp: Date.now(),
|
|
|
-// });
|
|
|
-// // 发送 POST 请求
|
|
|
-// fetch('http://39.97.59.228:8000/v1/chat-messages', {
|
|
|
-// method: 'POST',
|
|
|
-// headers: {
|
|
|
-// 'Content-Type': 'application/json',
|
|
|
-// Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
-// },
|
|
|
-// body: JSON.stringify({
|
|
|
-// conversation_id: currentSessionID.value,
|
|
|
-// query: data,
|
|
|
-// response_mode: 'streaming',
|
|
|
-// user: userid,
|
|
|
-// inputs: {},
|
|
|
-// }),
|
|
|
-// }).then((response) => {
|
|
|
-// const decoder = new TextDecoder('utf-8');
|
|
|
-// let buffer = [];
|
|
|
-// // 获取可读流
|
|
|
-// const reader = response.body.getReader();
|
|
|
-// const newMessage = {
|
|
|
-// id: `response_${Date.now()}`,
|
|
|
-// type: 'response' as any,
|
|
|
-// content: '',
|
|
|
-// contentR1: '',
|
|
|
-// timestamp: Date.now(),
|
|
|
-// };
|
|
|
-// messageHistory.value.push(newMessage);
|
|
|
-// // 读取数据
|
|
|
-// function read() {
|
|
|
-// return reader.read().then(({ done, value }) => {
|
|
|
-// if (done) {
|
|
|
-// return buffer;
|
|
|
-// }
|
|
|
-// // 解码数据块
|
|
|
-// const chunk = decoder.decode(value, { stream: false });
|
|
|
-// // 处理每段数据
|
|
|
-// const processedData = processStreamChunk(chunk);
|
|
|
-// buffer = buffer.concat(processedData);
|
|
|
-// // 继续读取
|
|
|
-// return read();
|
|
|
-// });
|
|
|
-// }
|
|
|
-// // 开始读取
|
|
|
-// return read();
|
|
|
-// function processStreamChunk(chunk) {
|
|
|
-// try {
|
|
|
-// // 移除 "data: " 前缀
|
|
|
-// const jsonStr = chunk.replace('data: ', '');
|
|
|
-// const data = JSON.parse(jsonStr);
|
|
|
-// const targetMessage = messageHistory.value.find((msg) => msg.id === newMessage.id);
|
|
|
-// if (!targetMessage) return;
|
|
|
-// // 根据事件类型分发处理
|
|
|
-// switch (data.event) {
|
|
|
-// case 'message':
|
|
|
-// if (!taskID.value && !currentSessionID.value) {
|
|
|
-// taskID.value = data.task_id;
|
|
|
-// currentSessionID.value = data.conversation_id;
|
|
|
-// }
|
|
|
-// targetMessage.content += data.answer; // 追加内容
|
|
|
-// break;
|
|
|
-// }
|
|
|
-// return data;
|
|
|
-// } catch (error) {
|
|
|
-// // 请求失败时设置系统消息
|
|
|
-// return null;
|
|
|
-// }
|
|
|
-// }
|
|
|
-// });
|
|
|
-// inputText.value = '';
|
|
|
-// }
|
|
|
-// 复制消息
|
|
|
+//复制消息
|
|
|
function copyToClipboard(text) {
|
|
|
if (!text || text.trim() === '') {
|
|
|
message.warn('没有可复制的内容');
|
|
|
@@ -285,24 +260,37 @@ function copyToClipboard(text) {
|
|
|
document.body.removeChild(textarea);
|
|
|
}
|
|
|
}
|
|
|
+//获取消息列表
|
|
|
async function handleSend(data) {
|
|
|
inputText.value = '';
|
|
|
- messageHistory.value.push({
|
|
|
- id: `user_${Date.now()}`,
|
|
|
- type: 'user',
|
|
|
- content: data,
|
|
|
- contentR1: '',
|
|
|
- timestamp: Date.now(),
|
|
|
- });
|
|
|
+ if (isThinking) {
|
|
|
+ messageHistory.value.push({
|
|
|
+ id: `user_${Date.now()}`,
|
|
|
+ type: 'user',
|
|
|
+ content: data,
|
|
|
+ contentR1: '',
|
|
|
+ timestamp: Date.now(),
|
|
|
+ isShowThink: true,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ messageHistory.value.push({
|
|
|
+ id: `user_${Date.now()}`,
|
|
|
+ type: 'user',
|
|
|
+ content: data,
|
|
|
+ contentR1: '',
|
|
|
+ timestamp: Date.now(),
|
|
|
+ isShowThink: false,
|
|
|
+ });
|
|
|
+ }
|
|
|
try {
|
|
|
const response = await fetch('http://39.97.59.228:8000/v1/chat-messages', {
|
|
|
method: 'POST',
|
|
|
headers: {
|
|
|
'Content-Type': 'application/json',
|
|
|
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
},
|
|
|
body: JSON.stringify({
|
|
|
- conversation_id: currentSessionID.value,
|
|
|
+ conversation_id: currentSessionID.value ? currentSessionID.value : '',
|
|
|
query: data,
|
|
|
response_mode: 'streaming',
|
|
|
user: userid,
|
|
|
@@ -317,14 +305,31 @@ async function handleSend(data) {
|
|
|
const decoder = new TextDecoder('utf-8');
|
|
|
const reader = response.body.getReader();
|
|
|
let textBuffer = ''; // 使用字符串缓冲区来累积数据
|
|
|
- const newMessage = {
|
|
|
- id: `response_${Date.now()}`,
|
|
|
- type: 'response',
|
|
|
- content: '',
|
|
|
- contentR1: '',
|
|
|
- timestamp: Date.now(),
|
|
|
- };
|
|
|
- messageHistory.value.push(newMessage);
|
|
|
+ // 在组件中定义
|
|
|
+ const currentProcessingMessage = ref(null);
|
|
|
+ if (isThinking) {
|
|
|
+ const newMessage = {
|
|
|
+ id: `response_${Date.now()}`,
|
|
|
+ type: 'response',
|
|
|
+ content: '',
|
|
|
+ contentR1: '',
|
|
|
+ timestamp: Date.now(),
|
|
|
+ isShowThink: true,
|
|
|
+ };
|
|
|
+ messageHistory.value.push(newMessage);
|
|
|
+ currentProcessingMessage.value = newMessage; // 保存引用
|
|
|
+ } else {
|
|
|
+ const newMessage = {
|
|
|
+ id: `response_${Date.now()}`,
|
|
|
+ type: 'response',
|
|
|
+ content: '',
|
|
|
+ contentR1: '',
|
|
|
+ timestamp: Date.now(),
|
|
|
+ isShowThink: false,
|
|
|
+ };
|
|
|
+ messageHistory.value.push(newMessage);
|
|
|
+ currentProcessingMessage.value = newMessage; // 保存引用
|
|
|
+ }
|
|
|
while (true) {
|
|
|
const { done, value } = await reader.read();
|
|
|
if (done) {
|
|
|
@@ -353,13 +358,53 @@ async function handleSend(data) {
|
|
|
switch (data.event) {
|
|
|
case 'message':
|
|
|
if (data.answer) {
|
|
|
- const targetMessage = messageHistory.value.find((msg) => msg.id === newMessage.id);
|
|
|
- if (targetMessage) {
|
|
|
- targetMessage.content += data.answer;
|
|
|
+ const targetMessage = messageHistory.value.find((msg) => msg.id === currentProcessingMessage.value.id);
|
|
|
+
|
|
|
+ if (!targetMessage) break;
|
|
|
+ let currentChunk = data.answer;
|
|
|
+
|
|
|
+ // 检查是否包含起始标签
|
|
|
+ const startIndex = currentChunk.indexOf('<think>');
|
|
|
+ if (startIndex !== -1) {
|
|
|
+ // 找到起始标签:将标签前的内容作为正文
|
|
|
+ if (startIndex > 0) {
|
|
|
+ targetMessage.content += currentChunk.substring(0, startIndex);
|
|
|
+ }
|
|
|
+ // 进入思考模式,将标签后的内容追加到contentR1
|
|
|
+ Thinking.value = true;
|
|
|
+ const remainingContent = currentChunk.substring(startIndex + '<think>'.length);
|
|
|
+ if (remainingContent) {
|
|
|
+ targetMessage.contentR1 += remainingContent;
|
|
|
+ }
|
|
|
+ } else if (Thinking.value) {
|
|
|
+ // 结束标签
|
|
|
+ const endIndex = currentChunk.indexOf('</think>');
|
|
|
+ if (endIndex !== -1) {
|
|
|
+ // 找到结束标签标签前的内容追加到contentR1
|
|
|
+ if (endIndex > 0) {
|
|
|
+ targetMessage.contentR1 += currentChunk.substring(0, endIndex);
|
|
|
+ }
|
|
|
+ //将标签后的内容作为正文
|
|
|
+ const remainingContent = currentChunk.substring(endIndex + '</think>'.length);
|
|
|
+ if (remainingContent) {
|
|
|
+ targetMessage.content += remainingContent;
|
|
|
+ }
|
|
|
+ targetMessage.content += currentChunk;
|
|
|
+ Thinking.value = false;
|
|
|
+ targetMessage.isShowThink = false;
|
|
|
+ } else {
|
|
|
+ // 没有结束标签,继续追加到contentR1
|
|
|
+ targetMessage.contentR1 += currentChunk;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ targetMessage.content += currentChunk;
|
|
|
}
|
|
|
}
|
|
|
if (data.task_id && !taskID.value) taskID.value = data.task_id;
|
|
|
if (data.conversation_id && !currentSessionID.value) currentSessionID.value = data.conversation_id;
|
|
|
+ if (data.message_id && !messageID.value) {
|
|
|
+ messageID.value = data.message_id;
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
} catch (error) {
|
|
|
@@ -367,6 +412,7 @@ async function handleSend(data) {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ getNextSuggest();
|
|
|
} catch (error) {
|
|
|
console.error('Error in handleSend:', error);
|
|
|
// 在 UI 上显示错误信息
|
|
|
@@ -376,41 +422,57 @@ async function handleSend(data) {
|
|
|
content: '请求错误',
|
|
|
contentR1: '',
|
|
|
timestamp: Date.now(),
|
|
|
+ isShowThink: false,
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
+//创建新对话
|
|
|
+async function addNew() {
|
|
|
+ messageHistory.value = [];
|
|
|
+ currentSessionID.value = '';
|
|
|
+ taskID.value = '';
|
|
|
+ messageID.value = '';
|
|
|
+}
|
|
|
// 上传文件
|
|
|
const showModal = () => {
|
|
|
open.value = !open.value;
|
|
|
};
|
|
|
-async function customUpload(data) {
|
|
|
+// 上传文件
|
|
|
+const handleBeforeUpload = async (file) => {
|
|
|
const formData = new FormData();
|
|
|
- if (!data) {
|
|
|
- return message.warn('请选择文件');
|
|
|
- }
|
|
|
- // 添加文件参数
|
|
|
- formData.append('file', data.file);
|
|
|
- // 添加用户标识参数
|
|
|
+ formData.append('file', file);
|
|
|
formData.append('user', userid);
|
|
|
+
|
|
|
try {
|
|
|
- let response = await fetch(`http://39.97.59.228:8000/v1/files/upload`, {
|
|
|
+ const response = await fetch(`http://39.97.59.228:8000/v1/files/upload`, {
|
|
|
method: 'POST',
|
|
|
headers: {
|
|
|
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
},
|
|
|
body: formData,
|
|
|
});
|
|
|
- // if (response) {
|
|
|
- // message.success('上传成功');
|
|
|
- // }
|
|
|
- console.log(response, '123');
|
|
|
- if (!response) {
|
|
|
- throw new Error('Network response was not ok');
|
|
|
+
|
|
|
+ const result = await response.json();
|
|
|
+ if (response.ok) {
|
|
|
+ message.success('上传成功');
|
|
|
+ const linkText = `[${result.name}](${result.source_url})`;
|
|
|
+ inputText.value += `\n${linkText}`;
|
|
|
+ } else {
|
|
|
+ message.error(`上传失败: ${result.message || '网络错误'}`);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('保存失败:', error);
|
|
|
+ message.error('上传失败,请重试');
|
|
|
}
|
|
|
-}
|
|
|
+ return false;
|
|
|
+};
|
|
|
+
|
|
|
+const handleRemove = (file) => {
|
|
|
+ const index = fileList.value.findIndex((item) => item.uid === file.uid);
|
|
|
+ if (index !== -1) {
|
|
|
+ fileList.value.splice(index, 1);
|
|
|
+ }
|
|
|
+};
|
|
|
//停止响应
|
|
|
async function stopReq() {
|
|
|
try {
|
|
|
@@ -418,7 +480,7 @@ async function stopReq() {
|
|
|
method: 'POST',
|
|
|
headers: {
|
|
|
'Content-Type': 'application/json',
|
|
|
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
},
|
|
|
body: JSON.stringify({
|
|
|
user: userid,
|
|
|
@@ -433,13 +495,12 @@ async function stopReq() {
|
|
|
}
|
|
|
//获取具体会话记录
|
|
|
async function sessionsHistory(id: string) {
|
|
|
- console.log(id, '123');
|
|
|
try {
|
|
|
let response = await fetch(`http://39.97.59.228:8000/v1/messages?conversation_id=${id}&user=${userid}`, {
|
|
|
method: 'GET',
|
|
|
headers: {
|
|
|
'Content-Type': 'application/json',
|
|
|
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
},
|
|
|
});
|
|
|
const data = await response.json();
|
|
|
@@ -447,19 +508,45 @@ async function sessionsHistory(id: string) {
|
|
|
if (data.data.length > 0) {
|
|
|
messageHistory.value = [];
|
|
|
data.data.forEach((item: any) => {
|
|
|
+ currentSessionID.value = item.conversation_id;
|
|
|
messageHistory.value.push({
|
|
|
id: `user_${Date.now()}`,
|
|
|
type: 'user',
|
|
|
content: item.query,
|
|
|
contentR1: '',
|
|
|
timestamp: Date.now(),
|
|
|
+ isShowThink: false,
|
|
|
});
|
|
|
+ const answer = item.answer;
|
|
|
+ // 查找<think>
|
|
|
+ const startIndex = answer.indexOf('<think>');
|
|
|
+ const endIndex = answer.indexOf('</think>');
|
|
|
+ let content = '';
|
|
|
+ let contentR1 = '';
|
|
|
+ // 根据标签判断
|
|
|
+ if (startIndex !== -1 && endIndex !== -1) {
|
|
|
+ content = answer.substring(0, startIndex) + answer.substring(endIndex + '</think>'.length);
|
|
|
+ contentR1 = answer.substring(startIndex + '<think>'.length, endIndex);
|
|
|
+ } else if (startIndex !== -1) {
|
|
|
+ // 只有起始标签
|
|
|
+ content = answer.substring(0, startIndex);
|
|
|
+ contentR1 = answer.substring(startIndex + '<think>'.length);
|
|
|
+ } else if (endIndex !== -1) {
|
|
|
+ // 只有结束标签
|
|
|
+ content = answer.substring(endIndex + '</think>'.length);
|
|
|
+ contentR1 = answer.substring(0, endIndex);
|
|
|
+ } else {
|
|
|
+ // 没有标签
|
|
|
+ content = answer;
|
|
|
+ }
|
|
|
+ // 添加到消息历史
|
|
|
messageHistory.value.push({
|
|
|
id: `system_${Date.now()}`,
|
|
|
type: 'system',
|
|
|
- content: item.answer,
|
|
|
- contentR1: '',
|
|
|
+ content: content.trim(),
|
|
|
+ contentR1: contentR1.trim(),
|
|
|
timestamp: Date.now(),
|
|
|
+ isShowThink: contentR1.length > 0, // 如果有思考内容则显示
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
@@ -471,37 +558,32 @@ async function sessionsHistory(id: string) {
|
|
|
}
|
|
|
editingId.value = null;
|
|
|
}
|
|
|
-//编辑标题
|
|
|
-const startEditing = (item: ListItem) => {
|
|
|
- editingId.value = item.id;
|
|
|
- editText.value = item.name || '';
|
|
|
-};
|
|
|
-// 输入框确认按钮点击事件
|
|
|
-async function handlePathConfirm() {
|
|
|
- const formData = new FormData();
|
|
|
- // 添加文件参数
|
|
|
- formData.append('file', filePath.value);
|
|
|
- // 添加用户标识参数
|
|
|
- formData.append('user', userid);
|
|
|
+//获取下一轮建议问题列表
|
|
|
+//停止响应
|
|
|
+async function getNextSuggest() {
|
|
|
+ console.log(messageID.value, '123');
|
|
|
try {
|
|
|
- let response = await fetch(`http://39.97.59.228:8000/v1/files/upload`, {
|
|
|
- method: 'POST',
|
|
|
+ let response = await fetch(`http://39.97.59.228:8000/v1/messages/${messageID.value}/suggested?user=${userid}`, {
|
|
|
+ method: 'GET',
|
|
|
headers: {
|
|
|
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
},
|
|
|
- body: formData,
|
|
|
});
|
|
|
- if (!response.ok) {
|
|
|
+ const data = await response.json();
|
|
|
+ suggestList.value = data.data;
|
|
|
+ if (!response) {
|
|
|
throw new Error('Network response was not ok');
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('保存失败:', error);
|
|
|
}
|
|
|
- console.log('确认的文件路径:', filePath.value);
|
|
|
- // 这里可以添加路径验证、保存等逻辑
|
|
|
- filePath.value = ''; // 可选:确认后清空输入框
|
|
|
- showConfirmBtn.value = false;
|
|
|
}
|
|
|
+//编辑标题
|
|
|
+const startEditing = (item: ListItem) => {
|
|
|
+ editingId.value = item.id;
|
|
|
+ editText.value = item.name || '';
|
|
|
+};
|
|
|
// 保存修改
|
|
|
const handleSave = async (item: ListItem) => {
|
|
|
try {
|
|
|
@@ -509,7 +591,7 @@ const handleSave = async (item: ListItem) => {
|
|
|
method: 'POST',
|
|
|
headers: {
|
|
|
'Content-Type': 'application/json',
|
|
|
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
},
|
|
|
body: JSON.stringify({
|
|
|
name: editText.value,
|
|
|
@@ -539,7 +621,7 @@ const startDelete = async (item: ListItem) => {
|
|
|
method: 'DELETE',
|
|
|
headers: {
|
|
|
'Content-Type': 'application/json',
|
|
|
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
},
|
|
|
body: JSON.stringify({
|
|
|
user: userid,
|
|
|
@@ -571,14 +653,33 @@ async function getHistoryList() {
|
|
|
method: 'get',
|
|
|
headers: {
|
|
|
'Content-Type': 'application/json',
|
|
|
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
},
|
|
|
});
|
|
|
const data = await response.json();
|
|
|
sessionHistory.value = data.data;
|
|
|
- console.log(sessionHistory.value, '123');
|
|
|
+ data.data.forEach((item) => {
|
|
|
+ currentSessionID.value = item.conversation_id;
|
|
|
+ });
|
|
|
}
|
|
|
-
|
|
|
+//格式化消息
|
|
|
+const formatMessage = (text) => {
|
|
|
+ if (!text) return '';
|
|
|
+ let formatted = text
|
|
|
+ // 处理换行
|
|
|
+ .replace(/\n\n/g, '<br>')
|
|
|
+ .replace(/\n###/g, '<br> ')
|
|
|
+ .replace(/###/g, '')
|
|
|
+ .replace(/---/g, '')
|
|
|
+ .replace(/^- /gm, '<br> - ')
|
|
|
+ // 处理粗体
|
|
|
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
|
+ // 处理斜体
|
|
|
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
|
|
|
+ // 处理行内代码
|
|
|
+ .replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
|
+ return formatted;
|
|
|
+};
|
|
|
// 初始化按钮定位
|
|
|
onMounted(() => {
|
|
|
getHistoryList();
|
|
|
@@ -594,15 +695,15 @@ onMounted(() => {
|
|
|
}
|
|
|
.mini-chat {
|
|
|
display: flex;
|
|
|
- width: 500px;
|
|
|
- height: 400px;
|
|
|
+ width: 550px;
|
|
|
+ height: 85%;
|
|
|
border-radius: 4px;
|
|
|
position: fixed;
|
|
|
- top: 60px;
|
|
|
+ top: 70px;
|
|
|
right: 20px;
|
|
|
background-color: rgb(255, 255, 255);
|
|
|
background: url('../../assets/images/warn-dialog-bg.png') no-repeat center;
|
|
|
- background-size: 100% 100%;
|
|
|
+ background-size: 103% 130%;
|
|
|
z-index: 9999999;
|
|
|
color: #fff;
|
|
|
}
|
|
|
@@ -617,8 +718,21 @@ onMounted(() => {
|
|
|
width: 40px; /* 折叠时宽度 */
|
|
|
}
|
|
|
|
|
|
+.addBtn {
|
|
|
+ height: 30px;
|
|
|
+ position: absolute;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ background-position: center;
|
|
|
+ padding: 2px;
|
|
|
+ right: 10px;
|
|
|
+ bottom: 40px;
|
|
|
+ left: 10px;
|
|
|
+ align-items: center;
|
|
|
+ border-radius: 3px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
.custom-list {
|
|
|
- height: 325px;
|
|
|
+ height: 680px;
|
|
|
overflow-y: auto;
|
|
|
}
|
|
|
.text-container {
|
|
|
@@ -682,7 +796,7 @@ onMounted(() => {
|
|
|
color: #fff;
|
|
|
white-space: nowrap;
|
|
|
margin-left: 30px;
|
|
|
- line-height: 35px;
|
|
|
+ line-height: 29px;
|
|
|
}
|
|
|
.historyBtn {
|
|
|
width: 20px;
|
|
|
@@ -726,7 +840,7 @@ onMounted(() => {
|
|
|
flex: 1; /* 占据剩余空间 */
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
-
|
|
|
+ width: calc(100% - 134px) !important;
|
|
|
.dialog-area {
|
|
|
flex: 1; /* 占据剩余空间 */
|
|
|
gap: 10px; /* 消息块间隔统一控制 */
|
|
|
@@ -740,7 +854,12 @@ onMounted(() => {
|
|
|
padding: 10px;
|
|
|
border-radius: 5px;
|
|
|
background: #0c2842;
|
|
|
- max-width: 80%;
|
|
|
+ max-width: 300px;
|
|
|
+ white-space: pre-wrap;
|
|
|
+ word-wrap: break-word;
|
|
|
+ overflow-wrap: break-word;
|
|
|
+ overflow: auto;
|
|
|
+ line-height: 1.5;
|
|
|
}
|
|
|
.answer-message {
|
|
|
padding: 10px;
|
|
|
@@ -793,9 +912,9 @@ onMounted(() => {
|
|
|
}
|
|
|
|
|
|
.ant-input {
|
|
|
- background-color: rgba(255, 255, 255, 0) !important;
|
|
|
border: none;
|
|
|
- outline: none;
|
|
|
+ background-color: rgba(255, 255, 255, 0) !important;
|
|
|
+ color: #fff;
|
|
|
}
|
|
|
.ant-input:focus {
|
|
|
border: none; /* 聚焦时无边框 */
|
|
|
@@ -828,12 +947,12 @@ onMounted(() => {
|
|
|
font-size: 10px;
|
|
|
margin-right: 10px;
|
|
|
cursor: pointer;
|
|
|
- transition: all 0.2s;
|
|
|
+ transition: background-color 0.2s ease; /* 平滑过渡效果 */
|
|
|
}
|
|
|
-
|
|
|
- .control-btn:hover {
|
|
|
- background-color: #043256;
|
|
|
- color: #e2e8f0;
|
|
|
+ /* 激活状态样式(点击后) */
|
|
|
+ .control-btn.active {
|
|
|
+ background-color: #2cb6ff; /* 蓝色背景(Ant Design 主色) */
|
|
|
+ color: white; /* 白色文字 */
|
|
|
}
|
|
|
.control-btn1 {
|
|
|
height: 20px;
|
|
|
@@ -932,6 +1051,48 @@ onMounted(() => {
|
|
|
.custom-upload .ant-upload-select:hover .ant-btn {
|
|
|
border-color: #1f84bd !important;
|
|
|
}
|
|
|
+ .message-wrapper.ai-message-wrapper {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ }
|
|
|
+ .answerIcon {
|
|
|
+ flex: 0 0 45px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .suggestion-item {
|
|
|
+ height: 30px;
|
|
|
+ margin-left: 45px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 10px 16px;
|
|
|
+ border: 1px solid #1890ff;
|
|
|
+ color: white;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ width: 33%;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+ .thinking-section {
|
|
|
+ border-left: 3px solid #e5e7eb;
|
|
|
+ padding-left: 12px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .thinking-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 8px 0;
|
|
|
+ cursor: pointer;
|
|
|
+ user-select: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .thinking-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #6b7280;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|
|
|
<style scoped>
|