537 lines
20 KiB
TypeScript
537 lines
20 KiB
TypeScript
import {
|
||
ExhibitOverlay,
|
||
GuideSteps,
|
||
GuideStepsItem,
|
||
Image,
|
||
Title,
|
||
StudyGuideModal,
|
||
Video,
|
||
type VideoRef,
|
||
type DragFillResult,
|
||
} from "@/components";
|
||
import { useNavigate, useParams } from "react-router-dom";
|
||
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
|
||
import { useState, useRef } from "react";
|
||
import Cursor from "@/components/cursor";
|
||
import { SuccessAlert, DragQuizPanel } from "@/components/layout";
|
||
import { MultipleChoiceQuizPanel } from "@/components/layout/quiz";
|
||
|
||
// starName 固定为 sunzhongshan
|
||
const starName = "sunzhongshan";
|
||
|
||
// 展品 ID 与资源路径映射,ID 用来做唯一标识
|
||
const exhibitSrcMap: Record<1 | 2 | 3 | 4 | 5, string> = {
|
||
1: "sunzhongshan/place1/“南北议和”纪念章",
|
||
2: "sunzhongshan/place1/“四大寇”合影",
|
||
3: "sunzhongshan/place1/民立报",
|
||
4: "sunzhongshan/place1/申报",
|
||
5: "sunzhongshan/place1/郑观应的推荐信",
|
||
};
|
||
|
||
const SunzhongshanPlace1 = () => {
|
||
const navigate = useNavigate();
|
||
const { moduleName } = useParams<{
|
||
moduleName: string;
|
||
}>();
|
||
|
||
// 学习引导弹窗
|
||
const [showGuideModal, setShowGuideModal] = useState(true);
|
||
// 对话结束后的学习引导弹窗
|
||
const [showAfterDialogGuide, setShowAfterDialogGuide] = useState(false);
|
||
const [afterDialogGuideShown, setAfterDialogGuideShown] = useState(false);
|
||
// 引导对话显示状态
|
||
const [visible, setVisible] = useState(false);
|
||
// 当前激活的展品 ID(只存 ID,不直接存路径,便于作为唯一标识)
|
||
const [activeExhibitId, setActiveExhibitId] = useState<
|
||
1 | 2 | 3 | 4 | 5 | null
|
||
>(null);
|
||
// 已浏览展品的 ID 集合
|
||
const [visited, setVisited] = useState<Set<number>>(new Set());
|
||
|
||
// 光标2的对话状态
|
||
const [guide2Visible, setGuide2Visible] = useState(false);
|
||
const [guide2Step, setGuide2Step] = useState(0);
|
||
// 光标5的对话状态
|
||
const [guide5Visible, setGuide5Visible] = useState(false);
|
||
const [guide5Step, setGuide5Step] = useState(0);
|
||
// 光标4的对话状态
|
||
const [guide4Visible, setGuide4Visible] = useState(false);
|
||
const [guide4Step, setGuide4Step] = useState(0);
|
||
// 光标4的视频状态
|
||
const [showVideo4, setShowVideo4] = useState(false);
|
||
const video4Ref = useRef<VideoRef>(null);
|
||
// 光标4的拖拽题状态
|
||
const [dragQuiz4Visible, setDragQuiz4Visible] = useState(false);
|
||
const [dragQuiz4Value, setDragQuiz4Value] = useState<DragFillResult[]>([]);
|
||
const [dragQuiz4Error, setDragQuiz4Error] = useState("");
|
||
const [successAlert4Visible, setSuccessAlert4Visible] = useState(false);
|
||
// 光标4的第二个视频状态
|
||
const [showVideo4Second, setShowVideo4Second] = useState(false);
|
||
const video4SecondRef = useRef<VideoRef>(null);
|
||
// 光标3的对话状态
|
||
const [guide3Visible, setGuide3Visible] = useState(false);
|
||
const [guide3Step, setGuide3Step] = useState(0);
|
||
// 光标1的对话状态
|
||
const [guide1Visible, setGuide1Visible] = useState(false);
|
||
const [guide1Step, setGuide1Step] = useState(0);
|
||
// 光标1的多选题状态
|
||
const [quiz1Visible, setQuiz1Visible] = useState(false);
|
||
const [successAlert1Visible, setSuccessAlert1Visible] = useState(false);
|
||
|
||
const handleOpenExhibit = (index: 1 | 2 | 3 | 4 | 5) => {
|
||
setActiveExhibitId(index);
|
||
// 根据不同的展品显示对应的对话,并重置进度
|
||
if (index === 2) {
|
||
setGuide2Step(0);
|
||
setGuide2Visible(true);
|
||
} else if (index === 5) {
|
||
setGuide5Step(0);
|
||
setGuide5Visible(true);
|
||
} else if (index === 4) {
|
||
setGuide4Step(0);
|
||
setGuide4Visible(true);
|
||
} else if (index === 3) {
|
||
setGuide3Step(0);
|
||
setGuide3Visible(true);
|
||
} else if (index === 1) {
|
||
setGuide1Step(0);
|
||
setGuide1Visible(true);
|
||
}
|
||
};
|
||
|
||
const progressCount = visited.size;
|
||
const total = 5;
|
||
const completed = progressCount >= total;
|
||
const subtitle = completed
|
||
? "探索孙中山故居纪念馆老宅堂屋(已完成)"
|
||
: `探索孙中山故居纪念馆老宅堂屋(${progressCount}/${total})`;
|
||
const subtitleClassName = completed
|
||
? "text-[#fff998] [text-shadow:_0_8px_8px_rgba(0,0,0,0.35),_0_0_16px_rgba(0,0,0,0.25)]"
|
||
: undefined;
|
||
|
||
return (
|
||
<div
|
||
className="w-full h-full bg-cover bg-center bg-no-repeat"
|
||
style={{
|
||
backgroundImage: getBackgroundImage(`sunzhongshan/place1/背景`),
|
||
}}
|
||
>
|
||
{/* 标题 */}
|
||
<Title
|
||
title="立志救国·推翻帝制"
|
||
subtitle={subtitle}
|
||
subtitleClassName={subtitleClassName}
|
||
className="absolute top-10 left-1/2 -translate-x-1/2"
|
||
/>
|
||
|
||
<Image
|
||
className="absolute right-5 top-5"
|
||
src={getStaticImage("components/离开")}
|
||
button
|
||
onClick={() => {
|
||
if (moduleName && starName) {
|
||
navigate(`/${moduleName}/${starName}/home`);
|
||
}
|
||
}}
|
||
/>
|
||
|
||
{/* 光标1 */}
|
||
<Cursor
|
||
className="absolute left-75 bottom-80"
|
||
onClick={() => handleOpenExhibit(1)}
|
||
read={visited.has(1)}
|
||
/>
|
||
{/* 光标2 */}
|
||
<Cursor
|
||
className="absolute left-108 bottom-118"
|
||
onClick={() => handleOpenExhibit(2)}
|
||
read={visited.has(2)}
|
||
/>
|
||
{/* 光标3 */}
|
||
<Cursor
|
||
className="absolute left-165 bottom-90"
|
||
onClick={() => handleOpenExhibit(3)}
|
||
read={visited.has(3)}
|
||
/>
|
||
{/* 光标4 */}
|
||
<Cursor
|
||
className="absolute right-192 bottom-118"
|
||
onClick={() => handleOpenExhibit(4)}
|
||
read={visited.has(4)}
|
||
/>
|
||
{/* 光标5 */}
|
||
<Cursor
|
||
className="absolute right-188 bottom-88"
|
||
onClick={() => handleOpenExhibit(5)}
|
||
read={visited.has(5)}
|
||
/>
|
||
|
||
{/* 继续前进 - 仅在完成所有展品后显示 */}
|
||
{completed && (
|
||
<Image
|
||
className="absolute bottom-10 right-0"
|
||
src={getStaticImage("components/place/r-3d前进")}
|
||
button
|
||
onClick={() => {
|
||
navigate(`/${moduleName}/${starName}/place2`);
|
||
}}
|
||
/>
|
||
)}
|
||
|
||
{/* 遮罩层 */}
|
||
<ExhibitOverlay
|
||
visible={activeExhibitId !== null}
|
||
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ""}
|
||
onClose={() => {
|
||
const currentId = activeExhibitId;
|
||
// 关闭展品框时,重置对应对话的进度并隐藏对话
|
||
if (currentId === 2) {
|
||
setGuide2Step(0);
|
||
setGuide2Visible(false);
|
||
} else if (currentId === 5) {
|
||
setGuide5Step(0);
|
||
setGuide5Visible(false);
|
||
} else if (currentId === 4) {
|
||
setGuide4Step(0);
|
||
setGuide4Visible(false);
|
||
// 光标4:关闭窗口后播放视频
|
||
setActiveExhibitId(null);
|
||
setShowVideo4(true);
|
||
return;
|
||
} else if (currentId === 3) {
|
||
setGuide3Step(0);
|
||
setGuide3Visible(false);
|
||
} else if (currentId === 1) {
|
||
setGuide1Step(0);
|
||
setGuide1Visible(false);
|
||
// 光标1:关闭窗口后显示题目
|
||
setActiveExhibitId(null);
|
||
setQuiz1Visible(true);
|
||
return;
|
||
}
|
||
if (currentId !== null) {
|
||
setVisited((prev) => {
|
||
const next = new Set(prev);
|
||
next.add(currentId);
|
||
return next;
|
||
});
|
||
}
|
||
setActiveExhibitId(null);
|
||
}}
|
||
/>
|
||
|
||
{/* 学习引导词 */}
|
||
<StudyGuideModal
|
||
visible={showGuideModal}
|
||
onContinue={() => {
|
||
setShowGuideModal(false);
|
||
setVisible(true);
|
||
}}
|
||
>
|
||
{`同学你好,欢迎来到走进故居-对话学习模块!
|
||
进入孙中山故居纪念馆后,我们一起来了解孙中山同志经历的7个阶段:"立志救国 推翻帝制"、"讨袁护法 捍卫共和"、"与时俱进探索新路"、"抱病北上鞠躬尽瘁"、"革命先驱 永垂不朽"、"思想传承 影响深远"、"精神财富 激励后人";`}
|
||
</StudyGuideModal>
|
||
|
||
{/* 引导 */}
|
||
<GuideSteps
|
||
visible={visible}
|
||
onVisibleChange={(nextVisible) => {
|
||
setVisible(nextVisible);
|
||
if (!nextVisible && !afterDialogGuideShown) {
|
||
setAfterDialogGuideShown(true);
|
||
setShowAfterDialogGuide(true);
|
||
}
|
||
}}
|
||
showMask={true}
|
||
highlightSelectors={[]}
|
||
>
|
||
<GuideStepsItem speaker="student">
|
||
小沪,老师说过,孙中山先生早年学习的是医学。从治病救人到革命救国,他为什么会做出这种转变呢?
|
||
</GuideStepsItem>
|
||
<GuideStepsItem speaker="guide">
|
||
来看看这张孙中山先生青年时期的相片吧。
|
||
</GuideStepsItem>
|
||
</GuideSteps>
|
||
|
||
{/* 对话结束后的学习引导 */}
|
||
<StudyGuideModal
|
||
visible={showAfterDialogGuide}
|
||
onContinue={() => setShowAfterDialogGuide(false)}
|
||
ailgn="left"
|
||
>
|
||
1、场景中出现动态光点的展品表示未浏览,可通过点击光点来了解展品背后的故事,浏览展品后蓝色动态光点会变为已浏览状态;
|
||
{"\n"}
|
||
2、在学习过程中会掉落拼图碎片,拼图碎片会自动收入拼图盒中,拼图盒可以点开查看当前拼图收集情况,点击单个拼图可以回到获得拼图的场景再次学习;
|
||
{"\n"}
|
||
3、所有动态光点的展品全部学习后才能进入下一个场景学习;
|
||
</StudyGuideModal>
|
||
|
||
{/* 光标2的对话 */}
|
||
<GuideSteps
|
||
visible={guide2Visible}
|
||
currentStep={guide2Step}
|
||
onStepChange={setGuide2Step}
|
||
onVisibleChange={(nextVisible) => {
|
||
setGuide2Visible(nextVisible);
|
||
if (!nextVisible) {
|
||
setVisited((prev) => {
|
||
const next = new Set(prev);
|
||
next.add(2);
|
||
return next;
|
||
});
|
||
}
|
||
}}
|
||
showMask={true}
|
||
highlightSelectors={[]}
|
||
zIndex={10000}
|
||
>
|
||
<GuideStepsItem speaker="student">"革命四杰"?</GuideStepsItem>
|
||
<GuideStepsItem speaker="guide">
|
||
是呀,这是孙中山先生在香港西医书院就读之时与同学的留影。孙先生1887年入学,其间广泛阅读西方自然科学和社会政治学说书籍。常与同学陈少白、尤列、杨鹤龄一起畅谈革命,抨击时政,被戏称为"四大寇"。
|
||
</GuideStepsItem>
|
||
</GuideSteps>
|
||
|
||
{/* 光标5的对话 */}
|
||
<GuideSteps
|
||
visible={guide5Visible}
|
||
currentStep={guide5Step}
|
||
onStepChange={setGuide5Step}
|
||
onVisibleChange={(nextVisible) => {
|
||
setGuide5Visible(nextVisible);
|
||
if (!nextVisible) {
|
||
setVisited((prev) => {
|
||
const next = new Set(prev);
|
||
next.add(5);
|
||
return next;
|
||
});
|
||
}
|
||
}}
|
||
showMask={false}
|
||
highlightSelectors={[]}
|
||
zIndex={10000}
|
||
>
|
||
<GuideStepsItem speaker="student">
|
||
这是郑观应写给盛宣怀的信。
|
||
</GuideStepsItem>
|
||
<GuideStepsItem speaker="guide">
|
||
是的。1894年,孙中山先生回乡起草了"上李鸿章书",希望劝谏清政府改良救国。他多方寻找上书门径,这就是作为同乡和前辈的郑观应帮忙写给盛宣怀,希望他向李鸿章推荐孙先生的书信。
|
||
</GuideStepsItem>
|
||
</GuideSteps>
|
||
|
||
{/* 光标4的对话 */}
|
||
<GuideSteps
|
||
visible={guide4Visible}
|
||
currentStep={guide4Step}
|
||
onStepChange={setGuide4Step}
|
||
onVisibleChange={(nextVisible) => {
|
||
setGuide4Visible(nextVisible);
|
||
}}
|
||
showMask={false}
|
||
highlightSelectors={[]}
|
||
zIndex={10000}
|
||
>
|
||
<GuideStepsItem speaker="student">
|
||
哇!这张《申报》好震撼,"中华民国万岁""孙大总统万岁",就连发行日期写的都是"中华民国元年元月二日"!
|
||
</GuideStepsItem>
|
||
<GuideStepsItem speaker="guide">
|
||
没错!资产阶级民主革命的尝试走过了风雨兼程的十余年,这一过程中孙中山先生一直在探寻救国道路,1905年孙先生在亲自撰写的《民报》发刊词中明确提出了三民主义,即民族、民权、民生这一资产阶级民主革命的政纲。
|
||
</GuideStepsItem>
|
||
</GuideSteps>
|
||
|
||
{/* 光标4的第一个视频 */}
|
||
{showVideo4 && (
|
||
<Video
|
||
ref={video4Ref}
|
||
src={getPublicPath("video/sunzhongshan/sunzhongshan_02.mp4")}
|
||
className="fixed inset-0 z-[20000]"
|
||
autoPlay
|
||
playsInline
|
||
showSkipButton
|
||
onEnded={() => {
|
||
setShowVideo4(false);
|
||
setDragQuiz4Visible(true);
|
||
}}
|
||
/>
|
||
)}
|
||
|
||
{/* 光标4的拖拽题 */}
|
||
<DragQuizPanel
|
||
visible={dragQuiz4Visible}
|
||
zIndex={10000}
|
||
title="题目 1:"
|
||
questionPrefix="三民主义是孙中山所倡导的民主革命纲领,是其民主思想的精髓和高度概括,具体包括("
|
||
questionSuffix=")。(拖拽题)"
|
||
options={[
|
||
{ value: "民族", label: "民族" },
|
||
{ value: "民权", label: "民权" },
|
||
{ value: "民生", label: "民生" },
|
||
{ value: "民主", label: "民主" },
|
||
]}
|
||
blanks={[
|
||
{ id: "blank1", label: "____" },
|
||
{ id: "blank2", label: "____" },
|
||
{ id: "blank3", label: "____" },
|
||
]}
|
||
value={dragQuiz4Value}
|
||
error={dragQuiz4Error}
|
||
onChange={(value) => {
|
||
setDragQuiz4Value(value);
|
||
setDragQuiz4Error("");
|
||
if (value.length < 3) return;
|
||
// 检查答案:需要三个空白都填满,且答案正确
|
||
const correctAnswers = ["民族", "民权", "民生"];
|
||
const filledAnswers = value.map((v) => v.optionValue);
|
||
const answerSet = new Set(correctAnswers);
|
||
const filledSet = new Set(filledAnswers);
|
||
const isCorrect =
|
||
answerSet.size === filledSet.size &&
|
||
[...answerSet].every((answer) => filledSet.has(answer));
|
||
if (isCorrect) {
|
||
setDragQuiz4Visible(false);
|
||
setSuccessAlert4Visible(true);
|
||
} else {
|
||
setDragQuiz4Error(
|
||
'三民主义是"民族主义""民权主义""民生主义"的合称,是孙中山在进行了一系列挽救民族危亡、探索国家近代化建设的尝试后,从经验中概括提炼而成的理论成果。三民主义形成后,又指导了进一步的革命实践并得到了新的发展。它是中国革命的宝贵精神遗产。'
|
||
);
|
||
}
|
||
}}
|
||
/>
|
||
|
||
{/* 光标4的成功提示 */}
|
||
<SuccessAlert
|
||
visible={successAlert4Visible}
|
||
onContinue={() => {
|
||
setSuccessAlert4Visible(false);
|
||
setShowVideo4Second(true);
|
||
}}
|
||
onClose={() => {
|
||
setSuccessAlert4Visible(false);
|
||
setShowVideo4Second(true);
|
||
}}
|
||
/>
|
||
|
||
{/* 光标4的第二个视频 */}
|
||
{showVideo4Second && (
|
||
<Video
|
||
ref={video4SecondRef}
|
||
src={getPublicPath("video/sunzhongshan/sunzhongshan_03.mp4")}
|
||
className="fixed inset-0 z-[20000]"
|
||
autoPlay
|
||
playsInline
|
||
showSkipButton
|
||
onEnded={() => {
|
||
setShowVideo4Second(false);
|
||
// 修改cursor状态
|
||
setVisited((prev) => {
|
||
const next = new Set(prev);
|
||
next.add(4);
|
||
return next;
|
||
});
|
||
}}
|
||
/>
|
||
)}
|
||
|
||
{/* 光标3的对话 */}
|
||
<GuideSteps
|
||
visible={guide3Visible}
|
||
currentStep={guide3Step}
|
||
onStepChange={setGuide3Step}
|
||
onVisibleChange={(nextVisible) => {
|
||
setGuide3Visible(nextVisible);
|
||
if (!nextVisible) {
|
||
setVisited((prev) => {
|
||
const next = new Set(prev);
|
||
next.add(3);
|
||
return next;
|
||
});
|
||
}
|
||
}}
|
||
showMask={false}
|
||
highlightSelectors={[]}
|
||
zIndex={10000}
|
||
>
|
||
<GuideStepsItem speaker="guide">
|
||
这些尝试终于在武昌起义过后迎来了回响。1911年年末,湖南、广东等14个省和上海纷纷宣布脱离清政府独立。
|
||
</GuideStepsItem>
|
||
<GuideStepsItem speaker="guide">
|
||
1912年1月1日上午,上海有一万多人聚集在沪宁车站,欢送孙先生前去南京就任临时大总统。当天夜里11点,孙先生在原清朝两江总督府宣誓就职,新的共和政体就此诞生。彼时仍属农历辛亥年,因此历史上将这一系列事件称为"辛亥革命"。
|
||
</GuideStepsItem>
|
||
<GuideStepsItem speaker="student">
|
||
孙中山先生领导的辛亥革命推动了中国社会的变革,可是这张《民立报》让我有点不明白,孙先生刚当上临时大总统,为什么又很快宣布"解职"了呀?
|
||
</GuideStepsItem>
|
||
<GuideStepsItem speaker="guide">
|
||
中华民国的建立有个重要背景——"南北议和"。辛亥革命中宣告独立的省份大多集中在中国南方,负隅顽抗的清政府将最后希望寄托在主导北洋军阀势力的袁世凯身上。
|
||
</GuideStepsItem>
|
||
<GuideStepsItem speaker="guide">
|
||
在列强支持下,袁世凯率军南下,一边以武力威胁革命势力,一边又迎合革命党人急于完成统一的愿望,诱使其开展和议。当时,革命党人力量还不够强。为避免国家陷入前途未卜的内战,孙中山先生发表声明:只要清帝退位,袁世凯赞成共和,就推举他做大总统。
|
||
</GuideStepsItem>
|
||
</GuideSteps>
|
||
|
||
{/* 光标1的对话 */}
|
||
<GuideSteps
|
||
visible={guide1Visible}
|
||
currentStep={guide1Step}
|
||
onStepChange={setGuide1Step}
|
||
onVisibleChange={(nextVisible) => {
|
||
setGuide1Visible(nextVisible);
|
||
}}
|
||
showMask={false}
|
||
highlightSelectors={[]}
|
||
zIndex={10000}
|
||
>
|
||
<GuideStepsItem speaker="student">这算不算一种"妥协"呀?</GuideStepsItem>
|
||
<GuideStepsItem speaker="guide">
|
||
可以说,这是当时客观条件限制下的一种无奈妥协。因为革命党人的首要目标是推翻帝制,在武装力量不足以对抗北洋军阀的情况下,只能暂时信任袁世凯,希望他能维护共和。你看旁边的"南北议和"纪念章和纪念墨盒,就是当时这段历史的见证。
|
||
</GuideStepsItem>
|
||
</GuideSteps>
|
||
|
||
{/* 光标1的多选题 */}
|
||
<MultipleChoiceQuizPanel
|
||
visible={quiz1Visible}
|
||
zIndex={10000}
|
||
title="题目2:关于辛亥革命的意义,下列表述正确的有()"
|
||
options={[
|
||
{ value: "1", label: "结束了中国两千多年的君主专制制度" },
|
||
{ value: "2", label: "传播了民主共和理念,推动了中华民族思想解放" },
|
||
{ value: "3", label: "冲破了封建主义藩篱,打击了帝国主义在华势力" },
|
||
{ value: "4", label: "完成了民族独立、人民解放的历史任务" },
|
||
]}
|
||
correctAnswer={["1", "2", "3"]}
|
||
errorMessage="辛亥革命未能改变旧中国半殖民地半封建的社会性质和中国人民的悲惨境遇,因此也未能完成实现民族独立、人民解放的历史任务。但它拉开了中国完全意义上的近代民族民主革命的序幕。"
|
||
onSubmit={(values: string[], isCorrect: boolean) => {
|
||
if (isCorrect) {
|
||
setQuiz1Visible(false);
|
||
setSuccessAlert1Visible(true);
|
||
}
|
||
}}
|
||
/>
|
||
|
||
{/* 光标1的成功提示 */}
|
||
<SuccessAlert
|
||
visible={successAlert1Visible}
|
||
title="恭喜你, 选择正确!"
|
||
onContinue={() => {
|
||
setSuccessAlert1Visible(false);
|
||
// 修改cursor状态
|
||
setVisited((prev) => {
|
||
const next = new Set(prev);
|
||
next.add(1);
|
||
return next;
|
||
});
|
||
}}
|
||
onClose={() => {
|
||
setSuccessAlert1Visible(false);
|
||
// 修改cursor状态
|
||
setVisited((prev) => {
|
||
const next = new Set(prev);
|
||
next.add(1);
|
||
return next;
|
||
});
|
||
}}
|
||
/>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default SunzhongshanPlace1;
|