feat: add general chat feature (without project/agent concept)

This commit is contained in:
AI Clinical Dev Team
2025-10-12 09:48:49 +08:00
parent 07704817ba
commit 348af7fee9
9 changed files with 688 additions and 0 deletions

View File

@@ -0,0 +1,179 @@
import { useState, useEffect } from 'react'
import { message } from 'antd'
import chatApi, { type GeneralMessage } from '../api/chatApi'
import MessageList from '../components/chat/MessageList'
import MessageInput from '../components/chat/MessageInput'
import ModelSelector, { type ModelType } from '../components/chat/ModelSelector'
import { useKnowledgeBaseStore } from '../stores/useKnowledgeBaseStore'
const ChatPage = () => {
const { knowledgeBases, fetchKnowledgeBases } = useKnowledgeBaseStore()
const [messages, setMessages] = useState<GeneralMessage[]>([])
const [selectedModel, setSelectedModel] = useState<ModelType>('deepseek-v3')
const [sending, setSending] = useState(false)
const [streamingContent, setStreamingContent] = useState('')
const [currentConversationId, setCurrentConversationId] = useState<string>()
// 加载知识库列表
useEffect(() => {
fetchKnowledgeBases()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
// 发送消息(流式)
const handleSendMessage = async (content: string, knowledgeBaseIds: string[]) => {
if (sending) return
console.log('🔵 [ChatPage] 发送消息', { content, knowledgeBaseIds, currentConversationId })
setSending(true)
setStreamingContent('')
// 添加用户消息到列表
const userMessage: GeneralMessage = {
id: `temp-${Date.now()}`,
conversationId: currentConversationId || 'temp',
role: 'user',
content,
createdAt: new Date().toISOString(),
}
setMessages(prev => [...prev, userMessage])
try {
let fullContent = ''
await chatApi.sendMessageStream(
{
content,
modelType: selectedModel,
knowledgeBaseIds,
conversationId: currentConversationId,
},
// onChunk
(chunk) => {
fullContent += chunk
setStreamingContent(fullContent)
},
// onComplete
(conversationId) => {
console.log('✅ [ChatPage] 对话完成', { conversationId })
// 如果是新对话保存conversationId
if (!currentConversationId && conversationId) {
setCurrentConversationId(conversationId)
}
// 添加完整的助手消息
const assistantMessage: GeneralMessage = {
id: `temp-assistant-${Date.now()}`,
conversationId: conversationId || currentConversationId || 'temp',
role: 'assistant',
content: fullContent,
model: selectedModel,
createdAt: new Date().toISOString(),
}
setMessages(prev => [...prev, assistantMessage])
setStreamingContent('')
setSending(false)
},
// onError
(error) => {
console.error('❌ [ChatPage] 发送失败:', error)
message.error('发送消息失败:' + error.message)
setStreamingContent('')
setSending(false)
}
)
} catch (err) {
console.error('❌ [ChatPage] 发送消息异常:', err)
message.error('发送消息失败')
setStreamingContent('')
setSending(false)
}
}
return (
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
{/* 顶部工具栏 */}
<div style={{
padding: '12px 24px',
background: '#fff',
borderBottom: '1px solid #e8e8e8',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
flexShrink: 0,
}}>
<div style={{ fontSize: 16, fontWeight: 600 }}>
💬
</div>
{/* 模型选择器 */}
<ModelSelector
value={selectedModel}
onChange={setSelectedModel}
disabled={sending}
/>
</div>
{/* 聊天区域 */}
<div
style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
minHeight: 0,
background: '#fff',
}}
>
{/* 消息列表区域 */}
<div style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
minHeight: 0,
overflow: 'hidden',
}}>
{messages.length === 0 && !sending ? (
<div style={{
flex: 1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#fafafa'
}}>
<div style={{ textAlign: 'center', color: '#999' }}>
<div style={{ fontSize: 48, marginBottom: 16 }}>💬</div>
<div style={{ fontSize: 18, fontWeight: 500, marginBottom: 8 }}>
AI自由对话
</div>
<div style={{ fontSize: 14 }}>
使@知识库引用文献
</div>
</div>
</div>
) : (
<MessageList
messages={messages}
loading={sending}
streamingContent={streamingContent}
/>
)}
</div>
{/* 消息输入 */}
<MessageInput
onSend={handleSendMessage}
loading={sending}
knowledgeBases={knowledgeBases}
placeholder="输入你的问题...Shift+Enter换行Enter发送"
/>
</div>
</div>
)
}
export default ChatPage