新增 现在参考图可以拖动排序了;

修复 双参考图生成结果显示问题;
This commit is contained in:
2025-09-22 22:39:45 +08:00
parent 690a530031
commit 260a7e4f0f
6 changed files with 430 additions and 192 deletions

View File

@@ -612,11 +612,20 @@ export const HistoryPanel: React.FC<{
)}
onClick={() => {
selectGeneration(generation.id);
// 设置画布图像为参考图像,如果没有参考图像则使用第一个输出资产
// 设置画布图像为生成结果图像,而不是参考图像
let imageUrl = null;
// 首先尝试获取参考图像
if (generation.sourceAssets && generation.sourceAssets.length > 0) {
// 优先使用生成结果图像
if (generation.outputAssets && generation.outputAssets.length > 0) {
const asset = generation.outputAssets[0];
if (asset.url) {
const uploadedUrl = getUploadedImageUrl(generation, 0);
imageUrl = uploadedUrl || asset.url;
}
}
// 如果没有生成结果图像,则使用参考图像
if (!imageUrl && generation.sourceAssets && generation.sourceAssets.length > 0) {
// 参考图像在uploadResults中从索引outputAssets.length开始
const outputAssetsCount = generation.outputAssets?.length || 0;
const uploadResultIndex = outputAssetsCount; // 第一个参考图像
@@ -628,8 +637,57 @@ export const HistoryPanel: React.FC<{
}
}
// 如果没有参考图像,则使用生成结果图像
if (!imageUrl && generation.outputAssets && generation.outputAssets.length > 0) {
if (imageUrl) {
// 检查是否是Blob URL并且可能已经失效
if (imageUrl.startsWith('blob:')) {
// 预先检查Blob URL是否有效
fetch(imageUrl)
.then(response => {
if (!response.ok) {
// Blob URL失效尝试从AppStore重新获取
const { getBlob } = useAppStore.getState();
const blob = getBlob(imageUrl);
if (blob) {
console.log('从AppStore找到Blob重新创建URL...');
const newUrl = URL.createObjectURL(blob);
setCanvasImage(newUrl);
} else {
// 如果AppStore中也没有直接设置原URL让ImageCanvas处理
setCanvasImage(imageUrl);
}
} else {
// Blob URL有效直接使用
setCanvasImage(imageUrl);
}
})
.catch(() => {
// 网络错误尝试从AppStore重新获取
const { getBlob } = useAppStore.getState();
const blob = getBlob(imageUrl);
if (blob) {
console.log('从AppStore找到Blob重新创建URL...');
const newUrl = URL.createObjectURL(blob);
setCanvasImage(newUrl);
} else {
// 如果AppStore中也没有直接设置原URL让ImageCanvas处理
setCanvasImage(imageUrl);
}
});
} else {
// 非Blob URL直接设置
setCanvasImage(imageUrl);
}
}
}}
onMouseEnter={(e) => {
// 设置当前悬停的记录
setHoveredRecord({type: 'generation', id: generation.id});
// 优先显示生成结果图像,如果没有生成结果图像则显示参考图像
let imageUrl = null;
// 优先使用生成结果图像
if (generation.outputAssets && generation.outputAssets.length > 0) {
const asset = generation.outputAssets[0];
if (asset.url) {
const uploadedUrl = getUploadedImageUrl(generation, 0);
@@ -637,19 +695,8 @@ export const HistoryPanel: React.FC<{
}
}
if (imageUrl) {
setCanvasImage(imageUrl);
}
}}
onMouseEnter={(e) => {
// 设置当前悬停的记录
setHoveredRecord({type: 'generation', id: generation.id});
// 优先显示参考图像,如果没有参考图像则显示生成结果图像
let imageUrl = null;
// 首先尝试获取参考图像
if (generation.sourceAssets && generation.sourceAssets.length > 0) {
// 如果没有生成结果图像,则使用参考图像
if (!imageUrl && generation.sourceAssets && generation.sourceAssets.length > 0) {
// 参考图像在uploadResults中从索引outputAssets.length开始
const outputAssetsCount = generation.outputAssets?.length || 0;
const uploadResultIndex = outputAssetsCount; // 第一个参考图像
@@ -657,12 +704,6 @@ export const HistoryPanel: React.FC<{
(generation.sourceAssets[0].url ? generation.sourceAssets[0].url : null);
}
// 如果没有参考图像,则使用生成结果图像
if (!imageUrl) {
imageUrl = getUploadedImageUrl(generation, 0) ||
(generation.outputAssets && generation.outputAssets.length > 0 && generation.outputAssets[0].url ? generation.outputAssets[0].url : null);
}
if (imageUrl) {
// 创建图像对象以获取尺寸
const img = new Image();
@@ -769,11 +810,20 @@ export const HistoryPanel: React.FC<{
}}
>
{(() => {
// 优先显示参考图像,如果没有参考图像则显示生成结果图像
// 优先显示生成结果图像,如果没有生成结果图像则显示参考图像
let imageUrl = null;
// 首先尝试获取参考图像
if (generation.sourceAssets && generation.sourceAssets.length > 0) {
// 优先使用生成结果图像
if (generation.outputAssets && generation.outputAssets.length > 0) {
const asset = generation.outputAssets[0];
if (asset.url) {
const uploadedUrl = getUploadedImageUrl(generation, 0);
imageUrl = uploadedUrl || asset.url;
}
}
// 如果没有生成结果图像,则使用参考图像
if (!imageUrl && generation.sourceAssets && generation.sourceAssets.length > 0) {
// 参考图像在uploadResults中从索引outputAssets.length开始
const outputAssetsCount = generation.outputAssets?.length || 0;
const uploadResultIndex = outputAssetsCount; // 第一个参考图像
@@ -781,12 +831,6 @@ export const HistoryPanel: React.FC<{
(generation.sourceAssets[0].url ? generation.sourceAssets[0].url : null);
}
// 如果没有参考图像,则使用生成结果图像
if (!imageUrl) {
imageUrl = getUploadedImageUrl(generation, 0) ||
(generation.outputAssets && generation.outputAssets.length > 0 && generation.outputAssets[0].url ? generation.outputAssets[0].url : null);
}
if (imageUrl) {
return <img src={imageUrl} alt="生成的变体" className="w-full h-full object-cover" />;
} else {
@@ -919,11 +963,20 @@ export const HistoryPanel: React.FC<{
onClick={() => {
selectEdit(edit.id);
selectGeneration(null);
// 设置画布图像为参考图像,如果没有参考图像则使用第一个输出资产
// 设置画布图像为编辑结果图像,而不是参考图像
let imageUrl = null;
// 首先尝试获取参考图像
if (edit.sourceAssets && edit.sourceAssets.length > 0) {
// 优先使用编辑结果图像
if (edit.outputAssets && edit.outputAssets.length > 0) {
const asset = edit.outputAssets[0];
if (asset.url) {
const uploadedUrl = getUploadedImageUrl(edit, 0);
imageUrl = uploadedUrl || asset.url;
}
}
// 如果没有编辑结果图像,则使用参考图像
if (!imageUrl && edit.sourceAssets && edit.sourceAssets.length > 0) {
// 参考图像在uploadResults中从索引outputAssets.length开始
const outputAssetsCount = edit.outputAssets?.length || 0;
const uploadResultIndex = outputAssetsCount; // 第一个参考图像
@@ -935,8 +988,57 @@ export const HistoryPanel: React.FC<{
}
}
// 如果没有参考图像,则使用编辑结果图像
if (!imageUrl && edit.outputAssets && edit.outputAssets.length > 0) {
if (imageUrl) {
// 检查是否是Blob URL并且可能已经失效
if (imageUrl.startsWith('blob:')) {
// 预先检查Blob URL是否有效
fetch(imageUrl)
.then(response => {
if (!response.ok) {
// Blob URL失效尝试从AppStore重新获取
const { getBlob } = useAppStore.getState();
const blob = getBlob(imageUrl);
if (blob) {
console.log('从AppStore找到Blob重新创建URL...');
const newUrl = URL.createObjectURL(blob);
setCanvasImage(newUrl);
} else {
// 如果AppStore中也没有直接设置原URL让ImageCanvas处理
setCanvasImage(imageUrl);
}
} else {
// Blob URL有效直接使用
setCanvasImage(imageUrl);
}
})
.catch(() => {
// 网络错误尝试从AppStore重新获取
const { getBlob } = useAppStore.getState();
const blob = getBlob(imageUrl);
if (blob) {
console.log('从AppStore找到Blob重新创建URL...');
const newUrl = URL.createObjectURL(blob);
setCanvasImage(newUrl);
} else {
// 如果AppStore中也没有直接设置原URL让ImageCanvas处理
setCanvasImage(imageUrl);
}
});
} else {
// 非Blob URL直接设置
setCanvasImage(imageUrl);
}
}
}}
onMouseEnter={(e) => {
// 设置当前悬停的记录
setHoveredRecord({type: 'edit', id: edit.id});
// 优先显示编辑结果图像,如果没有编辑结果图像则显示参考图像
let imageUrl = null;
// 优先使用编辑结果图像
if (edit.outputAssets && edit.outputAssets.length > 0) {
const asset = edit.outputAssets[0];
if (asset.url) {
const uploadedUrl = getUploadedImageUrl(edit, 0);
@@ -944,19 +1046,8 @@ export const HistoryPanel: React.FC<{
}
}
if (imageUrl) {
setCanvasImage(imageUrl);
}
}}
onMouseEnter={(e) => {
// 设置当前悬停的记录
setHoveredRecord({type: 'edit', id: edit.id});
// 优先显示参考图像,如果没有参考图像则显示编辑结果图像
let imageUrl = null;
// 首先尝试获取参考图像
if (edit.sourceAssets && edit.sourceAssets.length > 0) {
// 如果没有编辑结果图像,则使用参考图像
if (!imageUrl && edit.sourceAssets && edit.sourceAssets.length > 0) {
// 参考图像在uploadResults中从索引outputAssets.length开始
const outputAssetsCount = edit.outputAssets?.length || 0;
const uploadResultIndex = outputAssetsCount; // 第一个参考图像
@@ -964,12 +1055,6 @@ export const HistoryPanel: React.FC<{
(edit.sourceAssets[0].url ? edit.sourceAssets[0].url : null);
}
// 如果没有参考图像,则使用编辑结果图像
if (!imageUrl) {
imageUrl = getUploadedImageUrl(edit, 0) ||
(edit.outputAssets && edit.outputAssets.length > 0 && edit.outputAssets[0].url ? edit.outputAssets[0].url : null);
}
if (imageUrl) {
// 创建图像对象以获取尺寸
const img = new Image();
@@ -1016,11 +1101,20 @@ export const HistoryPanel: React.FC<{
}}
>
{(() => {
// 优先显示参考图像,如果没有参考图像则显示编辑结果图像
// 优先显示编辑结果图像,如果没有编辑结果图像则显示参考图像
let imageUrl = null;
// 首先尝试获取参考图像
if (edit.sourceAssets && edit.sourceAssets.length > 0) {
// 优先使用编辑结果图像
if (edit.outputAssets && edit.outputAssets.length > 0) {
const asset = edit.outputAssets[0];
if (asset.url) {
const uploadedUrl = getUploadedImageUrl(edit, 0);
imageUrl = uploadedUrl || asset.url;
}
}
// 如果没有编辑结果图像,则使用参考图像
if (!imageUrl && edit.sourceAssets && edit.sourceAssets.length > 0) {
// 参考图像在uploadResults中从索引outputAssets.length开始
const outputAssetsCount = edit.outputAssets?.length || 0;
const uploadResultIndex = outputAssetsCount; // 第一个参考图像
@@ -1028,12 +1122,6 @@ export const HistoryPanel: React.FC<{
(edit.sourceAssets[0].url ? edit.sourceAssets[0].url : null);
}
// 如果没有参考图像,则使用编辑结果图像
if (!imageUrl) {
imageUrl = getUploadedImageUrl(edit, 0) ||
(edit.outputAssets && edit.outputAssets.length > 0 && edit.outputAssets[0].url ? edit.outputAssets[0].url : null);
}
if (imageUrl) {
return <img src={imageUrl} alt="编辑的变体" className="w-full h-full object-cover" />;
} else {

View File

@@ -8,7 +8,9 @@ export const ImageCanvas: React.FC = () => {
const {
canvasImage,
canvasZoom,
canvasPan,
setCanvasZoom,
setCanvasPan,
brushStrokes,
addBrushStroke,
showMasks,
@@ -48,132 +50,194 @@ export const ImageCanvas: React.FC = () => {
// 加载图像
useEffect(() => {
let img: HTMLImageElement | null = null;
console.log('useEffect triggered, canvasImage:', canvasImage);
if (canvasImage) {
console.log('开始加载图像URL:', canvasImage);
// 如果没有图像URL直接返回
if (!canvasImage) {
console.log('没有图像需要加载');
setImage(null);
return;
}
let img: HTMLImageElement | null = null;
let isCancelled = false;
console.log('开始加载图像URL:', canvasImage);
img = new window.Image();
img.onload = () => {
// 检查是否已取消
if (isCancelled) {
console.log('图像加载被取消');
return;
}
img = new window.Image();
const isCancelled = false;
console.log('图像加载成功,尺寸:', img.width, 'x', img.height);
setImage(img);
img.onload = () => {
// 检查是否已取消
if (isCancelled) {
console.log('图像加载被取消');
return;
}
// 只在图像首次加载时自动适应画布
if (!isCancelled && img) {
const isMobile = window.innerWidth < 768;
const padding = isMobile ? 0.9 : 0.8;
console.log('图像加载成功,尺寸:', img.width, 'x', img.height);
setImage(img);
const scaleX = (stageSize.width * padding) / img.width;
const scaleY = (stageSize.height * padding) / img.height;
// 只在图像首次加载时自动适应画布
if (!isCancelled && img) {
const isMobile = window.innerWidth < 768;
const padding = isMobile ? 0.9 : 0.8;
const maxZoom = isMobile ? 0.3 : 0.8;
const optimalZoom = Math.min(scaleX, scaleY, maxZoom);
// 立即更新React状态以确保Konva Image组件使用正确的缩放值
setCanvasZoom(optimalZoom);
setCanvasPan({ x: 0, y: 0 });
// 使用setTimeout确保DOM已更新后再设置Stage
setTimeout(() => {
// 检查是否已取消
if (isCancelled) {
return;
}
const scaleX = (stageSize.width * padding) / img.width;
const scaleY = (stageSize.height * padding) / img.height;
const maxZoom = isMobile ? 0.3 : 0.8;
const optimalZoom = Math.min(scaleX, scaleY, maxZoom);
// 立即更新React状态以确保Konva Image组件使用正确的缩放值
setCanvasZoom(optimalZoom);
setCanvasPan({ x: 0, y: 0 });
// 使用setTimeout确保DOM已更新后再设置Stage
setTimeout(() => {
if (!isCancelled && img) {
// 直接设置缩放但保持Stage居中
const stage = stageRef.current;
if (stage) {
stage.scale({ x: optimalZoom, y: optimalZoom });
// 重置Stage位置以确保居中
stage.position({ x: 0, y: 0 });
stage.batchDraw();
if (!isCancelled && img) {
// 直接设置缩放但保持Stage居中
const stage = stageRef.current;
if (stage) {
stage.scale({ x: optimalZoom, y: optimalZoom });
// 重置Stage位置以确保居中
stage.position({ x: 0, y: 0 });
stage.batchDraw();
}
console.log('图像自动适应画布完成,缩放:', optimalZoom);
}
}, 0);
}
};
img.onerror = (error) => {
// 检查是否已取消
if (isCancelled) {
return;
}
console.error('图像加载失败:', error);
console.error('图像URL:', canvasImage);
// 检查是否是IndexedDB URL
if (canvasImage.startsWith('indexeddb://')) {
console.log('正在处理IndexedDB图像...');
// 从IndexedDB获取图像并创建Blob URL
const imageId = canvasImage.replace('indexeddb://', '');
import('../services/referenceImageService').then((module) => {
const referenceImageService = module;
referenceImageService.getReferenceImage(imageId)
.then(blob => {
// 检查是否已取消
if (isCancelled) {
return;
}
console.log('图像自动适应画布完成,缩放:', optimalZoom);
}
}, 0);
}
};
img.onerror = (error) => {
if (!isCancelled) {
console.error('图像加载失败:', error);
console.error('图像URL:', canvasImage);
if (blob) {
const newUrl = URL.createObjectURL(blob);
console.log('从IndexedDB创建新的Blob URL:', newUrl);
// 更新canvasImage为新的URL
import('../store/useAppStore').then((storeModule) => {
const useAppStore = storeModule.useAppStore;
// 检查是否已取消
if (!isCancelled) {
useAppStore.getState().setCanvasImage(newUrl);
}
});
} else {
console.error('IndexedDB中未找到图像');
}
})
.catch(err => {
// 检查是否已取消
if (isCancelled) {
return;
}
console.error('从IndexedDB获取图像时出错:', err);
});
}).catch(err => {
// 检查是否已取消
if (isCancelled) {
return;
}
// 检查是否是Blob URL
if (canvasImage.startsWith('blob:')) {
console.log('正在检查Blob URL是否有效...');
console.error('导入referenceImageService时出错:', err);
});
}
// 检查是否是Blob URL
else if (canvasImage.startsWith('blob:')) {
console.log('正在检查Blob URL是否有效...');
// 尝试从AppStore重新获取Blob并创建新的URL
import('../store/useAppStore').then((module) => {
const useAppStore = module.useAppStore;
const blob = useAppStore.getState().getBlob(canvasImage);
if (blob) {
// 检查是否已取消
if (isCancelled) {
return;
}
// 检查Blob URL是否仍然有效
console.log('从AppStore找到Blob尝试重新创建URL...');
// 重新创建Blob URL并重试加载
const newUrl = URL.createObjectURL(blob);
console.log('创建新的Blob URL:', newUrl);
// 更新canvasImage为新的URL
useAppStore.getState().setCanvasImage(newUrl);
} else {
// 检查是否已取消
if (isCancelled) {
return;
}
console.error('AppStore中未找到Blob');
// 如果AppStore中也没有尝试通过fetch检查URL
fetch(canvasImage)
.then(response => {
// 检查是否已取消
if (isCancelled) {
return;
}
if (!response.ok) {
console.error('Blob URL无法访问:', response.status, response.statusText);
// 尝试从AppStore重新获取Blob并创建新的URL
import('../store/useAppStore').then((module) => {
const useAppStore = module.useAppStore;
const blob = useAppStore.getState().getBlob(canvasImage);
if (blob) {
console.log('从AppStore找到Blob尝试重新创建URL...');
// 重新创建Blob URL并重试加载
const newUrl = URL.createObjectURL(blob);
console.log('创建新的Blob URL:', newUrl);
// 更新canvasImage为新的URL
useAppStore.getState().setCanvasImage(newUrl);
} else {
console.error('AppStore中未找到Blob');
}
}).catch(err => {
console.error('导入AppStore时出错:', err);
});
} else {
console.log('Blob URL可以访问');
console.log('Blob URL可以访问,但图像加载仍然失败');
}
})
.catch(err => {
console.error('检查Blob URL时出错:', err);
// 尝试从AppStore重新获取Blob
import('../store/useAppStore').then((module) => {
const useAppStore = module.useAppStore;
const blob = useAppStore.getState().getBlob(canvasImage);
if (blob) {
console.log('从AppStore找到Blob尝试重新创建URL...');
// 重新创建Blob URL并重试加载
const newUrl = URL.createObjectURL(blob);
console.log('创建新的Blob URL:', newUrl);
// 更新canvasImage为新的URL
useAppStore.getState().setCanvasImage(newUrl);
} else {
console.error('AppStore中未找到Blob');
}
}).catch(err => {
console.error('导入AppStore时出错:', err);
});
.catch(fetchErr => {
// 检查是否已取消
if (isCancelled) {
return;
}
console.error('检查Blob URL时出错:', fetchErr);
});
}
}
};
img.src = canvasImage;
} else {
console.log('没有图像需要加载');
// 当没有图像时,清理之前的图像对象
if (image) {
// 清理图像对象以释放内存
image.onload = null;
image.onerror = null;
image.src = '';
}).catch(err => {
// 检查是否已取消
if (isCancelled) {
return;
}
console.error('导入AppStore时出错:', err);
});
}
setImage(null);
}
};
img.src = canvasImage;
// 清理函数
return () => {
console.log('清理图像加载资源');
// 标记为已取消
isCancelled = true;
// 取消图像加载
if (img) {
img.onload = null;
@@ -181,15 +245,8 @@ export const ImageCanvas: React.FC = () => {
// 清理图像源以释放内存
img.src = '';
}
// 清理之前的图像对象
if (image) {
image.onload = null;
image.onerror = null;
image.src = '';
}
};
}, [canvasImage, image, setCanvasZoom, stageSize.height, stageSize.width]); // 添加所有依赖项
}, [canvasImage, setCanvasZoom, setCanvasPan, stageSize.height, stageSize.width]); // 移除image依赖项
// 处理舞台大小调整
useEffect(() => {

View File

@@ -16,7 +16,12 @@ const ImagePreview: React.FC<{
index: number;
selectedTool: 'generate' | 'edit' | 'mask';
onRemove: () => void;
}> = ({ image, index, onRemove }) => {
onDragStart?: (e: React.DragEvent<HTMLDivElement>, index: number) => void;
onDragOver?: (e: React.DragEvent<HTMLDivElement>, index: number) => void;
onDragEnd?: (e: React.DragEvent<HTMLDivElement>) => void;
onDrop?: (e: React.DragEvent<HTMLDivElement>, index: number) => void;
isDragging?: boolean;
}> = ({ image, index, onRemove, onDragStart, onDragOver, onDragEnd, onDrop, isDragging }) => {
const [imageSrc, setImageSrc] = useState<string>(image);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<boolean>(false);
@@ -74,7 +79,20 @@ const ImagePreview: React.FC<{
}
return (
<div className="relative">
<div
className={cn("relative", isDragging ? "opacity-50" : "")}
draggable
onDragStart={(e) => onDragStart && onDragStart(e, index)}
onDragOver={(e) => {
e.preventDefault();
onDragOver && onDragOver(e, index);
}}
onDragEnd={(e) => onDragEnd && onDragEnd(e)}
onDrop={(e) => {
e.preventDefault();
onDrop && onDrop(e, index);
}}
>
<img
src={imageSrc}
alt={`参考图像 ${index + 1}`}
@@ -111,6 +129,7 @@ export const PromptComposer: React.FC = () => {
uploadedImages,
addUploadedImage,
removeUploadedImage,
reorderUploadedImage,
clearUploadedImages,
editReferenceImages,
addEditReferenceImage,
@@ -130,6 +149,7 @@ export const PromptComposer: React.FC = () => {
const [showClearConfirm, setShowClearConfirm] = useState(false);
const [showHintsModal, setShowHintsModal] = useState(false);
const [isDragOver, setIsDragOver] = useState(false);
const [draggedIndex, setDraggedIndex] = useState<number | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
// 初始化参考图像数据库
@@ -237,6 +257,13 @@ export const PromptComposer: React.FC = () => {
// 创建一个特殊的URL来标识这是存储在IndexedDB中的图像
const imageUrl = `indexeddb://${imageId}`;
// 同时创建一个可以直接在画布上显示的Blob URL
const blob = await referenceImageService.getReferenceImage(imageId);
let displayUrl = imageUrl; // 默认使用IndexedDB URL
if (blob) {
displayUrl = URL.createObjectURL(blob);
}
if (selectedTool === 'generate') {
// 添加到参考图像最多2张
if (uploadedImages.length < 2) {
@@ -247,15 +274,15 @@ export const PromptComposer: React.FC = () => {
if (editReferenceImages.length < 2) {
addEditReferenceImage(imageUrl);
}
// 如果没有画布图像,则设置为画布图像
// 如果没有画布图像,则设置为画布图像使用可以直接显示的URL
if (!canvasImage) {
setCanvasImage(imageUrl);
setCanvasImage(displayUrl);
}
} else if (selectedTool === 'mask') {
// 遮罩模式下,将图像添加为参考图像而不是清除现有图像
// 只有在没有画布图像时才设置为画布图像
// 只有在没有画布图像时才设置为画布图像使用可以直接显示的URL
if (!canvasImage) {
setCanvasImage(imageUrl);
setCanvasImage(displayUrl);
}
// 不清除现有的上传图像,而是将新图像添加为参考图像(如果还有空间)
if (uploadedImages.length < 2) {
@@ -294,6 +321,31 @@ export const PromptComposer: React.FC = () => {
}
};
// 拖拽排序处理函数
const handleDragStart = (e: React.DragEvent<HTMLDivElement>, index: number) => {
setDraggedIndex(index);
e.dataTransfer.effectAllowed = 'move';
// 在Firefox中需要设置dataTransfer数据
e.dataTransfer.setData('text/plain', index.toString());
};
const handleDragOverPreview = (e: React.DragEvent<HTMLDivElement>, index: number) => {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
};
const handleDropPreview = (e: React.DragEvent<HTMLDivElement>, index: number) => {
e.preventDefault();
if (draggedIndex !== null && draggedIndex !== index) {
reorderUploadedImage(draggedIndex, index);
}
setDraggedIndex(null);
};
const handleDragEnd = () => {
setDraggedIndex(null);
};
const handleClearSession = async () => {
setCurrentPrompt('');
clearUploadedImages();
@@ -311,6 +363,9 @@ export const PromptComposer: React.FC = () => {
} catch (error) {
console.error('清空IndexedDB中的参考图像失败:', error);
}
// 清理所有Blob URL
useAppStore.getState().cleanupAllBlobUrls();
};
const tools = [
@@ -461,6 +516,11 @@ export const PromptComposer: React.FC = () => {
index={index}
selectedTool={selectedTool}
onRemove={() => selectedTool === 'generate' ? removeUploadedImage(index) : removeEditReferenceImage(index)}
onDragStart={selectedTool === 'generate' ? handleDragStart : undefined}
onDragOver={selectedTool === 'generate' ? handleDragOverPreview : undefined}
onDragEnd={selectedTool === 'generate' ? handleDragEnd : undefined}
onDrop={selectedTool === 'generate' ? handleDropPreview : undefined}
isDragging={selectedTool === 'generate' && draggedIndex === index}
/>
))}
</div>