You've already forked Nano-Banana-AI-Image-Editor
新增 历史记录删除功能
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useAppStore } from '../store/useAppStore';
|
||||
import { Button } from './ui/Button';
|
||||
import { History, Download, Image as ImageIcon } from 'lucide-react';
|
||||
import { History, Download, Trash2, Image as ImageIcon } from 'lucide-react';
|
||||
import { cn } from '../utils/cn';
|
||||
import { ImagePreviewModal } from './ImagePreviewModal';
|
||||
import * as indexedDBService from '../services/indexedDBService';
|
||||
@@ -23,7 +23,9 @@ export const HistoryPanel: React.FC<{
|
||||
showHistory,
|
||||
setShowHistory,
|
||||
setCanvasImage,
|
||||
selectedTool
|
||||
selectedTool,
|
||||
removeGeneration,
|
||||
removeEdit
|
||||
} = useAppStore();
|
||||
|
||||
const { getBlob } = useAppStore.getState();
|
||||
@@ -46,6 +48,9 @@ export const HistoryPanel: React.FC<{
|
||||
// 使用自定义hook获取IndexedDB记录
|
||||
const { generations: dbGenerations, edits: dbEdits, loading, error, refresh } = useIndexedDBListener();
|
||||
|
||||
// 跟踪当前悬停的记录
|
||||
const [hoveredRecord, setHoveredRecord] = useState<{type: 'generation' | 'edit', id: string} | null>(null);
|
||||
|
||||
// 筛选和搜索状态
|
||||
const [startDate, setStartDate] = useState<string>(() => {
|
||||
const today = new Date();
|
||||
@@ -212,18 +217,47 @@ export const HistoryPanel: React.FC<{
|
||||
decodeBlobImages();
|
||||
}, [generations, edits, getBlob, decodedImages]);
|
||||
|
||||
// 监听鼠标离开窗口事件,确保悬浮预览正确关闭
|
||||
useEffect(() => {
|
||||
const handleMouseLeave = (e: MouseEvent) => {
|
||||
// 当鼠标离开浏览器窗口时,关闭悬浮预览
|
||||
if (e.relatedTarget === null) {
|
||||
setHoveredImage(null);
|
||||
if (setPreviewPosition) {
|
||||
setPreviewPosition(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
// 当窗口失去焦点时,关闭悬浮预览
|
||||
setHoveredImage(null);
|
||||
if (setPreviewPosition) {
|
||||
setPreviewPosition(null);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('mouseleave', handleMouseLeave);
|
||||
window.addEventListener('blur', handleBlur);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mouseleave', handleMouseLeave);
|
||||
window.removeEventListener('blur', handleBlur);
|
||||
};
|
||||
}, [setHoveredImage, setPreviewPosition]);
|
||||
|
||||
if (!showHistory) {
|
||||
return (
|
||||
<div className="w-8 bg-white flex flex-col items-center justify-center rounded-r-xl">
|
||||
<div className="w-8 bg-white flex flex-col items-center justify-center rounded-r-xl overflow-hidden">
|
||||
<button
|
||||
onClick={() => setShowHistory(true)}
|
||||
className="w-6 h-16 bg-gray-100 hover:bg-gray-200 rounded-l-lg flex items-center justify-center transition-colors group"
|
||||
className="w-6 h-16 bg-gray-100 hover:bg-gray-200 rounded-l-lg flex items-center justify-center transition-all duration-300 ease-in-out group"
|
||||
title="显示历史面板"
|
||||
>
|
||||
<div className="flex flex-col space-y-1">
|
||||
<div className="w-1 h-1 bg-gray-500 group-hover:bg-gray-400 rounded-full"></div>
|
||||
<div className="w-1 h-1 bg-gray-500 group-hover:bg-gray-400 rounded-full"></div>
|
||||
<div className="w-1 h-1 bg-gray-500 group-hover:bg-gray-400 rounded-full"></div>
|
||||
<div className="w-1 h-1 bg-gray-500 group-hover:bg-gray-400 rounded-full transition-colors duration-200"></div>
|
||||
<div className="w-1 h-1 bg-gray-500 group-hover:bg-gray-400 rounded-full transition-colors duration-200"></div>
|
||||
<div className="w-1 h-1 bg-gray-500 group-hover:bg-gray-400 rounded-full transition-colors duration-200"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
@@ -522,6 +556,9 @@ export const HistoryPanel: React.FC<{
|
||||
}
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
// 设置当前悬停的记录
|
||||
setHoveredRecord({type: 'generation', id: generation.id});
|
||||
|
||||
// 优先使用上传后的远程链接,如果没有则使用原始链接
|
||||
let imageUrl = getUploadedImageUrl(generation, 0);
|
||||
if (!imageUrl && generation.outputAssets && generation.outputAssets.length > 0) {
|
||||
@@ -563,6 +600,9 @@ export const HistoryPanel: React.FC<{
|
||||
// 不需要处理鼠标移动事件,因为预览现在在页面中心显示
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
// 清除当前悬停的记录
|
||||
setHoveredRecord(null);
|
||||
|
||||
setHoveredImage(null);
|
||||
if (setPreviewPosition) {
|
||||
setPreviewPosition(null);
|
||||
@@ -589,6 +629,47 @@ export const HistoryPanel: React.FC<{
|
||||
<div className="absolute top-1 left-1 bg-gray-900/80 text-xs px-1 py-0.5 rounded text-white">
|
||||
G{globalIndex + 1}
|
||||
</div>
|
||||
|
||||
{/* 悬停时显示的按钮 */}
|
||||
{hoveredRecord && hoveredRecord.type === 'generation' && hoveredRecord.id === generation.id && (
|
||||
<div className="absolute inset-0 bg-black/50 flex items-center justify-center gap-2 opacity-0 hover:opacity-100 transition-opacity duration-200">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 bg-white/90 hover:bg-white text-gray-700 rounded-full shadow-md"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// 下载图像
|
||||
const imageUrl = getUploadedImageUrl(generation, 0) ||
|
||||
(generation.outputAssets && generation.outputAssets.length > 0 && generation.outputAssets[0].url ? generation.outputAssets[0].url : null);
|
||||
if (imageUrl) {
|
||||
const link = document.createElement('a');
|
||||
link.href = imageUrl;
|
||||
link.download = `generation-G${globalIndex + 1}.png`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}}
|
||||
title="下载图像"
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 bg-red-500/90 hover:bg-red-500 text-white rounded-full shadow-md"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// 删除记录
|
||||
removeGeneration(generation.id);
|
||||
}}
|
||||
title="删除记录"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -629,6 +710,9 @@ export const HistoryPanel: React.FC<{
|
||||
}
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
// 设置当前悬停的记录
|
||||
setHoveredRecord({type: 'edit', id: edit.id});
|
||||
|
||||
// 优先使用上传后的远程链接,如果没有则使用原始链接
|
||||
let imageUrl = getUploadedImageUrl(edit, 0);
|
||||
if (!imageUrl && edit.outputAssets && edit.outputAssets.length > 0) {
|
||||
@@ -671,6 +755,9 @@ export const HistoryPanel: React.FC<{
|
||||
// 不需要处理鼠标移动事件,因为预览现在在页面中心显示
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
// 清除当前悬停的记录
|
||||
setHoveredRecord(null);
|
||||
|
||||
setHoveredImage(null);
|
||||
if (setPreviewPosition) {
|
||||
setPreviewPosition(null);
|
||||
@@ -697,6 +784,47 @@ export const HistoryPanel: React.FC<{
|
||||
<div className="absolute top-1 left-1 bg-purple-900/80 text-xs px-1 py-0.5 rounded text-white">
|
||||
E{globalIndex + 1}
|
||||
</div>
|
||||
|
||||
{/* 悬停时显示的按钮 */}
|
||||
{hoveredRecord && hoveredRecord.type === 'edit' && hoveredRecord.id === edit.id && (
|
||||
<div className="absolute inset-0 bg-black/50 flex items-center justify-center gap-2 opacity-0 hover:opacity-100 transition-opacity duration-200">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 bg-white/90 hover:bg-white text-gray-700 rounded-full shadow-md"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// 下载图像
|
||||
const imageUrl = getUploadedImageUrl(edit, 0) ||
|
||||
(edit.outputAssets && edit.outputAssets.length > 0 && edit.outputAssets[0].url ? edit.outputAssets[0].url : null);
|
||||
if (imageUrl) {
|
||||
const link = document.createElement('a');
|
||||
link.href = imageUrl;
|
||||
link.download = `edit-E${globalIndex + 1}.png`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}}
|
||||
title="下载图像"
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 bg-red-500/90 hover:bg-red-500 text-white rounded-full shadow-md"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// 删除记录
|
||||
removeEdit(edit.id);
|
||||
}}
|
||||
title="删除记录"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -818,7 +946,8 @@ export const HistoryPanel: React.FC<{
|
||||
const uploadedUrl = gen.uploadResults && gen.uploadResults[index + 1] && gen.uploadResults[index + 1].success
|
||||
? `${gen.uploadResults[index + 1].url}?x-oss-process=image/quality,q_30`
|
||||
: null;
|
||||
const displayUrl = uploadedUrl || asset.url;
|
||||
// 对于Blob URL,我们需要从decodedImages中获取解码后的图像数据
|
||||
const displayUrl = uploadedUrl || decodedImages[asset.blobUrl] || asset.blobUrl || asset.url;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -926,7 +1055,8 @@ export const HistoryPanel: React.FC<{
|
||||
const uploadedUrl = parentGen.uploadResults && parentGen.uploadResults[index + 1] && parentGen.uploadResults[index + 1].success
|
||||
? `${parentGen.uploadResults[index + 1].url}?x-oss-process=image/quality,q_30`
|
||||
: null;
|
||||
const displayUrl = uploadedUrl || asset.url;
|
||||
// 对于Blob URL,我们需要从decodedImages中获取解码后的图像数据
|
||||
const displayUrl = uploadedUrl || decodedImages[asset.blobUrl] || asset.blobUrl || asset.url;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user