/** * 应用核心逻辑 */ // 配置Markdown解析器 if (typeof marked !== 'undefined') { marked.setOptions({ breaks: true, // 支持换行 gfm: true, // 支持GitHub风格的Markdown sanitize: false, // 允许HTML(因为我们自己处理SVG) smartLists: true, // 智能列表 smartypants: true // 智能标点 }); } class ProductCanvasApp { constructor() { this.currentMode = 'canvas'; // 'canvas' 或 'swot' this.svgStorage = {}; this.currentSvgId = null; this.conversationHistory = {}; this.isProcessing = false; this.currentStreamingMessage = null; this.initElements(); this.initEventListeners(); this.loadSavedData(); this.updateModeUI(); } // 初始化DOM元素引用 initElements() { // 模式切换按钮 this.canvasBtn = document.getElementById('canvas-mode-btn'); this.swotBtn = document.getElementById('swot-mode-btn'); this.pageTitle = document.getElementById('page-title'); // 对话相关 this.chatInput = document.getElementById('chat-input'); this.sendButton = document.getElementById('send-button'); this.clearHistoryBtn = document.getElementById('clear-history-btn'); this.chatHistory = document.getElementById('chat-history'); // SVG显示 this.svgViewer = document.getElementById('svg-viewer'); this.placeholderText = document.getElementById('placeholder-text'); // 底部操作按钮 this.downloadSvgBtn = document.getElementById('download-svg-btn'); this.exportImageBtn = document.getElementById('export-image-btn'); this.viewCodeBtn = document.getElementById('view-code-btn'); // API配置模态窗 this.settingsBtn = document.getElementById('settings-btn'); this.configModal = document.getElementById('config-modal'); this.closeModalBtn = document.getElementById('close-modal-btn'); this.apiUrlInput = document.getElementById('api-url'); this.apiKeyInput = document.getElementById('api-key'); this.apiModelInput = document.getElementById('api-model'); this.testApiBtn = document.getElementById('test-api-btn'); this.saveConfigBtn = document.getElementById('save-config-btn'); this.configStatus = document.getElementById('config-status'); this.statusText = document.getElementById('status-text'); } // 初始化事件监听器 initEventListeners() { // 模式切换 this.canvasBtn.addEventListener('click', () => this.switchMode('canvas')); this.swotBtn.addEventListener('click', () => this.switchMode('swot')); // 发送消息 this.sendButton.addEventListener('click', () => this.sendMessage()); this.clearHistoryBtn.addEventListener('click', () => this.clearCurrentConversation()); // 输入框事件 this.chatInput.addEventListener('keypress', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.sendMessage(); } }); // 自动调整输入框高度 this.chatInput.addEventListener('input', () => { Utils.autoResizeTextarea(this.chatInput); }); // 底部操作按钮 this.downloadSvgBtn.addEventListener('click', () => this.downloadSVG()); this.exportImageBtn.addEventListener('click', () => this.exportAsImage()); this.viewCodeBtn.addEventListener('click', () => this.viewSVGCode()); // API配置模态窗 this.settingsBtn.addEventListener('click', () => this.openConfigModal()); this.closeModalBtn.addEventListener('click', () => this.closeConfigModal()); this.configModal.addEventListener('click', (e) => { if (e.target === this.configModal) { this.closeConfigModal(); } }); this.testApiBtn.addEventListener('click', () => this.testAPIConnection()); this.saveConfigBtn.addEventListener('click', () => this.saveAPIConfig()); } // 加载保存的数据 loadSavedData() { // 加载模式 const savedMode = Utils.storage.get('currentMode', 'canvas'); this.currentMode = savedMode; // 加载对话历史(按模式分别存储) const savedCanvasHistory = Utils.storage.get('canvasHistory', []); const savedSwotHistory = Utils.storage.get('swotHistory', []); this.conversationHistory = { canvas: savedCanvasHistory, swot: savedSwotHistory }; this.renderConversationHistory(); // 加载SVG存储(按模式分别存储) const savedCanvasSVGs = Utils.storage.get('canvasSVGs', {}); const savedSwotSVGs = Utils.storage.get('swotSVGs', {}); this.svgStorage = { canvas: savedCanvasSVGs, swot: savedSwotSVGs }; // 加载API配置 const apiConfig = window.apiClient.getConfig(); this.apiUrlInput.value = apiConfig.url || ''; this.apiKeyInput.value = apiConfig.key || ''; this.apiModelInput.value = apiConfig.model || ''; } // 切换模式 switchMode(mode) { if (this.currentMode === mode) return; this.currentMode = mode; Utils.storage.set('currentMode', mode); this.updateModeUI(); } // 更新模式UI updateModeUI() { if (this.currentMode === 'canvas') { this.canvasBtn.classList.add('mode-btn-active'); this.canvasBtn.classList.remove('mode-btn-inactive'); this.swotBtn.classList.remove('mode-btn-active'); this.swotBtn.classList.add('mode-btn-inactive'); this.pageTitle.textContent = '产品画布'; if (!this.currentSvgId) { this.placeholderText.textContent = '生成的产品画布将在此处显示'; } } else { this.swotBtn.classList.add('mode-btn-active'); this.swotBtn.classList.remove('mode-btn-inactive'); this.canvasBtn.classList.remove('mode-btn-active'); this.canvasBtn.classList.add('mode-btn-inactive'); this.pageTitle.textContent = 'SWOT分析'; if (!this.currentSvgId) { this.placeholderText.textContent = '生成的SWOT分析将在此处显示'; } } } // 发送消息 async sendMessage() { const message = this.chatInput.value.trim(); if (!message || this.isProcessing) return; // 检查API配置 if (!window.apiClient.isConfigValid()) { alert('⚠️ 请先配置API设置!点击右上角齿轮图标进行配置。'); this.openConfigModal(); return; } this.isProcessing = true; this.sendButton.disabled = true; this.sendButton.innerHTML = ''; // 添加用户消息 this.addUserMessage(message); this.chatInput.value = ''; Utils.autoResizeTextarea(this.chatInput); try { // 获取对话上下文 const contextMessages = this.conversationHistory[this.currentMode] .slice(-10) // 只取最近10条消息作为上下文 .map(msg => ({ role: msg.type === 'user' ? 'user' : 'assistant', content: msg.content })); // 开始流式接收消息 await this.startStreamingMessage(message, contextMessages); } catch (error) { console.error('发送消息失败:', error); this.addErrorMessage(error.message); this.isProcessing = false; this.sendButton.disabled = false; this.sendButton.innerHTML = ''; } } // 开始流式接收消息 async startStreamingMessage(userMessage, contextMessages) { // 创建流式消息容器 const messageId = Utils.generateId('msg'); const messageContainer = this.createStreamingMessageContainer(messageId); this.chatHistory.appendChild(messageContainer); Utils.scrollToBottom(this.chatHistory); let fullContent = ''; let svgStarted = false; let svgContent = ''; let svgId = null; let beforeText = ''; const onChunk = (chunk) => { if (chunk.choices && chunk.choices[0] && chunk.choices[0].delta) { const content = chunk.choices[0].delta.content || ''; fullContent += content; // 检测SVG开始标记 if (!svgStarted) { // 使用正则表达式更准确地检测SVG代码块开始 const svgStartMatch = fullContent.match(/```(?:svg)?\s*/i); if (svgStartMatch) { svgStarted = true; svgId = Utils.generateId('svg'); // 提取SVG开始前的文本 const svgStartIndex = svgStartMatch.index; beforeText = fullContent.substring(0, svgStartIndex); // 显示绘制中占位符 this.updateStreamingMessageWithPlaceholder(messageContainer, beforeText, svgId); // 初始化SVG显示区域 this.svgViewer.innerHTML = `

