feat(dc): Complete Tool C Day 5 - AI Chat + Ant Design X Integration

Summary:
- Upgrade to Ant Design 6.0.1 + install Ant Design X (2.1.0) + X SDK (2.1.0)
- Develop frontend common capability layer: Chat component library (~968 lines)
  * ChatContainer.tsx - Core container component
  * MessageRenderer.tsx - Message renderer
  * CodeBlockRenderer.tsx - Code block renderer with syntax highlighting
  * Complete TypeScript types and documentation
- Integrate ChatContainer into Tool C
- Fix 7 critical UI issues:
  * AG Grid module registration error
  * UI refinement (borders, shadows, gradients)
  * Add AI welcome message
  * Auto-clear input field after sending
  * Remove page scrollbars
  * Manual code execution (not auto-run)
  * Support simple Q&A (new /ai/chat API)
- Complete end-to-end testing
- Update all documentation (4 status docs + 6 dev logs)

Technical Stack:
- Frontend: React 19 + Ant Design 6.0 + Ant Design X 2.1
- Components: Bubble, Sender from @ant-design/x
- Total code: ~5418 lines

Status: Tool C MVP completed, production ready
This commit is contained in:
2025-12-07 22:02:14 +08:00
parent 2c7ed94161
commit af325348b8
30 changed files with 5005 additions and 976 deletions

View File

@@ -0,0 +1,162 @@
/**
* ChatContainer - 通用 AI 对话容器组件
*
* 基于 Ant Design X + X SDK 构建
* 支持多场景AIA、PKB、Tool C
*
* 注意:由于 Ant Design X SDK 的复杂性,这里采用简化实现
* 直接管理消息状态,不使用 useXChat Hook
*/
import React, { useState, useCallback } from 'react';
import { Bubble, Sender } from '@ant-design/x';
import type { ChatContainerProps, ChatMessage } from './types';
import type { BubbleItemType } from '@ant-design/x/es/bubble/interface';
import './styles/chat.css';
/**
* ChatContainer 组件(简化实现)
*/
export const ChatContainer: React.FC<ChatContainerProps> = ({
conversationKey: _conversationKey,
defaultMessages = [],
providerConfig,
customMessageRenderer,
senderProps = {},
onMessageSent,
onMessageReceived,
onError,
className = '',
style = {},
}) => {
// 如果没有默认消息,添加欢迎语
const initialMessages = defaultMessages.length > 0 ? defaultMessages : [{
id: 'welcome',
role: 'assistant' as const,
content: '您好!我是您的 AI 数据分析师。我可以帮您编写代码来清洗数据。试试说:"把年龄大于60的设为老年组"。',
status: 'success' as const,
timestamp: Date.now(),
}];
const [messages, setMessages] = useState<ChatMessage[]>(initialMessages);
const [isLoading, setIsLoading] = useState(false);
// 处理消息发送
const handleSend = useCallback(async (messageContent: string) => {
if (!messageContent.trim()) return; // 防止发送空消息
// 1. 添加用户消息
const userMessage: ChatMessage = {
id: Date.now(),
role: 'user',
content: messageContent,
status: 'success',
timestamp: Date.now(),
};
setMessages(prev => [...prev, userMessage]);
if (onMessageSent) {
onMessageSent(userMessage);
}
// 2. 显示加载状态
const loadingMessage: ChatMessage = {
id: 'loading',
role: 'assistant',
content: '正在思考...',
status: 'loading',
timestamp: Date.now(),
};
setMessages(prev => [...prev, loadingMessage]);
setIsLoading(true);
try {
// 3. 调用后端 API
let response;
if (providerConfig.requestFn) {
response = await providerConfig.requestFn(messageContent, { messages });
} else {
const res = await fetch(providerConfig.apiEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: messageContent }),
});
if (!res.ok) throw new Error('API 请求失败');
response = await res.json();
}
// 4. 移除加载消息,添加 AI 响应
const aiMessage: ChatMessage = {
id: response.messageId || Date.now(),
role: 'assistant',
content: response.explanation || response.message || response.content || '',
status: 'success',
code: response.code,
explanation: response.explanation,
timestamp: Date.now(),
metadata: response.metadata,
};
setMessages(prev => prev.filter(m => m.id !== 'loading').concat(aiMessage));
if (onMessageReceived) {
onMessageReceived(aiMessage);
}
} catch (error: any) {
// 5. 错误处理
const errorMessage: ChatMessage = {
id: Date.now(),
role: 'assistant',
content: `抱歉,处理您的请求时出现错误:${error.message}`,
status: 'error',
timestamp: Date.now(),
};
setMessages(prev => prev.filter(m => m.id !== 'loading').concat(errorMessage));
if (onError) {
onError(error);
}
} finally {
setIsLoading(false);
}
}, [messages, providerConfig, onMessageSent, onMessageReceived, onError]);
// 转换消息为 Bubble.List 所需格式
const bubbleItems: BubbleItemType[] = messages.map((msg) => ({
key: msg.id,
role: msg.role,
content: customMessageRenderer
? customMessageRenderer({ id: msg.id, message: msg, status: msg.status || 'success' })
: msg.content,
status: msg.status,
}));
return (
<div className={`chat-container ${className}`} style={style}>
{/* 消息列表 */}
<div className="chat-messages">
<Bubble.List
items={bubbleItems}
autoScroll={true}
/>
</div>
{/* 输入框 */}
<div className="chat-input">
<Sender
placeholder="输入消息..."
loading={isLoading}
onSubmit={handleSend}
{...senderProps}
/>
</div>
</div>
);
};
export default ChatContainer;