优化APIClient和conversation-service优先使用rawContent提升上下文完整性;调整app-shell.js中对话气泡流式内容渲染逻辑及HTML模块特殊处理;提升STREAM_DEFAULT_OPTIONS maxTokens至30000;重构onepage-prompt.txt交付描述;大幅美化产品画布页面UI,采用Apple和Google风格边框、按钮、气泡,优化配色、阴影、交互效果和模态窗样式,提升整体视觉和交互体验。

This commit is contained in:
史悦
2025-11-19 11:28:44 +08:00
parent 24adda3f14
commit 19afa19462
5 changed files with 170 additions and 119 deletions

View File

@@ -298,7 +298,8 @@ class APIClient {
.filter(msg => msg.id <= messageId) .filter(msg => msg.id <= messageId)
.map(msg => ({ .map(msg => ({
role: msg.type === 'user' ? 'user' : 'assistant', role: msg.type === 'user' ? 'user' : 'assistant',
content: msg.content // 优先使用原始内容,保证上下文完整性
content: msg.rawContent || msg.content || ''
})); }));
if (contextMessages.length === 0) { if (contextMessages.length === 0) {

View File

@@ -2,7 +2,7 @@
'use strict'; 'use strict';
const STREAM_DEFAULT_OPTIONS = { const STREAM_DEFAULT_OPTIONS = {
maxTokens: 13000, maxTokens: 30000,
temperature: 0.7 temperature: 0.7
}; };
@@ -691,7 +691,8 @@
.filter((msg) => msg.type === 'user' || msg.type === 'ai') .filter((msg) => msg.type === 'user' || msg.type === 'ai')
.map((msg) => ({ .map((msg) => ({
role: msg.type === 'user' ? 'user' : 'assistant', role: msg.type === 'user' ? 'user' : 'assistant',
content: msg.content // 使用原始内容作为 LLM 上下文,兼容无 rawContent 的旧记录
content: msg.rawContent || msg.content || ''
})); }));
this.isProcessing = true; this.isProcessing = true;
@@ -836,7 +837,7 @@
const delta = chunk?.choices?.[0]?.delta?.content || ''; const delta = chunk?.choices?.[0]?.delta?.content || '';
if (!delta) return; if (!delta) return;
fullContent += delta; fullContent += delta;
this.updateStreamingContent(container, fullContent); this.updateStreamingContent(container, fullContent, streamState, manifest);
if (manifest.artifact?.type === 'svg') { if (manifest.artifact?.type === 'svg') {
this.processSvgStreamChunk(manifest, fullContent, streamState); this.processSvgStreamChunk(manifest, fullContent, streamState);
} else if (manifest.artifact?.type === 'mermaid') { } else if (manifest.artifact?.type === 'mermaid') {
@@ -893,13 +894,21 @@
return messageDiv; return messageDiv;
} }
updateStreamingContent(container, content) { updateStreamingContent(container, content, streamContext = null, manifest = null) {
const cursor = container.querySelector('.typing-cursor'); const cursor = container.querySelector('.typing-cursor');
if (!cursor) return; if (!cursor) return;
// 对于 HTML 模块HTML 内容已经在右侧预览区域实时渲染,
// 为避免在对话气泡中直接注入 <html>/<body> 文档结构,这里不再渲染流式内容。
if (manifest?.artifact?.type === 'html') {
return;
}
const displayContent = content;
if (typeof marked !== 'undefined') { if (typeof marked !== 'undefined') {
cursor.innerHTML = marked.parse(content); cursor.innerHTML = marked.parse(displayContent);
} else { } else {
cursor.textContent = content; cursor.textContent = displayContent;
} }
Utils.scrollToBottom(this.el.chatHistory); Utils.scrollToBottom(this.el.chatHistory);
} }
@@ -994,7 +1003,10 @@
const messageRecord = { const messageRecord = {
id: messageId, id: messageId,
type: 'ai', type: 'ai',
// content 用于展示(可能是裁剪/清洗后的文本)
content: messageContent, content: messageContent,
// rawContent 保留完整的 LLM 原始响应,用于上下文与重新生成
rawContent: fullContent,
timestamp, timestamp,
artifactId artifactId
}; };
@@ -1027,6 +1039,7 @@
type: 'ai', type: 'ai',
content: content:
content || '生成已被手动终止,您可以点击重新生成继续。', content || '生成已被手动终止,您可以点击重新生成继续。',
rawContent: fullContent || content || '',
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
artifactId: null, artifactId: null,
interrupted: true interrupted: true
@@ -1084,7 +1097,21 @@
} }
} }
const content = segments.filter(Boolean).join('\n\n').trim(); let content = segments.filter(Boolean).join('\n\n').trim();
// 对于 HTML 模块,额外清理尾部可能残留的空 ```html 代码块
// 场景:模型输出两个 ```html 代码块,第二个为空;解析器只提取第一个,
// 剩余的空代码块会进入 before/after最终在气泡中渲染出一个空的 <pre><code>。
if (manifest.artifact?.type === 'html' && content) {
const withoutEmptyHtmlFence = content.replace(
/\n*```(?:html|htm)?\s*```[\s]*$/i,
''
);
if (withoutEmptyHtmlFence.trim()) {
content = withoutEmptyHtmlFence.trim();
}
}
if (content) { if (content) {
return content; return content;
} }
@@ -1093,6 +1120,15 @@
return `已生成 ${manifest.label} 图表,请点击占位卡片查看。`; return `已生成 ${manifest.label} 图表,请点击占位卡片查看。`;
} }
// HTML 模块:避免将完整 HTML 文档渲染到对话气泡中
if (manifest.artifact?.type === 'html') {
const safeText = (rawContent || '')
// 去掉主/副 ```html 代码块,保留其余说明文字
.replace(/```(?:html|htm)?[\s\S]*?```/gi, '')
.trim();
return safeText || `已生成 ${manifest.label} 页面内容,请查看预览区域。`;
}
return rawContent.trim(); return rawContent.trim();
} }

