You've already forked Nano-Banana-AI-Image-Editor
优化 调整了整体的UI界面
This commit is contained in:
@@ -211,19 +211,19 @@ export const HistoryPanel: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-80 bg-white border-l border-gray-200 p-6 flex flex-col h-full">
|
||||
<div className="w-72 bg-white border-l border-gray-200 p-4 flex flex-col h-full">
|
||||
{/* 头部 */}
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<History className="h-5 w-5 text-gray-400" />
|
||||
<h3 className="text-sm font-medium text-gray-300">历史记录和变体</h3>
|
||||
<History className="h-4 w-4 text-gray-500" />
|
||||
<h3 className="text-sm font-medium text-gray-700">历史记录</h3>
|
||||
</div>
|
||||
<div className="flex space-x-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={refresh}
|
||||
className="h-6 w-6"
|
||||
className="h-6 w-6 text-gray-400 hover:text-gray-600 hover:bg-gray-100"
|
||||
title="刷新历史记录"
|
||||
>
|
||||
⟳
|
||||
@@ -232,29 +232,32 @@ export const HistoryPanel: React.FC = () => {
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setShowHistory(!showHistory)}
|
||||
className="h-6 w-6"
|
||||
className="h-6 w-6 text-gray-400 hover:text-gray-600 hover:bg-gray-100"
|
||||
title="隐藏历史面板"
|
||||
>
|
||||
×
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 筛选和搜索控件 */}
|
||||
<div className="mb-4 space-y-3">
|
||||
<div className="flex space-x-2">
|
||||
<div className="mb-3 space-y-2">
|
||||
<div className="flex space-x-1">
|
||||
<input
|
||||
type="date"
|
||||
value={startDate}
|
||||
onChange={(e) => setStartDate(e.target.value)}
|
||||
className="flex-1 text-xs p-1 border rounded"
|
||||
className="flex-1 text-xs p-1.5 border border-gray-200 rounded text-gray-600 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-yellow-400"
|
||||
placeholder="开始日期"
|
||||
/>
|
||||
<input
|
||||
type="date"
|
||||
value={endDate}
|
||||
onChange={(e) => setEndDate(e.target.value)}
|
||||
className="flex-1 text-xs p-1 border rounded"
|
||||
className="flex-1 text-xs p-1.5 border border-gray-200 rounded text-gray-600 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-yellow-400"
|
||||
placeholder="结束日期"
|
||||
/>
|
||||
</div>
|
||||
@@ -263,13 +266,13 @@ export const HistoryPanel: React.FC = () => {
|
||||
type="text"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="flex-1 text-xs p-1 border rounded-l"
|
||||
placeholder="搜索提示词或编辑指令..."
|
||||
className="flex-1 text-xs p-1.5 border border-gray-200 rounded-l bg-gray-50 focus:outline-none focus:ring-1 focus:ring-yellow-400"
|
||||
placeholder="搜索提示词..."
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="text-xs p-1 rounded-l-none"
|
||||
className="text-xs p-1.5 rounded-l-none h-7 border-gray-200 text-gray-600 hover:bg-gray-100"
|
||||
onClick={() => {
|
||||
setStartDate('');
|
||||
setEndDate('');
|
||||
@@ -283,28 +286,28 @@ export const HistoryPanel: React.FC = () => {
|
||||
|
||||
{/* 变体网格 */}
|
||||
<div className="mb-6 flex-shrink-0">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h4 className="text-xs font-medium text-gray-400">当前变体</h4>
|
||||
<span className="text-xs text-gray-500">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h4 className="text-xs font-medium text-gray-500 uppercase tracking-wide">变体</h4>
|
||||
<span className="text-xs text-gray-400">
|
||||
{filteredGenerations.length + filteredEdits.length}/1000
|
||||
</span>
|
||||
</div>
|
||||
{filteredGenerations.length === 0 && filteredEdits.length === 0 ? (
|
||||
<div className="text-center py-8">
|
||||
<div className="text-4xl mb-2">🖼️</div>
|
||||
<p className="text-sm text-gray-500">暂无生成记录</p>
|
||||
<div className="text-2xl mb-2 text-gray-300">🖼️</div>
|
||||
<p className="text-xs text-gray-400">暂无历史记录</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-3 gap-2 max-h-80 overflow-y-auto">
|
||||
<div className="grid grid-cols-3 gap-1.5 max-h-72 overflow-y-auto">
|
||||
{/* 显示生成记录 */}
|
||||
{[...filteredGenerations].sort((a, b) => b.timestamp - a.timestamp).slice(0, 50).map((generation, index) => (
|
||||
<div
|
||||
key={generation.id}
|
||||
className={cn(
|
||||
'relative aspect-square rounded-lg border-2 cursor-pointer transition-all duration-200 overflow-hidden',
|
||||
'relative aspect-square rounded border cursor-pointer transition-all duration-200 overflow-hidden',
|
||||
selectedGenerationId === generation.id
|
||||
? 'border-yellow-400'
|
||||
: 'border-gray-300 hover:border-gray-400'
|
||||
: 'border-gray-200 hover:border-gray-300'
|
||||
)}
|
||||
onClick={() => {
|
||||
selectGeneration(generation.id);
|
||||
@@ -400,10 +403,10 @@ export const HistoryPanel: React.FC = () => {
|
||||
<div
|
||||
key={edit.id}
|
||||
className={cn(
|
||||
'relative aspect-square rounded-lg border-2 cursor-pointer transition-all duration-200 overflow-hidden',
|
||||
'relative aspect-square rounded border cursor-pointer transition-all duration-200 overflow-hidden',
|
||||
selectedEditId === edit.id
|
||||
? 'border-purple-400'
|
||||
: 'border-gray-300 hover:border-gray-400'
|
||||
: 'border-gray-200 hover:border-gray-300'
|
||||
)}
|
||||
onClick={() => {
|
||||
selectEdit(edit.id);
|
||||
@@ -518,56 +521,56 @@ export const HistoryPanel: React.FC = () => {
|
||||
)}
|
||||
|
||||
{/* 生成详情 */}
|
||||
<div className="mb-6 p-4 bg-gray-50 rounded-lg border border-gray-200 flex-1 overflow-y-auto min-h-0">
|
||||
<h4 className="text-xs font-medium text-gray-500 mb-2">生成详情</h4>
|
||||
<div className="flex-1 overflow-y-auto min-h-0">
|
||||
<h4 className="text-xs font-medium text-gray-500 mb-2 uppercase tracking-wide">详情</h4>
|
||||
{(() => {
|
||||
const gen = filteredGenerations.find(g => g.id === selectedGenerationId) || dbGenerations.find(g => g.id === selectedGenerationId);
|
||||
const selectedEdit = filteredEdits.find(e => e.id === selectedEditId) || dbEdits.find(e => e.id === selectedEditId);
|
||||
|
||||
if (gen) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2 text-xs text-gray-600">
|
||||
<div className="space-y-3 p-3 bg-gray-50 rounded-lg">
|
||||
<div className="space-y-2.5 text-xs text-gray-700">
|
||||
<div>
|
||||
<span className="text-gray-500">提示:</span>
|
||||
<p className="text-gray-800 mt-1">{gen.prompt}</p>
|
||||
<p className="text-gray-800 mt-1 leading-relaxed">{gen.prompt}</p>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>模型:</span>
|
||||
<span>{gen.modelVersion}</span>
|
||||
<span className="text-gray-500">模型:</span>
|
||||
<span className="text-gray-700">{gen.modelVersion}</span>
|
||||
</div>
|
||||
{gen.parameters.seed && (
|
||||
<div className="flex justify-between">
|
||||
<span>种子:</span>
|
||||
<span>{gen.parameters.seed}</span>
|
||||
<span className="text-gray-500">种子:</span>
|
||||
<span className="text-gray-700 font-mono">{gen.parameters.seed}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between">
|
||||
<span>时间:</span>
|
||||
<span>{new Date(gen.timestamp).toLocaleString()}</span>
|
||||
<span className="text-gray-500">时间:</span>
|
||||
<span className="text-gray-700">{new Date(gen.timestamp).toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 上传结果 */}
|
||||
{gen.uploadResults && gen.uploadResults.length > 0 && (
|
||||
<div>
|
||||
<div className="pt-2 border-t border-gray-200">
|
||||
<h5 className="text-xs font-medium text-gray-500 mb-2">上传结果</h5>
|
||||
<div className="space-y-1">
|
||||
<div className="space-y-1.5">
|
||||
{gen.uploadResults.map((result, index) => (
|
||||
<div key={index} className="text-xs">
|
||||
<div className="flex justify-between">
|
||||
<span>图像 {index + 1}:</span>
|
||||
<span className="text-gray-500">图像 {index + 1}:</span>
|
||||
<span className={result.success ? 'text-green-600' : 'text-red-600'}>
|
||||
{result.success ? '成功' : '失败'}
|
||||
</span>
|
||||
</div>
|
||||
{result.success && result.url && (
|
||||
<div className="text-blue-600 truncate">
|
||||
<div className="text-blue-600 truncate text-xs mt-0.5">
|
||||
{result.url.split('/').pop()}
|
||||
</div>
|
||||
)}
|
||||
{result.error && (
|
||||
<div className="text-red-600 truncate">
|
||||
<div className="text-red-600 truncate text-xs mt-0.5">
|
||||
{result.error}
|
||||
</div>
|
||||
)}
|
||||
@@ -579,7 +582,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
|
||||
{/* 参考图像信息 */}
|
||||
{gen.sourceAssets && gen.sourceAssets.length > 0 && (
|
||||
<div>
|
||||
<div className="pt-2 border-t border-gray-200">
|
||||
<h5 className="text-xs font-medium text-gray-500 mb-2">参考图像</h5>
|
||||
<div className="text-xs text-gray-600 mb-2">
|
||||
{gen.sourceAssets.length} 个参考图像
|
||||
@@ -588,7 +591,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
{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"
|
||||
className="relative w-16 h-16 rounded border overflow-hidden cursor-pointer hover:ring-2 hover:ring-yellow-400"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setPreviewModal({
|
||||
@@ -617,25 +620,25 @@ export const HistoryPanel: React.FC = () => {
|
||||
</div>
|
||||
);
|
||||
} else if (selectedEdit) {
|
||||
const parentGen = dbGenerations.find(g => g.id === selectedEdit.parentGenerationId);
|
||||
const parentGen = filteredGenerations.find(g => g.id === selectedEdit.parentGenerationId) || dbGenerations.find(g => g.id === selectedEdit.parentGenerationId);
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2 text-xs text-gray-600">
|
||||
<div className="space-y-3 p-3 bg-gray-50 rounded-lg">
|
||||
<div className="space-y-2.5 text-xs text-gray-700">
|
||||
<div>
|
||||
<span className="text-gray-500">编辑指令:</span>
|
||||
<p className="text-gray-800 mt-1">{selectedEdit.instruction}</p>
|
||||
<p className="text-gray-800 mt-1 leading-relaxed">{selectedEdit.instruction}</p>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>类型:</span>
|
||||
<span>图像编辑</span>
|
||||
<span className="text-gray-500">类型:</span>
|
||||
<span className="text-gray-700">图像编辑</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>创建时间:</span>
|
||||
<span>{new Date(selectedEdit.timestamp).toLocaleString()}</span>
|
||||
<span className="text-gray-500">创建时间:</span>
|
||||
<span className="text-gray-700">{new Date(selectedEdit.timestamp).toLocaleString()}</span>
|
||||
</div>
|
||||
{selectedEdit.maskAssetId && (
|
||||
<div className="flex justify-between">
|
||||
<span>遮罩:</span>
|
||||
<span className="text-gray-500">遮罩:</span>
|
||||
<span className="text-purple-600">已应用</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -643,24 +646,24 @@ export const HistoryPanel: React.FC = () => {
|
||||
|
||||
{/* 上传结果 */}
|
||||
{selectedEdit.uploadResults && selectedEdit.uploadResults.length > 0 && (
|
||||
<div>
|
||||
<div className="pt-2 border-t border-gray-200">
|
||||
<h5 className="text-xs font-medium text-gray-500 mb-2">上传结果</h5>
|
||||
<div className="space-y-1">
|
||||
<div className="space-y-1.5">
|
||||
{selectedEdit.uploadResults.map((result, index) => (
|
||||
<div key={index} className="text-xs">
|
||||
<div className="flex justify-between">
|
||||
<span>图像 {index + 1}:</span>
|
||||
<span className="text-gray-500">图像 {index + 1}:</span>
|
||||
<span className={result.success ? 'text-green-600' : 'text-red-600'}>
|
||||
{result.success ? '成功' : '失败'}
|
||||
</span>
|
||||
</div>
|
||||
{result.success && result.url && (
|
||||
<div className="text-blue-600 truncate">
|
||||
<div className="text-blue-600 truncate text-xs mt-0.5">
|
||||
{result.url.split('/').pop()}
|
||||
</div>
|
||||
)}
|
||||
{result.error && (
|
||||
<div className="text-red-600 truncate">
|
||||
<div className="text-red-600 truncate text-xs mt-0.5">
|
||||
{result.error}
|
||||
</div>
|
||||
)}
|
||||
@@ -672,7 +675,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
|
||||
{/* 原始生成参考 */}
|
||||
{parentGen && (
|
||||
<div>
|
||||
<div className="pt-2 border-t border-gray-200">
|
||||
<h5 className="text-xs font-medium text-gray-500 mb-2">原始生成</h5>
|
||||
<div className="text-xs text-gray-600">
|
||||
基于: G{dbGenerations.findIndex(g => g.id === parentGen.id) + 1}
|
||||
@@ -687,7 +690,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
{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"
|
||||
className="relative w-16 h-16 rounded border overflow-hidden cursor-pointer hover:ring-2 hover:ring-yellow-400"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setPreviewModal({
|
||||
@@ -719,8 +722,8 @@ export const HistoryPanel: React.FC = () => {
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="space-y-2 text-xs text-gray-500">
|
||||
<p className="text-gray-500">选择一个生成记录或编辑记录以查看详细信息</p>
|
||||
<div className="space-y-2 text-xs text-gray-500 p-3 text-center">
|
||||
<p className="text-gray-400">选择一个记录以查看详细信息</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -728,11 +731,11 @@ export const HistoryPanel: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* 操作 */}
|
||||
<div className="space-y-3 flex-shrink-0">
|
||||
<div className="space-y-2 flex-shrink-0 pt-2 border-t border-gray-100">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="w-full"
|
||||
className="w-full h-9 text-sm"
|
||||
onClick={() => {
|
||||
// 查找当前显示的图像(生成记录或编辑记录)
|
||||
let imageUrl: string | null = null;
|
||||
@@ -775,7 +778,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
disabled={!selectedGenerationId && !useAppStore.getState().canvasImage}
|
||||
>
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
下载
|
||||
下载图像
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user