DetailModal.tsx 5.72 KB
import React from 'react';
import { ImageItem, ImageGenerationParams } from '../types';
import { X, Copy, Download, Edit3, Heart, Zap } from 'lucide-react';

interface DetailModalProps {
  image: ImageItem | null;
  onClose: () => void;
  onEdit?: (image: ImageItem) => void;
  onGenerateSimilar?: (params: ImageGenerationParams) => void;
}

const DetailModal: React.FC<DetailModalProps> = ({ image, onClose, onEdit, onGenerateSimilar }) => {
  if (!image) return null;

  const copyToClipboard = (text: string) => {
    navigator.clipboard.writeText(text);
  };

  const handleGenerateSimilar = () => {
    if (onGenerateSimilar) {
      const params: ImageGenerationParams = {
        prompt: image.prompt,
        width: image.width,
        height: image.height,
        num_inference_steps: image.num_inference_steps,
        guidance_scale: image.guidance_scale,
        seed: image.seed
      };
      onGenerateSimilar(params);
      onClose();
    }
  };

  return (
    <div className="fixed inset-0 z-[100] flex items-center justify-center p-4 md:p-8">
      <div className="absolute inset-0 bg-black/90 backdrop-blur-sm transition-opacity" onClick={onClose}/>
      
      <div className="relative bg-white dark:bg-gray-900 w-full max-w-6xl max-h-[90vh] rounded-2xl overflow-hidden shadow-2xl flex flex-col md:flex-row animate-fade-in">
        <button onClick={onClose} className="absolute top-4 right-4 z-10 p-2 bg-black/50 rounded-full text-white md:hidden"><X size={20} /></button>

        <div className="w-full md:w-2/3 bg-black flex items-center justify-center overflow-hidden h-[50vh] md:h-auto relative group">
          <img src={image.url} alt={image.prompt} className="max-w-full max-h-full object-contain" />
        </div>

        <div className="w-full md:w-1/3 p-6 md:p-8 flex flex-col overflow-y-auto">
          <div className="flex justify-between items-start mb-6">
            <h2 className="text-2xl font-bold text-gray-800 dark:text-white">参数详情</h2>
            <div className="flex gap-2">
              {onEdit && (
                <button
                  onClick={() => onEdit(image)}
                  className="p-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 rounded-full transition-colors"
                  title="管理"
                >
                  <Edit3 size={18} />
                </button>
              )}
              <button onClick={onClose} className="hidden md:block p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full transition-colors"><X size={24} /></button>
            </div>
          </div>

          <div className="space-y-6">
            <div className="flex items-center gap-2 text-red-500 font-medium">
               <Heart size={18} fill="currentColor" />
               <span>{image.likes || 0} Likes</span>
            </div>

            <div>
              <label className="block text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2">提示词 (Prompt)</label>
              <div className="relative group">
                <p className="text-gray-700 dark:text-gray-200 text-sm leading-relaxed p-3 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-100 dark:border-gray-700">
                  {image.prompt}
                </p>
                <button 
                  onClick={() => copyToClipboard(image.prompt)}
                  className="absolute top-2 right-2 p-1.5 bg-white dark:bg-gray-700 rounded-md shadow-sm opacity-0 group-hover:opacity-100 transition-opacity text-gray-500 hover:text-blue-500"
                  title="复制提示词"
                >
                  <Copy size={14} />
                </button>
              </div>
            </div>

            <div className="grid grid-cols-2 gap-4">
              <div>
                <label className="block text-xs font-semibold text-gray-400 uppercase mb-1">分辨率</label>
                <p className="text-gray-800 dark:text-gray-200 font-mono">{image.width} x {image.height}</p>
              </div>
              <div>
                <label className="block text-xs font-semibold text-gray-400 uppercase mb-1">随机种子</label>
                <p className="text-gray-800 dark:text-gray-200 font-mono">{image.seed}</p>
              </div>
              <div>
                <label className="block text-xs font-semibold text-gray-400 uppercase mb-1">生成步数</label>
                <p className="text-gray-800 dark:text-gray-200 font-mono">{image.num_inference_steps}</p>
              </div>
              <div>
                <label className="block text-xs font-semibold text-gray-400 uppercase mb-1">引导系数</label>
                <p className="text-gray-800 dark:text-gray-200 font-mono">{image.guidance_scale.toFixed(1)}</p>
              </div>
            </div>

            <div className="pt-6 mt-auto space-y-3">
               <button 
                 onClick={handleGenerateSimilar}
                 className="flex items-center justify-center w-full py-3 bg-purple-600 text-white rounded-xl font-bold hover:bg-purple-700 transition-colors gap-2 shadow-lg shadow-purple-200 dark:shadow-none"
               >
                 <Zap size={18} fill="currentColor" />
                 生成同款
               </button>

               <a 
                 href={image.url} 
                 target="_blank"
                 rel="noopener noreferrer"
                 download={`z-image-${image.id}.png`}
                 className="flex items-center justify-center w-full py-3 bg-black dark:bg-white text-white dark:text-black rounded-xl font-medium hover:opacity-90 transition-opacity gap-2"
               >
                 <Download size={18} />
                 下载原图
               </a>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default DetailModal;