Skip to content

Commit 15e86c5

Browse files
committed
feat: 进一步调整UI和交互
1 parent 6767ab7 commit 15e86c5

File tree

8 files changed

+206
-99
lines changed

8 files changed

+206
-99
lines changed

PageDesc.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
## Index.tsx UI渲染结构分析
2+
3+
### 页面整体布局
4+
5+
1. **主容器分为左右两栏**
6+
- 左侧:配置与控制面板
7+
- 右侧:图片预览与历史记录区域
8+
9+
### 左侧配置面板
10+
11+
1. **页面标题与介绍**
12+
- 包含标题、服务描述和GitHub链接
13+
- 页面底部有版权信息
14+
15+
2. **模型选择与API配置**
16+
- 模型选择下拉菜单
17+
- 子模型选择界面(当支持多个子模型时)
18+
- API密钥输入区域(包括显示/隐藏切换和复制功能)
19+
- 安全提示说明(关于API密钥的保存位置和安全性)
20+
21+
3. **提示词输入区域**
22+
- 正向提示词文本框(带有字数限制提示)
23+
- 负向提示词文本框(当选择的模型支持时显示)
24+
- 字数计数器,显示当前输入/最大字数
25+
26+
4. **图片参数设置**
27+
- 尺寸比例选择按钮组(如16:9、4:3、1:1等)
28+
- 图像宽度和高度输入框
29+
- 图片数量选择器(批量生成)
30+
31+
5. **生成按钮区域**
32+
- 生成图片按钮(包含加载状态)
33+
- 按钮下方显示选定的模型和价格信息
34+
35+
### 右侧图片预览区域
36+
37+
1. **图片展示区域**
38+
- 图片上方显示模型信息、尺寸和提示词
39+
- 支持单图和多图(批量生成)展示
40+
- 图片加载过程中显示加载动画
41+
- 生成时间和耗时显示
42+
43+
2. **图片操作按钮**
44+
- 重新生成按钮
45+
- 复制图片链接按钮
46+
- 下载图片按钮
47+
- 多图模式下支持每张图片的独立操作
48+
49+
3. **空状态提示**
50+
- 当没有生成图片时显示图像占位符
51+
- 提示用户在左侧输入提示词并设置参数
52+
53+
4. **历史记录区域**
54+
- 可折叠的历史记录面板
55+
- 横向滚动的历史图片缩略图列表
56+
- 每个历史记录显示缩略图、提示词摘要和基本信息
57+
- 鼠标悬停时显示更多详细信息
58+
- 支持清空历史记录和删除单条记录
59+
- 支持点击历史记录以恢复之前的设置
60+
61+
### 调试与隐藏功能
62+
63+
1. **调试面板**
64+
- 默认隐藏的调试日志面板
65+
- 记录API请求和响应信息
66+
- 可通过顶部导航栏的按钮打开
67+
68+
### 交互功能设计
69+
70+
1. **响应式布局**
71+
- 支持不同屏幕尺寸的适配
72+
- 移动设备上有特殊的历史记录显示策略
73+
74+
2. **用户体验优化**
75+
- 实时字数统计和限制
76+
- 通过toast消息提供操作反馈
77+
- 图片生成过程中的加载状态提示
78+
- 历史记录的悬停效果和交互
79+
80+
3. **本地存储功能**
81+
- API密钥的本地存储与安全处理
82+
- 历史记录的持久化保存
83+
- 上次使用设置的记忆功能
84+
85+
这个界面设计注重用户体验,将复杂的AI图像生成过程通过清晰的界面展示出来,并且通过合理的布局和交互设计,使用户能够方便地调整参数、生成图片并管理历史记录。同时,界面也考虑到了安全性(API密钥处理)和调试需求(隐藏的调试面板)。