正在绘制${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'}...

`; } } // 如果SVG已经开始,收集SVG内容 if (svgStarted) { // 检查是否有SVG结束标记 if (fullContent.includes('')) { const svgEndIndex = fullContent.indexOf('') + 6; // +6 是 '' 的长度 // 提取完整的SVG内容 const svgStartMatch = fullContent.match(/```(?:svg)?\s*/i); if (svgStartMatch) { const svgStartIndex = svgStartMatch.index; let svgWithMarkers = fullContent.substring(svgStartIndex, svgEndIndex); // 移除代码块标记 svgContent = svgWithMarkers.replace(/```(?:svg)?\s*/, '').replace(/```$/, '').trim(); // 补全SVG结束标签(如果没有的话) if (!svgContent.endsWith('')) { svgContent += ''; } // 实时显示SVG this.svgViewer.innerHTML = svgContent; // 存储SVG内容 this.svgStorage[this.currentMode][svgId] = { content: svgContent, messageId: messageId, mode: this.currentMode, timestamp: new Date().toISOString() }; // 更新占位符为可点击状态 this.updatePlaceholderToClickable(messageContainer, svgId); // 重置SVG状态,继续接收剩余文本 svgStarted = false; const afterText = fullContent.substring(svgEndIndex); this.updateStreamingMessageAfterSVG(messageContainer, beforeText, svgId, afterText); } } else { // SVG还在继续,更新内容 const svgStartMatch = fullContent.match(/```(?:svg)?\s*/i); if (svgStartMatch) { const svgStartIndex = svgStartMatch.index; let svgWithMarkers = fullContent.substring(svgStartIndex); // 移除代码块标记 svgContent = svgWithMarkers.replace(/```(?:svg)?\s*/, '').replace(/```$/, '').trim(); // 补全SVG结束标签以便实时显示 let tempSvgContent = svgContent; if (!tempSvgContent.endsWith('')) { tempSvgContent += ''; } // 实时更新SVG显示 this.svgViewer.innerHTML = tempSvgContent; } } } else { // 普通文本更新 this.updateStreamingMessage(messageContainer, fullContent); } } }; const onComplete = () => { // 流式接收完成,处理完整消息 this.finalizeStreamingMessage(messageId, fullContent, svgId); this.isProcessing = false; this.sendButton.disabled = false; this.sendButton.innerHTML = ''; }; // 调用流式API if (this.currentMode === 'canvas') { await window.apiClient.generateProductCanvasStream(userMessage, contextMessages, onChunk, onComplete); } else { await window.apiClient.generateSWOTAnalysisStream(userMessage, contextMessages, onChunk, onComplete); } } // 创建流式消息容器 createStreamingMessageContainer(messageId) { const messageDiv = document.createElement('div'); messageDiv.className = 'flex justify-start'; messageDiv.dataset.messageId = messageId; messageDiv.innerHTML = `
`; return messageDiv; } // 更新流式消息内容 updateStreamingMessage(container, content) { const contentDiv = container.querySelector('.typing-cursor'); if (contentDiv) { // 使用Markdown解析内容 if (typeof marked !== 'undefined') { contentDiv.innerHTML = marked.parse(content); } else { contentDiv.textContent = content; } Utils.scrollToBottom(this.chatHistory); } } // 更新流式消息内容并显示SVG占位符 updateStreamingMessageWithPlaceholder(container, beforeText, svgId) { // 使用Markdown解析beforeText const parsedBeforeText = typeof marked !== 'undefined' ? marked.parse(beforeText) : Utils.escapeHtml(beforeText); container.innerHTML = `
${parsedBeforeText}
🎨 正在绘制${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'}...
`; Utils.scrollToBottom(this.chatHistory); } // 更新占位符为可点击状态 updatePlaceholderToClickable(container, svgId) { const placeholder = container.querySelector('.svg-drawing-placeholder'); if (placeholder) { placeholder.classList.remove('svg-drawing-placeholder'); placeholder.classList.add('svg-placeholder-block'); placeholder.innerHTML = `📊 点击查看${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'} SVG`; placeholder.setAttribute('onclick', `app.viewSVG('${svgId}')`); } } // 更新SVG后的消息内容 updateStreamingMessageAfterSVG(container, beforeText, svgId, afterText) { // 使用Markdown解析文本 const parsedBeforeText = typeof marked !== 'undefined' ? marked.parse(beforeText) : Utils.escapeHtml(beforeText); const parsedAfterText = typeof marked !== 'undefined' ? marked.parse(afterText) : Utils.escapeHtml(afterText); container.innerHTML = `
${parsedBeforeText}
📊 点击查看${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'} SVG
${parsedAfterText}
`; Utils.scrollToBottom(this.chatHistory); } // 组装标准化的SVG消息字符串 buildSVGMessageContent(beforeText = '', svgBody = '', afterText = '') { const segments = []; const trimmedBefore = (beforeText || '').trim(); const trimmedAfter = (afterText || '').trim(); const trimmedSvg = (svgBody || '').trim(); if (trimmedBefore) { segments.push(trimmedBefore); } if (trimmedSvg) { segments.push('```svg'); segments.push(trimmedSvg); segments.push('```'); } if (trimmedAfter) { segments.push(trimmedAfter); } return segments.join('\n\n').trim(); } // 完成流式消息 finalizeStreamingMessage(messageId, fullContent, svgId = null) { let container = document.querySelector(`.chat-bubble-ai[data-message-id="${messageId}"]`); if (!container) { const fallback = document.querySelector(`[data-message-id="${messageId}"]`); if (fallback) { container = fallback.classList.contains('chat-bubble-ai') ? fallback : fallback.querySelector('.chat-bubble-ai'); } } if (!container) return; const parsed = Utils.parseSVGResponse(fullContent); const timestamp = new Date().toISOString(); const message = { id: messageId, type: 'ai', content: '', timestamp }; container.classList.remove('streaming-text'); container.setAttribute('data-message-id', messageId); const actionFooter = `
`; if (parsed.svgContent && parsed.svgContent.includes('') ? parsed.svgContent.trim() : `${parsed.svgContent.trim()}\n`; let targetSvgId = svgId || null; if (!targetSvgId || !this.svgStorage[this.currentMode][targetSvgId]) { targetSvgId = targetSvgId || Utils.generateId('svg'); } this.svgStorage[this.currentMode][targetSvgId] = { content: svgBody, messageId, mode: this.currentMode, timestamp }; const beforeHtml = parsed.beforeText ? (typeof marked !== 'undefined' ? marked.parse(parsed.beforeText) : Utils.escapeHtml(parsed.beforeText)) : ''; const afterHtml = parsed.afterText ? (typeof marked !== 'undefined' ? marked.parse(parsed.afterText) : Utils.escapeHtml(parsed.afterText)) : ''; container.innerHTML = `
${beforeHtml}
📊 点击查看 ${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'} SVG
${afterHtml}
${actionFooter} `; this.currentSvgId = targetSvgId; this.svgViewer.innerHTML = svgBody; message.content = this.buildSVGMessageContent(parsed.beforeText, svgBody, parsed.afterText); } else { const sanitizedText = fullContent.replace(/^\s*```/, '').replace(/```$/, '').trim(); const parsedContent = sanitizedText ? (typeof marked !== 'undefined' ? marked.parse(sanitizedText) : Utils.escapeHtml(sanitizedText)) : ''; container.innerHTML = `
${parsedContent}
${actionFooter} `; message.content = sanitizedText; } this.conversationHistory[this.currentMode].push(message); // 保存数据 Utils.storage.set('canvasHistory', this.conversationHistory.canvas); Utils.storage.set('swotHistory', this.conversationHistory.swot); Utils.storage.set('canvasSVGs', this.svgStorage.canvas); Utils.storage.set('swotSVGs', this.svgStorage.swot); Utils.scrollToBottom(this.chatHistory); } // 清空当前对话 clearCurrentConversation() { if (!confirm(`确定要清空当前的${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'}对话吗?`)) { return; } // 清空当前模式的对话历史 this.conversationHistory[this.currentMode] = []; // 清空当前模式的SVG存储 this.svgStorage[this.currentMode] = {}; // 如果当前显示的是被清空的模式的SVG,清空显示 if (this.currentSvgId && this.svgStorage[this.currentMode][this.currentSvgId]) { this.currentSvgId = null; this.svgViewer.innerHTML = `

生成的${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'}将在此处显示

`; } // 保存数据 Utils.storage.set('canvasHistory', this.conversationHistory.canvas); Utils.storage.set('swotHistory', this.conversationHistory.swot); Utils.storage.set('canvasSVGs', this.svgStorage.canvas); Utils.storage.set('swotSVGs', this.svgStorage.swot); // 重新渲染对话历史 this.renderConversationHistory(); } // 添加用户消息 addUserMessage(text) { const messageId = Utils.generateId('msg'); const message = { id: messageId, type: 'user', content: text, timestamp: new Date().toISOString() }; this.conversationHistory[this.currentMode].push(message); this.renderMessage(message); Utils.scrollToBottom(this.chatHistory); Utils.storage.set('canvasHistory', this.conversationHistory.canvas); Utils.storage.set('swotHistory', this.conversationHistory.swot); } // 添加AI消息(非流式,保留用于错误情况) addAIMessage(text) { const messageId = Utils.generateId('msg'); const parsed = Utils.parseSVGResponse(text); const message = { id: messageId, type: 'ai', content: text, timestamp: new Date().toISOString() }; this.conversationHistory[this.currentMode].push(message); // 如果包含SVG,存储SVG内容 if (parsed.svgContent) { const svgId = Utils.generateId('svg'); this.svgStorage[this.currentMode][svgId] = { content: parsed.svgContent, messageId: messageId, mode: this.currentMode, timestamp: new Date().toISOString() }; Utils.storage.set('canvasSVGs', this.svgStorage.canvas); Utils.storage.set('swotSVGs', this.svgStorage.swot); this.viewSVG(svgId); // 渲染包含SVG占位符的消息 this.renderMessageWithSVG(message, parsed, svgId); } else { // 渲染普通消息 this.renderMessage(message); } Utils.scrollToBottom(this.chatHistory); Utils.storage.set('canvasHistory', this.conversationHistory.canvas); Utils.storage.set('swotHistory', this.conversationHistory.swot); } // 添加错误消息 addErrorMessage(errorText) { const messageId = Utils.generateId('msg'); const message = { id: messageId, type: 'error', content: errorText, timestamp: new Date().toISOString() }; this.conversationHistory[this.currentMode].push(message); this.renderMessage(message); Utils.scrollToBottom(this.chatHistory); Utils.storage.set('canvasHistory', this.conversationHistory.canvas); Utils.storage.set('swotHistory', this.conversationHistory.swot); } // 渲染消息 renderMessage(message) { const messageDiv = document.createElement('div'); if (message.type === 'user') { messageDiv.className = 'flex justify-end'; messageDiv.innerHTML = `
${Utils.escapeHtml(message.content)}
`; } else if (message.type === 'error') { messageDiv.className = 'flex justify-start'; messageDiv.innerHTML = `
${Utils.escapeHtml(message.content)}
`; } else { messageDiv.className = 'flex justify-start'; messageDiv.innerHTML = `
${typeof marked !== 'undefined' ? marked.parse(message.content) : Utils.escapeHtml(message.content)}
`; } this.chatHistory.appendChild(messageDiv); } // 渲染包含SVG的消息 renderMessageWithSVG(message, parsed, svgId) { const messageDiv = document.createElement('div'); messageDiv.className = 'flex justify-start'; messageDiv.innerHTML = `
${typeof marked !== 'undefined' ? marked.parse(parsed.beforeText) : Utils.escapeHtml(parsed.beforeText)}
📊 点击查看 ${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'} SVG
${typeof marked !== 'undefined' ? marked.parse(parsed.afterText) : Utils.escapeHtml(parsed.afterText)}
`; this.chatHistory.appendChild(messageDiv); } // 渲染对话历史 renderConversationHistory() { this.chatHistory.innerHTML = ''; // 获取当前模式的对话历史 const currentHistory = this.conversationHistory[this.currentMode] || []; for (const message of currentHistory) { if (message.type === 'ai') { const parsed = Utils.parseSVGResponse(message.content); // 查找对应的SVG let svgId = null; const currentSvgStorage = this.svgStorage[this.currentMode] || {}; for (const [id, svg] of Object.entries(currentSvgStorage)) { if (svg.messageId === message.id) { svgId = id; break; } } if (svgId && parsed.svgContent) { this.renderMessageWithSVG(message, parsed, svgId); } else { this.renderMessage(message); } } else { this.renderMessage(message); } } } // 显示SVG viewSVG(svgId) { if (!this.svgStorage[this.currentMode][svgId]) { console.error('SVG not found:', svgId); return; } this.currentSvgId = svgId; const svgContent = this.svgStorage[this.currentMode][svgId].content; this.svgViewer.innerHTML = svgContent; } // 退回到指定消息 rollbackToMessage(messageId) { const messageIndex = this.conversationHistory[this.currentMode].findIndex(msg => msg.id === messageId); if (messageIndex === -1) return; // 删除指定消息之后的所有消息 const messagesToRemove = this.conversationHistory[this.currentMode].slice(messageIndex + 1); // 删除相关的SVG for (const message of messagesToRemove) { for (const [svgId, svg] of Object.entries(this.svgStorage[this.currentMode])) { if (svg.messageId === message.id) { delete this.svgStorage[this.currentMode][svgId]; // 如果当前显示的是被删除的SVG,清空显示 if (this.currentSvgId === svgId) { this.currentSvgId = null; this.svgViewer.innerHTML = `

生成的${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'}将在此处显示

`; } } } } // 更新对话历史 this.conversationHistory[this.currentMode] = this.conversationHistory[this.currentMode].slice(0, messageIndex + 1); // 保存数据 Utils.storage.set('canvasHistory', this.conversationHistory.canvas); Utils.storage.set('swotHistory', this.conversationHistory.swot); Utils.storage.set('canvasSVGs', this.svgStorage.canvas); Utils.storage.set('swotSVGs', this.svgStorage.swot); // 重新渲染对话历史 this.renderConversationHistory(); } // 重新生成消息 async regenerateMessage(messageId) { if (this.isProcessing) return; this.isProcessing = true; this.sendButton.disabled = true; this.sendButton.innerHTML = ''; try { // 重新生成响应 const response = await window.apiClient.regenerateResponse(messageId, this.conversationHistory[this.currentMode]); // 退回到指定消息 this.rollbackToMessage(messageId); // 添加新的AI回复 this.addAIMessage(response); } catch (error) { console.error('重新生成失败:', error); this.addErrorMessage(error.message); } finally { this.isProcessing = false; this.sendButton.disabled = false; this.sendButton.innerHTML = ''; } } // 下载SVG downloadSVG() { if (!this.currentSvgId) { alert('请先生成SVG图表'); return; } const svgContent = this.svgStorage[this.currentMode][this.currentSvgId].content; const filename = `${this.currentMode}-${Utils.formatDateTime().replace(/[/:]/g, '-')}.svg`; Utils.downloadFile(svgContent, filename, 'image/svg+xml'); } // 导出为图片 exportAsImage() { if (!this.currentSvgId) { alert('请先生成SVG图表'); return; } // 这里可以实现SVG转PNG的功能 // 由于需要额外的库,这里先提示用户 alert('SVG转PNG功能需要额外的库支持,您可以使用下载SVG功能,然后使用在线工具转换。'); } // 查看SVG代码 viewSVGCode() { if (!this.currentSvgId) { alert('请先生成SVG图表'); return; } const svgContent = this.svgStorage[this.currentMode][this.currentSvgId].content; // 创建代码查看模态窗 const modal = document.createElement('div'); modal.className = 'modal-overlay active'; modal.innerHTML = ` `; document.body.appendChild(modal); // 关闭模态窗 const closeModal = () => { document.body.removeChild(modal); }; modal.querySelector('.close-modal').addEventListener('click', closeModal); modal.addEventListener('click', (e) => { if (e.target === modal) { closeModal(); } }); // 复制代码 modal.querySelector('.copy-btn').addEventListener('click', () => { navigator.clipboard.writeText(svgContent).then(() => { const btn = modal.querySelector('.copy-btn'); const originalHTML = btn.innerHTML; btn.innerHTML = ' 已复制'; btn.classList.remove('bg-blue-500', 'hover:bg-blue-600'); btn.classList.add('bg-green-500', 'hover:bg-green-600'); setTimeout(() => { btn.innerHTML = originalHTML; btn.classList.remove('bg-green-500', 'hover:bg-green-600'); btn.classList.add('bg-blue-500', 'hover:bg-blue-600'); }, 2000); }); }); } // 打开API配置模态窗 openConfigModal() { this.configModal.classList.add('active'); const apiConfig = window.apiClient.getConfig(); this.apiUrlInput.value = apiConfig.url || ''; this.apiKeyInput.value = apiConfig.key || ''; this.apiModelInput.value = apiConfig.model || ''; } // 关闭API配置模态窗 closeConfigModal() { this.configModal.classList.remove('active'); } // 保存API配置 saveAPIConfig() { const config = { url: this.apiUrlInput.value.trim(), key: this.apiKeyInput.value.trim(), model: this.apiModelInput.value.trim() }; if (!config.url || !config.key || !config.model) { Utils.showStatus(this.configStatus, '⚠️ 请填写所有字段', 'error'); return; } window.apiClient.saveConfig(config); Utils.showStatus(this.configStatus, '✅ 配置已保存成功!', 'success'); setTimeout(() => { this.closeConfigModal(); }, 1500); } // 测试API连接 async testAPIConnection() { const config = { url: this.apiUrlInput.value.trim(), key: this.apiKeyInput.value.trim(), model: this.apiModelInput.value.trim() }; if (!config.url || !config.key || !config.model) { Utils.showStatus(this.configStatus, '⚠️ 请先填写所有字段', 'error'); return; } Utils.showStatus(this.configStatus, '🔄 正在测试连接...', 'loading'); try { // 临时保存配置进行测试 window.apiClient.saveConfig(config); await window.apiClient.testConnection(); Utils.showStatus(this.configStatus, '✅ 连接测试成功!', 'success'); } catch (error) { Utils.showStatus(this.configStatus, `❌ 连接失败: ${error.message}`, 'error'); } } } // 页面加载完成后初始化应用 document.addEventListener('DOMContentLoaded', () => { window.app = new ProductCanvasApp(); });