/** * AIChatComponent - AI 对话客户端组件 (完整版) * * 功能特性: * - 支持多种对话模式 (助手/查询/创作/工作流指导) * - 智能建议展示 * - 输入状态指示器 * - 附件上传支持 * - Markdown 渲染 * - 代码高亮显示 * - 自动滚动到底部 * - 上下文记忆 * * @example * const chat = new AIChatComponent({ * containerId: 'chat-container', * mode: 'assistant', * apiEndpoint: '/api/chat', * enableSuggestions: true, * placeholder: '请输入您的问题...' * }); * chat.initialize(); * chat.sendMessage('如何创建工作流?'); */ class AIChatComponent { constructor(options = {}) { // 配置项 this.containerId = options.containerId || 'ai-chat-container'; this.mode = options.mode || 'assistant'; // assistant/query/creative/workflow_guidance this.apiEndpoint = options.apiEndpoint || '/api/chat/completions'; this.apiBaseUrl = options.apiBaseUrl || 'http://124.70.146.192:8001'; this.enableSuggestions = options.enableSuggestions !== false; this.enableAttachments = options.enableAttachments || false; this.enableMarkdown = options.enableMarkdown || true; this.placeholder = options.placeholder || '请输入...'; this.suggestions = options.suggestions || []; this.contextWindow = options.contextWindow || 10; // 保留最近 10 轮对话 this.onMessageSent = options.onMessageSent || null; // 状态 this.messages = []; this.isLoading = false; this.conversationId = null; // DOM 元素 this.container = null; this.messagesContainer = null; this.inputField = null; this.sendButton = null; this.attachmentsInput = null; // 样式配置 this.styles = { primaryColor: '#667eea', secondaryColor: '#764ba2', userBubbleBg: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', aiBubbleBg: '#f0f0f0' }; } /** * 初始化组件 */ initialize() { this.container = document.getElementById(this.containerId); if (!this.container) { console.error(`AIChat: 容器 #${this.containerId} 不存在`); return false; } this.render(); this.bindEvents(); this.loadSuggestions(); // 生成会话 ID this.conversationId = `conv_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; return true; } /** * 渲染组件 HTML 结构 */ render() { this.container.innerHTML = `
`; // 获取 DOM 引用 this.messagesContainer = document.getElementById(`${this.containerId}-messages`); this.inputField = document.getElementById(`${this.containerId}-input`); this.sendButton = document.getElementById(`${this.containerId}-send`); if (this.enableAttachments) { this.attachmentsInput = document.getElementById(`${this.containerId}-attachments`); } } /** * 绑定事件监听器 */ bindEvents() { // 发送按钮点击 this.sendButton.addEventListener('click', () => this.sendMessage()); // 回车发送 (Shift+Enter 换行) this.inputField.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.sendMessage(); } }); // 自动调整 textarea 高度 this.inputField.addEventListener('input', () => { this.inputField.style.height = 'auto'; const newHeight = Math.min(this.inputField.scrollHeight, 120); this.inputField.style.height = `${newHeight}px`; }); // 附件变化 if (this.attachmentsInput) { this.attachmentsInput.addEventListener('change', (e) => { const count = e.target.files.length; const counter = document.getElementById(`${this.containerId}-attachments-count`); if (counter) { counter.textContent = count > 0 ? `已选择 ${count} 个文件` : ''; } }); } } /** * 加载智能建议 */ async loadSuggestions() { if (!this.enableSuggestions) return; // 根据模式加载不同的建议 const suggestionMap = { 'assistant': ['如何使用这个系统?', '有哪些核心功能?', '怎样快速上手?'], 'query': ['查询我的数据', '查看统计信息', '导出报表'], 'creative': ['帮我写一篇文章', '生成营销文案', '创作短视频脚本'], 'workflow_guidance': ['如何创建工作流?', '怎样配置节点?', '执行失败怎么办?'] }; let suggestions = this.suggestions.length > 0 ? this.suggestions : (suggestionMap[this.mode] || []); if (suggestions.length === 0) return; const suggestionsContainer = document.getElementById(`${this.containerId}-suggestions`); const suggestionsList = suggestionsContainer.querySelector('div'); suggestions.forEach(text => { const chip = document.createElement('button'); chip.textContent = text; chip.style.cssText = ` background: #f0f0f0; border: none; padding: 6px 12px; border-radius: 16px; font-size: 13px; color: #666; cursor: pointer; transition: all 0.2s; `; chip.addEventListener('mouseenter', () => { chip.style.background = this.styles.primaryColor; chip.style.color = 'white'; }); chip.addEventListener('mouseleave', () => { chip.style.background = '#f0f0f0'; chip.style.color = '#666'; }); chip.addEventListener('click', () => { this.inputField.value = text; this.sendMessage(); }); suggestionsList.appendChild(chip); }); suggestionsContainer.style.display = 'block'; } /** * 添加消息到聊天历史 */ appendMessage(role, content, options = {}) { const message = { id: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, role: role, // 'user' or 'ai' content: content, timestamp: new Date().toISOString(), attachments: options.attachments || [], metadata: options.metadata || {} }; this.messages.push(message); // 限制上下文窗口大小 if (this.messages.length > this.contextWindow) { this.messages = this.messages.slice(-this.contextWindow); } this.renderMessage(message); this.scrollToBottom(); return message; } /** * 渲染单条消息 */ renderMessage(message) { const messageEl = document.createElement('div'); messageEl.className = `ai-chat-message ${message.role}-message`; messageEl.style.cssText = ` margin-bottom: 20px; display: flex; ${message.role === 'user' ? 'justify-content: flex-end;' : 'justify-content: flex-start;'} `; const bubble = document.createElement('div'); bubble.style.cssText = ` max-width: 70%; padding: 12px 16px; border-radius: 12px; background: ${message.role === 'user' ? this.styles.userBubbleBg : this.styles.aiBubbleBg}; color: ${message.role === 'user' ? 'white' : '#333'}; word-wrap: break-word; box-shadow: 0 2px 8px rgba(0,0,0,0.1); `; // 消息内容 let contentHtml = message.content; if (this.enableMarkdown && message.role === 'ai') { // TODO: 集成 Markdown 渲染 contentHtml = this.simpleMarkdownRender(message.content); } bubble.innerHTML = `${code.trim()}`;
});
// 行内代码
text = text.replace(/`([^`]+)`/g, '$1');
// 粗体
text = text.replace(/\*\*([^*]+)\*\*/g, '$1');
// 斜体
text = text.replace(/\*([^*]+)\*/g, '$1');
// 链接
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1');
// 换行
text = text.replace(/\n/g, '