diff --git a/src/components/overlay/EndingOverlay.tsx b/src/components/overlay/EndingOverlay.tsx index 240529b..3fcb478 100644 --- a/src/components/overlay/EndingOverlay.tsx +++ b/src/components/overlay/EndingOverlay.tsx @@ -36,7 +36,7 @@ export const EndingOverlay = ({ const [isAnimating, setIsAnimating] = useState(false); // 结语音频:默认自动播放,支持按 channel 分组互斥 - const { currentSrc, play, stop } = useAudioManager( + const { currentSrc, isPlaying, play, stop } = useAudioManager( audioChannelId ? { mode: "channel", @@ -83,18 +83,42 @@ export const EndingOverlay = ({ play(audioSrc); }, [visible, autoPlayAudio, audioSrc, play]); - // 全部文字出现后 1 秒自动关闭 + // 全部文字出现后自动关闭(如果有音频,需等待音频播放完成) useEffect(() => { if (!visible) return; if (isAnimating) return; if (!text || displayedText !== text) return; + // 如果有音频,需要等待音频播放完成 + if (audioSrc) { + // 检查当前播放的音频是否是我们期望的音频,且已播放完成 + if (currentSrc === audioSrc && !isPlaying) { + // 音频播放完成后,等待 800ms 再关闭 + const timer = setTimeout(() => { + onContinue(); + }, 800); + return () => clearTimeout(timer); + } + // 音频还在播放,等待播放完成 + return; + } + + // 没有音频,文字动画完成后等待 800ms 关闭 const timer = setTimeout(() => { onContinue(); }, 800); return () => clearTimeout(timer); - }, [visible, isAnimating, displayedText, text, onContinue]); + }, [ + visible, + isAnimating, + displayedText, + text, + audioSrc, + currentSrc, + isPlaying, + onContinue, + ]); if (!visible) return null; diff --git a/src/pages/huangyanpei/internalization.tsx b/src/pages/huangyanpei/internalization.tsx index b45d04c..95eb318 100644 --- a/src/pages/huangyanpei/internalization.tsx +++ b/src/pages/huangyanpei/internalization.tsx @@ -401,13 +401,19 @@ const HuangyanpeiInternalization = () => { }} showMask={false} > - + 小沪,刚刚结束的"小职成长记"太有意思了!我变成百年前的川沙少年选课程、做手工、设计国货推销方案,好像真的走进了黄炎培先生的职教世界。 但我还有一个疑问:先生当年倡导的"手脑并用、工学结合",放在我们现在的中职学习中,到底有什么样的意义呢? - + 这个问题问到点子上啦!黄炎培先生的职教思想,从来不是尘封的历史,而是活在当下的行动指南。今天我们特别邀请到中华职业教育社第十三届理事会常务理事、中华职业学校党总支副书记、校长黄玉璟,让她带我们解码先生精神的时代密码! @@ -424,7 +430,10 @@ const HuangyanpeiInternalization = () => { showMask={false} zIndex={60} > - + 听完黄校长的讲解,相信你对黄炎培先生的职教精神有了更深的理解。现在,就请你把这份感悟写下来,既总结参观的收获,也给自己未来许下一个承诺吧! diff --git a/src/pages/huangyanpei/place/place1.tsx b/src/pages/huangyanpei/place/place1.tsx index 30aa9f1..9d29bff 100644 --- a/src/pages/huangyanpei/place/place1.tsx +++ b/src/pages/huangyanpei/place/place1.tsx @@ -7,7 +7,7 @@ import { StudyGuideModal, } from "@/components"; import { useNavigate, useParams } from "react-router-dom"; -import { getBackgroundImage, getStaticImage } from "@/lib/utils"; +import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils"; import { useState } from "react"; import Cursor from "@/components/cursor"; @@ -119,6 +119,7 @@ const HuangyanpeiPlace1 = () => { setShowGuideModal(false); setVisible(true); }} + audioSrc={getPublicPath("audio/huangyanpei/mp3_huangyanpei_01.mp3")} > 同学你好,欢迎来到“走进故居·对话”学习模块!{"\n"} 此刻你正站在黄炎培先生故居门前,接下来就跟着时光的脉络,拆解黄先生人生里五段滚烫的旅程:思想启蒙、教育实践、创立职教、延安对话、职教传薪。准备好一起走进这段炽热的岁月了吗? @@ -135,12 +136,17 @@ const HuangyanpeiPlace1 = () => { } }} showMask={false} - highlightSelectors={[]} > - + 小沪,这栋古色古香的江南大院,就是黄炎培先生从小生活、读书的地方吗 - + 没错,1878年10月1日,黄炎培先生就出生在当时江苏省川沙县城关镇(现上海市浦东新区川沙镇)的这处“内史第”,宅子原是他外祖父沈树镛家,也是他后来所说的“生于斯、读于斯、长于斯”之地。 @@ -153,6 +159,7 @@ const HuangyanpeiPlace1 = () => { visible={showAfterDialogGuide} onContinue={() => setShowAfterDialogGuide(false)} ailgn="left" + audioSrc={getPublicPath("audio/huangyanpei/mp3_huangyanpei_02.mp3")} > 1.场景中出现动态光点的展品表示未浏览,可通过点击光点来了解展品背后的故事,通过滚轮可以放大展品,浏览展品后蓝色动态光点会变为已浏览状态。 {"\n"} diff --git a/src/pages/huangyanpei/place/place2.tsx b/src/pages/huangyanpei/place/place2.tsx index afe0cab..65cdd5e 100644 --- a/src/pages/huangyanpei/place/place2.tsx +++ b/src/pages/huangyanpei/place/place2.tsx @@ -7,7 +7,7 @@ import { GuideStepsItem, GuideSteps, } from "@/components"; -import { getBackgroundImage, getStaticImage } from "@/lib/utils"; +import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils"; import { useNavigate, useParams } from "react-router-dom"; import { useCursorController } from "@/hooks/useCursorController"; @@ -138,10 +138,16 @@ const HuangyanpeiPlace2 = () => { highlightSelectors={[]} zIndex={10000} > - + 哇,这就是南洋公学!是不是现在上海交通大学的前身呀?黄炎培先生中了秀才之后,就来这里读书了吗? - + 你说得特别对!1901年,黄炎培先生考入南洋公学(今上海交通大学)特班,当时的老师正是蔡元培先生。 diff --git a/src/pages/huangyanpei/place/place3.tsx b/src/pages/huangyanpei/place/place3.tsx index 7298db0..1307886 100644 --- a/src/pages/huangyanpei/place/place3.tsx +++ b/src/pages/huangyanpei/place/place3.tsx @@ -9,7 +9,7 @@ import { SingleChoiceQuizPanel, } from "@/components"; import { SuccessAlert } from "@/components/layout"; -import { getBackgroundImage, getStaticImage } from "@/lib/utils"; +import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils"; import { useNavigate, useParams } from "react-router-dom"; import { useCursorController } from "@/hooks/useCursorController"; @@ -186,10 +186,16 @@ const HuangyanpeiPlace3 = () => { highlightSelectors={[]} zIndex={10000} > - + 这所川沙小学堂就是黄炎培先生“教育救国”的第一个试验田吧?我们现在学习的课程有专业课、实习实训课都提倡技能培养,当年他是不是也很注重动手能力呀? - + 太有共鸣啦!1903年,24岁的黄炎培先生和好友张志鹤等人一起,把原来的“观澜书院”改成了“川沙小学堂”,他自己当校长。 @@ -239,13 +245,18 @@ const HuangyanpeiPlace3 = () => { visible={guide12Visible} onVisibleChange={setGuide12Visible} showMask={true} - highlightSelectors={[]} zIndex={10000} > - + 原来当年还有专门的女子学校呀?在那个年代,女孩子能上学是不是特别不容易?后来的浦东中学也是黄炎培先生办的吗? - + 你观察得特别仔细!川沙小学堂开学没多久,黄炎培就和堂兄黄洪培、堂嫂陆开群一起,策划创建了“开群女学”——这所学校专门面向社会招收女学生,还用全力支持办学、腾出自家住房的陆开群名字命名。 diff --git a/src/pages/huangyanpei/place/place4.tsx b/src/pages/huangyanpei/place/place4.tsx index fd3b704..ba72c8f 100644 --- a/src/pages/huangyanpei/place/place4.tsx +++ b/src/pages/huangyanpei/place/place4.tsx @@ -210,10 +210,16 @@ const HuangyanpeiPlace4 = () => { showMask={false} zIndex={10000} > - + 真没想到中华职校还组织过义勇军!我们现在也会参加国防教育、志愿服务,当年的职业教育不仅教技能,还这么注重爱国主义教育呀? - + 没错!黄炎培先生一直怀揣着“教育救国”的理想,职业教育从来都和国家命运紧紧连在一起。 @@ -241,10 +247,16 @@ const HuangyanpeiPlace4 = () => { highlightSelectors={[]} zIndex={10000} > - + 职业教育这个词是黄炎培先生创造的吗? - + 这个词不是他创造的,但体系是他建立的。1917年5月6日,黄炎培联合蔡元培、张謇等48位有识之士,在上海成立了“中华职业教育社”,并发表了《中华职业教育社宣言》。 @@ -266,7 +278,10 @@ const HuangyanpeiPlace4 = () => { showMask={false} zIndex={10000} > - + 这期间,1935年中华职业教育社还与荣氏三兄弟合作,再申新三厂办理劳动工自治区,对工人进行文化教育。 diff --git a/src/pages/huangyanpei/place/place5.tsx b/src/pages/huangyanpei/place/place5.tsx index 8c8b87c..3989184 100644 --- a/src/pages/huangyanpei/place/place5.tsx +++ b/src/pages/huangyanpei/place/place5.tsx @@ -182,10 +182,16 @@ const HuangyanpeiPlace5 = () => { showMask={false} zIndex={10000} > - + 抗战胜利后,黄炎培先生除了继续推进职业教育,还参与了政党活动呀?他创建民主政团同盟,是想为国家做些什么呢? - + 这和当时的历史背景分不开。1940年,国民党政府还坚持“攘外必先安内”的政策,让国共两党的关系越来越紧张。黄炎培等有识之士看到这种严峻形势,觉得中间派应该联合起来,为国家寻求一条和平发展的道路。 @@ -215,10 +221,16 @@ const HuangyanpeiPlace5 = () => { highlightSelectors={[]} zIndex={10000} > - + 哇!这就是历史课上学的“窑洞对”!黄炎培先生和毛主席的这次谈话,是不是谈到了“历史周期率”呀? - + 完全正确!1945年7月1日,黄炎培以国民参政员的身份,带着考察团一行六人赴延安考察,中共中央领导人毛泽东、朱德、周恩来等都亲自到机场迎接。7月4日,他和毛泽东主席长谈了十几个小时,这场对话能和诸葛亮的“隆中对”相媲美。 diff --git a/src/pages/huangyanpei/place/place6.tsx b/src/pages/huangyanpei/place/place6.tsx index 72d9c4a..76d5e6e 100644 --- a/src/pages/huangyanpei/place/place6.tsx +++ b/src/pages/huangyanpei/place/place6.tsx @@ -7,7 +7,7 @@ import { GuideSteps, GuideStepsItem, } from "@/components"; -import { getBackgroundImage, getStaticImage } from "@/lib/utils"; +import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils"; import { useNavigate, useParams } from "react-router-dom"; import { EndingOverlay } from "@/components/overlay/EndingOverlay"; @@ -127,10 +127,16 @@ const HuangyanpeiPlace6 = () => { highlightSelectors={[]} zIndex={10000} > - + 新中国成立后,黄炎培先生还继续从事教育工作吗?我们现在的中职教育体系,是不是也有他当年的理念呀? - + 当然啦!1949年10月19日,71岁的黄炎培出任政务院副总理兼轻工业部部长,依然没有放下他毕生追求的职业教育事业。他把中华职教社的骨干力量全部调到北京,亲自起草《关于改革学制的决定》。 @@ -151,6 +157,8 @@ const HuangyanpeiPlace6 = () => { setHasShownEnding(true); setLeave(true); }} + animationSpeed={200} + audioSrc={getPublicPath("audio/huangyanpei/mp3_huangyanpei_22.mp3")} /> ); diff --git a/src/pages/huangyanpei/reflection/scene1.tsx b/src/pages/huangyanpei/reflection/scene1.tsx index 409baa6..880f696 100644 --- a/src/pages/huangyanpei/reflection/scene1.tsx +++ b/src/pages/huangyanpei/reflection/scene1.tsx @@ -2,7 +2,7 @@ import { useState, useCallback, useEffect } from "react"; import { Title, Image, Cursor, StudyGuideModal } from "@/components"; import { BambooPuzzleGame, QuestionModal } from "./components"; import { SuccessAlert } from "@/components/layout"; -import { getBackgroundImage, getStaticImage } from "@/lib/utils"; +import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils"; import { useNavigate, useParams } from "react-router-dom"; const starName = "huangyanpei"; @@ -76,6 +76,7 @@ const HuangyanpeiReflectionScene1 = () => { onContinue={() => { setShowGuideModal(false); }} + audioSrc={getPublicPath("audio/huangyanpei/mp3_huangyanpei_23.mp3")} > 同学你好,欢迎来到“跨越时空·体悟”学习模块!{"\n"} 请登陆“时空回溯・亲历时代”体验仓,下一站:1903,川沙小学堂。{"\n"} diff --git a/src/pages/huangyanpei/reflection/scene2.tsx b/src/pages/huangyanpei/reflection/scene2.tsx index 8f3378c..856d08e 100644 --- a/src/pages/huangyanpei/reflection/scene2.tsx +++ b/src/pages/huangyanpei/reflection/scene2.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; import { Title, Image, Cursor, StudyGuideModal } from "@/components"; -import { getBackgroundImage, getStaticImage } from "@/lib/utils"; +import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils"; import { useNavigate, useParams } from "react-router-dom"; import ChoiceDialog1 from "./scene2/ChoiceDialog1"; import ChoiceDialog2 from "./scene2/ChoiceDialog2"; @@ -88,6 +88,7 @@ const HuangyanpeiReflectionScene2 = () => { setShowGuideModal(false)} + audioSrc={getPublicPath("audio/huangyanpei/mp3_huangyanpei_24.mp3")} > 欢迎来到浦东中学!{"\n"} 这是近代上海第一所完全中学,黄炎培先生当首任校长,在这里能学到更多实用知识,还能树立成才的理想哦。 diff --git a/src/pages/huangyanpei/reflection/scene3.tsx b/src/pages/huangyanpei/reflection/scene3.tsx index 455548b..0ce759a 100644 --- a/src/pages/huangyanpei/reflection/scene3.tsx +++ b/src/pages/huangyanpei/reflection/scene3.tsx @@ -24,6 +24,7 @@ const HuangyanpeiReflectionScene3 = () => { const [showQuiz, setShowQuiz] = useState(false); const [showSuccessAlert, setShowSuccessAlert] = useState(false); const [showErrorAlert, setShowErrorAlert] = useState(false); + const [showErrorAlertForQuiz, setShowErrorAlertForQuiz] = useState(false); const [showCompleteGuide, setShowCompleteGuide] = useState(false); const [showVideo, setShowVideo] = useState(false); const [visited, setVisited] = useState>(new Set()); @@ -73,7 +74,12 @@ const HuangyanpeiReflectionScene3 = () => { src={getStaticImage("huangyanpei/reflection/components/喇叭1")} button onClick={() => { - setShowQuizDialog(true); + // 检查是否完成前两个任务(visited.has(1) && visited.has(2)) + if (visited.has(1) && visited.has(2)) { + setShowQuizDialog(true); + } else { + setShowErrorAlertForQuiz(true); + } }} /> @@ -83,6 +89,7 @@ const HuangyanpeiReflectionScene3 = () => { onContinue={() => { setShowGuide1(false); }} + audioSrc={getPublicPath("audio/huangyanpei/mp3_huangyanpei_25.mp3")} > 终于到中华职业学校啦!这里是中国近代职教的摇篮,"免学费、包分配",还能学真手艺,快来练就一技之长。 {"\n"} @@ -180,6 +187,13 @@ const HuangyanpeiReflectionScene3 = () => { onClose={() => setShowErrorAlert(false)} /> + {/* 错误提示:请先完成前两个任务 */} + setShowErrorAlertForQuiz(false)} + /> + {/* 组装台钳完成提示 */} { const [showVideo, setShowVideo] = useState(false); const videoRef = useRef(null); const [showGuide1, setShowGuide1] = useState(true); + const [showErrorAlertForQuiz, setShowErrorAlertForQuiz] = useState(false); //喇叭考题弹窗 const [showQuizDialog, setShowQuizDialog] = useState(false); @@ -84,7 +85,12 @@ const HuangyanpeiReflectionScene4 = () => { src={getStaticImage("huangyanpei/reflection/components/喇叭2")} button onClick={() => { - setShowQuizDialog(true); + // 检查是否完成前两个任务(visited.has(1) && visited.has(2)) + if (visited.has(1) && visited.has(2)) { + setShowQuizDialog(true); + } else { + setShowErrorAlertForQuiz(true); + } }} /> @@ -185,6 +191,7 @@ const HuangyanpeiReflectionScene4 = () => { onContinue={() => { setShowGuide1(false); }} + audioSrc={getPublicPath("audio/huangyanpei/mp3_huangyanpei_26.mp3")} > 终于到中华职业学校啦!这里是中国近代职教的摇篮,“免学费、包分配”,还能学真手艺,快来练就一技之长。 {"\n"} @@ -214,6 +221,13 @@ const HuangyanpeiReflectionScene4 = () => { setVisited((prev) => new Set(prev).add(3)); }} /> + + {/* 错误提示:请先完成前两个任务 */} + setShowErrorAlertForQuiz(false)} + /> ); };