src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ const App = () => {
1919

2020
// 设置一个额外的变量用于内容区域,减去顶部和底部的固定区域
2121
// 这里可以根据实际项目中顶部和底部区域的高度进行调整
22-
const headerHeight = 70; // 估计的顶部区域高度
23-
const historyHeight = 140; // 估计的历史记录区域高度
22+
const headerHeight = 75; // 估计的顶部区域高度
23+
const historyHeight = 160; // 估计的历史记录区域高度
2424
const contentHeight = (window.innerHeight - headerHeight - historyHeight) * 0.01;
2525
document.documentElement.style.setProperty('--content-vh', `${contentHeight}px`);
2626

src/components/LayoutAdjuster.tsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,16 @@ const LayoutAdjuster: React.FC = () => {
2020
document.documentElement.style.setProperty('--doc-height', `${docHeight}px`);
2121

2222
// 计算历史区和主内容区的高度
23-
const headerHeight = 80; // 大致的头部高度
24-
const historyHeight = 150; // 历史记录区高度
23+
const headerHeight = 85; // 调整后的头部高度
24+
25+
// 检查历史记录的当前状态
26+
const historyIsCollapsed = document.querySelector('.history-container')?.classList.contains('max-h-[40px]');
27+
const historyHeight = historyIsCollapsed ? 40 : 160; // 根据历史记录的展开状态设置高度
28+
2529
const mainContentHeight = windowHeight - headerHeight - historyHeight;
2630

2731
document.documentElement.style.setProperty('--main-height', `${mainContentHeight}px`);
28-
29-
// 更新调试信息
30-
const debugInfo = document.querySelector('.debug-info');
31-
if (debugInfo) {
32-
debugInfo.textContent = `window: ${windowHeight}px, doc: ${docHeight}px, main: ${mainContentHeight}px`;
33-
}
32+
document.documentElement.style.setProperty('--history-height', `${historyHeight}px`);
3433
}, 100);
3534
};
3635

@@ -42,11 +41,22 @@ const LayoutAdjuster: React.FC = () => {
4241
window.addEventListener('orientationchange', setRealHeight);
4342
window.addEventListener('scroll', setRealHeight);
4443

44+
// 添加监听历史记录展开/收起事件
45+
const historyObserver = new MutationObserver(setRealHeight);
46+
const historyElement = document.querySelector('.history-container');
47+
if (historyElement) {
48+
historyObserver.observe(historyElement, {
49+
attributes: true,
50+
attributeFilter: ['class']
51+
});
52+
}
53+
4554
// 清理
4655
return () => {
4756
window.removeEventListener('resize', setRealHeight);
4857
window.removeEventListener('orientationchange', setRealHeight);
4958
window.removeEventListener('scroll', setRealHeight);
59+
historyObserver.disconnect();
5060
};
5161
}, []);
5262

src/index.css

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@
44

55
/* 安全提示样式 */
66
.security-banner {
7-
max-width: 60%;
8-
min-width: 280px;
97
display: flex;
108
align-items: center;
11-
margin-left: 0; /* 调整位置 */
9+
white-space: nowrap;
10+
padding-left: 8px; /* 从左侧稍微缩进一点 */
1211
}
1312

1413
.security-banner span {
1514
position: relative;
1615
transition: opacity 0.5s ease;
16+
text-overflow: ellipsis;
17+
overflow: hidden;
18+
display: block;
19+
width: 100%;
1720
}
1821

1922
/* 使用动态计算的视口高度 */
@@ -56,18 +59,17 @@
5659
overscroll-behavior: contain;
5760
}
5861

