新增 历史记录删除功能

This commit is contained in:
yuantao
2025-09-19 18:40:43 +08:00
parent eae15ced5a
commit 4b5b1a5eba
6 changed files with 261 additions and 47 deletions

View File

@@ -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