From 18f1aba08e02ab9b89ef2b3d9192d01f689e14af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B2=E6=82=A6?= Date: Fri, 24 Oct 2025 17:44:25 +0800 Subject: [PATCH] first commit --- css/style.css | 182 +++++++++ index.html | 192 +++++++++ js/apiclient.js | 299 ++++++++++++++ js/app.js | 767 ++++++++++++++++++++++++++++++++++++ js/utils.js | 289 ++++++++++++++ prompts/canvas-prompt.txt | 194 +++++++++ prompts/swot-prompt.txt | 144 +++++++ 功能概述.md | 99 +++++ 设计/原型.html | 806 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 2972 insertions(+) create mode 100644 css/style.css create mode 100644 index.html create mode 100644 js/apiclient.js create mode 100644 js/app.js create mode 100644 js/utils.js create mode 100644 prompts/canvas-prompt.txt create mode 100644 prompts/swot-prompt.txt create mode 100644 功能概述.md create mode 100644 设计/原型.html diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..98690e4 --- /dev/null +++ b/css/style.css @@ -0,0 +1,182 @@ +body { + font-family: 'Inter', sans-serif; +} + +/* 狂野线条效果 */ +.wild-border { + border: 3px solid; + box-shadow: 4px 4px 0px rgba(0,0,0,0.3); +} + +/* 切换按钮激活状态 */ +.mode-btn-active { + transform: translateY(-2px); + box-shadow: 0 4px 0 rgba(0,0,0,0.3); +} + +.mode-btn-inactive { + opacity: 0.6; +} + +/* 对话气泡样式 */ +.chat-bubble-user { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 10px 14px; + max-width: 80%; + border: 2px solid #000; + box-shadow: 2px 2px 0 rgba(0,0,0,0.2); +} + +.chat-bubble-ai { + background: #fff; + color: #1f2937; + padding: 10px 14px; + max-width: 85%; + border: 2px solid #10b981; + box-shadow: 2px 2px 0 rgba(16, 185, 129, 0.3); +} + +/* SVG占位符样式 - 块级换行 + 新配色 */ +.svg-placeholder-block { + display: block; + background: linear-gradient(135deg, #f59e0b 0%, #ef4444 100%); + color: white; + padding: 8px 14px; + margin: 8px 0; + border: 2px solid #000; + box-shadow: 3px 3px 0 rgba(0,0,0,0.25); + font-weight: bold; + font-size: 13px; + cursor: pointer; + transition: all 0.2s; + text-align: center; +} + +.svg-placeholder-block:hover { + transform: translateX(2px) translateY(-2px); + box-shadow: 4px 4px 0 rgba(0,0,0,0.3); + background: linear-gradient(135deg, #fb923c 0%, #f87171 100%); +} + +/* 气泡操作按钮 */ +.bubble-action-btn { + opacity: 0; + transition: opacity 0.2s; +} + +.chat-bubble-ai:hover .bubble-action-btn { + opacity: 1; +} + +/* 小手摇摆动画 */ +@keyframes wave { + 0%, 100% {transform: translateX(0px) rotate(90deg);} + 10%, 30%, 50%, 70%, 90% {transform: translateX(-1px) rotate(90deg);} + 20%, 40%, 60%, 80% {transform: translateX(1px) rotate(90deg);} +} + +.wave-hand { + animation: wave 3s ease-in-out infinite; + display: inline-block; + transform: rotate(90deg); +} + +/* 模态窗样式 */ +.modal-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.7); + z-index: 1000; + align-items: center; + justify-content: center; +} + +.modal-overlay.active { + display: flex; +} + +.modal-content { + background: white; + border: 4px solid #000; + box-shadow: 8px 8px 0 rgba(0,0,0,0.4); + max-width: 500px; + width: 90%; + max-height: 90vh; + overflow-y: auto; +} + +/* 表单输入框样式 */ +.config-input { + width: 100%; + padding: 10px; + border: 2px solid #000; + font-size: 14px; + transition: all 0.2s; +} + +.config-input:focus { + outline: none; + border-color: #667eea; + box-shadow: 3px 3px 0 rgba(102, 126, 234, 0.3); +} + +/* 齿轮旋转动画 */ +@keyframes rotate { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +.settings-btn:hover iconify-icon { + animation: rotate 1s linear infinite; +} + +/* 自适应高度输入框 */ +.auto-resize-input { + min-height: 40px; + max-height: 120px; /* 5行左右的高度 */ + resize: none; + overflow-y: auto; + line-height: 1.5; + padding: 8px 12px; +} + +/* 清空按钮样式 */ +.clear-btn { + transition: all 0.2s; +} + +.clear-btn:hover { + transform: scale(1.05); +} + +.clear-btn:active { + transform: scale(0.95); +} + +/* 流式输出动画 */ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} + +.streaming-text { + animation: fadeIn 0.3s ease-in-out; +} + +/* 打字机效果光标 */ +@keyframes blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0; } +} + +.typing-cursor::after { + content: '|'; + animation: blink 1s infinite; + color: #667eea; + font-weight: bold; +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..db6ba2a --- /dev/null +++ b/index.html @@ -0,0 +1,192 @@ + + + + + + 产品画布 / SWOT分析 + + + + + + + + + + +
+
+ +

产品画布

+
+ + +
+ + + + 点击切换模式 + + + + +
+
+ + +
+ + +
+ +
+ +
+
+ 👋 欢迎使用产品画布/SWOT分析工具!请输入您的需求,我将为您生成专业的分析图表。 +
+
+
+ + +
+
+ +
+ + +
+
+
+
+ + +
+
+
+ +

生成的产品画布将在此处显示

+
+
+ + +
+ + + +
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/js/apiclient.js b/js/apiclient.js new file mode 100644 index 0000000..5446aee --- /dev/null +++ b/js/apiclient.js @@ -0,0 +1,299 @@ +/** + * API客户端 - 处理与AI服务的交互 + */ + +class APIClient { + constructor() { + this.config = { + url: '', + key: '', + model: '' + }; + this.prompts = { + canvas: '', + swot: '' + }; + this.loadConfig(); + this.loadPrompts(); + } + + // 加载API配置 + loadConfig() { + const savedConfig = Utils.storage.get('apiConfig'); + if (savedConfig) { + this.config = { ...this.config, ...savedConfig }; + } + } + + // 加载系统提示词 + async loadPrompts() { + try { + // 加载产品画布提示词 + const canvasResponse = await fetch('prompts/canvas-prompt.txt'); + this.prompts.canvas = await canvasResponse.text(); + + // 加载SWOT分析提示词 + const swotResponse = await fetch('prompts/swot-prompt.txt'); + this.prompts.swot = await swotResponse.text(); + } catch (error) { + console.error('加载提示词失败:', error); + // 使用默认提示词 + this.prompts.canvas = '你是一个专业的产品战略分析师,擅长创建产品画布。'; + this.prompts.swot = '你是一个专业的商业战略分析师,擅长进行SWOT分析。'; + } + } + + // 保存API配置 + saveConfig(config) { + this.config = { ...this.config, ...config }; + return Utils.storage.set('apiConfig', this.config); + } + + // 获取当前配置 + getConfig() { + return { ...this.config }; + } + + // 验证配置是否完整 + isConfigValid() { + return this.config.url && this.config.key && this.config.model; + } + + // 测试API连接 + async testConnection() { + if (!this.isConfigValid()) { + throw new Error('API配置不完整,请填写所有字段'); + } + + try { + const response = await this.makeRequest([ + { role: 'user', content: '测试连接' } + ], 5); + + return { success: true, data: response }; + } catch (error) { + throw new Error(`连接测试失败: ${error.message}`); + } + } + + // 发送聊天请求 + async sendChatMessage(messages, options = {}) { + if (!this.isConfigValid()) { + throw new Error('API配置不完整,请先配置API设置'); + } + + const maxTokens = options.maxTokens || 2000; + const temperature = options.temperature || 0.7; + + try { + const response = await this.makeRequest(messages, maxTokens, temperature); + return response; + } catch (error) { + throw new Error(`API请求失败: ${error.message}`); + } + } + + // 核心请求方法 + async makeRequest(messages, maxTokens, temperature = 0.7) { + const requestBody = { + model: this.config.model, + messages: messages, + max_tokens: maxTokens, + temperature: temperature + }; + + const response = await fetch(this.config.url, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${this.config.key}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(requestBody) + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`); + } + + const data = await response.json(); + + if (!data.choices || !data.choices.length || !data.choices[0].message) { + throw new Error('API返回数据格式异常'); + } + + return data.choices[0].message.content; + } + + // 生成产品画布的专用方法 + async generateProductCanvas(userRequest, context = []) { + const messages = [ + { role: 'system', content: this.prompts.canvas }, + ...context, + { role: 'user', content: userRequest } + ]; + + return await this.sendChatMessage(messages, { maxTokens: 3000 }); + } + + // 生成SWOT分析的专用方法 + async generateSWOTAnalysis(userRequest, context = []) { + const messages = [ + { role: 'system', content: this.prompts.swot }, + ...context, + { role: 'user', content: userRequest } + ]; + + return await this.sendChatMessage(messages, { maxTokens: 3000 }); + } + + // 流式生成产品画布 + async generateProductCanvasStream(userRequest, context = [], onChunk, onComplete) { + const messages = [ + { role: 'system', content: this.prompts.canvas }, + ...context, + { role: 'user', content: userRequest } + ]; + + return await this.sendChatMessageStream(messages, { maxTokens: 3000 }, onChunk, onComplete); + } + + // 流式生成SWOT分析 + async generateSWOTAnalysisStream(userRequest, context = [], onChunk, onComplete) { + const messages = [ + { role: 'system', content: this.prompts.swot }, + ...context, + { role: 'user', content: userRequest } + ]; + + return await this.sendChatMessageStream(messages, { maxTokens: 3000 }, onChunk, onComplete); + } + + // 流式发送聊天请求 + async sendChatMessageStream(messages, options = {}, onChunk, onComplete) { + if (!this.isConfigValid()) { + throw new Error('API配置不完整,请先配置API设置'); + } + + const maxTokens = options.maxTokens || 2000; + const temperature = options.temperature || 0.7; + const stream = true; + + const requestBody = { + model: this.config.model, + messages: messages, + max_tokens: maxTokens, + temperature: temperature, + stream: stream + }; + + const url = this.config.url.replace('/chat/completions', '/chat/completions'); + + try { + await Utils.createStreamRequest( + url, + { + method: 'POST', + headers: { + 'Authorization': `Bearer ${this.config.key}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(requestBody) + }, + onChunk, + onComplete + ); + } catch (error) { + throw new Error(`流式API请求失败: ${error.message}`); + } + } + + // 重新生成响应 + async regenerateResponse(messageId, conversationHistory) { + // 找到指定消息ID之前的所有对话历史 + const contextMessages = conversationHistory + .filter(msg => msg.id <= messageId) + .map(msg => ({ + role: msg.type === 'user' ? 'user' : 'assistant', + content: msg.content + })); + + if (contextMessages.length === 0) { + throw new Error('没有找到有效的对话上下文'); + } + + // 移除最后一条消息(需要重新生成的消息) + if (contextMessages.length > 0 && contextMessages[contextMessages.length - 1].role === 'assistant') { + contextMessages.pop(); + } + + // 根据当前模式选择相应的生成方法 + const lastUserMessage = contextMessages.filter(msg => msg.role === 'user').pop(); + if (!lastUserMessage) { + throw new Error('没有找到用户消息'); + } + + const mode = Utils.storage.get('currentMode', 'canvas'); + + if (mode === 'canvas') { + return await this.generateProductCanvas(lastUserMessage.content, contextMessages.slice(0, -1)); + } else { + return await this.generateSWOTAnalysis(lastUserMessage.content, contextMessages.slice(0, -1)); + } + } + + // 模拟API响应(用于测试) + simulateAPIResponse(userMessage, mode = 'canvas') { + return new Promise((resolve) => { + setTimeout(() => { + const mockResponses = [ + `好的!我为您生成了一个${mode === 'canvas' ? '产品画布' : 'SWOT分析'} +\`\`\`svg + + + + + + + + + + + 这是${mode === 'canvas' ? '产品画布' : 'SWOT分析'}示例SVG + + + + +\`\`\` +包含了关键要素和模块。点击上方标签可在右侧查看详细图表。`, + + `已经为您调整完成! +\`\`\`svg + + + + + + + + + + + ${mode === 'canvas' ? '优化后的产品画布' : '优化后的SWOT分析'} + + + + +\`\`\` +采用了更加鲜明的色彩组合,希望您满意!` + ]; + + const response = mockResponses[Math.floor(Math.random() * mockResponses.length)]; + resolve(response); + }, 1000 + Math.random() * 1000); // 1-2秒的随机延迟 + }); + } +} + +// 创建全局API客户端实例 +window.apiClient = new APIClient(); \ No newline at end of file diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..fed973c --- /dev/null +++ b/js/app.js @@ -0,0 +1,767 @@ +/** + * 应用核心逻辑 + */ + +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.clearButton = document.getElementById('clear-button'); + 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.clearButton.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 = ''; + + const onChunk = (chunk) => { + if (chunk.choices && chunk.choices[0] && chunk.choices[0].delta) { + const content = chunk.choices[0].delta.content || ''; + fullContent += content; + this.updateStreamingMessage(messageContainer, fullContent); + } + }; + + const onComplete = () => { + // 流式接收完成,处理完整消息 + this.finalizeStreamingMessage(messageId, fullContent); + + 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.innerHTML = ` +
+
+
+ `; + return messageDiv; + } + + // 更新流式消息内容 + updateStreamingMessage(container, content) { + const contentDiv = container.querySelector('.typing-cursor'); + if (contentDiv) { + contentDiv.textContent = content; + Utils.scrollToBottom(this.chatHistory); + } + } + + // 完成流式消息 + finalizeStreamingMessage(messageId, fullContent) { + const container = document.querySelector(`[data-message-id="${messageId}"]`); + if (!container) return; + + const parsed = Utils.parseSVGResponse(fullContent); + + const message = { + id: messageId, + type: 'ai', + content: fullContent, + 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() + }; + + this.viewSVG(svgId); + + // 更新容器内容为包含SVG的消息 + container.innerHTML = ` +
+ ${Utils.escapeHtml(parsed.beforeText)} +
+ 📊 点击查看 ${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'} SVG +
+ ${Utils.escapeHtml(parsed.afterText)} +
+ +
+ + +
+ `; + } else { + // 更新容器内容为普通消息 + container.innerHTML = ` +
+ ${Utils.escapeHtml(fullContent)} +
+ +
+ + +
+ `; + } + + // 保存数据 + Utils.storage.set(`conversationHistory`, this.conversationHistory); + Utils.storage.set(`svgStorage`, this.svgStorage); + } + + // 清空当前对话 + 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('conversationHistory', this.conversationHistory); + Utils.storage.set('svgStorage', this.svgStorage); + + // 重新渲染对话历史 + 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('conversationHistory', this.conversationHistory); + } + + // 添加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('svgStorage', this.svgStorage); + this.viewSVG(svgId); + + // 渲染包含SVG占位符的消息 + this.renderMessageWithSVG(message, parsed, svgId); + } else { + // 渲染普通消息 + this.renderMessage(message); + } + + Utils.scrollToBottom(this.chatHistory); + Utils.storage.set('conversationHistory', this.conversationHistory); + } + + // 添加错误消息 + 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('conversationHistory', this.conversationHistory); + } + + // 渲染消息 + 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 = ` +
+
+ ${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 = ` +
+
+ ${Utils.escapeHtml(parsed.beforeText)} +
+ 📊 点击查看 ${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'} SVG +
+ ${Utils.escapeHtml(parsed.afterText)} +
+ +
+ + +
+
+ `; + + this.chatHistory.appendChild(messageDiv); + } + + // 渲染对话历史 + renderConversationHistory() { + this.chatHistory.innerHTML = ''; + + for (const message of this.conversationHistory) { + if (message.type === 'ai') { + const parsed = Utils.parseSVGResponse(message.content); + + // 查找对应的SVG + let svgId = null; + for (const [id, svg] of Object.entries(this.svgStorage)) { + 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[svgId]) { + console.error('SVG not found:', svgId); + return; + } + + this.currentSvgId = svgId; + const svgContent = this.svgStorage[svgId].content; + this.svgViewer.innerHTML = svgContent; + } + + // 退回到指定消息 + rollbackToMessage(messageId) { + const messageIndex = this.conversationHistory.findIndex(msg => msg.id === messageId); + if (messageIndex === -1) return; + + // 删除指定消息之后的所有消息 + const messagesToRemove = this.conversationHistory.slice(messageIndex + 1); + + // 删除相关的SVG + for (const message of messagesToRemove) { + for (const [svgId, svg] of Object.entries(this.svgStorage)) { + if (svg.messageId === message.id) { + delete this.svgStorage[svgId]; + + // 如果当前显示的是被删除的SVG,清空显示 + if (this.currentSvgId === svgId) { + this.currentSvgId = null; + this.svgViewer.innerHTML = ` +
+ +

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

+
+ `; + } + } + } + } + + // 更新对话历史 + this.conversationHistory = this.conversationHistory.slice(0, messageIndex + 1); + + // 保存数据 + Utils.storage.set('conversationHistory', this.conversationHistory); + Utils.storage.set('svgStorage', this.svgStorage); + + // 重新渲染对话历史 + 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.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.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.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(); +}); \ No newline at end of file diff --git a/js/utils.js b/js/utils.js new file mode 100644 index 0000000..e2f15de --- /dev/null +++ b/js/utils.js @@ -0,0 +1,289 @@ +/** + * 工具函数集合 + */ + +// HTML转义,防止XSS攻击 +function escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; +} + +// 滚动到指定元素的底部 +function scrollToBottom(element) { + if (element) { + element.scrollTop = element.scrollHeight; + } +} + +// 生成唯一ID +function generateId(prefix = 'id') { + return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; +} + +// 解析SVG响应,提取SVG内容和前后文本 +function parseSVGResponse(response) { + const svgRegex = /```svg\s*([\s\S]*?)```/i; + const match = response.match(svgRegex); + + if (match) { + const svgContent = match[1].trim(); + const beforeText = response.substring(0, match.index).trim(); + const afterText = response.substring(match.index + match[0].length).trim(); + + return { + svgContent, + beforeText, + afterText + }; + } + + return { + svgContent: null, + beforeText: response, + afterText: '' + }; +} + +// 下载文件 +function downloadFile(content, filename, mimeType = 'text/plain') { + const blob = new Blob([content], { type: mimeType }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); +} + +// 显示状态信息 +function showStatus(element, message, type = 'info') { + if (!element) return; + + element.classList.remove('hidden'); + element.textContent = message; + + // 移除所有状态类 + element.classList.remove('border-gray-300', 'bg-gray-50', 'text-gray-600'); + element.classList.remove('border-green-500', 'bg-green-50', 'text-green-700'); + element.classList.remove('border-red-500', 'bg-red-50', 'text-red-700'); + element.classList.remove('border-blue-500', 'bg-blue-50', 'text-blue-700'); + + // 根据类型添加相应的样式类 + switch (type) { + case 'success': + element.classList.add('border-green-500', 'bg-green-50', 'text-green-700'); + break; + case 'error': + element.classList.add('border-red-500', 'bg-red-50', 'text-red-700'); + break; + case 'loading': + element.classList.add('border-blue-500', 'bg-blue-50', 'text-blue-700'); + break; + default: + element.classList.add('border-gray-300', 'bg-gray-50', 'text-gray-600'); + } +} + +// 本地存储操作 +const storage = { + // 保存数据到本地存储 + set(key, value) { + try { + localStorage.setItem(key, JSON.stringify(value)); + return true; + } catch (error) { + console.error('保存到本地存储失败:', error); + return false; + } + }, + + // 从本地存储获取数据 + get(key, defaultValue = null) { + try { + const item = localStorage.getItem(key); + return item ? JSON.parse(item) : defaultValue; + } catch (error) { + console.error('从本地存储获取数据失败:', error); + return defaultValue; + } + }, + + // 删除本地存储中的数据 + remove(key) { + try { + localStorage.removeItem(key); + return true; + } catch (error) { + console.error('删除本地存储数据失败:', error); + return false; + } + } +}; + +// 防抖函数 +function debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; +} + +// 节流函数 +function throttle(func, limit) { + let inThrottle; + return function() { + const args = arguments; + const context = this; + if (!inThrottle) { + func.apply(context, args); + inThrottle = true; + setTimeout(() => inThrottle = false, limit); + } + }; +} + +// 格式化日期时间 +function formatDateTime(date = new Date()) { + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }); +} + +// 深拷贝对象 +function deepClone(obj) { + if (obj === null || typeof obj !== 'object') return obj; + if (obj instanceof Date) return new Date(obj.getTime()); + if (obj instanceof Array) return obj.map(item => deepClone(item)); + if (typeof obj === 'object') { + const clonedObj = {}; + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + clonedObj[key] = deepClone(obj[key]); + } + } + return clonedObj; + } +} + +// 检查对象是否为空 +function isEmpty(obj) { + if (obj == null) return true; + if (Array.isArray(obj) || typeof obj === 'string') return obj.length === 0; + if (typeof obj === 'object') return Object.keys(obj).length === 0; + return false; +} + +// 自动调整文本域高度 +function autoResizeTextarea(textarea) { + if (!textarea) return; + + // 重置高度以获取正确的scrollHeight + textarea.style.height = 'auto'; + + // 计算新高度,限制最大高度 + const newHeight = Math.min(textarea.scrollHeight, 120); // 最大120px(约5行) + textarea.style.height = newHeight + 'px'; +} + +// 流式文本处理 +class StreamProcessor { + constructor(onChunk, onComplete) { + this.onChunk = onChunk; + this.onComplete = onComplete; + this.buffer = ''; + } + + // 处理数据块 + processChunk(chunk) { + this.buffer += chunk; + + // 尝试解析完整的JSON行 + const lines = this.buffer.split('\n'); + this.buffer = lines.pop() || ''; // 保留不完整的行 + + for (const line of lines) { + if (line.trim()) { + try { + // 处理SSE格式 + if (line.startsWith('data: ')) { + const data = line.slice(6); + if (data === '[DONE]') { + this.onComplete(); + return; + } + + const parsed = JSON.parse(data); + this.onChunk(parsed); + } + } catch (error) { + console.warn('解析流数据失败:', error, line); + } + } + } + } +} + +// 创建流式请求 +async function createStreamRequest(url, options, onChunk, onComplete) { + const processor = new StreamProcessor(onChunk, onComplete); + + try { + const response = await fetch(url, { + ...options, + headers: { + ...options.headers, + 'Accept': 'text/event-stream', + 'Cache-Control': 'no-cache' + } + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const chunk = decoder.decode(value, { stream: true }); + processor.processChunk(chunk); + } + } catch (error) { + throw error; + } +} + +// 导出工具函数 +window.Utils = { + escapeHtml, + scrollToBottom, + generateId, + parseSVGResponse, + downloadFile, + showStatus, + storage, + debounce, + throttle, + formatDateTime, + deepClone, + isEmpty, + autoResizeTextarea, + StreamProcessor, + createStreamRequest +}; \ No newline at end of file diff --git a/prompts/canvas-prompt.txt b/prompts/canvas-prompt.txt new file mode 100644 index 0000000..02f11d2 --- /dev/null +++ b/prompts/canvas-prompt.txt @@ -0,0 +1,194 @@ +你是一个专业的产品战略分析师,擅长创建产品画布。 +请根据用户的需求生成一个详细的产品画布,并以SVG格式返回。 + + +请用中文回复,并在回复中包含SVG格式的产品画布图表。 +产品精益画布助手,下面是SVG画布的模板,注意使用markdown格式回复 +``` + + + + + + + + + + 产品精益画布 - 绿邻回收智能终端系统 + + + + + + + + + + 问题 + + 居民痛点: + • 卖废品麻烦,需囤积等人 + • 老人操作困难,现有方案不友好 + • 价格不透明,缺乏信任 + 商家痛点: + • 人工成本高,需专人管理 + • 技术门槛高,缺乏智能方案 + 现有替代方案局限: + • 流动回收车时间不固定 + • 人工回收点成本高 + 1 + + + + + + + + 解决方案 + + 智能终端系统: + • 平板显示二维码供扫描 + • 蓝牙电子秤自动称重 + • 大字体显示金额重量 + 多角色管理: + • 普通用户/管理员/清运员 + • 统一平台,智能分权 + 4 + + + + + + + + 独特卖点 + + + + 微信扫一扫, + 老少皆宜智能回收 + + + 对用户价值: + • 扫码即用,操作超简单 + • 价格透明,立即到账 + 对商家价值: + • 零人工成本,智能管理 + • 稳定增收,吸引客流 + 3 + + + + + + + + 门槛优势 + + 产品壁垒: + • 老人友好设计(市场空白) + • 低成本智能化方案 + 运营壁垒: + • 点位网络效应 + • 先发优势抢占资源 + 9 + + + + + + + + 客户群体分类 + + 核心用户 (C端): + • 社区中老年人 (扫码即用) + • 环保意识强的年轻家庭 + • 图方便的上班族 + 合作伙伴 (B端): + • 社区超市、便利店 + • 快递驿站、物业服务点 + 需求特征: + • 操作简单、零成本增收 + • 智能化管理、吸引客流 + 2 + + + + + + + + + 关键指标 + + 运营指标: + • 单终端日均交易量 + • 终端网络覆盖社区数 + 用户指标: + • 操作成功率、用户满意度 + • 用户复购率、推荐率 + 8 + + + + + + + + 渠道 + + 线下布点: + • 社区超市、快递站合作 + • 与物业/居委会合作 + 用户触达: + • 微信生态扫一扫直达 + • 业主群推广分享 + 5 + + + + + + + + + 成本分析 + + 单点成本: + • 硬件成本: 1000元/终端 • 部署成本: 2000元/点 + 运营成本: + • 技术维护、云服务 • 清运物流、客服支持 + 分成成本: + • 与场地方交易额分账 + 7 + + + + + + + + 收入分析 + + 主要收入流: + • 废品回收差价 (终端回收价 vs 批发销售价的差额) - 核心收入 + • 与合作场地按交易量分成 - 激励合作 + 增值收入流: + • 规模化后的数据服务收入 • 绿色积分商城佣金 + • 单点投入3000元,预期4-10个月回本 + 6 + + + + + +``` diff --git a/prompts/swot-prompt.txt b/prompts/swot-prompt.txt new file mode 100644 index 0000000..1f170f7 --- /dev/null +++ b/prompts/swot-prompt.txt @@ -0,0 +1,144 @@ +你是一个专业的商业战略分析师,擅长进行SWOT分析。 +请根据用户的需求生成一个详细的SWOT分析,并以SVG格式返回,注意使用markdown格式回复。 + +SWOT分析应包含以下四个维度: +1. 优势(Strengths) - 内部有利因素 + - 核心竞争力 + - 技术优势 + - 品牌价值 + - 团队能力 + - 资源优势 + +2. 劣势(Weaknesses) - 内部不利因素 + - 资源限制 + - 技术短板 + - 市场地位 + - 运营问题 + - 人才缺口 + +3. 机会(Opportunities) - 外部有利因素 + - 市场趋势 + - 政策支持 + - 技术发展 + - 消费变化 + - 合作可能 + +4. 威胁(Threats) - 外部不利因素 + - 竞争压力 + - 市场风险 + - 政策变化 + - 技术颠覆 + - 经济环境 + +请用中文回复,并在回复中包含SVG格式的SWOT分析图表。SVG应该使用现代、专业的设计风格,色彩搭配要协调,布局清晰易读。 + +下面是SVG模板 +``` + + + + + + + + SWOT战略分析模板 + + Strengths 优势 | Weaknesses 劣势 | Opportunities 机会 | Threats 威胁 + + + + + + + + S - 内部优势 (Strengths) + + 核心能力: + • 技术优势、专利技术、专业团队 + • 品牌声誉、客户忠诚度 + 资源优势: + • 资金实力、供应链优势 + • 渠道资源、合作伙伴关系 + 运营优势: + • 成本控制、效率优势 + • 组织文化、创新能力 + S + + + + + + + W - 内部劣势 (Weaknesses) + + 资源局限: + • 资金短缺、人才缺失 + • 技术短板、设备落后 + 运营问题: + • 管理混乱、流程不完善 + • 成本过高、效率低下 + 市场劣势: + • 品牌知名度低、客户基础薄弱 + • 产品竞争力不足、市场份额小 + W + + + + + + + O - 外部机会 (Opportunities) + + 市场机会: + • 新兴市场增长、政策扶持 + • 消费趋势变化、需求增长 + 技术机会: + • 新技术应用、数字化转型 + • 产业链升级、技术合作 + 合作机会: + • 战略联盟、并购机会 + • 国际化扩张、跨界合作 + O + + + + + + + T - 外部威胁 (Threats) + + 竞争威胁: + • 新进入者、替代产品 + • 价格战、市场份额争夺 + 环境威胁: + • 经济下行、政策变化 + • 供应链风险、原材料涨价 + 其他威胁: + • 技术变革冲击、消费者偏好变化 + • 法律法规风险、地缘政治影响 + T + + + + + + 内部因素 Internal + 外部因素 External + 积极因素 Positive + 消极因素 Negative + + + + 战略组合:SO增长型策略 | WO扭转型策略 | ST多元化策略 | WT防御型策略 + + + +``` \ No newline at end of file diff --git a/功能概述.md b/功能概述.md new file mode 100644 index 0000000..5cfda6f --- /dev/null +++ b/功能概述.md @@ -0,0 +1,99 @@ +# 产品画布/SWOT分析工具 - 功能概述 + +## 产品定位 +一个基于AI对话的产品战略分析工具,帮助用户快速生成产品画布和SWOT分析图表,支持产品规划和决策。 + +## 核心功能模块 + +### 1. 双模式切换系统 +- **产品画布模式**:生成和展示产品画布图表 +- **SWOT分析模式**:生成和展示SWOT分析图表 +- 模式切换时界面标题和提示文本同步更新 +- 当前模式状态视觉反馈(激活/非激活状态) + +### 2. AI对话交互系统 +- **消息发送**:用户输入文本请求,支持Enter键快捷发送 +- **对话历史**:保存并展示用户与AI的完整对话记录 +- **消息类型**: + - 用户消息 + - AI消息 +- **自动滚动**:新消息自动滚动到底部 + +### 3. 图表生成与展示系统 +- **SVG图表生成**:根据用户请求生成相应的SVG图表 +- **图表占位符**:对话中显示可点击的图表预览块 +- **图表渲染**:点击占位符在右侧面板完整展示SVG图表 +- **图表存储**:本地存储生成的SVG内容,支持历史查看 + +### 4. 消息操作功能 +- **退回功能**:回退到指定消息,删除该消息之后的所有对话 +- **重新生成**:针对最后一条AI消息重新请求生成内容,上下文是该消息之前的所有对话,包括SVG +- **悬浮显示**:鼠标悬停时显示操作按钮 + +### 5. 图表导出功能 +- **SVG下载**:将当前图表导出为SVG文件 +- **图片导出**:将当前图表导出为PNG图片格式 +- **代码查看**:查看当前图表的SVG源代码 + +## MVP功能范围 + +### 核心必备功能 +1. 基础对话交互(发送消息、显示回复) +2. 产品画布和SWOT分析两种模式切换 +3. SVG图表生成与基础展示 +4. 图表占位符点击查看功能 +5. 简单的图表导出功能(至少一种格式) + +### 次要功能(可后续迭代) +1. 消息退回和重新生成功能 +2. 多格式图表导出 +3. 对话历史持久化存储 +4. 图表编辑和自定义功能 +5. 分享和协作功能 + +## 用户流程 + +### 主要使用路径 +1. 用户访问应用,默认进入产品画布模式 +2. 用户在左侧输入框输入需求(如"生成一个电商产品画布") +3. AI回复并生成图表占位符 +4. 用户点击占位符,右侧显示完整图表 +5. 用户可切换到SWOT模式进行不同类型分析 +6. 用户可导出生成的图表 + +### 交互细节 +- 所有按钮和可点击元素都有悬浮效果 +- 图表占位符有明显的视觉提示和点击反馈 +- 模式切换有即时视觉反馈 +- 对话消息有操作按钮的渐显效果 + +## 技术要点 + +### 前端技术栈 +- HTML5 + CSS3 + JavaScript +- Tailwind CSS框架 +- Iconify图标库 +- SVG图形渲染 + +### 数据处理 +- SVG内容本地存储 +- 对话历史内容本地存储 +- 模式状态管理 + +### API集成 +- AI对话接口(模拟实现) +- 图表生成服务(模拟实现) + +## 用户体验设计原则 +1. **简洁直观**:界面布局清晰,功能一目了然 +2. **即时反馈**:所有操作都有视觉反馈 +3. **容错设计**:异常情况有友好提示 +4. **响应式布局**:适配不同屏幕尺寸 +5. **流畅交互**:动画过渡自然,操作流畅 + +## 成功指标 +1. 用户能够成功生成至少一种类型的图表 +2. 模式切换功能正常工作 +3. 图表能够正确导出 +4. 对话交互流畅无卡顿 +5. 界面响应时间在可接受范围内 \ No newline at end of file diff --git a/设计/原型.html b/设计/原型.html new file mode 100644 index 0000000..72a395b --- /dev/null +++ b/设计/原型.html @@ -0,0 +1,806 @@ + + + + + + 产品画布 / SWOT分析 + + + + + + + + + + +
+
+ +

产品画布

+
+ + +
+ + + + 点击切换模式 + + + + +
+
+ + +
+ + +
+ +
+ + +
+
+ 帮我生成一个电商产品的画布 +
+
+ + +
+
+
+ 好的!我为您生成了一个电商产品画布, +
+ 📊 点击查看产品画布 SVG +
+ 包含了目标用户、核心价值、关键功能等模块。点击上方标签可在右侧查看详细图表。 +
+ + +
+ + +
+
+
+ + +
+
+ 能不能调整一下配色方案? +
+
+ + +
+
+
+ 当然可以!我已经为您调整了配色, +
+ 📊 点击查看优化后的 SVG +
+ 采用了更加现代和鲜明的色彩组合,同时保持了良好的视觉层次。 +
+ + +
+ + +
+
+
+ +
+ + +
+
+ + +
+
+
+ + +
+
+
+ +

生成的产品画布将在此处显示

+
+
+ + +
+ + + +
+
+ +
+ + + + + + + + +``` \ No newline at end of file