- 在多个组件中引入 MatchQuizPanel 和 SingleChoiceQuizPanel,提升用户参与度。 - 更新 HuangyanpeiPlace1、HuangyanpeiPlace3、LuxunPlace1、LuxunPlace2 组件,增加新的选择题和连线题,丰富学习内容。 - 调整成功提示逻辑,确保用户体验流畅。 - 优化视频播放中的锚点逻辑,提升互动性和学习效果。
420 lines
14 KiB
TypeScript
420 lines
14 KiB
TypeScript
import {
|
|
ExhibitOverlay,
|
|
GuideSteps,
|
|
GuideStepsItem,
|
|
Image,
|
|
Title,
|
|
Cursor,
|
|
SingleChoiceQuizPanel,
|
|
Video,
|
|
type VideoAnchor,
|
|
} from "@/components";
|
|
import type { VideoRef } from "@/components/video";
|
|
import { useNavigate, useParams } from "react-router-dom";
|
|
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
|
|
import { useState, useRef, useMemo } from "react";
|
|
import SuccessAlert from "@/components/layout/alert/success-alert";
|
|
import { useCursorController } from "@/hooks/useCursorController";
|
|
import { usePuzzleAnimation } from "@/contexts/PuzzleAnimationContext";
|
|
|
|
// starName 固定为 luxun
|
|
const starName = "luxun";
|
|
|
|
// 展品 ID 与资源路径映射
|
|
const exhibitSrcMap: Record<1 | 2 | 3, string> = {
|
|
1: "luxun/place4/展品11",
|
|
2: "luxun/place4/展品12",
|
|
3: "luxun/place4/展品13",
|
|
};
|
|
|
|
const quizDefaultError = "请结合刚刚的旁白内容,再思考一下吧!";
|
|
const quizExplanations: Record<string, string> = {
|
|
// A: "陈独秀与鲁迅有思想交往,但他并未在这间“红色庇护所”中避难、与鲁迅夜谈革命议题。",
|
|
// B: "恭喜你,选对啦。",
|
|
// C: "李大钊于1927年牺牲,时间上不符合这间客房被用作“红色庇护所”的背景。",
|
|
// D: "周恩来虽长期在上海从事革命活动,但并未在这里避难并与鲁迅夜谈。",
|
|
A: "请你结合刚刚学习的内容,再思考一下吧!",
|
|
B: "恭喜你,选择正确!",
|
|
C: "请你结合刚刚学习的内容,再思考一下吧!",
|
|
D: "请你结合刚刚学习的内容,再思考一下吧!",
|
|
};
|
|
|
|
const LuxunPlace5 = () => {
|
|
const navigate = useNavigate();
|
|
const { moduleName } = useParams<{
|
|
moduleName: string;
|
|
}>();
|
|
|
|
//碎片动画触发方法
|
|
const { triggerPuzzleAnimation } = usePuzzleAnimation();
|
|
|
|
const [introGuideVisible, setIntroGuideVisible] = useState(true);
|
|
const [introGuideStep, setIntroGuideStep] = useState(0);
|
|
|
|
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | 3 | null>(
|
|
null
|
|
);
|
|
const [visited, setVisited] = useState<Set<number>>(new Set());
|
|
|
|
// Cursor 顺序控制:1 -> 2 -> 3
|
|
const { isEnabled } = useCursorController({
|
|
initialEnabled: [1],
|
|
dependencies: {
|
|
2: [1],
|
|
3: [2],
|
|
},
|
|
visited: visited,
|
|
});
|
|
|
|
// 光标2的题目状态
|
|
const [quizVisible, setQuizVisible] = useState(false);
|
|
const [quizError, setQuizError] = useState(quizDefaultError);
|
|
const [successAlertVisible, setSuccessAlertVisible] = useState(false);
|
|
|
|
// 光标3的引导对话状态
|
|
const [guide3Visible, setGuide3Visible] = useState(false);
|
|
const [guide3Step, setGuide3Step] = useState(0);
|
|
|
|
// 结尾视频与对话
|
|
const [showEndingVideo, setShowEndingVideo] = useState(false);
|
|
const [endingGuideVisible, setEndingGuideVisible] = useState(false);
|
|
// 视频锚点相关状态
|
|
const [activeVideoSuccess, setActiveVideoSuccess] = useState(false);
|
|
const [resumeVideoCallback, setResumeVideoCallback] = useState<(() => void) | null>(null);
|
|
const videoRef = useRef<VideoRef>(null);
|
|
|
|
const handleOpenExhibit = (index: 1 | 2 | 3) => {
|
|
if (index === 3) {
|
|
setActiveExhibitId(3);
|
|
setGuide3Visible(true);
|
|
setGuide3Step(0);
|
|
return;
|
|
}
|
|
setActiveExhibitId(index);
|
|
};
|
|
|
|
// 视频锚点配置
|
|
const videoAnchors: VideoAnchor[] = useMemo(
|
|
() => [
|
|
{
|
|
id: "quiz-anchor",
|
|
time: 5,
|
|
once: true,
|
|
afterResume: "remove",
|
|
render: ({ resume }) => (
|
|
<>
|
|
<SingleChoiceQuizPanel
|
|
visible={!activeVideoSuccess}
|
|
title='【单选】毛泽东主席称赞鲁迅先生为"最伟大和最英勇的___"。'
|
|
options={[
|
|
{ value: "A", label: "A. 旗手" },
|
|
{ value: "B", label: "B. 作家" },
|
|
{ value: "C", label: "C. 战士" },
|
|
]}
|
|
correctAnswer="A"
|
|
onWrongSelect={(value: string) => {
|
|
switch (value) {
|
|
case "B":
|
|
return "这不是毛泽东主席的原话。请你再想想吧!";
|
|
case "C":
|
|
return "这不是毛泽东主席的原话。请你再想想吧!";
|
|
default:
|
|
return "请你再想想吧!";
|
|
}
|
|
}}
|
|
onSubmit={(value: string, isCorrect: boolean) => {
|
|
if (isCorrect) {
|
|
setActiveVideoSuccess(true);
|
|
setResumeVideoCallback(() => resume);
|
|
}
|
|
}}
|
|
zIndex={20000}
|
|
/>
|
|
|
|
<SuccessAlert
|
|
visible={activeVideoSuccess}
|
|
onContinue={() => {
|
|
setActiveVideoSuccess(false);
|
|
const cb = resumeVideoCallback;
|
|
setResumeVideoCallback(null);
|
|
cb?.();
|
|
}}
|
|
onClose={() => {
|
|
setActiveVideoSuccess(false);
|
|
const cb = resumeVideoCallback;
|
|
setResumeVideoCallback(null);
|
|
cb?.();
|
|
}}
|
|
/>
|
|
</>
|
|
),
|
|
},
|
|
],
|
|
[activeVideoSuccess, resumeVideoCallback]
|
|
);
|
|
|
|
const progressCount = visited.size;
|
|
const total = 3;
|
|
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(`luxun/place4/背景`),
|
|
}}
|
|
>
|
|
<Title
|
|
title="6平方米的红色庇护所"
|
|
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}/place3`);
|
|
}
|
|
}}
|
|
/>
|
|
|
|
{/* 展品 */}
|
|
<Cursor
|
|
className="absolute left-167 bottom-30"
|
|
onClick={() => handleOpenExhibit(1)}
|
|
read={visited.has(1)}
|
|
disabled={!isEnabled(1)}
|
|
/>
|
|
<Cursor
|
|
className="absolute left-228 bottom-30"
|
|
onClick={() => handleOpenExhibit(2)}
|
|
read={visited.has(2)}
|
|
disabled={!isEnabled(2)}
|
|
/>
|
|
<Cursor
|
|
className="absolute right-115 bottom-18 z-20"
|
|
onClick={() => handleOpenExhibit(3)}
|
|
read={visited.has(3)}
|
|
disabled={!isEnabled(3)}
|
|
/>
|
|
|
|
{completed && (
|
|
<Image
|
|
className="absolute bottom-0 right-0"
|
|
src={getStaticImage("components/place/继续前进")}
|
|
button
|
|
onClick={() => {
|
|
// 完成后先播放视频,不立即跳转页面
|
|
setShowEndingVideo(true);
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
<ExhibitOverlay
|
|
visible={activeExhibitId !== null}
|
|
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ""}
|
|
onOpen={()=>{
|
|
if( activeExhibitId === 1){
|
|
triggerPuzzleAnimation(11);
|
|
}
|
|
if( activeExhibitId === 2){
|
|
triggerPuzzleAnimation(12);
|
|
}
|
|
if( activeExhibitId === 3){
|
|
triggerPuzzleAnimation(13);
|
|
}
|
|
}}
|
|
description={
|
|
activeExhibitId === 1
|
|
? '瞿秋白同志曾说,"鲁迅是莱谟斯,是野兽的奶汁所喂养大的,是封建宗法社会的逆子,是绅士阶级的贰臣,而同时也是一些浪漫谛克的革命家的诤友!"'
|
|
: activeExhibitId === 2
|
|
? '鲁迅先生曾经抄录清朝诗人何瓦琴的对联赠给瞿秋白,"人生得一知己足矣,斯世当以同怀视之"'
|
|
: undefined
|
|
}
|
|
onClose={() => {
|
|
if (activeExhibitId === 2) {
|
|
setActiveExhibitId(null);
|
|
setQuizVisible(true);
|
|
return;
|
|
}
|
|
if (activeExhibitId === 3) {
|
|
// 对话关闭时再标记已读
|
|
setActiveExhibitId(null);
|
|
return;
|
|
}
|
|
setVisited((prev) => {
|
|
const next = new Set(prev);
|
|
if (activeExhibitId !== null) {
|
|
next.add(activeExhibitId);
|
|
}
|
|
return next;
|
|
});
|
|
setActiveExhibitId(null);
|
|
}}
|
|
/>
|
|
|
|
{/* 学习引导结束后的对话 */}
|
|
<GuideSteps
|
|
visible={introGuideVisible}
|
|
onVisibleChange={setIntroGuideVisible}
|
|
currentStep={introGuideStep}
|
|
onStepChange={setIntroGuideStep}
|
|
showMask={false}
|
|
highlightSelectors={[]}
|
|
zIndex={10000}
|
|
>
|
|
<GuideStepsItem
|
|
speaker="student"
|
|
audioSrc={getPublicPath("audio/luxun/mp3_luxun_26.mp3")}
|
|
>
|
|
你说的是这间小小的客房吗,这里为什么会被称为"红色庇护所"呢?
|
|
</GuideStepsItem>
|
|
<GuideStepsItem
|
|
speaker="guide"
|
|
audioSrc={getPublicPath("audio/luxun/mp3_luxun_27.mp3")}
|
|
>
|
|
你可别小看这间仅6平方米的客房,在白色恐怖笼罩的1933年至1936年,这里是很多共产党人的"安全港湾"。1933年,瞿秋白夫妇第三次到鲁迅家避难,就秘密住进了这里。
|
|
</GuideStepsItem>
|
|
<GuideStepsItem speaker="guide">
|
|
当时反动派到处搜捕共产党人,鲁迅冒着生命危险掩护他们,两人常彻夜长谈。瞿秋白的夫人杨之华回忆道:"秋白和鲁迅在一起,往往感觉夜太短。深刻的友谊的交谈,从政治到文艺,从实际到理论,从希腊到莫斯科,轻松,愉快,活泼。"
|
|
</GuideStepsItem>
|
|
</GuideSteps>
|
|
|
|
{/* 光标2的题目 */}
|
|
<SingleChoiceQuizPanel
|
|
visible={quizVisible}
|
|
title="【单选】上海鲁迅故居三楼客房被称为“红色庇护所”,以下哪位共产党人曾在此避难并与鲁迅探讨革命议题?"
|
|
options={[
|
|
{ value: "A", label: "A.陈独秀" },
|
|
{ value: "B", label: "B.瞿秋白" },
|
|
{ value: "C", label: "C.李大钊" },
|
|
{ value: "D", label: "D.周恩来" },
|
|
]}
|
|
correctAnswer="B"
|
|
errorMessage={quizError}
|
|
onSubmit={(value, isCorrect) => {
|
|
if (isCorrect) {
|
|
setQuizError(quizDefaultError);
|
|
setQuizVisible(false);
|
|
setSuccessAlertVisible(true);
|
|
}
|
|
}}
|
|
onWrongSelect={(value) => {
|
|
const explanation = quizExplanations[value];
|
|
if (explanation) {
|
|
setQuizError(explanation);
|
|
return explanation;
|
|
}
|
|
setQuizError(quizDefaultError);
|
|
return quizDefaultError;
|
|
}}
|
|
zIndex={10001}
|
|
/>
|
|
|
|
{/* 光标2的成功提示 */}
|
|
<SuccessAlert
|
|
visible={successAlertVisible}
|
|
content="1933年,瞿秋白夫妇第三次到鲁迅家避难,就住进了这间仅6平方米的客房,与鲁迅常常彻夜长谈革命与文学。"
|
|
onContinue={() => {
|
|
setSuccessAlertVisible(false);
|
|
setVisited((prev) => {
|
|
const next = new Set(prev);
|
|
next.add(2);
|
|
return next;
|
|
});
|
|
}}
|
|
/>
|
|
|
|
{/* 光标3的引导对话 */}
|
|
<GuideSteps
|
|
visible={guide3Visible}
|
|
onVisibleChange={(visible) => {
|
|
setGuide3Visible(visible);
|
|
if (!visible) {
|
|
setVisited((prev) => {
|
|
const next = new Set(prev);
|
|
next.add(3);
|
|
return next;
|
|
});
|
|
}
|
|
}}
|
|
currentStep={guide3Step}
|
|
onStepChange={setGuide3Step}
|
|
showMask={false}
|
|
highlightSelectors={[]}
|
|
zIndex={10000}
|
|
>
|
|
<GuideStepsItem
|
|
speaker="student"
|
|
audioSrc={getPublicPath("audio/luxun/mp3_luxun_28.mp3")}
|
|
>
|
|
除了瞿秋白,还有其他共产党人来过这里吗
|
|
</GuideStepsItem>
|
|
<GuideStepsItem
|
|
speaker="guide"
|
|
audioSrc={getPublicPath("audio/luxun/mp3_luxun_29.mp3")}
|
|
>
|
|
当然!1936年4月,中共中央特派员冯雪峰到上海开展工作,也先住进了这间客房。冯雪峰把红军长征胜利、遵义会议召开的消息告诉鲁迅,还带来了毛泽东同志提出的抗日民族统一战线主张。
|
|
</GuideStepsItem>
|
|
<GuideStepsItem speaker="guide">
|
|
鲁迅听后格外振奋,激动地说"我做一个小兵还是胜任的,用笔!",这句话也成了他晚年投身抗日救亡运动的誓言。
|
|
</GuideStepsItem>
|
|
</GuideSteps>
|
|
|
|
{/* 结尾视频播放 */}
|
|
{showEndingVideo && (
|
|
<Video
|
|
ref={videoRef}
|
|
src={getPublicPath("video/luxun/luxun_06.mp4")}
|
|
className="fixed inset-0 z-[20000]"
|
|
controls
|
|
autoPlay
|
|
playsInline
|
|
showSkipButton
|
|
anchors={videoAnchors}
|
|
onEnded={() => {
|
|
setEndingGuideVisible(true);
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
{/* 视频结束后的结束语对话 */}
|
|
<GuideSteps
|
|
visible={endingGuideVisible}
|
|
onVisibleChange={(visible) => {
|
|
setShowEndingVideo(false);
|
|
setEndingGuideVisible(visible);
|
|
if (!visible && moduleName) {
|
|
// 对话结束后返回鲁迅首页
|
|
navigate(`/${moduleName}/${starName}/home`);
|
|
}
|
|
}}
|
|
showMask={false}
|
|
zIndex={20010}
|
|
>
|
|
<GuideStepsItem
|
|
speaker="guide"
|
|
audioSrc={getPublicPath("audio/luxun/mp3_luxun_30.mp3")}
|
|
>
|
|
我们已经了解了鲁迅居住在上海大陆新村9号时的重要事件。
|
|
</GuideStepsItem>
|
|
<GuideStepsItem speaker="guide">
|
|
接下来,请你接过先生手中的“笔”,走出上海,跨越时空,追寻他从绍兴三味书屋到上海的创作足迹——解锁每个地标背后的故事,填充文字里的时代线索,在互动中,读懂先生笔墨间的温度与力量。
|
|
</GuideStepsItem>
|
|
</GuideSteps>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default LuxunPlace5;
|