You've already forked Nano-Banana-AI-Image-Editor
优化界面
This commit is contained in:
@@ -47,7 +47,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState<string>('');
|
||||
|
||||
// 悬浮预览状态
|
||||
const [hoveredImage, setHoveredImage] = useState<{url: string, title: string} | null>(null);
|
||||
const [hoveredImage, setHoveredImage] = useState<{url: string, title: string, width?: number, height?: number, size?: number} | null>(null);
|
||||
const [previewPosition, setPreviewPosition] = useState<{x: number, y: number}>({x: 0, y: 0});
|
||||
|
||||
const generations = currentProject?.generations || [];
|
||||
@@ -211,7 +211,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-72 bg-white p-4 flex flex-col h-full">
|
||||
<div className="w-72 bg-white p-4 flex flex-col h-full relative">
|
||||
{/* 头部 */}
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
@@ -298,7 +298,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
<p className="text-xs text-gray-400">暂无历史记录</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-3 gap-1.5 max-h-72 overflow-y-auto">
|
||||
<div className="grid grid-cols-3 gap-1.5 max-h-72 relative">
|
||||
{/* 显示生成记录 */}
|
||||
{[...filteredGenerations].sort((a, b) => b.timestamp - a.timestamp).slice(0, 50).map((generation, index) => (
|
||||
<div
|
||||
@@ -320,16 +320,122 @@ export const HistoryPanel: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (generation.outputAssets && generation.outputAssets.length > 0) {
|
||||
const asset = generation.outputAssets[0];
|
||||
if (asset.url) {
|
||||
// 优先使用上传后的远程链接,如果没有则使用原始链接
|
||||
let imageUrl = getUploadedImageUrl(generation, 0);
|
||||
if (!imageUrl && generation.outputAssets && generation.outputAssets.length > 0) {
|
||||
imageUrl = generation.outputAssets[0].url;
|
||||
}
|
||||
if (imageUrl) {
|
||||
// 创建图像对象以获取尺寸
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
// 计算文件大小(仅对base64数据)
|
||||
let size = 0;
|
||||
if (imageUrl.startsWith('data:')) {
|
||||
// 估算base64数据大小
|
||||
const base64Data = imageUrl.split(',')[1];
|
||||
size = Math.round((base64Data.length * 3) / 4);
|
||||
}
|
||||
|
||||
setHoveredImage({
|
||||
url: asset.url,
|
||||
url: imageUrl,
|
||||
title: `生成记录 G${index + 1}`,
|
||||
description: generation.prompt
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
size: size
|
||||
});
|
||||
setPreviewPosition({x: e.clientX, y: e.clientY});
|
||||
}
|
||||
|
||||
// 计算预览位置,确保不超出屏幕边界
|
||||
const previewWidth = 300;
|
||||
const previewHeight = 300;
|
||||
const offsetX = 10;
|
||||
const offsetY = 10;
|
||||
|
||||
|
||||
// 获取HistoryPanel的位置
|
||||
const historyPanel = document.querySelector('.w-72.bg-white.p-4');
|
||||
const panelRect = historyPanel ? historyPanel.getBoundingClientRect() : { left: 0, top: 0 };
|
||||
|
||||
// 计算相对于HistoryPanel的位置
|
||||
let x = e.clientX - panelRect.left + offsetX;
|
||||
let y = e.clientY - panelRect.top + offsetY;
|
||||
|
||||
|
||||
|
||||
// 确保预览窗口不会超出右边界
|
||||
if (x + previewWidth > window.innerWidth) {
|
||||
x = window.innerWidth - previewWidth - 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出下边界
|
||||
if (y + previewHeight > window.innerHeight) {
|
||||
y = window.innerHeight - previewHeight - 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出左边界
|
||||
if (x < 0) {
|
||||
x = 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出上边界
|
||||
if (y < 0) {
|
||||
y = 10;
|
||||
}
|
||||
|
||||
// 添加额外的安全边界检查
|
||||
x = Math.max(10, Math.min(x, window.innerWidth - previewWidth - 10));
|
||||
y = Math.max(10, Math.min(y, window.innerHeight - previewHeight - 10));
|
||||
|
||||
setPreviewPosition({x, y});
|
||||
};
|
||||
img.onerror = (error) => {
|
||||
console.error('图像加载失败:', error);
|
||||
// 即使图像加载失败,也显示预览
|
||||
setHoveredImage({
|
||||
url: imageUrl,
|
||||
title: `生成记录 G${index + 1}`,
|
||||
width: 0,
|
||||
height: 0,
|
||||
size: 0
|
||||
});
|
||||
|
||||
// 计算预览位置
|
||||
const previewWidth = 300;
|
||||
const previewHeight = 300;
|
||||
const offsetX = 10;
|
||||
const offsetY = 10;
|
||||
|
||||
|
||||
let x = e.clientX + offsetX;
|
||||
let y = e.clientY + offsetY;
|
||||
|
||||
// 确保预览窗口不会超出右边界
|
||||
if (x + previewWidth > window.innerWidth) {
|
||||
x = window.innerWidth - previewWidth - 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出下边界
|
||||
if (y + previewHeight > window.innerHeight) {
|
||||
y = window.innerHeight - previewHeight - 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出左边界
|
||||
if (x < 0) {
|
||||
x = 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出上边界
|
||||
if (y < 0) {
|
||||
y = 10;
|
||||
}
|
||||
|
||||
// 添加额外的安全边界检查
|
||||
x = Math.max(10, Math.min(x, window.innerWidth - previewWidth - 10));
|
||||
y = Math.max(10, Math.min(y, window.innerHeight - previewHeight - 10));
|
||||
|
||||
setPreviewPosition({x, y});
|
||||
};
|
||||
img.src = imageUrl;
|
||||
}
|
||||
}}
|
||||
onMouseMove={(e) => {
|
||||
@@ -339,58 +445,62 @@ export const HistoryPanel: React.FC = () => {
|
||||
const offsetX = 10;
|
||||
const offsetY = 10;
|
||||
|
||||
let x = e.clientX + offsetX;
|
||||
let y = e.clientY + offsetY;
|
||||
|
||||
// 检查是否超出右边界
|
||||
if (x + previewWidth > window.innerWidth) {
|
||||
x = window.innerWidth - previewWidth - 10;
|
||||
// 获取HistoryPanel的位置
|
||||
const historyPanel = document.querySelector('.w-72.bg-white.p-4');
|
||||
const panelRect = historyPanel ? historyPanel.getBoundingClientRect() : { left: 0, top: 0 };
|
||||
|
||||
// 计算相对于HistoryPanel的位置
|
||||
let x = e.clientX - panelRect.left + offsetX;
|
||||
let y = e.clientY - panelRect.top + offsetY;
|
||||
|
||||
// 确保预览窗口不会超出右边界
|
||||
if (x + previewWidth > (historyPanel ? historyPanel.clientWidth : window.innerWidth)) {
|
||||
x = (historyPanel ? historyPanel.clientWidth : window.innerWidth) - previewWidth - 10;
|
||||
}
|
||||
|
||||
// 检查是否超出下边界
|
||||
if (y + previewHeight > window.innerHeight) {
|
||||
y = window.innerHeight - previewHeight - 10;
|
||||
// 确保预览窗口不会超出下边界
|
||||
if (y + previewHeight > (historyPanel ? historyPanel.clientHeight : window.innerHeight)) {
|
||||
y = (historyPanel ? historyPanel.clientHeight : window.innerHeight) - previewHeight - 10;
|
||||
}
|
||||
|
||||
// 检查是否超出左边界
|
||||
// 确保预览窗口不会超出左边界
|
||||
if (x < 0) {
|
||||
x = 10;
|
||||
}
|
||||
|
||||
// 检查是否超出上边界
|
||||
// 确保预览窗口不会超出上边界
|
||||
if (y < 0) {
|
||||
y = 10;
|
||||
}
|
||||
|
||||
// 添加额外的安全边界检查
|
||||
const maxWidth = historyPanel ? historyPanel.clientWidth : window.innerWidth;
|
||||
const maxHeight = historyPanel ? historyPanel.clientHeight : window.innerHeight;
|
||||
x = Math.max(10, Math.min(x, maxWidth - previewWidth - 10));
|
||||
y = Math.max(10, Math.min(y, maxHeight - previewHeight - 10));
|
||||
|
||||
setPreviewPosition({x, y});
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setHoveredImage(null);
|
||||
}}
|
||||
>
|
||||
{generation.outputAssets && generation.outputAssets.length > 0 ? (
|
||||
(() => {
|
||||
const asset = generation.outputAssets[0];
|
||||
if (asset.url) {
|
||||
// 如果是base64数据URL,直接显示
|
||||
if (asset.url.startsWith('data:')) {
|
||||
return <img src={asset.url} alt="生成的变体" className="w-full h-full object-cover" />;
|
||||
}
|
||||
// 如果是普通URL,直接显示
|
||||
return <img src={asset.url} alt="生成的变体" className="w-full h-full object-cover" />;
|
||||
} else {
|
||||
return (
|
||||
<div className="w-full h-full bg-gray-100 flex items-center justify-center">
|
||||
<ImageIcon className="h-4 w-4 text-gray-400" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})()
|
||||
) : (
|
||||
<div className="w-full h-full bg-gray-100 flex items-center justify-center">
|
||||
<ImageIcon className="h-4 w-4 text-gray-400" />
|
||||
</div>
|
||||
)}
|
||||
{(() => {
|
||||
// 优先使用上传后的远程链接,如果没有则使用原始链接
|
||||
const imageUrl = getUploadedImageUrl(generation, 0) ||
|
||||
(generation.outputAssets && generation.outputAssets.length > 0 ? generation.outputAssets[0].url : null);
|
||||
|
||||
if (imageUrl) {
|
||||
return <img src={imageUrl} alt="生成的变体" className="w-full h-full object-cover" />;
|
||||
} else {
|
||||
return (
|
||||
<div className="w-full h-full bg-gray-100 flex items-center justify-center">
|
||||
<ImageIcon className="h-4 w-4 text-gray-400" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})()}
|
||||
|
||||
{/* 变体编号 */}
|
||||
<div className="absolute top-1 left-1 bg-gray-900/80 text-xs px-1 py-0.5 rounded text-white">
|
||||
@@ -421,16 +531,120 @@ export const HistoryPanel: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (edit.outputAssets && edit.outputAssets.length > 0) {
|
||||
const asset = edit.outputAssets[0];
|
||||
if (asset.url) {
|
||||
// 优先使用上传后的远程链接,如果没有则使用原始链接
|
||||
let imageUrl = getUploadedImageUrl(edit, 0);
|
||||
if (!imageUrl && edit.outputAssets && edit.outputAssets.length > 0) {
|
||||
imageUrl = edit.outputAssets[0].url;
|
||||
}
|
||||
|
||||
if (imageUrl) {
|
||||
// 创建图像对象以获取尺寸
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
// 计算文件大小(仅对base64数据)
|
||||
let size = 0;
|
||||
if (imageUrl.startsWith('data:')) {
|
||||
// 估算base64数据大小
|
||||
const base64Data = imageUrl.split(',')[1];
|
||||
size = Math.round((base64Data.length * 3) / 4);
|
||||
}
|
||||
|
||||
setHoveredImage({
|
||||
url: asset.url,
|
||||
url: imageUrl,
|
||||
title: `编辑记录 E${index + 1}`,
|
||||
description: edit.instruction
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
size: size
|
||||
});
|
||||
setPreviewPosition({x: e.clientX, y: e.clientY});
|
||||
}
|
||||
|
||||
// 计算预览位置,确保不超出屏幕边界
|
||||
const previewWidth = 300;
|
||||
const previewHeight = 300;
|
||||
const offsetX = 10;
|
||||
const offsetY = 10;
|
||||
|
||||
|
||||
let x = e.clientX + offsetX;
|
||||
let y = e.clientY + offsetY;
|
||||
|
||||
// 确保预览窗口不会超出右边界
|
||||
if (x + previewWidth > window.innerWidth) {
|
||||
x = window.innerWidth - previewWidth - 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出下边界
|
||||
if (y + previewHeight > window.innerHeight) {
|
||||
y = window.innerHeight - previewHeight - 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出左边界
|
||||
if (x < 0) {
|
||||
x = 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出上边界
|
||||
if (y < 0) {
|
||||
y = 10;
|
||||
}
|
||||
|
||||
// 添加额外的安全边界检查
|
||||
x = Math.max(10, Math.min(x, window.innerWidth - previewWidth - 10));
|
||||
y = Math.max(10, Math.min(y, window.innerHeight - previewHeight - 10));
|
||||
|
||||
setPreviewPosition({x, y});
|
||||
};
|
||||
img.onerror = (error) => {
|
||||
console.error('图像加载失败:', error);
|
||||
// 即使图像加载失败,也显示预览
|
||||
setHoveredImage({
|
||||
url: imageUrl,
|
||||
title: `编辑记录 E${index + 1}`,
|
||||
width: 0,
|
||||
height: 0,
|
||||
size: 0
|
||||
});
|
||||
|
||||
// 计算预览位置
|
||||
const previewWidth = 300;
|
||||
const previewHeight = 300;
|
||||
const offsetX = 10;
|
||||
const offsetY = 10;
|
||||
|
||||
// 获取HistoryPanel的位置信息
|
||||
const historyPanel = e.currentTarget.closest('.w-72');
|
||||
const panelRect = historyPanel ? historyPanel.getBoundingClientRect() : { left: 0, top: 0 };
|
||||
|
||||
// 计算相对于整个视窗的位置
|
||||
let x = e.clientX + offsetX;
|
||||
let y = e.clientY + offsetY;
|
||||
|
||||
// 确保预览窗口不会超出右边界
|
||||
if (x + previewWidth > window.innerWidth) {
|
||||
x = window.innerWidth - previewWidth - 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出下边界
|
||||
if (y + previewHeight > window.innerHeight) {
|
||||
y = window.innerHeight - previewHeight - 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出左边界
|
||||
if (x < 0) {
|
||||
x = 10;
|
||||
}
|
||||
|
||||
// 确保预览窗口不会超出上边界
|
||||
if (y < 0) {
|
||||
y = 10;
|
||||
}
|
||||
|
||||
// 添加额外的安全边界检查
|
||||
x = Math.max(10, Math.min(x, window.innerWidth - previewWidth - 10));
|
||||
y = Math.max(10, Math.min(y, window.innerHeight - previewHeight - 10));
|
||||
|
||||
setPreviewPosition({x, y});
|
||||
};
|
||||
img.src = imageUrl;
|
||||
}
|
||||
}}
|
||||
onMouseMove={(e) => {
|
||||
@@ -443,55 +657,51 @@ export const HistoryPanel: React.FC = () => {
|
||||
let x = e.clientX + offsetX;
|
||||
let y = e.clientY + offsetY;
|
||||
|
||||
// 检查是否超出右边界
|
||||
// 确保预览窗口不会超出右边界
|
||||
if (x + previewWidth > window.innerWidth) {
|
||||
x = window.innerWidth - previewWidth - 10;
|
||||
}
|
||||
|
||||
// 检查是否超出下边界
|
||||
// 确保预览窗口不会超出下边界
|
||||
if (y + previewHeight > window.innerHeight) {
|
||||
y = window.innerHeight - previewHeight - 10;
|
||||
}
|
||||
|
||||
// 检查是否超出左边界
|
||||
// 确保预览窗口不会超出左边界
|
||||
if (x < 0) {
|
||||
x = 10;
|
||||
}
|
||||
|
||||
// 检查是否超出上边界
|
||||
// 确保预览窗口不会超出上边界
|
||||
if (y < 0) {
|
||||
y = 10;
|
||||
}
|
||||
|
||||
// 添加额外的安全边界检查
|
||||
x = Math.max(10, Math.min(x, window.innerWidth - previewWidth - 10));
|
||||
y = Math.max(10, Math.min(y, window.innerHeight - previewHeight - 10));
|
||||
|
||||
setPreviewPosition({x, y});
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setHoveredImage(null);
|
||||
}}
|
||||
>
|
||||
{edit.outputAssets && edit.outputAssets.length > 0 ? (
|
||||
(() => {
|
||||
const asset = edit.outputAssets[0];
|
||||
if (asset.url) {
|
||||
// 如果是base64数据URL,直接显示
|
||||
if (asset.url.startsWith('data:')) {
|
||||
return <img src={asset.url} alt="编辑的变体" className="w-full h-full object-cover" />;
|
||||
}
|
||||
// 如果是普通URL,直接显示
|
||||
return <img src={asset.url} alt="编辑的变体" className="w-full h-full object-cover" />;
|
||||
} else {
|
||||
return (
|
||||
<div className="w-full h-full bg-gray-100 flex items-center justify-center">
|
||||
<ImageIcon className="h-4 w-4 text-gray-400" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})()
|
||||
) : (
|
||||
<div className="w-full h-full bg-gray-100 flex items-center justify-center">
|
||||
<ImageIcon className="h-4 w-4 text-gray-400" />
|
||||
</div>
|
||||
)}
|
||||
{(() => {
|
||||
// 优先使用上传后的远程链接,如果没有则使用原始链接
|
||||
const imageUrl = getUploadedImageUrl(edit, 0) ||
|
||||
(edit.outputAssets && edit.outputAssets.length > 0 ? edit.outputAssets[0].url : null);
|
||||
|
||||
if (imageUrl) {
|
||||
return <img src={imageUrl} alt="编辑的变体" className="w-full h-full object-cover" />;
|
||||
} else {
|
||||
return (
|
||||
<div className="w-full h-full bg-gray-100 flex items-center justify-center">
|
||||
<ImageIcon className="h-4 w-4 text-gray-400" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})()}
|
||||
|
||||
{/* 编辑标签 */}
|
||||
<div className="absolute top-1 left-1 bg-purple-900/80 text-xs px-1 py-0.5 rounded text-white">
|
||||
@@ -573,27 +783,36 @@ export const HistoryPanel: React.FC = () => {
|
||||
{gen.sourceAssets.length} 个参考图像
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{gen.sourceAssets.slice(0, 4).map((asset: any, index: number) => (
|
||||
<div
|
||||
key={asset.id}
|
||||
className="relative w-16 h-16 rounded border overflow-hidden cursor-pointer hover:ring-2 hover:ring-yellow-400"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setPreviewModal({
|
||||
open: true,
|
||||
imageUrl: asset.url,
|
||||
title: `参考图像 ${index + 1}`,
|
||||
description: `${asset.width} × ${asset.height}`
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={asset.url}
|
||||
alt={`参考图像 ${index + 1}`}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{gen.sourceAssets.slice(0, 4).map((asset: any, index: number) => {
|
||||
// 获取上传后的远程链接(如果存在)
|
||||
// 参考图像在uploadResults中从索引1开始(索引0是生成的图像)
|
||||
const uploadedUrl = gen.uploadResults && gen.uploadResults[index + 1] && gen.uploadResults[index + 1].success
|
||||
? `${gen.uploadResults[index + 1].url}?x-oss-process=image/quality,q_50`
|
||||
: null;
|
||||
const displayUrl = uploadedUrl || asset.url;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={asset.id}
|
||||
className="relative w-16 h-16 rounded border overflow-hidden cursor-pointer hover:ring-2 hover:ring-yellow-400"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setPreviewModal({
|
||||
open: true,
|
||||
imageUrl: displayUrl,
|
||||
title: `参考图像 ${index + 1}`,
|
||||
description: `${asset.width} × ${asset.height}`
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={displayUrl}
|
||||
alt={`参考图像 ${index + 1}`}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{gen.sourceAssets.length > 4 && (
|
||||
<div className="w-16 h-16 rounded border flex items-center justify-center bg-gray-100 text-xs text-gray-500">
|
||||
+{gen.sourceAssets.length - 4}
|
||||
@@ -672,27 +891,36 @@ export const HistoryPanel: React.FC = () => {
|
||||
原始参考图像:
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{parentGen.sourceAssets.slice(0, 4).map((asset: any, index: number) => (
|
||||
<div
|
||||
key={asset.id}
|
||||
className="relative w-16 h-16 rounded border overflow-hidden cursor-pointer hover:ring-2 hover:ring-yellow-400"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setPreviewModal({
|
||||
open: true,
|
||||
imageUrl: asset.url,
|
||||
title: `原始参考图像 ${index + 1}`,
|
||||
description: `${asset.width} × ${asset.height}`
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={asset.url}
|
||||
alt={`原始参考图像 ${index + 1}`}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{parentGen.sourceAssets.slice(0, 4).map((asset: any, index: number) => {
|
||||
// 获取上传后的远程链接(如果存在)
|
||||
// 参考图像在uploadResults中从索引1开始(索引0是生成的图像)
|
||||
const uploadedUrl = parentGen.uploadResults && parentGen.uploadResults[index + 1] && parentGen.uploadResults[index + 1].success
|
||||
? `${parentGen.uploadResults[index + 1].url}?x-oss-process=image/quality,q_50`
|
||||
: null;
|
||||
const displayUrl = uploadedUrl || asset.url;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={asset.id}
|
||||
className="relative w-16 h-16 rounded border overflow-hidden cursor-pointer hover:ring-2 hover:ring-yellow-400"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setPreviewModal({
|
||||
open: true,
|
||||
imageUrl: displayUrl,
|
||||
title: `原始参考图像 ${index + 1}`,
|
||||
description: `${asset.width} × ${asset.height}`
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={displayUrl}
|
||||
alt={`原始参考图像 ${index + 1}`}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{parentGen.sourceAssets.length > 4 && (
|
||||
<div className="w-16 h-16 rounded border flex items-center justify-center bg-gray-100 text-xs text-gray-500">
|
||||
+{parentGen.sourceAssets.length - 4}
|
||||
@@ -715,6 +943,28 @@ export const HistoryPanel: React.FC = () => {
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* 测试按钮 - 用于调试 */}
|
||||
<div className="mb-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="w-full h-8 text-xs card"
|
||||
onClick={() => {
|
||||
// 测试悬浮预览功能
|
||||
setHoveredImage({
|
||||
url: 'https://images.unsplash.com/photo-1501854140801-50d01698950b?w=200',
|
||||
title: '测试图像',
|
||||
width: 200,
|
||||
height: 200,
|
||||
size: 102400
|
||||
});
|
||||
setPreviewPosition({x: 100, y: 100});
|
||||
}}
|
||||
>
|
||||
测试悬浮预览
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 操作 */}
|
||||
<div className="space-y-2 flex-shrink-0 pt-2 border-t border-gray-100">
|
||||
<Button
|
||||
@@ -779,12 +1029,12 @@ export const HistoryPanel: React.FC = () => {
|
||||
{/* 悬浮预览 */}
|
||||
{hoveredImage && (
|
||||
<div
|
||||
className="fixed z-50 shadow-2xl border border-gray-300 rounded-lg overflow-hidden bg-white backdrop-blur-sm"
|
||||
className="absolute z-[9999] shadow-2xl border border-gray-300 rounded-lg overflow-hidden bg-white backdrop-blur-sm pointer-events-none"
|
||||
style={{
|
||||
left: Math.min(previewPosition.x + 10, window.innerWidth - 250),
|
||||
top: Math.min(previewPosition.y + 10, window.innerHeight - 250),
|
||||
maxWidth: '250px',
|
||||
maxHeight: '250px'
|
||||
left: `${previewPosition.x}px`,
|
||||
top: `${previewPosition.y}px`,
|
||||
maxWidth: '300px',
|
||||
maxHeight: '300px'
|
||||
}}
|
||||
>
|
||||
<div className="bg-gray-900 text-white text-xs p-2 truncate font-medium">
|
||||
@@ -793,20 +1043,22 @@ export const HistoryPanel: React.FC = () => {
|
||||
<img
|
||||
src={hoveredImage.url}
|
||||
alt="预览"
|
||||
className="w-full h-auto max-h-[150px] object-contain"
|
||||
className="w-full h-auto max-h-[200px] object-contain"
|
||||
/>
|
||||
{/* 图像信息 */}
|
||||
<div className="p-2 bg-gray-50 border-t border-gray-200 text-xs">
|
||||
{imageDimensions && (
|
||||
{hoveredImage.width && hoveredImage.height && (
|
||||
<div className="flex justify-between text-gray-600">
|
||||
<span>尺寸:</span>
|
||||
<span className="text-gray-800">{imageDimensions.width} × {imageDimensions.height}</span>
|
||||
<span className="text-gray-800">{hoveredImage.width} × {hoveredImage.height}</span>
|
||||
</div>
|
||||
)}
|
||||
{hoveredImage.size && (
|
||||
<div className="flex justify-between text-gray-600 mt-1">
|
||||
<span>大小:</span>
|
||||
<span className="text-gray-800">{Math.round(hoveredImage.size / 1024)} KB</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between text-gray-600 mt-1">
|
||||
<span>模式:</span>
|
||||
<span className="text-gray-800 capitalize">{selectedTool}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user