diff --git a/src/pages/Chat/Chat.jsx b/src/pages/Chat/Chat.jsx
index bbd4723..ae16ed3 100644
--- a/src/pages/Chat/Chat.jsx
+++ b/src/pages/Chat/Chat.jsx
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
-import { fetchChats, deleteChat, createChatRecord } from '../../store/chat/chat.thunks';
+import { fetchChats, deleteChat, createChatRecord, createConversation } from '../../store/chat/chat.thunks';
import { showNotification } from '../../store/notification.slice';
import ChatSidebar from './ChatSidebar';
import NewChat from './NewChat';
@@ -51,18 +51,28 @@ export default function Chat() {
if (knowledgeBaseId && !chatId && status === 'succeeded' && !status.includes('loading')) {
console.log('Chat.jsx: 检查是否需要创建聊天...');
- // 检查是否存在包含此知识库的聊天记录
+ // 处理可能的多个知识库ID (以逗号分隔)
+ const knowledgeBaseIds = knowledgeBaseId.split(',').map((id) => id.trim());
+ console.log('Chat.jsx: 处理知识库ID列表:', knowledgeBaseIds);
+
+ // 检查是否存在包含所有选中知识库的聊天记录
const existingChat = chatHistory.find((chat) => {
- // 检查知识库ID是否匹配
- if (chat.datasets && Array.isArray(chat.datasets)) {
- return chat.datasets.some((ds) => ds.id === knowledgeBaseId);
+ // 没有datasets属性或不是数组,跳过
+ if (!chat.datasets || !Array.isArray(chat.datasets)) {
+ return false;
}
- // 兼容旧格式
- if (chat.dataset_id_list && Array.isArray(chat.dataset_id_list)) {
- return chat.dataset_id_list.includes(knowledgeBaseId.replace(/-/g, ''));
- }
- return false;
+
+ // 获取当前聊天记录中的知识库ID列表
+ const chatDatasetIds = chat.datasets.map((ds) => ds.id);
+
+ // 检查所有选中的知识库是否都包含在这个聊天中
+ // 并且聊天中的知识库数量要和选中的相同(完全匹配)
+ return (
+ knowledgeBaseIds.length === chatDatasetIds.length &&
+ knowledgeBaseIds.every((id) => chatDatasetIds.includes(id))
+ );
});
+
console.log('Chat.jsx: existingChat', existingChat);
if (existingChat) {
@@ -73,11 +83,10 @@ export default function Chat() {
navigate(`/chat/${knowledgeBaseId}/${existingChat.conversation_id}`);
} else {
console.log('Chat.jsx: 创建新聊天...');
- // 创建新聊天
+ // 创建新聊天 - 使用新的API创建会话
dispatch(
- createChatRecord({
- dataset_id_list: [knowledgeBaseId.replace(/-/g, '')],
- question: '选择当前知识库,创建聊天',
+ createConversation({
+ dataset_id_list: knowledgeBaseIds,
})
)
.unwrap()
diff --git a/src/pages/Chat/ChatSidebar.jsx b/src/pages/Chat/ChatSidebar.jsx
index f76baf7..766482d 100644
--- a/src/pages/Chat/ChatSidebar.jsx
+++ b/src/pages/Chat/ChatSidebar.jsx
@@ -11,12 +11,10 @@ export default function ChatSidebar({ chatHistory = [], onDeleteChat, isLoading
navigate('/chat');
};
- const handleMouseEnter = (id) => {
- setActiveDropdown(id);
- };
-
- const handleMouseLeave = () => {
- setActiveDropdown(null);
+ const handleToggleDropdown = (e, id) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setActiveDropdown(activeDropdown === id ? null : id);
};
const handleDeleteChat = (e, id) => {
@@ -88,7 +86,9 @@ export default function ChatSidebar({ chatHistory = [], onDeleteChat, isLoading
}`}
>
ds.id).join(',') || knowledgeBaseId}/${
+ chat.conversation_id
+ }`}
className={`text-decoration-none d-flex align-items-center gap-2 py-2 text-dark ${
chatId === chat.conversation_id ? 'fw-bold' : ''
}`}
@@ -100,12 +100,13 @@ export default function ChatSidebar({ chatHistory = [], onDeleteChat, isLoading
handleMouseEnter(chat.conversation_id)}
- onMouseLeave={handleMouseLeave}
>
-
- {message.created_at && new Date(message.created_at).toLocaleTimeString()}
+ {message.created_at &&
+ (() => {
+ const messageDate = new Date(message.created_at);
+ const today = new Date();
+
+ // 检查是否是今天
+ const isToday =
+ messageDate.getDate() === today.getDate() &&
+ messageDate.getMonth() === today.getMonth() &&
+ messageDate.getFullYear() === today.getFullYear();
+
+ // 如果是今天,只显示时间;否则显示年月日和时间
+ if (isToday) {
+ return messageDate.toLocaleTimeString('zh-CN', {
+ hour: '2-digit',
+ minute: '2-digit',
+ });
+ } else {
+ return messageDate.toLocaleString('zh-CN', {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ });
+ }
+ })()}
{message.is_streaming && ' · 正在生成...'}
diff --git a/src/pages/Chat/NewChat.jsx b/src/pages/Chat/NewChat.jsx
index 69ede59..809d1de 100644
--- a/src/pages/Chat/NewChat.jsx
+++ b/src/pages/Chat/NewChat.jsx
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { showNotification } from '../../store/notification.slice';
-import { fetchAvailableDatasets, fetchChats, createChatRecord } from '../../store/chat/chat.thunks';
+import { fetchAvailableDatasets, fetchChats, createConversation } from '../../store/chat/chat.thunks';
import SvgIcon from '../../components/SvgIcon';
export default function NewChat() {
@@ -103,34 +103,31 @@ export default function NewChat() {
if (existingChat) {
// 找到现有聊天记录,导航到该聊天页面
- // 使用第一个知识库ID作为URL参数
- const primaryDatasetId = selectedDatasetIds[0];
- console.log(`找到现有聊天记录,直接导航到 /chat/${primaryDatasetId}/${existingChat.conversation_id}`);
- navigate(`/chat/${primaryDatasetId}/${existingChat.conversation_id}`);
+ // 使用所有知识库ID作为URL参数,以逗号分隔
+ const knowledgeBaseIdsParam = selectedDatasetIds.join(',');
+ console.log(
+ `找到现有聊天记录,直接导航到 /chat/${knowledgeBaseIdsParam}/${existingChat.conversation_id}`
+ );
+ navigate(`/chat/${knowledgeBaseIdsParam}/${existingChat.conversation_id}`);
} else {
// 没有找到现有聊天记录,创建新的聊天
- console.log(`未找到现有聊天记录,直接创建新的聊天,选中的知识库ID: ${selectedDatasetIds.join(', ')}`);
-
- // 创建新的聊天记录
- const formattedIds = selectedDatasetIds.map((id) => id.replace(/-/g, ''));
- console.log('格式化后的知识库ID:', formattedIds);
+ console.log(`未找到现有聊天记录,创建新会话,选中的知识库ID: ${selectedDatasetIds.join(', ')}`);
try {
- // 尝试创建聊天记录
+ // 调用createConversation创建新会话(不发送消息)
const response = await dispatch(
- createChatRecord({
- dataset_id_list: formattedIds,
- question: '选择当前知识库,创建聊天',
+ createConversation({
+ dataset_id_list: selectedDatasetIds,
})
).unwrap();
- console.log('创建聊天响应:', response);
+ console.log('创建会话响应:', response);
if (response && response.conversation_id) {
- // 使用第一个知识库ID作为URL参数
- const primaryDatasetId = selectedDatasetIds[0];
- console.log(`创建成功,导航到 /chat/${primaryDatasetId}/${response.conversation_id}`);
- navigate(`/chat/${primaryDatasetId}/${response.conversation_id}`);
+ // 使用所有知识库ID作为URL参数,以逗号分隔
+ const knowledgeBaseIdsParam = selectedDatasetIds.join(',');
+ console.log(`创建会话成功,导航到 /chat/${knowledgeBaseIdsParam}/${response.conversation_id}`);
+ navigate(`/chat/${knowledgeBaseIdsParam}/${response.conversation_id}`);
} else {
throw new Error('未能获取会话ID:' + JSON.stringify(response));
}
diff --git a/src/services/mockApi.js b/src/services/mockApi.js
index 0eae4f9..dd31eb5 100644
--- a/src/services/mockApi.js
+++ b/src/services/mockApi.js
@@ -885,6 +885,25 @@ export const mockPost = async (url, data, isMultipart = false) => {
};
}
+ // 创建会话 (不发送消息)
+ if (url === '/chat-history/create_conversation') {
+ const { dataset_id_list } = data;
+
+ // 生成新的会话ID
+ const conversation_id = `conv-${uuidv4()}`;
+
+ console.log(`[MOCK API] 创建新会话: ${conversation_id}, 知识库: ${dataset_id_list.join(', ')}`);
+
+ return {
+ code: 200,
+ message: '会话创建成功',
+ data: {
+ conversation_id: conversation_id,
+ dataset_id_list: dataset_id_list,
+ },
+ };
+ }
+
// 拒绝权限申请
if (url === '/permissions/reject/') {
const { id, responseMessage } = data;
@@ -1026,7 +1045,47 @@ export const mockDelete = async (url) => {
return { success: true };
}
- // Delete chat
+ // Delete chat (new endpoint)
+ if (url.match(/^\/chat-history\/delete_conversation/)) {
+ const params = new URLSearchParams(url.split('?')[1]);
+ const conversationId = params.get('conversation_id');
+
+ if (!conversationId) {
+ throw { response: { status: 400, data: { message: 'Missing conversation_id parameter' } } };
+ }
+
+ console.log(`[MOCK API] Deleting conversation: ${conversationId}`);
+
+ // 查找并删除会话
+ const index = mockChatHistory.findIndex(
+ (chat) => chat.id === conversationId || chat.conversation_id === conversationId
+ );
+
+ if (index === -1) {
+ // 即使找不到也返回成功,保持与API一致的行为
+ console.log(`[MOCK API] Conversation not found: ${conversationId}, but returning success`);
+ return {
+ code: 200,
+ message: '会话删除成功',
+ data: {},
+ };
+ }
+
+ mockChatHistory.splice(index, 1);
+
+ // 清除会话消息
+ if (chatMessages[conversationId]) {
+ delete chatMessages[conversationId];
+ }
+
+ return {
+ code: 200,
+ message: '会话删除成功',
+ data: {},
+ };
+ }
+
+ // Delete chat (old endpoint - keeping for backward compatibility)
if (url.match(/^\/chat-history\/[^/]+\/$/)) {
const id = url.split('/')[2];
return { data: mockDeleteChat(id) };
diff --git a/src/store/chat/chat.slice.js b/src/store/chat/chat.slice.js
index 328746b..890acda 100644
--- a/src/store/chat/chat.slice.js
+++ b/src/store/chat/chat.slice.js
@@ -7,6 +7,7 @@ import {
deleteChat,
createChatRecord,
fetchConversationDetail,
+ createConversation,
} from './chat.thunks';
import { fetchMessages, sendMessage } from './chat.messages.thunks';
@@ -276,6 +277,32 @@ const chatSlice = createSlice({
state.sendMessage.error = action.error.message;
})
+ // 处理创建会话
+ .addCase(createConversation.pending, (state) => {
+ state.createSession.status = 'loading';
+ state.createSession.error = null;
+ })
+ .addCase(createConversation.fulfilled, (state, action) => {
+ state.createSession.status = 'succeeded';
+ state.createSession.sessionId = action.payload.conversation_id;
+
+ // 当前聊天设置 - 使用与fetchConversationDetail相同的数据结构
+ state.currentChat.data = {
+ conversation_id: action.payload.conversation_id,
+ datasets: action.payload.datasets || [],
+ // 添加其他必要的字段,确保与fetchConversationDetail返回的数据结构兼容
+ messages: [],
+ create_time: new Date().toISOString(),
+ update_time: new Date().toISOString(),
+ };
+ state.currentChat.status = 'succeeded';
+ state.currentChat.error = null;
+ })
+ .addCase(createConversation.rejected, (state, action) => {
+ state.createSession.status = 'failed';
+ state.createSession.error = action.payload || action.error.message;
+ })
+
// 处理获取可用知识库
.addCase(fetchAvailableDatasets.pending, (state) => {
state.availableDatasets.status = 'loading';
diff --git a/src/store/chat/chat.thunks.js b/src/store/chat/chat.thunks.js
index a50ff05..607c120 100644
--- a/src/store/chat/chat.thunks.js
+++ b/src/store/chat/chat.thunks.js
@@ -78,7 +78,7 @@ export const updateChat = createAsyncThunk('chat/updateChat', async ({ id, data
*/
export const deleteChat = createAsyncThunk('chat/deleteChat', async (conversationId, { rejectWithValue }) => {
try {
- const response = await del(`/chat-history/conversation/${conversationId}/`);
+ const response = await del(`/chat-history/delete_conversation?conversation_id=${conversationId}`);
// 处理返回格式
if (response && response.code === 200) {
@@ -128,13 +128,8 @@ export const createChatRecord = createAsyncThunk(
const requestBody = {
question,
dataset_id_list,
+ conversation_id,
};
-
- // 如果存在对话 ID,添加到请求中
- if (conversation_id) {
- requestBody.conversation_id = conversation_id;
- }
-
console.log('准备发送聊天请求:', requestBody);
// 先添加用户消息到聊天窗口
@@ -366,8 +361,23 @@ export const createChatRecord = createAsyncThunk(
*/
export const fetchConversationDetail = createAsyncThunk(
'chat/fetchConversationDetail',
- async (conversationId, { rejectWithValue, dispatch }) => {
+ async (conversationId, { rejectWithValue, dispatch, getState }) => {
try {
+ // 先检查是否是刚创建的会话
+ const state = getState();
+ const createSession = state.chat.createSession || {};
+ const currentChat = state.chat.currentChat.data;
+
+ // 如果是刚创建成功的会话,且会话ID匹配,则直接返回现有会话数据
+ if (
+ createSession.status === 'succeeded' &&
+ createSession.sessionId === conversationId &&
+ currentChat?.conversation_id === conversationId
+ ) {
+ console.log('使用新创建的会话数据,跳过详情请求:', conversationId);
+ return currentChat;
+ }
+
const response = await get('/chat-history/conversation_detail', {
params: { conversation_id: conversationId },
});
@@ -386,8 +396,11 @@ export const fetchConversationDetail = createAsyncThunk(
return rejectWithValue('获取会话详情失败');
} catch (error) {
- // 如果是新聊天,API会返回404,此时不返回错误
- if (error.response && error.response.status === 404) {
+ // 明确检查是否是404错误
+ const is404Error = error.response && error.response.status === 404;
+
+ if (is404Error) {
+ console.log('会话未找到,可能是新创建的会话:', conversationId);
return null;
}
@@ -396,3 +409,83 @@ export const fetchConversationDetail = createAsyncThunk(
}
}
);
+
+/**
+ * 创建新会话(仅获取会话ID,不发送消息)
+ * @param {Object} params - 参数
+ * @param {string[]} params.dataset_id_list - 知识库ID列表
+ */
+export const createConversation = createAsyncThunk(
+ 'chat/createConversation',
+ async ({ dataset_id_list }, { dispatch, getState, rejectWithValue }) => {
+ try {
+ console.log('创建新会话,知识库ID列表:', dataset_id_list);
+ const params = {
+ dataset_id_list: dataset_id_list,
+ };
+ const response = await post('/chat-history/create_conversation/', params);
+
+ if (response && response.code === 200) {
+ const conversationData = response.data;
+ console.log('会话创建成功:', conversationData);
+
+ // 获取知识库信息
+ const state = getState();
+ const availableDatasets = state.chat.availableDatasets.items || [];
+
+ // 创建一个新的聊天记录对象添加到历史列表
+ const newChatEntry = {
+ conversation_id: conversationData.conversation_id,
+ datasets: dataset_id_list.map((id) => {
+ // 尝试查找知识库名称
+ const formattedId = id.includes('-')
+ ? id
+ : id.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5');
+ const dataset = availableDatasets.find((ds) => ds.id === formattedId);
+ return {
+ id: formattedId,
+ name: dataset?.name || '新知识库对话',
+ };
+ }),
+ create_time: new Date().toISOString(),
+ last_message: '',
+ message_count: 0,
+ };
+
+ // 更新聊天历史列表
+ dispatch({
+ type: 'chat/fetchChats/fulfilled',
+ payload: {
+ results: [newChatEntry],
+ total: 1,
+ append: true, // 标记为追加,而不是替换
+ },
+ });
+
+ // 设置为当前聊天
+ dispatch(
+ setCurrentChat({
+ conversation_id: conversationData.conversation_id,
+ datasets: newChatEntry.datasets,
+ })
+ );
+
+ return conversationData;
+ }
+
+ return rejectWithValue('创建会话失败');
+ } catch (error) {
+ console.error('创建会话失败:', error);
+
+ // 显示错误通知
+ dispatch(
+ showNotification({
+ message: `创建会话失败: ${error.message || '未知错误'}`,
+ type: 'danger',
+ })
+ );
+
+ return rejectWithValue(error.message || '创建会话失败');
+ }
+ }
+);