59-
/* 调试信息 */
60-
.debug-info {
61-
position: fixed;
62-
bottom: 60px;
63-
right: 10px;
64-
font-size: 10px;
65-
background: rgba(0,0,0,0.5);
66-
color: white;
67-
padding: 5px;
68-
border-radius: 4px;
69-
z-index: 9999;
70-
pointer-events: none;
62+
/* 底部版权区域 */
63+
.footer-copyright {
64+
/* 使用历史记录高度变量,确保紧贴历史记录区域 */
65+
margin-top: auto;
66+
}
67+
68+
/* 自适应高度的主内容区 */
69+
.flex-adaptive {
70+
flex: 1;
71+
display: flex;
72+
flex-direction: column;
7173
}
7274

7375
@media (max-width: 1024px) {
@@ -79,11 +81,10 @@
7981

8082
@media (max-width: 768px) {
8183
.security-banner {
82-
max-width: 45%;
83-
min-width: 200px;
84+
margin-right: 65px; /* 为调试按钮留出更多空间 */
8485
}
8586

86-
/* 在移动设备上优化历史记录区域 */
87+
/* 移动设备上优化历史记录区域 */
8788
.mobile-history {
8889
position: sticky;
8990
bottom: 0;

src/pages/Index.tsx

Lines changed: 62 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -895,20 +895,36 @@ const Index = () => {
895895
<LayoutAdjuster />
896896

897897
{/* 顶部带安全提示的导航栏 */}
898-
<div className="bg-gradient-to-r from-blue-50 via-white to-indigo-50 border border-red-200 rounded-lg mx-4 mt-4 mb-1 py-3 px-4 shadow-sm flex-shrink-0">
899-
<div className="max-w-7xl mx-auto flex items-center justify-between">
900-
<div className="flex items-center gap-2">
898+
<div className="bg-gradient-to-r from-blue-50 via-white to-indigo-50 border border-red-200 rounded-lg mx-4 mt-4 mb-1 py-4 px-4 shadow-sm flex-shrink-0 relative">
899+
<div className="max-w-7xl mx-auto w-full flex items-center">
900+
{/* 左侧Logo部分,占据0.618前的空间 */}
901+
<div className="flex items-center gap-2 w-[61.8%] md:w-[61.8%] pr-2">
901902
<img src="/logo.png" alt="Logo" className="h-8 w-auto rounded-lg" />
902-
<span className="text-xl font-bold tracking-tight text-gray-800">一站式AI绘图平台</span>
903+
<span className="text-xl font-bold tracking-tight text-gray-800 truncate">一站式AI绘图平台</span>
903904
</div>
904-
<div className="security-banner">
905-
<div className="flex items-center">
906-
<span className="text-xs text-blue-600 font-medium">
907-
{securityTips[currentTip]}
908-
</span>
905+
906+
{/* 右侧Tips部分,从黄金比例(0.618)位置开始 */}
907+
<div className="w-[38.2%] pr-20">
908+
<div className="security-banner w-full">
909+
<div className="flex items-center">
910+
<span className="text-xs text-blue-600 font-medium truncate">
911+
{securityTips[currentTip]}
912+
</span>
913+
</div>
909914
</div>
910915
</div>
911916
</div>
917+
918+
{/* 调试面板按钮 - 固定在右上角 */}
919+
<button
920+
className="absolute right-4 top-1/2 transform -translate-y-1/2 bg-white shadow border text-gray-700 hover:bg-gray-50 px-3 py-1.5 rounded text-sm flex items-center gap-1 z-10"
921+
onClick={() => document.getElementById('debug-panel')?.classList.toggle('hidden')}
922+
>
923+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
924+
<path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zm0 0V8m0 4h4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
925+
</svg>
926+
调试
927+
</button>
912928
</div>
913929

914930
{/* 设置面板 - 条件渲染 */}
@@ -1279,27 +1295,11 @@ const Index = () => {
12791295
)}
12801296
</div>
12811297
</div>
1282-
1283-
<div className="p-4 text-center text-xs text-gray-400 border-t border-gray-200 flex items-center justify-center gap-2">
1284-
<span>© {new Date().getFullYear()} Powered by Lovable</span>
1285-
<span>|</span>
1286-
<a
1287-
href="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/kevin1sMe/paintbot-hub"
1288-
target="_blank"
1289-
rel="noopener noreferrer"
1290-
className="text-gray-500 hover:text-gray-700 flex items-center gap-1"
1291-
>
1292-
<svg height="16" width="16" viewBox="0 0 16 16" fill="currentColor">
1293-
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
1294-
</svg>
1295-
<span>GitHub</span>
1296-
</a>
1297-
</div>
12981298
</div>
12991299
</div>
13001300

13011301
{/* 右侧图片预览区域 - 调整为弹性布局确保历史记录始终可见 */}
1302-
<div className="flex-1 bg-gray-50 flex flex-col overflow-hidden">
1302+
<div className="flex-1 bg-gray-50 flex flex-col overflow-hidden flex-adaptive">
13031303
{/* 滚动区域只应用于主内容,不包括历史记录 */}
13041304
<div className="p-4 flex-1 overflow-y-auto min-h-0 content-height">
13051305
{generatedImages.length > 0 || pendingImages > 0 ? (
@@ -1495,10 +1495,10 @@ const Index = () => {
14951495
</div>
14961496

14971497
{/* 历史记录区域 - 现在使用固定底部设计 */}
1498-
<div className={`border-t border-gray-200 bg-white flex-shrink-0 history-container md:max-h-[150px] overflow-hidden mobile-history ${
1498+
<div className={`border-t border-gray-200 bg-white flex-shrink-0 history-container md:max-h-[160px] overflow-hidden mobile-history ${
14991499
historyCollapsed
15001500
? 'max-h-[40px] min-h-[40px]'
1501-
: 'max-h-[150px] min-h-[100px] md:min-h-[120px]'
1501+
: 'max-h-[160px] min-h-[110px] md:min-h-[130px]'
15021502
}`}>
15031503
<div className="flex justify-between items-center p-3 cursor-pointer"
15041504
onClick={() => setHistoryCollapsed(!historyCollapsed)}>
@@ -1535,15 +1535,20 @@ const Index = () => {
15351535
strokeWidth="2"
15361536
strokeLinecap="round"
15371537
strokeLinejoin="round"
1538-
className={`text-gray-400 transform transition-transform ${historyCollapsed ? 'rotate-180' : ''}`}
1538+
className="text-gray-400"
15391539
>
1540-
<polyline points="18 15 12 9 6 15"></polyline>
1540+
{/* 根据折叠状态显示不同的箭头方向 */}
1541+
{historyCollapsed ? (
1542+
<polyline points="6 15 12 9 18 15"></polyline> // 收起状态时显示向下的箭头,表示可展开
1543+
) : (
1544+
<polyline points="6 9 12 15 18 9"></polyline> // 展开状态时显示向上的箭头,表示可收起
1545+
)}
15411546
</svg>
15421547
</div>
15431548
</div>
15441549

15451550
{!historyCollapsed && (
1546-
<div className="px-4 pb-3 overflow-y-auto overflow-scroll-fix" style={{maxHeight: "100px"}}>
1551+
<div className="px-4 pb-3 overflow-y-auto overflow-scroll-fix" style={{maxHeight: "110px"}}>
15471552
{history.length > 0 ? (
15481553
<div className="flex gap-3 overflow-x-auto pb-2">
15491554
{history.map((item, index) => (
@@ -1552,7 +1557,10 @@ const Index = () => {
15521557
src={item.imgUrl}
15531558
alt={item.prompt}
15541559
className="w-[100px] h-[100px] object-cover rounded-md border border-gray-200"
1555-
onClick={() => handleHistoryItemClick(item)}
1560+
onClick={(e) => {
1561+
e.stopPropagation(); // 阻止事件冒泡到外层div,避免触发收起操作
1562+
handleHistoryItemClick(item);
1563+
}}
15561564
/>
15571565
{/* 多图标记 */}
15581566
{item.imageCount && item.imageCount > 1 && (
@@ -1567,7 +1575,10 @@ const Index = () => {
15671575
{/* 悬停显示更多信息 - 添加点击事件以支持点击功能 */}
15681576
<div
15691577
className="absolute inset-0 bg-black bg-opacity-75 rounded-md opacity-0 group-hover:opacity-100 transition-opacity flex flex-col justify-between p-2 text-white text-xs"
1570-
onClick={() => handleHistoryItemClick(item)}
1578+
onClick={(e) => {
1579+
e.stopPropagation(); // 阻止事件冒泡到外层div,避免触发收起操作
1580+
handleHistoryItemClick(item);
1581+
}}
15711582
>
15721583
<div className="flex justify-between">
15731584
<span>{item.model.split('-').pop()}</span>
@@ -1609,31 +1620,31 @@ const Index = () => {
16091620
)}
16101621
</div>
16111622

1612-
{/* 调试面板按钮 - 位置调整 */}
1613-
<div className="fixed bottom-4 right-4 z-20">
1614-
<button
1615-
className="bg-white shadow border text-gray-700 hover:bg-gray-50 px-3 py-1.5 rounded text-sm flex items-center gap-1"
1616-
onClick={() => document.getElementById('debug-panel')?.classList.toggle('hidden')}
1617-
>
1618-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1619-
<path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zm0 0V8m0 4h4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
1620-
</svg>
1621-
调试
1622-
</button>
1623-
</div>
1623+
{/* 调试按钮已移至顶部导航栏 */}
16241624
</div>
16251625
</div>
16261626

1627+
{/* 版权信息 - 移至页面最底部 */}
1628+
<div className="bg-white border-t border-gray-200 py-4 px-4 text-center text-xs text-gray-400 flex items-center justify-center gap-2 w-full flex-shrink-0 footer-copyright">
1629+
<span>© {new Date().getFullYear()} Powered by Lovable</span>
1630+
<span>|</span>
1631+
<a
1632+
href="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/kevin1sMe/paintbot-hub"
1633+
target="_blank"
1634+
rel="noopener noreferrer"
1635+
className="text-gray-500 hover:text-gray-700 flex items-center gap-1"
1636+
>
1637+
<svg height="16" width="16" viewBox="0 0 16 16" fill="currentColor">
1638+
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
1639+
</svg>
1640+
<span>GitHub</span>
1641+
</a>
1642+
</div>
1643+
16271644
{/* 调试面板内容 - 保持隐藏状态 */}
16281645
<div id="debug-panel" className="hidden">
16291646
<DebugPanel logs={logs} clearLogs={clearLogs} />
16301647
</div>
1631-
1632-
{/* 显示调试信息 */}
1633-
<div className="debug-info">
1634-
inner: {typeof window !== 'undefined' ? window.innerHeight : 0}px,
1635-
outer: {typeof window !== 'undefined' ? window.outerHeight : 0}px
1636-
</div>
16371648
</div>
16381649
);
16391650
};

0 commit comments

Comments
 (0)