import { useState, useEffect } from "react"; import { createPortal } from "react-dom"; import { Image } from "@/components"; import ExperimentTitle from "./ExperimentTitle"; import { selectableTools, correctAnswers } from "./constants"; import { useDragAndDrop, useDropZone } from "@/hooks/useDragAndDrop"; import { getStaticImage } from "@/utils"; interface Tool { src: string; name: string; } interface SelectedTool extends Tool { id: string; } interface Step2SelectPageProps { onNext?: () => void; } export default function Step2SelectPage({ onNext }: Step2SelectPageProps) { const [selectedTools, setSelectedTools] = useState([]); const [isSubmitted, setIsSubmitted] = useState(false); const [isCorrect, setIsCorrect] = useState(false); const { isDragging, dragData, mousePosition, startDrag, endDrag, isPointInElement, } = useDragAndDrop(); const { dropZoneRef, isOver, checkIsOver } = useDropZone(); // 获取可用的工具列表(过滤掉已选择的) const availableTools = selectableTools.filter( (tool) => !selectedTools.some((selected) => selected.name === tool.name) ); // 开始拖拽工具 const handleMouseDown = (tool: Tool, e: React.MouseEvent) => { // 如果已经提交且答案正确,则不允许拖拽 if (isSubmitted && isCorrect) { return; } startDrag(tool, e); }; // 监听鼠标位置,检查是否在放置区域上 useEffect(() => { if (isDragging) { checkIsOver(mousePosition.x, mousePosition.y); } }, [isDragging, mousePosition, checkIsOver]); // 监听鼠标释放,处理拖拽结束 useEffect(() => { if (!isDragging) return; const handleMouseUp = () => { if ( dragData && isPointInElement(mousePosition.x, mousePosition.y, dropZoneRef.current) ) { // 检查是否已经添加过这个工具 const isAlreadyAdded = selectedTools.some( (tool) => tool.name === dragData.name ); if (!isAlreadyAdded) { const newTool: SelectedTool = { ...dragData, id: `selected-${Date.now()}`, }; setSelectedTools([...selectedTools, newTool]); } } endDrag(); }; window.addEventListener("mouseup", handleMouseUp); return () => window.removeEventListener("mouseup", handleMouseUp); }, [ isDragging, dragData, mousePosition, selectedTools, endDrag, isPointInElement, dropZoneRef, ]); // 清空选择 const handleClear = () => { setSelectedTools([]); setIsSubmitted(false); setIsCorrect(false); }; // 提交选择 const handleSubmit = () => { if (selectedTools.length === 0) { // alert("请至少选择一个工具!"); return; } // 验证答案 const selectedNames = selectedTools.map((tool) => tool.name); // 检查数量是否一致 if (selectedNames.length !== correctAnswers.length) { setIsSubmitted(true); setIsCorrect(false); return; } // 检查每个选择的工具是否都在正确答案中 const allCorrect = selectedNames.every((name) => correctAnswers.includes(name) ); setIsSubmitted(true); setIsCorrect(allCorrect); }; // 删除已选工具 const handleRemoveTool = (id: string) => { // 如果已经提交且答案正确,则不允许删除 if (isSubmitted && isCorrect) { return; } setSelectedTools(selectedTools.filter((tool) => tool.id !== id)); }; return ( <> {/* 主要内容区域 */}
{/* 左侧:可拖拽选项 */}

可拖拽选项

{availableTools.length === 0 ? (
所有工具已选择完毕
) : (
{availableTools.map((tool, index) => { const isBeingDragged = isDragging && dragData?.name === tool.name; const isDragDisabled = isSubmitted && isCorrect; return (
handleMouseDown(tool, e)} className={`flex flex-col items-center transition-transform select-none ${ isDragDisabled ? "cursor-not-allowed opacity-50" : "cursor-move hover:scale-105" } ${isBeingDragged ? "opacity-30" : ""}`} >

{tool.name}

); })}
)}
{/* 右侧:实验工具区域 */}

实验工具区域

{/* 提交后的结果显示 */} {isSubmitted ? (
{isCorrect ? ( <>

选择正确,继续实验吧

) : ( <>

选择错误,请重新选择

答案提示

{correctAnswers.join("、")}

)}
) : selectedTools.length === 0 ? (
拖拽工具到这里
) : (
{selectedTools.map((tool) => (

{tool.name}

{/* 删除按钮 - 答案正确后隐藏 */} {!(isSubmitted && isCorrect) && ( )}
))}
)}
{/* 底部按钮 */} {!isSubmitted || !isCorrect ? (
) : null}
{/* 底部提示文字或下一步按钮 */}
{isSubmitted && isCorrect ? ( ) : ( //

通过拖拽的方式,将正确选择拖拽到实验工具区域内吧。

)}
{/* 拖拽预览 - 使用 Portal 渲染到 body,避免受 scale 影响 */} {isDragging && dragData && createPortal(

{dragData.name}

, document.body )} ); }