import { useState, useEffect } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { cn, getBackgroundImage, getStaticImage } from "@/lib/utils"; import { usePuzzleStore } from "@/store/puzzleStore"; import { getPuzzlePieceData } from "@/config/puzzleData"; import { getPuzzlePiecePath, getPuzzleViewBox } from "@/config/puzzlePaths"; import { Image } from "@/components"; /** * 将英文故居名称转换为中文显示名称 */ const getResidenceDisplayName = (residence: string): string => { const nameMap: Record = { chenyun: "陈云", huangyanpei: "黄炎培", luxun: "鲁迅", sunzhongshan: "孙中山", }; return nameMap[residence] || residence; }; export interface PuzzleSystemProps { /** 是否可见 */ visible: boolean; /** 关闭回调 */ onClose: () => void; /** 故居名称,如 'chenyun' */ residence: string; } /** * SVG拼图碎片组件 */ const PuzzlePieceSvg = ({ residence, pieceId, isUnlocked, isSelected, isHovered, onClick, onHover, onLeave, }: { residence: string; pieceId: number; isUnlocked: boolean; isSelected: boolean; isHovered: boolean; onClick: () => void; onHover: (id: number) => void; onLeave: () => void; }) => { const pathData = getPuzzlePiecePath(residence, pieceId); if (!pathData) return null; // 为每个碎片生成唯一的pattern ID const patternId = `pattern-${residence}-${pieceId}`; // 碎片图片路径 const pieceImage = getStaticImage(`puzzle/${residence}/${pieceId}`); return ( <> (isUnlocked ? onHover(pieceId) : undefined)} onMouseLeave={onLeave} style={{ cursor: isUnlocked ? "pointer" : "not-allowed", }} className={cn(isUnlocked ? "puzzle-piece" : "")} > {/* 实际的路径:只有点击到路径填充区域才会触发 onClick */} ); }; /** * 拼图系统组件 * - 全屏弹窗,显示拼图碎片 * - 4x4网格布局(16块碎片) * - 已解锁显示正常图片,未解锁显示遮罩 * - 点击碎片可跳转到对应页面 */ const PuzzleSystem = ({ visible, onClose, residence }: PuzzleSystemProps) => { const navigate = useNavigate(); const { moduleName } = useParams<{ moduleName: string }>(); const { isPuzzleUnlocked, getPuzzleCollectProgress } = usePuzzleStore(); const [selectedPiece, setSelectedPiece] = useState(null); const [hoveredPiece, setHoveredPiece] = useState(null); // 当弹窗关闭时重置选中状态 useEffect(() => { if (!visible) { setSelectedPiece(null); } }, [visible]); if (!visible) return null; // 背景图片 const backgroundImage = getStaticImage("puzzle/底图"); // 处理碎片点击 const handlePieceClick = (pieceId: number) => { const isUnlocked = isPuzzleUnlocked(residence, pieceId); if (isUnlocked) { // 显示选中效果,更新详情框 setSelectedPiece(pieceId); } else { // 未解锁的碎片,可以显示提示或不做任何操作 // 暂时不做处理,或者可以添加提示 } }; // 处理前往按钮点击 const handleGoToPage = () => { if (moduleName && selectedPiece) { // console.log(residence, selectedPiece, moduleName); // const route = getPuzzleRoute(residence, selectedPiece, moduleName); // console.log(route); const route = selectedPieceData?.route; if (route) { navigate(`/${moduleName}/${residence}/${route}`); onClose(); } } }; // 获取当前选中碎片的数据 const selectedPieceData = selectedPiece ? getPuzzlePieceData(residence, selectedPiece) : null; // 生成16块碎片(1-16) const pieces = Array.from({ length: 16 }, (_, i) => i + 1); // 将悬停的碎片放到最后渲染,保证 hover 层级在最上 const renderPieces = hoveredPiece != null ? [...pieces.filter((p) => p !== hoveredPiece), hoveredPiece] : pieces; // 获取收集进度 const collectProgress = getPuzzleCollectProgress(residence); return (
{ // 点击背景关闭 if (e.target === e.currentTarget) { onClose(); } }} > {/* 背景图片 */}
{/* 关闭按钮 */} 关闭 {/* 收集进度 */}
{getResidenceDisplayName(residence)}故居碎片 碎片装饰
{/* 收集进度 */} {collectProgress.toFixed(2) == "1.00" ? "已集齐" : `收集进度${collectProgress.toFixed(2)}%`}
{/* 完整拼图底图 - 作为背景显示所有碎片拼好的样子 */}
{/* SVG拼图容器 - 使用3D变换和缩放 */}
{renderPieces.map((pieceId) => { const isUnlocked = isPuzzleUnlocked(residence, pieceId); return ( handlePieceClick(pieceId)} onHover={(id) => setHoveredPiece(id)} onLeave={() => setHoveredPiece(null)} /> ); })}
{selectedPieceData && (
{/* 标题 */} {selectedPieceData && (
{/* 空格换行 */} {selectedPieceData.title .split(" ") .map((word: string, index: number) => (
{word} {index < selectedPieceData.title.split(" ").length - 1 && " "}
))}
)} {/* 对应页面图片 */}
{selectedPieceData.title}
{/* 前往按钮 */} {selectedPieceData && ( 前往按钮 )}
)}
); }; export default PuzzleSystem;