|
@@ -430,6 +430,41 @@
|
|
|
v-html="renderMarkdown(message.displayContent)"
|
|
|
></div>
|
|
|
<div v-else>{{ message.content }}</div>
|
|
|
+
|
|
|
+ <!-- 知识源显示区域 -->
|
|
|
+ <div v-if="message.role === 'assistant' && message.knowledgeSources && message.knowledgeSources.length > 0" class="knowledge-sources">
|
|
|
+ <div class="knowledge-sources-header">
|
|
|
+ <DatabaseOutlined />
|
|
|
+ <span>知识来源</span>
|
|
|
+ </div>
|
|
|
+ <div class="knowledge-sources-list">
|
|
|
+
|
|
|
+ <div
|
|
|
+ v-for="(source, sourceIndex) in message.knowledgeSources"
|
|
|
+ :key="sourceIndex"
|
|
|
+ class="knowledge-source-group"
|
|
|
+ >
|
|
|
+
|
|
|
+ <div class="knowledge-source-items">
|
|
|
+ <div
|
|
|
+ class="knowledge-source-item"
|
|
|
+ @click="handleKnowledgeSourceClick(source)"
|
|
|
+ >
|
|
|
+ <div class="knowledge-source-content">
|
|
|
+ <div class="knowledge-source-title" @click.stop="previewDocument(source)">
|
|
|
+ {{ source.file_name }}
|
|
|
+ </div>
|
|
|
+ <div class="knowledge-source-text" v-if="source.content">{{ source.content }}</div>
|
|
|
+ <!-- <div class="knowledge-source-meta">
|
|
|
+ <span class="knowledge-source-doc">文档ID: {{ source.doc_id }}</span>
|
|
|
+ <span class="knowledge-source-score" v-if="source.rerank_score">相关度: {{ (source.rerank_score * 100).toFixed(1) }}%</span>
|
|
|
+ </div> -->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
<div v-if="message.analysis_result" style="background-color: #f5f5f5; padding: 12px; border-radius: 8px; margin: 8px 0;">
|
|
|
<div v-for="value in message.analysis_result.insights" style="margin-bottom: 6px; border-bottom: 1px dashed #e5e5e5;">
|
|
|
{{ value.message }}
|
|
@@ -1497,8 +1532,18 @@ const fetchDocumentSummary = async () => {
|
|
|
// 获取聊天历史
|
|
|
const fetchChatHistory = async () => {
|
|
|
try {
|
|
|
- const response = await axios.get(
|
|
|
- `${import.meta.env.VITE_BASE_AI_API}/api/chat/history`,
|
|
|
+ // 从个人信息中获取 user_id 和 merchant_id
|
|
|
+ const userInfo = JSON.parse(localStorage.getItem('userInfo'));
|
|
|
+ const userId = userInfo.user_id || userInfo.id || 1; // 备用默认值
|
|
|
+ const merchantId = userInfo.merchant?.merchant_id || 1;
|
|
|
+
|
|
|
+ // 使用 FormData 传参
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append('user_id', userId);
|
|
|
+ formData.append('merchant_id', merchantId);
|
|
|
+
|
|
|
+ const response = await axios.post(
|
|
|
+ `${import.meta.env.VITE_BASE_AI_API}/api/chat/history?merchant_id=${merchantId}&user_id=${userId}&full_content=1`,
|
|
|
{
|
|
|
headers: {
|
|
|
'Authorization': `JWT ${localStorage.getItem('token')}`
|
|
@@ -1556,6 +1601,31 @@ const getConversationType = (type) => {
|
|
|
return typeMap[type] || '对话';
|
|
|
};
|
|
|
|
|
|
+// 处理知识源点击事件
|
|
|
+const handleKnowledgeSourceClick = (item) => {
|
|
|
+ // 可以在这里实现跳转到具体文档或显示更多详情
|
|
|
+ console.log('点击知识源:', item);
|
|
|
+
|
|
|
+ // 示例:可以打开一个新窗口显示文档详情
|
|
|
+ if (item.doc_id) {
|
|
|
+ // 这里可以根据实际需求实现跳转逻辑
|
|
|
+ // 比如打开文档详情页面或显示文档内容
|
|
|
+ ElMessage.info(`查看文档 ${item.doc_id} 的详细信息`);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 处理文档预览点击事件
|
|
|
+const previewDocument = (source) => {
|
|
|
+ console.log('预览文档:', source);
|
|
|
+
|
|
|
+ if (source.minio_url) {
|
|
|
+ // 在新窗口中打开文档预览
|
|
|
+ window.open(source.minio_url, '_blank');
|
|
|
+ } else {
|
|
|
+ ElMessage.warning('该文档暂无预览链接');
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
// 处理步骤点击事件
|
|
|
const handleStepClick = (step) => {
|
|
|
// 显示进度条弹窗
|
|
@@ -2268,7 +2338,7 @@ const summaryHtml=ref('')
|
|
|
const response = await axios.post(apiEndpoint, {
|
|
|
message: currentMessage,
|
|
|
chat_config_id: "2",
|
|
|
- user_id: "13365429324",
|
|
|
+ user_id: JSON.parse(localStorage.getItem('userInfo')).user_id||"13365429324",
|
|
|
session_id: session_id.value,
|
|
|
source: "pc",
|
|
|
image_urls: imageUrls,
|
|
@@ -2325,6 +2395,7 @@ const summaryHtml=ref('')
|
|
|
content: messageContent,
|
|
|
displayContent: "",
|
|
|
audioData: audioInfo || null,
|
|
|
+ knowledgeSources: responseData.knowledge_sources || null, // 添加知识源数据
|
|
|
});
|
|
|
messages.value.push(aiMessage);
|
|
|
|
|
@@ -2654,8 +2725,53 @@ const addNewConversation = () => {
|
|
|
const switchConversation = (conversationId) => {
|
|
|
currentConversationId.value = conversationId;
|
|
|
const conversation = conversations.value.find((c) => c.id === conversationId);
|
|
|
- messages.value = conversation ? conversation.messages : [];
|
|
|
- session_id.value = ""; // 重置 session_id
|
|
|
+
|
|
|
+ if (conversation) {
|
|
|
+ // 如果会话有缓存的消息,直接使用
|
|
|
+ if (conversation.messages && conversation.messages.length > 0) {
|
|
|
+ messages.value = conversation.messages;
|
|
|
+ }
|
|
|
+ // 如果会话有 sessionData,从中提取消息
|
|
|
+ else if (conversation.sessionData) {
|
|
|
+ const sessionData = conversation.sessionData;
|
|
|
+ const extractedMessages = [];
|
|
|
+
|
|
|
+ // 添加用户消息
|
|
|
+ if (sessionData.user_message) {
|
|
|
+ extractedMessages.push({
|
|
|
+ id: `user_${sessionData.user_message.created_at}`,
|
|
|
+ role: 'user',
|
|
|
+ content: sessionData.user_message.content,
|
|
|
+ timestamp: sessionData.user_message.created_at,
|
|
|
+ type: 'text'
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加助手消息
|
|
|
+ if (sessionData.last_message && sessionData.last_message.role === 'assistant') {
|
|
|
+ extractedMessages.push({
|
|
|
+ id: `assistant_${sessionData.last_message.created_at}`,
|
|
|
+ role: 'assistant',
|
|
|
+ content: sessionData.last_message.content,
|
|
|
+ displayContent: sessionData.last_message.content, // 添加 displayContent 用于显示
|
|
|
+ timestamp: sessionData.last_message.created_at,
|
|
|
+ type: 'text'
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ messages.value = extractedMessages;
|
|
|
+ // 将提取的消息保存到会话中,避免重复提取
|
|
|
+ conversation.messages = extractedMessages;
|
|
|
+ } else {
|
|
|
+ messages.value = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置 session_id
|
|
|
+ session_id.value = conversationId;
|
|
|
+ } else {
|
|
|
+ messages.value = [];
|
|
|
+ session_id.value = "";
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
const handleFileDrop = async (event) => {
|
|
@@ -4432,6 +4548,7 @@ const updateCollapsedState = () => {
|
|
|
text-overflow: ellipsis;
|
|
|
display: -webkit-box;
|
|
|
-webkit-line-clamp: 2;
|
|
|
+ line-clamp: 2;
|
|
|
-webkit-box-orient: vertical;
|
|
|
white-space:nowrap;
|
|
|
overflow:hidden;
|
|
@@ -5453,6 +5570,127 @@ const updateCollapsedState = () => {
|
|
|
.message-text ::v-deep table tr:nth-child(even) {
|
|
|
background-color: #f2f2f2;
|
|
|
}
|
|
|
+
|
|
|
+/* 知识源样式 */
|
|
|
+.knowledge-sources {
|
|
|
+ margin-top: 12px;
|
|
|
+ padding: 12px;
|
|
|
+ background: #f8f9fa;
|
|
|
+ border-radius: 8px;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-sources-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #1890ff;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-sources-header .anticon {
|
|
|
+ font-size: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+.knowledge-source-group:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-source-type {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #666;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ padding: 4px 8px;
|
|
|
+ background: #e6f7ff;
|
|
|
+ border-radius: 4px;
|
|
|
+ display: inline-block;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-source-items {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+/* .knowledge-source-item {
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e8e8e8;
|
|
|
+ border-radius: 6px;
|
|
|
+ padding: 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-source-item:hover {
|
|
|
+ border-color: #1890ff;
|
|
|
+ box-shadow: 0 2px 8px rgba(24, 144, 255, 0.15);
|
|
|
+ transform: translateY(-1px);
|
|
|
+} */
|
|
|
+
|
|
|
+.knowledge-source-content {
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-source-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #1890ff;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ line-height: 1.4;
|
|
|
+ cursor: pointer;
|
|
|
+ text-decoration: underline;
|
|
|
+ transition: color 0.3s ease;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-source-title:hover {
|
|
|
+ color: #40a9ff;
|
|
|
+ text-decoration: underline;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-source-text {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #666;
|
|
|
+ line-height: 1.5;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ display: -webkit-box;
|
|
|
+ -webkit-line-clamp: 3;
|
|
|
+ line-clamp: 3;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-source-meta {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 11px;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-source-doc {
|
|
|
+ background: #f0f0f0;
|
|
|
+ padding: 2px 6px;
|
|
|
+ border-radius: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-source-score {
|
|
|
+ color: #52c41a;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+/* 知识源点击效果 */
|
|
|
+.knowledge-source-item:active {
|
|
|
+ transform: translateY(0);
|
|
|
+ box-shadow: 0 1px 4px rgba(24, 144, 255, 0.2);
|
|
|
+}
|
|
|
.video-preview {
|
|
|
position: relative;
|
|
|
width: 300px;
|