diff --git a/js/app.js b/js/app.js index 1d4366b..cfa09d5 100644 --- a/js/app.js +++ b/js/app.js @@ -14,22 +14,26 @@ if (typeof marked !== 'undefined') { } class ProductCanvasApp { - constructor() { - this.currentMode = 'canvas'; // 'canvas' 或 'swot' - this.svgStorage = {}; - this.currentSvgId = null; - this.conversationHistory = {}; + 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.updateModeUI(); + } + + getModeDisplayName(mode = this.currentMode) { + return mode === 'canvas' ? '产品画布' : 'SWOT分析'; + } + + // 初始化DOM元素引用 + initElements() { // 模式切换按钮 this.canvasBtn = document.getElementById('canvas-mode-btn'); this.swotBtn = document.getElementById('swot-mode-btn'); @@ -117,54 +121,97 @@ class ProductCanvasApp { canvas: savedCanvasHistory, swot: savedSwotHistory }; - this.renderConversationHistory(); - - // 加载SVG存储(按模式分别存储) - const savedCanvasSVGs = Utils.storage.get('canvasSVGs', {}); - const savedSwotSVGs = Utils.storage.get('swotSVGs', {}); + this.renderConversationHistory(); + + // 加载SVG存储(按模式分别存储) + const savedCanvasSVGs = Utils.storage.get('canvasSVGs', {}); + const savedSwotSVGs = Utils.storage.get('swotSVGs', {}); this.svgStorage = { canvas: savedCanvasSVGs, - swot: savedSwotSVGs - }; - - // 加载API配置 + swot: savedSwotSVGs + }; + + this.renderSvgViewerForMode(); + + // 加载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(); - } + // 切换模式 + switchMode(mode) { + if (this.currentMode === mode) return; + + this.currentMode = mode; + Utils.storage.set('currentMode', mode); + this.currentSvgId = null; + this.currentMode = mode; + Utils.storage.set('currentMode', mode); + this.updateModeUI(); + this.renderConversationHistory(); + this.renderSvgViewerForMode(); + } // 更新模式UI - updateModeUI() { - if (this.currentMode === 'canvas') { - this.canvasBtn.classList.add('mode-btn-active'); + 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.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分析将在此处显示'; - } - } - } + this.pageTitle.textContent = 'SWOT分析'; + if (!this.currentSvgId) { + this.placeholderText.textContent = '生成的SWOT分析将在此处显示'; + } + } + } + + showSvgPlaceholder() { + const label = this.getModeDisplayName(); + this.currentSvgId = null; + this.svgViewer.innerHTML = ` +
+ +

生成的${label}将在此处显示

+
+ `; + } + + renderSvgViewerForMode() { + const svgStore = this.svgStorage[this.currentMode] || {}; + const history = this.conversationHistory[this.currentMode] || []; + + let latestSvgId = null; + for (let i = history.length - 1; i >= 0; i--) { + const message = history[i]; + if (message.type !== 'ai') continue; + for (const [svgId, svg] of Object.entries(svgStore)) { + if (svg.messageId === message.id) { + latestSvgId = svgId; + break; + } + } + if (latestSvgId) break; + } + + if (latestSvgId && svgStore[latestSvgId]) { + this.currentSvgId = latestSvgId; + this.svgViewer.innerHTML = svgStore[latestSvgId].content; + } else { + this.showSvgPlaceholder(); + } + } // 发送消息 async sendMessage() { @@ -723,37 +770,69 @@ class ProductCanvasApp { 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); - } - } - } + // 渲染对话历史 + renderConversationHistory() { + this.chatHistory.innerHTML = ''; + + // 获取当前模式的对话历史 + const currentHistory = this.conversationHistory[this.currentMode] || []; + const currentSvgStorage = this.svgStorage[this.currentMode] || {}; + let hasStorageUpdate = false; + let hasHistoryUpdate = false; + + for (const message of currentHistory) { + if (message.type === 'ai') { + const parsed = Utils.parseSVGResponse(message.content); + + // 查找或补建对应的SVG + let svgId = null; + for (const [id, svg] of Object.entries(currentSvgStorage)) { + if (svg.messageId === message.id) { + svgId = id; + break; + } + } + + const hasSvgContent = parsed.svgContent && parsed.svgContent.includes('') + ? parsed.svgContent.trim() + : `${parsed.svgContent.trim()}\n`; + svgId = Utils.generateId('svg'); + currentSvgStorage[svgId] = { + content: normalizedSvg, + messageId: message.id, + mode: this.currentMode, + timestamp: message.timestamp || new Date().toISOString() + }; + parsed.svgContent = normalizedSvg; + message.content = this.buildSVGMessageContent(parsed.beforeText, normalizedSvg, parsed.afterText); + hasStorageUpdate = true; + hasHistoryUpdate = true; + } + this.renderMessageWithSVG(message, parsed, svgId); + continue; + } + + this.renderMessage(message); + } else { + this.renderMessage(message); + } + } + + if (hasStorageUpdate) { + this.svgStorage[this.currentMode] = currentSvgStorage; + Utils.storage.set('canvasSVGs', this.svgStorage.canvas || {}); + Utils.storage.set('swotSVGs', this.svgStorage.swot || {}); + } + if (hasHistoryUpdate) { + Utils.storage.set('canvasHistory', this.conversationHistory.canvas || []); + Utils.storage.set('swotHistory', this.conversationHistory.swot || []); + } + + Utils.scrollToBottom(this.chatHistory); + } // 显示SVG viewSVG(svgId) { diff --git a/prompts/canvas-prompt.txt b/prompts/canvas-prompt.txt index 02f11d2..0e3ff02 100644 --- a/prompts/canvas-prompt.txt +++ b/prompts/canvas-prompt.txt @@ -3,7 +3,11 @@ 请用中文回复,并在回复中包含SVG格式的产品画布图表。 -产品精益画布助手,下面是SVG画布的模板,注意使用markdown格式回复 +产品精益画布助手,下面是SVG画布的模板,注意使用markdown格式回复, + +- 解决方案、门槛优势、关键指标、渠道 文字不要超过7行 +- 成本分析、收入分析 文字不要超过6行 + ``` @@ -71,12 +75,6 @@ 独特卖点 - - - 微信扫一扫, - 老少皆宜智能回收 - - 对用户价值: • 扫码即用,操作超简单 • 价格透明,立即到账