// 新增:导出模态框显示状态

import React, { useState, useRef } from ‘react’;
import { Download, ShieldCheck, Plus, Loader2, Image as ImageIcon, Sparkles, X, Check } from ‘lucide-react’;

export default function App() {
const [rank, setRank] = useState(‘A’);
const [suit, setSuit] = useState(‘♠’);
const [imageSrc, setImageSrc] = useState(null);
const [isDownloading, setIsDownloading] = useState(false);


const [showExportModal, setShowExportModal] = useState(false);
// 新增:文件命名称状态
const [fileName, setFileName] = useState(”);

const cardRef = useRef(null);

const alphaRanks = Array.from({ length: 26 }, (_, i) => String.fromCharCode(65 + i));

const suits = [
{ symbol: ‘♠’, name: ‘黑桃’, color: ‘text-zinc-100’, glow: ‘shadow-[0_0_15px_rgba(255,255,255,0.2)]’ },
{ symbol: ‘♥’, name: ‘红心’, color: ‘text-[#FF2E2E]’, glow: ‘shadow-[0_0_15px_rgba(255,46,46,0.3)]’ },
{ symbol: ‘♣’, name: ‘梅花’, color: ‘text-[#10B981]’, glow: ‘shadow-[0_0_15px_rgba(16,185,129,0.3)]’ },
{ symbol: ‘♦’, name: ‘方块’, color: ‘text-[#FBBF24]’, glow: ‘shadow-[0_0_15px_rgba(251,191,36,0.3)]’ }
];

const currentSuitObj = suits.find(s => s.symbol === suit) || suits[0];

const handleImageUpload = (e) => {
const file = e.target.files[0];
if (file) {
const url = URL.createObjectURL(file);
setImageSrc(url);
}
};

// 第一步:触发下载意向(打开输入框)
const triggerExportFlow = () => {
// 预设一个默认名字:[花色][数值]-StarCard
setFileName(${suit}${rank}-StarCard);
setShowExportModal(true);
};

// 第二步:正式执行下载逻辑
const executeDownload = async () => {
if (!cardRef.current) return;
setIsDownloading(true);
setShowExportModal(false); // 关闭模态框

try {
  if (!window.html2canvas) {
    const script = document.createElement('script');
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js';
    document.head.appendChild(script);
    await new Promise((resolve) => (script.onload = resolve));
  }
  const canvas = await window.html2canvas(cardRef.current, {
    useCORS: true,
    scale: 4, // 4倍缩放确保高清打印 (300DPI级别)
    backgroundColor: null,
  });
  const link = document.createElement('a');
  link.download = `${fileName || 'poker-card'}.png`;
  link.href = canvas.toDataURL('image/png', 1.0);
  link.click();
} catch (error) {
  console.error("Export failed:", error);
} finally {
  setIsDownloading(false);
}

};

return (

  {/* 背景动态星尘 */}
  <div className="absolute inset-0 pointer-events-none opacity-20" 
       style={{ backgroundImage: `radial-gradient(1px 1px at 20px 30px, #eee, rgba(0,0,0,0)), radial-gradient(1px 1px at 40px 70px, #fff, rgba(0,0,0,0)), radial-gradient(2px 2px at 50px 160px, #ddd, rgba(0,0,0,0))`, backgroundSize: '200px 200px' }}></div>

  {/* 控制台 */}
  <div className="w-full md:w-80 bg-zinc-900/50 backdrop-blur-3xl p-8 rounded-[2rem] border border-zinc-800/50 flex flex-col gap-8 h-fit z-50 shadow-2xl">
    <header className="flex items-center gap-3">
      <div className="w-10 h-10 bg-gradient-to-br from-zinc-700 to-zinc-900 rounded-xl flex items-center justify-center text-white shadow-inner border border-zinc-700/50">
        <Sparkles size={20} className="text-blue-400" />
      </div>
      <div>
        <h1 className="text-lg font-bold text-zinc-100 tracking-tight">Starry Lab</h1>
        <p className="text-[9px] text-zinc-500 font-mono tracking-widest uppercase">Premium Edition</p>
      </div>
    </header>

    <section className="space-y-6">
      <div className="space-y-3">
        <span className="text-[10px] font-bold text-zinc-600 uppercase tracking-[0.2em] block">Alpha Rank / 字母全集</span>
        <div className="grid grid-cols-7 gap-1 p-1 bg-black/40 rounded-xl border border-zinc-800/50 max-h-40 overflow-y-auto scrollbar-hide">
          {alphaRanks.map(r => (
            <button key={r} onClick={() => setRank(r)} 
                    className={`aspect-square text-[10px] rounded-lg transition-all ${rank === r ? 'bg-zinc-100 text-black shadow-lg scale-105' : 'hover:text-zinc-200 text-zinc-600'}`}>
              {r}
            </button>
          ))}
        </div>
      </div>

      <div className="flex gap-2">
        {suits.map(s => (
          <button key={s.symbol} onClick={() => setSuit(s.symbol)} 
                  className={`flex-1 py-3 rounded-xl border transition-all ${suit === s.symbol ? 'border-zinc-100 bg-zinc-100 text-black' : 'border-zinc-800 bg-black/30 hover:border-zinc-700'}`}>
            <span className={`text-xl ${suit === s.symbol ? 'text-black' : s.color}`}>{s.symbol}</span>
          </button>
        ))}
      </div>
    </section>

    {/* 触发导出流程 */}
    <button 
      onClick={triggerExportFlow} 
      disabled={isDownloading} 
      className="w-full py-4 rounded-2xl bg-zinc-100 text-black font-bold text-xs tracking-[0.2em] uppercase hover:bg-white active:scale-[0.98] transition-all shadow-[0_0_20px_rgba(255,255,255,0.1)] flex items-center justify-center gap-2"
    >
      {isDownloading ? <Loader2 className="animate-spin" size={16} /> : "Finalize & Export"}
    </button>
  </div>

  {/* 美术展示区 */}
  <div className="relative group">
    <div 
      ref={cardRef}
      className="relative flex items-center justify-center bg-[#020617] transition-all duration-700"
      style={{ 
        width: '340px', height: '476px', borderRadius: '36px', 
        border: '8px solid #111827',
        boxShadow: '0 50px 100px -20px rgba(0,0,0,1), inset 0 0 20px rgba(255,255,255,0.02)'
      }}
    >
      <div className="absolute inset-0 opacity-40 pointer-events-none rounded-[24px] overflow-hidden" 
           style={{ backgroundImage: `radial-gradient(circle at 50% 50%, #1e293b 0%, transparent 80%)` }}></div>

      <div className={`absolute top-2 left-3 flex flex-col items-center leading-none ${currentSuitObj.color} z-40 select-none drop-shadow-[0_0_8px_rgba(255,255,255,0.1)]`}>
        <span className="font-serif font-black text-[26px] tracking-[-0.05em] h-[24px] w-[30px] flex items-center justify-center text-center">{rank}</span>
        <span className="text-[18px] mt-1 opacity-90">{suit}</span>
      </div>

      <div className={`absolute bottom-2 right-3 flex flex-col items-center leading-none rotate-180 ${currentSuitObj.color} z-40 select-none drop-shadow-[0_0_8px_rgba(255,255,255,0.1)]`}>
        <span className="font-serif font-black text-[26px] tracking-[-0.05em] h-[24px] w-[30px] flex items-center justify-center text-center">{rank}</span>
        <span className="text-[18px] mt-1 opacity-90">{suit}</span>
      </div>

      <div className="relative z-20 flex p-[8px]" style={{ width: '304px', height: '416px' }}>
        <div className="absolute top-0 left-0 w-8 h-8 border-t border-l border-zinc-700/30 rounded-tl-[45px] pointer-events-none"></div>
        <div className="absolute bottom-0 right-0 w-8 h-8 border-b border-r border-zinc-700/30 rounded-br-[45px] pointer-events-none"></div>

        <div 
          className="absolute inset-0 bg-white/[0.02] backdrop-blur-[20px] border border-white/[0.08] shadow-[0_20px_50px_rgba(0,0,0,0.5),inset_0_1px_1px_rgba(255,255,255,0.05)] overflow-hidden"
          style={{ borderRadius: '90px 25px 90px 25px' }} 
        >
          <div className="absolute inset-0 bg-gradient-to-br from-white/[0.04] via-transparent to-black/30 pointer-events-none"></div>
        </div>

        <div 
          className="relative w-full h-full overflow-hidden flex items-center justify-center bg-black/40 shadow-[inset_0_4px_30px_rgba(0,0,0,0.7)] z-10"
          style={{ borderRadius: '82px 17px 82px 17px' }}
        >
          {imageSrc ? (
            <img src={imageSrc} alt="Art" className="w-full h-full object-cover grayscale-[10%] hover:grayscale-0 transition-all duration-700" />
          ) : (
            <div className="flex flex-col items-center gap-3 opacity-10 group cursor-pointer">
              <ImageIcon size={40} strokeWidth={1} />
              <span className="text-[7px] font-black uppercase tracking-[0.8em]">A-Z Full Set</span>
            </div>
          )}
          <input type="file" accept="image/*" onChange={handleImageUpload} className="absolute inset-0 opacity-0 cursor-pointer z-50" />
        </div>
      </div>

      <div className="absolute inset-[0.5px] border border-white/5 rounded-[34px] pointer-events-none"></div>
    </div>
  </div>

  {/* 导出重命名模态框 (Modal Layer) */}
  {showExportModal && (
    <div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
      <div className="absolute inset-0 bg-[#020617]/80 backdrop-blur-md" onClick={() => setShowExportModal(false)}></div>
      <div className="relative bg-zinc-900 border border-zinc-800 rounded-[2rem] p-8 w-full max-w-sm shadow-[0_30px_100px_rgba(0,0,0,0.8)] flex flex-col gap-6 animate-in fade-in zoom-in duration-300">
        <div className="space-y-2">
          <h2 className="text-zinc-100 font-bold text-xl tracking-tight">Naming Your Asset</h2>
          <p className="text-zinc-500 text-xs">请输入导出文件的名称,无需填写后缀名。</p>
        </div>

        <div className="relative">
          <input 
            autoFocus
            type="text" 
            value={fileName}
            onChange={(e) => setFileName(e.target.value)}
            onKeyDown={(e) => e.key === 'Enter' && executeDownload()}
            className="w-full bg-black/50 border border-zinc-800 rounded-xl px-4 py-4 text-zinc-100 outline-none focus:border-blue-500 transition-all font-mono text-sm"
            placeholder="Card name..."
          />
          <div className="absolute right-4 top-1/2 -translate-y-1/2 text-[10px] text-zinc-600 font-mono">.png</div>
        </div>

        <div className="flex gap-3 mt-2">
          <button 
            onClick={() => setShowExportModal(false)}
            className="flex-1 py-3 rounded-xl border border-zinc-800 text-zinc-400 font-bold text-[10px] uppercase tracking-widest hover:bg-zinc-800 transition-all"
          >
            Cancel
          </button>
          <button 
            onClick={executeDownload}
            className="flex-1 py-3 rounded-xl bg-zinc-100 text-black font-bold text-[10px] uppercase tracking-widest hover:bg-white shadow-xl transition-all flex items-center justify-center gap-2"
          >
            <Download size={14} />
            Download
          </button>
        </div>
      </div>
    </div>
  )}
</div>

);
}

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注