View File

@@ -102,7 +102,8 @@
.filter((msg) => msg.type === 'user' || msg.type === 'ai') .filter((msg) => msg.type === 'user' || msg.type === 'ai')
.map((msg) => ({ .map((msg) => ({
role: msg.type === 'user' ? 'user' : 'assistant', role: msg.type === 'user' ? 'user' : 'assistant',
content: msg.content // 优先使用原始内容构建上下文,兼容旧数据回退到 content
content: msg.rawContent || msg.content || ''
})); }));
return { return {

View File

@@ -7,7 +7,7 @@
**我的操作流程 (Operational Protocol):** **我的操作流程 (Operational Protocol):**
1. **分析与规划 (Analyze & Plan):** 我首先会分析用户的需求并制定一个简明的实现计划。我将概述我将采取的具体步骤、关键的设计元素并简要提及潜在的挑战。这个计划将以自然语言描述简洁2-4行 1. **分析与规划 (Analyze & Plan):** 我首先会分析用户的需求并制定一个简明的实现计划。我将概述我将采取的具体步骤、关键的设计元素并简要提及潜在的挑战。这个计划将以自然语言描述简洁2-4行
2. **生成代码 (Generate Code):** 我将根据规划编写完整的、统一的HTML文件。这个文件包括HTML结构、用于Tailwind CSS定制的`<style>`标签以及任何必需的JavaScript代码。 2. **生成代码 (Generate Code):** 我将根据规划编写完整的、统一的HTML文件。这个文件包括HTML结构、用于Tailwind CSS定制的`<style>`标签以及任何必需的JavaScript代码。
3. **交付与总结 (Deliver & Summarize):** 我将最终的HTML文件呈现在一个markdown代码块中。代码之后我将提供一份简短的工作总结。 3. **交付与总结 (Deliver & Summarize):** 我将最终的HTML文件呈现在一个markdown代码块中。代码之后我将提供一份简短的工作总结。我要保证每次对话只输出唯一markdown代码块并保证是完整的HTML
**我的技术栈与限制 (Tech Stack & Constraints):** **我的技术栈与限制 (Tech Stack & Constraints):**
* **技术 (Technology):** 我仅使用原生HTML、CSS和JavaScript进行构建。我不会使用任何前端框架如React、Vue或Angular。 * **技术 (Technology):** 我仅使用原生HTML、CSS和JavaScript进行构建。我不会使用任何前端框架如React、Vue或Angular。
@@ -27,7 +27,7 @@
* **丰富交互 (Rich Interactivity):** 我为交互而设计,实现精致的悬停效果、流畅的动画过渡、视差滚动和滚动触发事件,使页面充满活力。 * **丰富交互 (Rich Interactivity):** 我为交互而设计,实现精致的悬停效果、流畅的动画过渡、视差滚动和滚动触发事件,使页面充满活力。
* **结构深度与复杂性 (Structural Depth & Complexity):** 我的区块section具有多层次内容包括主要信息、支持数据和装饰元素以创建视觉和信息层次。我确保每个区块包含至少6个精心设计的子元素构建多层次结构。 * **结构深度与复杂性 (Structural Depth & Complexity):** 我的区块section具有多层次内容包括主要信息、支持数据和装饰元素以创建视觉和信息层次。我确保每个区块包含至少6个精心设计的子元素构建多层次结构。
* **内容多样性 (Content Diversity):** 我采用多种内容呈现方式,如网格、卡片、时间轴和图文混排布局,以避免单调。 * **内容多样性 (Content Diversity):** 我采用多种内容呈现方式,如网格、卡片、时间轴和图文混排布局,以避免单调。
* **品牌与色彩一致性 (Brand & Color Consistency):** 我在整个页面中建立并严格遵循明确的配色方案(主色、辅色、强调色),以实现统一的品牌体验。所有元素,从按钮到边框,都遵循此配色方案。 * **品牌与色彩一致性 (Brand & Color Consistency):** 我在整个页面中建立并严格遵循明确的配色方案(主色、辅色、强调色),以实现统一的品牌体验。我会注意前景色和背景色,不会让文字和背景过于重叠导致文字看不清。所有元素,从按钮到边框,都遵循此配色方案。
* **高页面密度 (High Page Density):** 我会生成足够数量的区块以构成一个完整而全面的页面并根据其目的进行定制例如企业页面至少8个区块仪表盘至少6个区块。简单的页面如联系表单是例外但其功能将是完整的。 * **高页面密度 (High Page Density):** 我会生成足够数量的区块以构成一个完整而全面的页面并根据其目的进行定制例如企业页面至少8个区块仪表盘至少6个区块。简单的页面如联系表单是例外但其功能将是完整的。
**我的边界条件 (Boundary Conditions):** **我的边界条件 (Boundary Conditions):**

View File

@@ -14,61 +14,63 @@
font-family: 'Inter', sans-serif; font-family: 'Inter', sans-serif;
} }
/* 狂野线条效果 */ /* Apple 风格边框 */
.wild-border { .apple-card {
border: 3px solid; border: 1px solid rgba(0,0,0,0.08);
box-shadow: 4px 4px 0px rgba(0,0,0,0.3); 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 { .mode-btn-active {
transform: translateY(-2px); box-shadow: 0 2px 8px rgba(0,0,0,0.15);
box-shadow: 0 4px 0 rgba(0,0,0,0.3); transform: scale(1.02);
} }
.mode-btn-inactive { .mode-btn-inactive {
opacity: 0.6; opacity: 0.6;
} }
/* 对话气泡样式 */ /* 对话气泡样式 - Google 风格 */
.chat-bubble-user { .chat-bubble-user {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: #4C76AB;
color: white; color: white;
padding: 10px 14px; padding: 12px 16px;
max-width: 80%; max-width: 80%;
border: 2px solid #000; border-radius: 18px 18px 4px 18px;
box-shadow: 2px 2px 0 rgba(0,0,0,0.2); box-shadow: 0 1px 2px rgba(0,0,0,0.1);
} }
.chat-bubble-ai { .chat-bubble-ai {
background: #fff; background: #f8f9fa;
color: #1f2937; color: #464646;
padding: 10px 14px; padding: 12px 16px;
max-width: 85%; max-width: 85%;
border: 2px solid #10b981; border-radius: 4px 18px 18px 18px;
box-shadow: 2px 2px 0 rgba(16, 185, 129, 0.3); box-shadow: 0 1px 2px rgba(0,0,0,0.1);
} }
/* SVG占位符样式 - 块级换行 + 新配色 */ /* SVG占位符样式 - Google 风格 */
.svg-placeholder-block { .svg-placeholder-block {
display: block; display: block;
background: linear-gradient(135deg, #f59e0b 0%, #ef4444 100%); background: #2B4269;
color: white; color: white;
padding: 8px 14px; padding: 10px 16px;
margin: 8px 0; margin: 8px 0;
border: 2px solid #000; border-radius: 8px;
box-shadow: 3px 3px 0 rgba(0,0,0,0.25); box-shadow: 0 2px 4px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
font-weight: bold; font-weight: 600;
font-size: 13px; font-size: 13px;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
text-align: center; text-align: center;
} }
.svg-placeholder-block:hover { .svg-placeholder-block:hover {
transform: translateX(2px) translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.16), 0 2px 4px rgba(0,0,0,0.08);
box-shadow: 4px 4px 0 rgba(0,0,0,0.3); transform: translateY(-1px);
background: linear-gradient(135deg, #fb923c 0%, #f87171 100%); background: #5A6270;
} }
/* 气泡操作按钮 */ /* 气泡操作按钮 */
@@ -113,28 +115,33 @@
} }
.modal-content { .modal-content {
background: white; background: rgba(255,255,255,0.95);
border: 4px solid #000; border-radius: 20px;
box-shadow: 8px 8px 0 rgba(0,0,0,0.4); 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; max-width: 500px;
width: 90%; width: 90%;
max-height: 90vh; max-height: 90vh;
overflow-y: auto; overflow-y: auto;
backdrop-filter: blur(20px);
border: 1px solid rgba(255,255,255,0.2);
} }
/* 表单输入框样式 */ /* 表单输入框样式 - Google 风格 */
.config-input { .config-input {
width: 100%; width: 100%;
padding: 10px; padding: 12px 16px;
border: 2px solid #000; border: 1px solid #dadce0;
border-radius: 4px;
font-size: 14px; font-size: 14px;
transition: all 0.2s; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
color: #464646;
} }
.config-input:focus { .config-input:focus {
outline: none; outline: none;
border-color: #667eea; border-color: #4C76AB;
box-shadow: 3px 3px 0 rgba(102, 126, 234, 0.3); box-shadow: 0 0 0 2px rgba(76, 118, 171, 0.1);
} }
/* 齿轮旋转动画 */ /* 齿轮旋转动画 */
@@ -148,31 +155,31 @@
} }
</style> </style>
</head> </head>
<body class="bg-gray-100 h-screen flex flex-col"> <body class="bg-gray-50 h-screen flex flex-col" style="background: linear-gradient(180deg, #f8f9fa 0%, #ffffff 100%);">
<!-- 顶部标题栏 --> <!-- 顶部标题栏 -->
<header class="bg-gradient-to-r from-orange-500 to-pink-500 p-3 flex items-center justify-between border-b-4 border-black"> <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-2"> <div class="flex items-center space-x-4">
<iconify-icon icon="ph:lightning-fill" class="text-3xl text-white"></iconify-icon> <iconify-icon icon="ph:lightning-fill" class="text-3xl" style="color: #4C76AB;"></iconify-icon>
<h1 id="page-title" class="text-2xl font-black text-white tracking-tight">产品画布</h1> <h1 id="page-title" class="text-2xl font-semibold tracking-tight" style="color: #464646;">产品画布</h1>
</div> </div>
<!-- 右侧按钮组 --> <!-- 右侧按钮组 -->
<div class="flex items-center gap-3"> <div class="flex items-center gap-4">
<!-- API配置按钮 --> <!-- API配置按钮 -->
<button id="settings-btn" class="settings-btn bg-white/20 text-white p-2 border-2 border-white hover:bg-white/30 transition-all" title="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-2xl"></iconify-icon> <iconify-icon icon="ph:gear-six-fill" class="text-xl"></iconify-icon>
</button> </button>
<span class="text-white font-bold text-sm">点击切换模式</span> <span class="font-medium text-sm" style="color: #888888;">点击切换模式</span>
<iconify-icon icon="ph:hand-pointing-fill" class="text-2xl text-yellow-300 wave-hand"></iconify-icon> <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 bg-white text-orange-600 px-4 py-2 font-bold border-2 border-black hover:bg-orange-50 transition-all duration-200"> <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-1"></iconify-icon> <iconify-icon icon="ph:pen-nib-duotone" class="align-middle mr-2"></iconify-icon>
产品画布 产品画布
</button> </button>
<button id="swot-mode-btn" class="mode-btn-inactive bg-white text-purple-600 px-4 py-2 font-bold border-2 border-black hover:bg-purple-50 transition-all duration-200"> <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-1"></iconify-icon> <iconify-icon icon="ph:chart-bar-duotone" class="align-middle mr-2"></iconify-icon>
SWOT分析 SWOT分析
</button> </button>
</div> </div>
@@ -182,7 +189,7 @@
<main class="flex-1 grid grid-cols-1 md:grid-cols-3 gap-4 p-4 overflow-hidden"> <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 wild-border border-cyan-500 flex flex-col"> <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 id="chat-history" class="flex-1 p-4 overflow-y-auto space-y-3">
@@ -253,40 +260,43 @@
</div> </div>
<!-- 输入区 --> <!-- 输入区 -->
<div class="p-3 border-t-3 border-gray-300 bg-yellow-50"> <div class="p-5 border-t bg-white" style="border-color: rgba(0,0,0,0.06);">
<div class="relative flex items-center gap-2"> <div class="relative flex items-center gap-3">
<input <input
id="chat-input" id="chat-input"
type="text" type="text"
placeholder="输入您的想法按Enter发送..." placeholder="输入您的想法按Enter发送..."
class="flex-1 p-2 border-2 border-gray-800 focus:border-cyan-500 focus:outline-none transition-colors font-medium" 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="text-cyan-600 hover:text-cyan-700 transition-colors p-2 hover:scale-110 transform duration-200"> <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-3xl"></iconify-icon> <iconify-icon icon="ph:paper-plane-tilt-fill" class="text-xl"></iconify-icon>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<!-- 右侧显示面板 --> <!-- 右侧显示面板 -->
<div class="md:col-span-2 bg-white wild-border border-purple-600 flex flex-col"> <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-4 bg-gradient-to-br from-purple-50 to-pink-50 overflow-auto"> <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 text-gray-400"> <div id="svg-placeholder" class="text-center" style="color: #787878;">
<iconify-icon icon="ph:image-square" class="text-6xl mx-auto text-purple-400"></iconify-icon> <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> <p class="mt-2 font-bold" id="placeholder-text">生成的产品画布将在此处显示</p>
</div> </div>
</div> </div>
<!-- 底部操作栏 --> <!-- 底部操作栏 -->
<div class="p-3 border-t-3 border-gray-300 flex justify-end items-center gap-2 bg-gray-800"> <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-2 bg-orange-500 text-white border-2 border-black hover:bg-orange-600 transition-all" title="下载SVG"> <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-xl"></iconify-icon> <iconify-icon icon="mdi:download-outline" class="text-lg"></iconify-icon>
</button> </button>
<button id="export-image-btn" class="p-2 bg-green-500 text-white border-2 border-black hover:bg-green-600 transition-all" title="导出为图片"> <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-xl"></iconify-icon> <iconify-icon icon="mdi:image-outline" class="text-lg"></iconify-icon>
</button> </button>
<button id="view-code-btn" class="p-2 bg-blue-500 text-white border-2 border-black hover:bg-blue-600 transition-all" title="查看代码"> <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-xl"></iconify-icon> <iconify-icon icon="mdi:code-tags" class="text-lg"></iconify-icon>
</button> </button>
</div> </div>
</div> </div>
@@ -297,13 +307,13 @@
<div id="config-modal" class="modal-overlay"> <div id="config-modal" class="modal-overlay">
<div class="modal-content"> <div class="modal-content">
<!-- 模态窗头部 --> <!-- 模态窗头部 -->
<div class="bg-gradient-to-r from-blue-600 to-purple-600 p-4 border-b-4 border-black flex items-center justify-between"> <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-2"> <div class="flex items-center gap-4">
<iconify-icon icon="ph:plugs-connected-fill" class="text-3xl text-white"></iconify-icon> <iconify-icon icon="ph:plugs-connected-fill" class="text-2xl" style="color: #4C76AB;"></iconify-icon>
<h2 class="text-xl font-black text-white">API 配置</h2> <h2 class="text-xl font-semibold" style="color: #464646;">API 配置</h2>
</div> </div>
<button id="close-modal-btn" class="text-white hover:bg-white/20 p-2 transition-all"> <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-2xl"></iconify-icon> <iconify-icon icon="ph:x-bold" class="text-xl"></iconify-icon>
</button> </button>
</div> </div>
@@ -311,8 +321,8 @@
<div class="p-6 space-y-4"> <div class="p-6 space-y-4">
<!-- API URL --> <!-- API URL -->
<div> <div>
<label class="block font-bold text-gray-800 mb-2 flex items-center gap-2"> <label class="block font-semibold mb-3 flex items-center gap-3" style="color: #464646;">
<iconify-icon icon="ph:link-bold" class="text-lg text-blue-600"></iconify-icon> <iconify-icon icon="ph:link-bold" class="text-lg" style="color: #4C76AB;"></iconify-icon>
API URL API URL
</label> </label>
<input <input
@@ -326,8 +336,8 @@
<!-- API Key --> <!-- API Key -->
<div> <div>
<label class="block font-bold text-gray-800 mb-2 flex items-center gap-2"> <label class="block font-semibold mb-3 flex items-center gap-3" style="color: #464646;">
<iconify-icon icon="ph:key-bold" class="text-lg text-green-600"></iconify-icon> <iconify-icon icon="ph:key-bold" class="text-lg" style="color: #2B4269;"></iconify-icon>
API Key API Key
</label> </label>
<input <input
@@ -341,8 +351,8 @@
<!-- Model --> <!-- Model -->
<div> <div>
<label class="block font-bold text-gray-800 mb-2 flex items-center gap-2"> <label class="block font-semibold mb-3 flex items-center gap-3" style="color: #464646;">
<iconify-icon icon="ph:robot-bold" class="text-lg text-purple-600"></iconify-icon> <iconify-icon icon="ph:robot-bold" class="text-lg" style="color: #5A6270;"></iconify-icon>
模型 (Model) 模型 (Model)
</label> </label>
<input <input
@@ -355,19 +365,19 @@
</div> </div>
<!-- 状态显示 --> <!-- 状态显示 -->
<div id="config-status" class="p-3 border-2 border-gray-300 bg-gray-50 text-sm text-gray-600 hidden"> <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> <iconify-icon icon="ph:info" class="align-middle"></iconify-icon>
<span id="status-text">等待操作...</span> <span id="status-text">等待操作...</span>
</div> </div>
</div> </div>
<!-- 模态窗底部按钮 --> <!-- 模态窗底部按钮 -->
<div class="p-4 border-t-3 border-gray-300 bg-gray-100 flex gap-3 justify-end"> <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-4 py-2 bg-yellow-500 text-white font-bold border-2 border-black hover:bg-yellow-600 transition-all flex items-center gap-2"> <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> <iconify-icon icon="ph:flask-bold"></iconify-icon>
测试连接 测试连接
</button> </button>
<button id="save-config-btn" class="px-4 py-2 bg-green-500 text-white font-bold border-2 border-black hover:bg-green-600 transition-all flex items-center gap-2"> <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> <iconify-icon icon="ph:floppy-disk-bold"></iconify-icon>
保存配置 保存配置
</button> </button>
@@ -505,19 +515,22 @@
configStatus.classList.remove('hidden'); configStatus.classList.remove('hidden');
statusText.textContent = message; statusText.textContent = message;
configStatus.classList.remove('border-gray-300', 'bg-gray-50', 'text-gray-600'); configStatus.style.borderColor = '#787878';
configStatus.classList.remove('border-green-500', 'bg-green-50', 'text-green-700'); configStatus.style.backgroundColor = '#f9fafb';
configStatus.classList.remove('border-red-500', 'bg-red-50', 'text-red-700'); configStatus.style.color = '#787878';
configStatus.classList.remove('border-blue-500', 'bg-blue-50', 'text-blue-700');
if (type === 'success') { if (type === 'success') {
configStatus.classList.add('border-green-500', 'bg-green-50', 'text-green-700'); configStatus.style.borderColor = '#47A74F';
configStatus.style.backgroundColor = '#e8f5e9';
configStatus.style.color = '#2e7d32';
} else if (type === 'error') { } else if (type === 'error') {
configStatus.classList.add('border-red-500', 'bg-red-50', 'text-red-700'); configStatus.style.borderColor = '#E04639';
configStatus.style.backgroundColor = '#ffebee';
configStatus.style.color = '#c62828';
} else if (type === 'loading') { } else if (type === 'loading') {
configStatus.classList.add('border-blue-500', 'bg-blue-50', 'text-blue-700'); configStatus.style.borderColor = '#5586F5';
} else { configStatus.style.backgroundColor = '#e3f2fd';
configStatus.classList.add('border-gray-300', 'bg-gray-50', 'text-gray-600'); configStatus.style.color = '#1565c0';
} }
} }