819 lines
33 KiB
HTML
819 lines
33 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>产品画布 / SWOT分析</title>
|
||
<script src="https://cdn.tailwindcss.com/3.4.1"></script>
|
||
<script src="https://code.iconify.design/iconify-icon/2.1.0/iconify-icon.min.js"></script>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
|
||
<style>
|
||
body {
|
||
font-family: 'Inter', sans-serif;
|
||
}
|
||
|
||
/* Apple 风格边框 */
|
||
.apple-card {
|
||
border: 1px solid rgba(0,0,0,0.08);
|
||
border-radius: 12px;
|
||
box-shadow: 0 4px 6px rgba(0,0,0,0.07), 0 1px 3px rgba(0,0,0,0.06);
|
||
backdrop-filter: blur(20px);
|
||
}
|
||
|
||
/* 切换按钮激活状态 - Apple 风格 */
|
||
.mode-btn-active {
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
||
transform: scale(1.02);
|
||
}
|
||
|
||
.mode-btn-inactive {
|
||
opacity: 0.6;
|
||
}
|
||
|
||
/* 对话气泡样式 - Google 风格 */
|
||
.chat-bubble-user {
|
||
background: #4C76AB;
|
||
color: white;
|
||
padding: 12px 16px;
|
||
max-width: 80%;
|
||
border-radius: 18px 18px 4px 18px;
|
||
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.chat-bubble-ai {
|
||
background: #f8f9fa;
|
||
color: #464646;
|
||
padding: 12px 16px;
|
||
max-width: 85%;
|
||
border-radius: 4px 18px 18px 18px;
|
||
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
/* SVG占位符样式 - Google 风格 */
|
||
.svg-placeholder-block {
|
||
display: block;
|
||
background: #2B4269;
|
||
color: white;
|
||
padding: 10px 16px;
|
||
margin: 8px 0;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
|
||
font-weight: 600;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||
text-align: center;
|
||
}
|
||
|
||
.svg-placeholder-block:hover {
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.16), 0 2px 4px rgba(0,0,0,0.08);
|
||
transform: translateY(-1px);
|
||
background: #5A6270;
|
||
}
|
||
|
||
/* 气泡操作按钮 */
|
||
.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: rgba(255,255,255,0.95);
|
||
border-radius: 20px;
|
||
box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1),
|
||
0 10px 10px -5px rgba(0,0,0,0.04);
|
||
max-width: 500px;
|
||
width: 90%;
|
||
max-height: 90vh;
|
||
overflow-y: auto;
|
||
backdrop-filter: blur(20px);
|
||
border: 1px solid rgba(255,255,255,0.2);
|
||
}
|
||
|
||
/* 表单输入框样式 - Google 风格 */
|
||
.config-input {
|
||
width: 100%;
|
||
padding: 12px 16px;
|
||
border: 1px solid #dadce0;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||
color: #464646;
|
||
}
|
||
|
||
.config-input:focus {
|
||
outline: none;
|
||
border-color: #4C76AB;
|
||
box-shadow: 0 0 0 2px rgba(76, 118, 171, 0.1);
|
||
}
|
||
|
||
/* 齿轮旋转动画 */
|
||
@keyframes rotate {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
.settings-btn:hover iconify-icon {
|
||
animation: rotate 1s linear infinite;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body class="bg-gray-50 h-screen flex flex-col" style="background: linear-gradient(180deg, #f8f9fa 0%, #ffffff 100%);">
|
||
|
||
<!-- 顶部标题栏 -->
|
||
<header class="px-6 py-4 flex items-center justify-between" style="background: rgba(255,255,255,0.8); backdrop-filter: blur(20px); border-bottom: 1px solid rgba(0,0,0,0.06);">
|
||
<div class="flex items-center space-x-4">
|
||
<iconify-icon icon="ph:lightning-fill" class="text-3xl" style="color: #4C76AB;"></iconify-icon>
|
||
<h1 id="page-title" class="text-2xl font-semibold tracking-tight" style="color: #464646;">产品画布</h1>
|
||
</div>
|
||
|
||
<!-- 右侧按钮组 -->
|
||
<div class="flex items-center gap-4">
|
||
<!-- API配置按钮 -->
|
||
<button id="settings-btn" class="settings-btn p-3 rounded-full hover:bg-gray-100 transition-all" style="color: #666666;" title="API配置">
|
||
<iconify-icon icon="ph:gear-six-fill" class="text-xl"></iconify-icon>
|
||
</button>
|
||
|
||
<span class="font-medium text-sm" style="color: #888888;">点击切换模式</span>
|
||
<iconify-icon icon="ph:hand-pointing-fill" class="text-2xl wave-hand" style="color: #5A6270;"></iconify-icon>
|
||
|
||
<button id="canvas-mode-btn" class="mode-btn-active px-5 py-2.5 font-medium rounded-full hover:bg-gray-100 transition-all duration-300" style="color: #464646; background: rgba(76,118,171,0.1);">
|
||
<iconify-icon icon="ph:pen-nib-duotone" class="align-middle mr-2"></iconify-icon>
|
||
产品画布
|
||
</button>
|
||
<button id="swot-mode-btn" class="mode-btn-inactive px-5 py-2.5 font-medium rounded-full hover:bg-gray-100 transition-all duration-300" style="color: #666666;">
|
||
<iconify-icon icon="ph:chart-bar-duotone" class="align-middle mr-2"></iconify-icon>
|
||
SWOT分析
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- 主内容区 -->
|
||
<main class="flex-1 grid grid-cols-1 md:grid-cols-3 gap-4 p-4 overflow-hidden">
|
||
|
||
<!-- 左侧对话面板 -->
|
||
<div class="md:col-span-1 bg-white apple-card flex flex-col">
|
||
<!-- 对话历史区 -->
|
||
<div id="chat-history" class="flex-1 p-4 overflow-y-auto space-y-3">
|
||
|
||
<!-- 用户消息 -->
|
||
<div class="flex justify-end">
|
||
<div class="chat-bubble-user">
|
||
帮我生成一个电商产品的画布
|
||
</div>
|
||
</div>
|
||
|
||
<!-- AI回复 - 包含SVG占位符(换行) -->
|
||
<div class="flex justify-start">
|
||
<div class="chat-bubble-ai relative group" data-message-id="msg-1">
|
||
<div>
|
||
好的!我为您生成了一个电商产品画布,
|
||
<div class="svg-placeholder-block" data-svg-id="svg-1" onclick="viewSVG('svg-1')">
|
||
📊 点击查看产品画布 SVG
|
||
</div>
|
||
包含了目标用户、核心价值、关键功能等模块。点击上方标签可在右侧查看详细图表。
|
||
</div>
|
||
|
||
<!-- 悬浮操作按钮 -->
|
||
<div class="flex gap-2 mt-2 pt-2 border-t border-gray-200">
|
||
<button class="bubble-action-btn flex items-center gap-1 text-xs text-gray-600 hover:text-blue-600 transition-colors" onclick="rollbackToMessage('msg-1')">
|
||
<iconify-icon icon="ph:arrow-u-up-left-bold"></iconify-icon>
|
||
<span>退回</span>
|
||
</button>
|
||
<button class="bubble-action-btn flex items-center gap-1 text-xs text-gray-600 hover:text-green-600 transition-colors" onclick="regenerateMessage('msg-1')">
|
||
<iconify-icon icon="ph:arrow-clockwise-bold"></iconify-icon>
|
||
<span>重新生成</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 用户消息 -->
|
||
<div class="flex justify-end">
|
||
<div class="chat-bubble-user">
|
||
能不能调整一下配色方案?
|
||
</div>
|
||
</div>
|
||
|
||
<!-- AI回复 - 包含SVG占位符(换行) -->
|
||
<div class="flex justify-start">
|
||
<div class="chat-bubble-ai relative group" data-message-id="msg-2">
|
||
<div>
|
||
当然可以!我已经为您调整了配色,
|
||
<div class="svg-placeholder-block" data-svg-id="svg-2" onclick="viewSVG('svg-2')">
|
||
📊 点击查看优化后的 SVG
|
||
</div>
|
||
采用了更加现代和鲜明的色彩组合,同时保持了良好的视觉层次。
|
||
</div>
|
||
|
||
<!-- 悬浮操作按钮 -->
|
||
<div class="flex gap-2 mt-2 pt-2 border-t border-gray-200">
|
||
<button class="bubble-action-btn flex items-center gap-1 text-xs text-gray-600 hover:text-blue-600 transition-colors" onclick="rollbackToMessage('msg-2')">
|
||
<iconify-icon icon="ph:arrow-u-up-left-bold"></iconify-icon>
|
||
<span>退回</span>
|
||
</button>
|
||
<button class="bubble-action-btn flex items-center gap-1 text-xs text-gray-600 hover:text-green-600 transition-colors" onclick="regenerateMessage('msg-2')">
|
||
<iconify-icon icon="ph:arrow-clockwise-bold"></iconify-icon>
|
||
<span>重新生成</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- 输入区 -->
|
||
<div class="p-5 border-t bg-white" style="border-color: rgba(0,0,0,0.06);">
|
||
<div class="relative flex items-center gap-3">
|
||
<input
|
||
id="chat-input"
|
||
type="text"
|
||
placeholder="输入您的想法,按Enter发送..."
|
||
class="flex-1 px-5 py-4 border focus:outline-none transition-all font-normal rounded-2xl"
|
||
style="border-color: rgba(0,0,0,0.08); color: #464646; background: rgba(248,249,250,0.8);"
|
||
onfocus="this.style.borderColor='#4C76AB'; this.style.boxShadow='0 0 0 3px rgba(76,118,171,0.15)'; this.style.background='rgba(255,255,255,0.95)'"
|
||
onblur="this.style.borderColor='rgba(0,0,0,0.08)'; this.style.boxShadow='none'; this.style.background='rgba(248,249,250,0.8)'"
|
||
/>
|
||
<button id="send-button" class="p-3 rounded-full transition-all hover:scale-105" style="color: white; background: #4C76AB; box-shadow: 0 2px 8px rgba(76,118,171,0.3);" onmouseover="this.style.transform='scale(1.05)'" onmouseout="this.style.transform='scale(1)'">
|
||
<iconify-icon icon="ph:paper-plane-tilt-fill" class="text-xl"></iconify-icon>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧显示面板 -->
|
||
<div class="md:col-span-2 bg-white apple-card flex flex-col">
|
||
<div id="svg-viewer" class="flex-1 flex items-center justify-center p-8 overflow-auto" style="background: linear-gradient(135deg, #fafafa 0%, #f8f9fa 100%);">
|
||
<div id="svg-placeholder" class="text-center" style="color: #787878;">
|
||
<iconify-icon icon="ph:image-square" class="text-6xl mx-auto" style="color: #5586F5;"></iconify-icon>
|
||
<p class="mt-2 font-bold" id="placeholder-text">生成的产品画布将在此处显示</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 底部操作栏 -->
|
||
<div class="p-5 border-t flex justify-end items-center gap-4" style="border-color: rgba(0,0,0,0.06); background: rgba(248,249,250,0.8);">
|
||
<button id="download-svg-btn" class="p-3 text-white rounded-xl transition-all" style="background: #5A6270; box-shadow: 0 4px 12px rgba(90,98,112,0.25);" title="下载SVG" onmouseover="this.style.transform='translateY(-2px)'" onmouseout="this.style.transform='translateY(0)'">
|
||
<iconify-icon icon="mdi:download-outline" class="text-lg"></iconify-icon>
|
||
</button>
|
||
<button id="export-image-btn" class="p-3 text-white rounded-xl transition-all" style="background: #2B4269; box-shadow: 0 4px 12px rgba(43,66,105,0.25);" title="导出为图片" onmouseover="this.style.transform='translateY(-2px)'" onmouseout="this.style.transform='translateY(0)'">
|
||
<iconify-icon icon="mdi:image-outline" class="text-lg"></iconify-icon>
|
||
</button>
|
||
<button id="view-code-btn" class="p-3 text-white rounded-xl transition-all" style="background: #4C76AB; box-shadow: 0 4px 12px rgba(76,118,171,0.25);" title="查看代码" onmouseover="this.style.transform='translateY(-2px)'" onmouseout="this.style.transform='translateY(0)'">
|
||
<iconify-icon icon="mdi:code-tags" class="text-lg"></iconify-icon>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<!-- API配置模态窗 -->
|
||
<div id="config-modal" class="modal-overlay">
|
||
<div class="modal-content">
|
||
<!-- 模态窗头部 -->
|
||
<div class="p-6 border-b flex items-center justify-between" style="background: rgba(255,255,255,0.95); border-color: rgba(0,0,0,0.06); border-radius: 20px 20px 0 0;">
|
||
<div class="flex items-center gap-4">
|
||
<iconify-icon icon="ph:plugs-connected-fill" class="text-2xl" style="color: #4C76AB;"></iconify-icon>
|
||
<h2 class="text-xl font-semibold" style="color: #464646;">API 配置</h2>
|
||
</div>
|
||
<button id="close-modal-btn" class="hover:bg-gray-100 p-3 rounded-full transition-all" style="color: #666666;" onmouseover="this.style.backgroundColor='#f0f0f0'" onmouseout="this.style.backgroundColor='transparent'">
|
||
<iconify-icon icon="ph:x-bold" class="text-xl"></iconify-icon>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 模态窗内容 -->
|
||
<div class="p-6 space-y-4">
|
||
<!-- API URL -->
|
||
<div>
|
||
<label class="block font-semibold mb-3 flex items-center gap-3" style="color: #464646;">
|
||
<iconify-icon icon="ph:link-bold" class="text-lg" style="color: #4C76AB;"></iconify-icon>
|
||
API URL
|
||
</label>
|
||
<input
|
||
id="api-url"
|
||
type="text"
|
||
placeholder="https://api.example.com/v1/chat"
|
||
class="config-input"
|
||
value=""
|
||
/>
|
||
</div>
|
||
|
||
<!-- API Key -->
|
||
<div>
|
||
<label class="block font-semibold mb-3 flex items-center gap-3" style="color: #464646;">
|
||
<iconify-icon icon="ph:key-bold" class="text-lg" style="color: #2B4269;"></iconify-icon>
|
||
API Key
|
||
</label>
|
||
<input
|
||
id="api-key"
|
||
type="password"
|
||
placeholder="sk-xxxxxxxxxxxxxxxx"
|
||
class="config-input"
|
||
value=""
|
||
/>
|
||
</div>
|
||
|
||
<!-- Model -->
|
||
<div>
|
||
<label class="block font-semibold mb-3 flex items-center gap-3" style="color: #464646;">
|
||
<iconify-icon icon="ph:robot-bold" class="text-lg" style="color: #5A6270;"></iconify-icon>
|
||
模型 (Model)
|
||
</label>
|
||
<input
|
||
id="api-model"
|
||
type="text"
|
||
placeholder="gpt-4-turbo"
|
||
class="config-input"
|
||
value=""
|
||
/>
|
||
</div>
|
||
|
||
<!-- 状态显示 -->
|
||
<div id="config-status" class="p-4 border rounded-2xl bg-gray-50 text-sm hidden" style="border-color: rgba(0,0,0,0.06); color: #888888;">
|
||
<iconify-icon icon="ph:info" class="align-middle"></iconify-icon>
|
||
<span id="status-text">等待操作...</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 模态窗底部按钮 -->
|
||
<div class="p-6 border-t bg-white flex gap-4 justify-end" style="border-color: rgba(0,0,0,0.06); border-radius: 0 0 20px 20px;">
|
||
<button id="test-api-btn" class="px-6 py-3 text-white font-medium rounded-2xl transition-all flex items-center gap-3" style="background: #5A6270; box-shadow: 0 4px 12px rgba(90,98,112,0.25);" onmouseover="this.style.transform='translateY(-2px)'" onmouseout="this.style.transform='translateY(0)'">
|
||
<iconify-icon icon="ph:flask-bold"></iconify-icon>
|
||
测试连接
|
||
</button>
|
||
<button id="save-config-btn" class="px-6 py-3 text-white font-medium rounded-2xl transition-all flex items-center gap-3" style="background: #4C76AB; box-shadow: 0 4px 12px rgba(76,118,171,0.25);" onmouseover="this.style.transform='translateY(-2px)'" onmouseout="this.style.transform='translateY(0)'">
|
||
<iconify-icon icon="ph:floppy-disk-bold"></iconify-icon>
|
||
保存配置
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// ===== 全局变量 =====
|
||
let currentMode = 'canvas';
|
||
let svgStorage = {};
|
||
let currentSvgId = null;
|
||
let apiConfig = {
|
||
url: '',
|
||
key: '',
|
||
model: ''
|
||
};
|
||
|
||
// ===== DOM元素 =====
|
||
const canvasBtn = document.getElementById('canvas-mode-btn');
|
||
const swotBtn = document.getElementById('swot-mode-btn');
|
||
const pageTitle = document.getElementById('page-title');
|
||
const placeholderText = document.getElementById('placeholder-text');
|
||
const chatInput = document.getElementById('chat-input');
|
||
const sendButton = document.getElementById('send-button');
|
||
const chatHistory = document.getElementById('chat-history');
|
||
const svgViewer = document.getElementById('svg-viewer');
|
||
|
||
// 模态窗元素
|
||
const settingsBtn = document.getElementById('settings-btn');
|
||
const configModal = document.getElementById('config-modal');
|
||
const closeModalBtn = document.getElementById('close-modal-btn');
|
||
const apiUrlInput = document.getElementById('api-url');
|
||
const apiKeyInput = document.getElementById('api-key');
|
||
const apiModelInput = document.getElementById('api-model');
|
||
const testApiBtn = document.getElementById('test-api-btn');
|
||
const saveConfigBtn = document.getElementById('save-config-btn');
|
||
const configStatus = document.getElementById('config-status');
|
||
const statusText = document.getElementById('status-text');
|
||
|
||
// ===== 初始化 - 加载已保存的配置 =====
|
||
function loadConfig() {
|
||
const saved = localStorage.getItem('apiConfig');
|
||
if (saved) {
|
||
apiConfig = JSON.parse(saved);
|
||
apiUrlInput.value = apiConfig.url || '';
|
||
apiKeyInput.value = apiConfig.key || '';
|
||
apiModelInput.value = apiConfig.model || '';
|
||
}
|
||
}
|
||
|
||
// ===== 模态窗控制 =====
|
||
settingsBtn.addEventListener('click', () => {
|
||
configModal.classList.add('active');
|
||
loadConfig();
|
||
});
|
||
|
||
closeModalBtn.addEventListener('click', () => {
|
||
configModal.classList.remove('active');
|
||
});
|
||
|
||
configModal.addEventListener('click', (e) => {
|
||
if (e.target === configModal) {
|
||
configModal.classList.remove('active');
|
||
}
|
||
});
|
||
|
||
// ===== 保存配置 =====
|
||
saveConfigBtn.addEventListener('click', () => {
|
||
apiConfig.url = apiUrlInput.value.trim();
|
||
apiConfig.key = apiKeyInput.value.trim();
|
||
apiConfig.model = apiModelInput.value.trim();
|
||
|
||
if (!apiConfig.url || !apiConfig.key || !apiConfig.model) {
|
||
showStatus('⚠️ 请填写所有字段', 'error');
|
||
return;
|
||
}
|
||
|
||
localStorage.setItem('apiConfig', JSON.stringify(apiConfig));
|
||
showStatus('✅ 配置已保存成功!', 'success');
|
||
|
||
setTimeout(() => {
|
||
configModal.classList.remove('active');
|
||
}, 1500);
|
||
});
|
||
|
||
// ===== 测试API连接 =====
|
||
testApiBtn.addEventListener('click', async () => {
|
||
const url = apiUrlInput.value.trim();
|
||
const key = apiKeyInput.value.trim();
|
||
const model = apiModelInput.value.trim();
|
||
|
||
if (!url || !key || !model) {
|
||
showStatus('⚠️ 请先填写所有字段', 'error');
|
||
return;
|
||
}
|
||
|
||
showStatus('🔄 正在测试连接...', 'loading');
|
||
|
||
// TODO: 实现真实的API测试
|
||
setTimeout(() => {
|
||
// 模拟测试成功
|
||
showStatus('✅ 连接测试成功!', 'success');
|
||
|
||
// 真实实现示例:
|
||
/*
|
||
try {
|
||
const response = await fetch(url, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': `Bearer ${key}`,
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
model: model,
|
||
messages: [{role: 'user', content: 'test'}],
|
||
max_tokens: 5
|
||
})
|
||
});
|
||
|
||
if (response.ok) {
|
||
showStatus('✅ 连接测试成功!', 'success');
|
||
} else {
|
||
showStatus('❌ 连接失败: ' + response.statusText, 'error');
|
||
}
|
||
} catch (error) {
|
||
showStatus('❌ 连接失败: ' + error.message, 'error');
|
||
}
|
||
*/
|
||
}, 1500);
|
||
});
|
||
|
||
// ===== 显示状态信息 =====
|
||
function showStatus(message, type) {
|
||
configStatus.classList.remove('hidden');
|
||
statusText.textContent = message;
|
||
|
||
configStatus.style.borderColor = '#787878';
|
||
configStatus.style.backgroundColor = '#f9fafb';
|
||
configStatus.style.color = '#787878';
|
||
|
||
if (type === 'success') {
|
||
configStatus.style.borderColor = '#47A74F';
|
||
configStatus.style.backgroundColor = '#e8f5e9';
|
||
configStatus.style.color = '#2e7d32';
|
||
} else if (type === 'error') {
|
||
configStatus.style.borderColor = '#E04639';
|
||
configStatus.style.backgroundColor = '#ffebee';
|
||
configStatus.style.color = '#c62828';
|
||
} else if (type === 'loading') {
|
||
configStatus.style.borderColor = '#5586F5';
|
||
configStatus.style.backgroundColor = '#e3f2fd';
|
||
configStatus.style.color = '#1565c0';
|
||
}
|
||
}
|
||
|
||
// ===== 模式切换 =====
|
||
canvasBtn.addEventListener('click', () => {
|
||
if (currentMode !== 'canvas') {
|
||
currentMode = 'canvas';
|
||
updateModeUI();
|
||
}
|
||
});
|
||
|
||
swotBtn.addEventListener('click', () => {
|
||
if (currentMode !== 'swot') {
|
||
currentMode = 'swot';
|
||
updateModeUI();
|
||
}
|
||
});
|
||
|
||
function updateModeUI() {
|
||
if (currentMode === 'canvas') {
|
||
canvasBtn.classList.add('mode-btn-active');
|
||
canvasBtn.classList.remove('mode-btn-inactive');
|
||
swotBtn.classList.remove('mode-btn-active');
|
||
swotBtn.classList.add('mode-btn-inactive');
|
||
pageTitle.textContent = '产品画布';
|
||
if (!currentSvgId) {
|
||
placeholderText.textContent = '生成的产品画布将在此处显示';
|
||
}
|
||
} else {
|
||
swotBtn.classList.add('mode-btn-active');
|
||
swotBtn.classList.remove('mode-btn-inactive');
|
||
canvasBtn.classList.remove('mode-btn-active');
|
||
canvasBtn.classList.add('mode-btn-inactive');
|
||
pageTitle.textContent = 'SWOT分析';
|
||
if (!currentSvgId) {
|
||
placeholderText.textContent = '生成的SWOT分析将在此处显示';
|
||
}
|
||
}
|
||
}
|
||
|
||
// ===== 发送消息 =====
|
||
sendButton.addEventListener('click', sendMessage);
|
||
chatInput.addEventListener('keypress', (e) => {
|
||
if (e.key === 'Enter') {
|
||
sendMessage();
|
||
}
|
||
});
|
||
|
||
function sendMessage() {
|
||
const message = chatInput.value.trim();
|
||
if (!message) return;
|
||
|
||
// 检查API配置
|
||
if (!apiConfig.url || !apiConfig.key || !apiConfig.model) {
|
||
alert('⚠️ 请先配置API设置!点击右上角齿轮图标进行配置。');
|
||
settingsBtn.click();
|
||
return;
|
||
}
|
||
|
||
addUserMessage(message);
|
||
chatInput.value = '';
|
||
|
||
// 模拟API调用(TODO: 替换为真实调用)
|
||
setTimeout(() => {
|
||
simulateAPIResponse(message);
|
||
}, 1000);
|
||
}
|
||
|
||
// ===== 添加用户消息 =====
|
||
function addUserMessage(text) {
|
||
const messageDiv = document.createElement('div');
|
||
messageDiv.className = 'flex justify-end';
|
||
messageDiv.innerHTML = `
|
||
<div class="chat-bubble-user">
|
||
${escapeHtml(text)}
|
||
</div>
|
||
`;
|
||
chatHistory.appendChild(messageDiv);
|
||
scrollToBottom();
|
||
}
|
||
|
||
// ===== 添加AI消息 =====
|
||
function addAIMessage(fullResponse) {
|
||
const messageId = 'msg-' + Date.now();
|
||
const parsed = parseSVGResponse(fullResponse);
|
||
|
||
if (parsed.svgContent) {
|
||
const svgId = 'svg-' + Date.now();
|
||
svgStorage[svgId] = {
|
||
content: parsed.svgContent,
|
||
messageId: messageId
|
||
};
|
||
|
||
viewSVG(svgId);
|
||
|
||
const messageDiv = document.createElement('div');
|
||
messageDiv.className = 'flex justify-start';
|
||
messageDiv.innerHTML = `
|
||
<div class="chat-bubble-ai relative group" data-message-id="${messageId}">
|
||
<div>
|
||
${escapeHtml(parsed.beforeText)}
|
||
<div class="svg-placeholder-block" data-svg-id="${svgId}" onclick="viewSVG('${svgId}')">
|
||
📊 点击查看 SVG
|
||
</div>
|
||
${escapeHtml(parsed.afterText)}
|
||
</div>
|
||
|
||
<div class="flex gap-2 mt-2 pt-2 border-t border-gray-200">
|
||
<button class="bubble-action-btn flex items-center gap-1 text-xs text-gray-600 hover:text-blue-600 transition-colors" onclick="rollbackToMessage('${messageId}')">
|
||
<iconify-icon icon="ph:arrow-u-up-left-bold"></iconify-icon>
|
||
<span>退回</span>
|
||
</button>
|
||
<button class="bubble-action-btn flex items-center gap-1 text-xs text-gray-600 hover:text-green-600 transition-colors" onclick="regenerateMessage('${messageId}')">
|
||
<iconify-icon icon="ph:arrow-clockwise-bold"></iconify-icon>
|
||
<span>重新生成</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
chatHistory.appendChild(messageDiv);
|
||
} else {
|
||
const messageDiv = document.createElement('div');
|
||
messageDiv.className = 'flex justify-start';
|
||
messageDiv.innerHTML = `
|
||
<div class="chat-bubble-ai relative group" data-message-id="${messageId}">
|
||
<div class="mb-1">
|
||
${escapeHtml(fullResponse)}
|
||
</div>
|
||
|
||
<div class="flex gap-2 mt-2 pt-2 border-t border-gray-200">
|
||
<button class="bubble-action-btn flex items-center gap-1 text-xs text-gray-600 hover:text-blue-600 transition-colors" onclick="rollbackToMessage('${messageId}')">
|
||
<icon ify-icon icon="ph:arrow-u-up-left-bold"></iconify-icon>
|
||
<span>退回</span>
|
||
</button>
|
||
<button class="bubble-action-btn flex items-center gap-1 text-xs text-gray-600 hover:text-green-600 transition-colors" onclick="regenerateMessage('${messageId}')">
|
||
<iconify-icon icon="ph:arrow-clockwise-bold"></iconify-icon>
|
||
<span>重新生成</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
chatHistory.appendChild(messageDiv);
|
||
}
|
||
|
||
scrollToBottom();
|
||
}
|
||
|
||
// ===== 解析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: ''
|
||
};
|
||
}
|
||
|
||
// ===== 显示SVG =====
|
||
function viewSVG(svgId) {
|
||
if (!svgStorage[svgId]) {
|
||
console.error('SVG not found:', svgId);
|
||
return;
|
||
}
|
||
|
||
currentSvgId = svgId;
|
||
const svgContent = svgStorage[svgId].content;
|
||
svgViewer.innerHTML = svgContent;
|
||
}
|
||
|
||
// ===== 气泡操作功能 =====
|
||
function rollbackToMessage(messageId) {
|
||
console.log('退回到消息:', messageId);
|
||
// TODO: 实现退回逻辑
|
||
}
|
||
|
||
function regenerateMessage(messageId) {
|
||
console.log('重新生成消息:', messageId);
|
||
// TODO: 实现重新生成逻辑
|
||
}
|
||
|
||
// ===== 底部操作按钮 =====
|
||
document.getElementById('download-svg-btn').addEventListener('click', () => {
|
||
if (!currentSvgId) {
|
||
alert('请先生成SVG图表');
|
||
return;
|
||
}
|
||
|
||
const svgContent = svgStorage[currentSvgId].content;
|
||
const blob = new Blob([svgContent], { type: 'image/svg+xml' });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = `${currentMode}-${Date.now()}.svg`;
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
});
|
||
|
||
document.getElementById('export-image-btn').addEventListener('click', () => {
|
||
if (!currentSvgId) {
|
||
alert('请先生成SVG图表');
|
||
return;
|
||
}
|
||
console.log('导出为图片:', currentSvgId);
|
||
// TODO: 实现SVG转PNG功能
|
||
});
|
||
|
||
document.getElementById('view-code-btn').addEventListener('click', () => {
|
||
if (!currentSvgId) {
|
||
alert('请先生成SVG图表');
|
||
return;
|
||
}
|
||
|
||
const svgContent = svgStorage[currentSvgId].content;
|
||
alert('SVG代码:\n\n' + svgContent);
|
||
// TODO: 使用更好的代码展示弹窗
|
||
});
|
||
|
||
// ===== 工具函数 =====
|
||
function escapeHtml(text) {
|
||
const div = document.createElement('div');
|
||
div.textContent = text;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
function scrollToBottom() {
|
||
chatHistory.scrollTop = chatHistory.scrollHeight;
|
||
}
|
||
|
||
// ===== 模拟API响应(测试用) =====
|
||
function simulateAPIResponse(userMessage) {
|
||
const mockResponses = [
|
||
`好的!我为您生成了一个${currentMode === 'canvas' ? '产品画布' : 'SWOT分析'}
|
||
\`\`\`svg
|
||
<svg width="600" height="400" xmlns="http://www.w3.org/2000/svg">
|
||
<rect width="100%" height="100%" fill="#f0f0f0"/>
|
||
<text x="300" y="200" text-anchor="middle" font-size="24" fill="#333">
|
||
这是${currentMode === 'canvas' ? '产品画布' : 'SWOT分析'}示例SVG
|
||
</text>
|
||
<circle cx="300" cy="250" r="50" fill="#667eea" opacity="0.5"/>
|
||
</svg>
|
||
\`\`\`
|
||
包含了关键要素和模块。点击上方标签可在右侧查看详细图表。`,
|
||
|
||
`已经为您调整完成!
|
||
\`\`\`svg
|
||
<svg width="600" height="400" xmlns="http://www.w3.org/2000/svg">
|
||
<defs>
|
||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
|
||
<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />
|
||
<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />
|
||
</linearGradient>
|
||
</defs>
|
||
<rect width="100%" height="100%" fill="url(#grad1)"/>
|
||
<text x="300" y="200" text-anchor="middle" font-size="28" fill="white" font-weight="bold">
|
||
${currentMode === 'canvas' ? '优化后的产品画布' : '优化后的SWOT分析'}
|
||
</text>
|
||
</svg>
|
||
\`\`\`
|
||
采用了更加鲜明的色彩组合,希望您满意!`
|
||
];
|
||
|
||
const response = mockResponses[Math.floor(Math.random() * mockResponses.length)];
|
||
addAIMessage(response);
|
||
}
|
||
|
||
// ===== 页面加载时初始化 =====
|
||
loadConfig();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|
||
``` |