diff --git a/src/pages/chenyun/reflection/constants/mimian.ts b/src/pages/chenyun/reflection/constants/mimian.ts index b83a2d3..40a93ff 100644 --- a/src/pages/chenyun/reflection/constants/mimian.ts +++ b/src/pages/chenyun/reflection/constants/mimian.ts @@ -45,7 +45,7 @@ export const MIMIAN_CLUE_DATA: Record< { character: '群众2', avatar: 'chenyun/reflection/群众2', - text: '钱庄都在囤媒,冬天没法取暖', + text: '钱庄都在囤煤,冬天没法取暖', }, ], }, diff --git a/src/pages/chenyun/reflection/constants/shared.ts b/src/pages/chenyun/reflection/constants/shared.ts index bcf6061..b72f919 100644 --- a/src/pages/chenyun/reflection/constants/shared.ts +++ b/src/pages/chenyun/reflection/constants/shared.ts @@ -8,5 +8,5 @@ export const starName = 'chenyun' // 欢迎语文案(使用 StudyGuideModal 展示) export const WELCOME_TEXT = `同学你好,欢迎来到“跨越时空·体悟”学习模块! -请登陆“时空回溯・亲历时代”体验仓,下一站:1949年,上海。 +请登录“时空回溯・亲历时代”体验仓,下一站:1949年,上海。 你将扎进“银元之战”的旋涡,踏过“米棉之战”的浪潮,以陈云同志特派员的身份体验陈云如何以智破局,稳稳托住这座城的经济天平。时空门已开,准备好入戏了吗?` diff --git a/src/pages/chenyun/reflection/mimian/MimianBattle.tsx b/src/pages/chenyun/reflection/mimian/MimianBattle.tsx index 5ce6442..34296f8 100644 --- a/src/pages/chenyun/reflection/mimian/MimianBattle.tsx +++ b/src/pages/chenyun/reflection/mimian/MimianBattle.tsx @@ -60,7 +60,7 @@ export const MimianBattle = ({ if (isCorrect) { state.setShowMimianQuiz1(false) state.setShowMimianSuccessAlert(true) - state.setMimianSuccessAlertContent('恭喜你,选择正确!') + state.setMimianSuccessAlertContent('恭喜你,选择正确!') } } diff --git a/src/pages/chenyun/reflection/mimian/MimianQuizzes.tsx b/src/pages/chenyun/reflection/mimian/MimianQuizzes.tsx index 41b57d3..db16816 100644 --- a/src/pages/chenyun/reflection/mimian/MimianQuizzes.tsx +++ b/src/pages/chenyun/reflection/mimian/MimianQuizzes.tsx @@ -111,7 +111,7 @@ export const MimianQuizzes = ({ {/* 米棉之战 - 成功提示框1 */} @@ -195,7 +195,7 @@ export const MimianQuizzes = ({ showSuccessAlert && (successAlertContent.includes('地方配合') || successAlertContent.includes('地方抵触')) } - title={'恭喜你,选择正确!'} + title={'恭喜你,选择正确!'} content={successAlertContent} onContinue={() => { const isCorrect = successAlertContent.includes('地方配合') @@ -269,7 +269,7 @@ export const MimianQuizzes = ({ (successAlertContent.includes('这契合米棉之战史实') || successAlertContent.includes('投机商此前曾吃下')) } - title={'恭喜你,选择正确!'} + title={'恭喜你,选择正确!'} content={successAlertContent} onContinue={() => { const isEndSuccess = successAlertContent.includes('这契合米棉之战史实') @@ -282,7 +282,7 @@ export const MimianQuizzes = ({ visible={showNarration5} onContinue={onNarration5Continue} text={ - '从“银元之战”查封投机枢纽、稳固人民币根基,到“米棉之战”全国统筹调运、集中抛售击溃投机势力,这场经济战线的“淮海战役”,不仅平定了物价、安定了民心,更印证了“全国一盘棋” strategic智慧与制度优势。当市场秩序重归稳定,新生政权在经济领域站稳脚跟,这份由陈云同志等老一辈革命家铸就的治理经验,也为新时代经济建设埋下了思想伏笔,等待我们在传承中探索新的实践路径。' + '从“银元之战”查封投机枢纽、稳固人民币根基,到“米棉之战”全国统筹调运、集中抛售击溃投机势力,这场经济战线的“淮海战役”,不仅平定了物价、安定了民心,更印证了“全国一盘棋”的战略智慧与制度优势。当市场秩序重归稳定,新生政权在经济领域站稳脚跟,这份由陈云同志等老一辈革命家铸就的治理经验,也为新时代经济建设埋下了思想伏笔,等待我们在传承中探索新的实践路径。' } animationSpeed={200} audioSrc={getPublicPath('audio/chenyun/mp3_chenyun_35.mp3')} diff --git a/src/pages/chenyun/reflection/yinyuan/YinyuanFile.tsx b/src/pages/chenyun/reflection/yinyuan/YinyuanFile.tsx index 6957efa..2218342 100644 --- a/src/pages/chenyun/reflection/yinyuan/YinyuanFile.tsx +++ b/src/pages/chenyun/reflection/yinyuan/YinyuanFile.tsx @@ -79,7 +79,7 @@ export const YinyuanFile = ({ {/* 走势图 - 银元之战解决后显示,点击后隐藏 */} - {isSolved && ( + {isSolved && showTrendChartIcon && (
{ diff --git a/src/pages/rongdesheng/place/place2.tsx b/src/pages/rongdesheng/place/place2.tsx index a35f464..737eb3c 100644 --- a/src/pages/rongdesheng/place/place2.tsx +++ b/src/pages/rongdesheng/place/place2.tsx @@ -3,16 +3,19 @@ import { GuideSteps, GuideStepsItem, Image, + MultipleChoiceQuizPanel, SingleChoiceQuizPanel, Title, Video, } from '@/components' +import { SuccessAlert } from '@/components/layout' import Cursor from '@/components/cursor' import { useCursorController } from '@/hooks/useCursorController' import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils' -import { useMemo, useState } from 'react' +import { useState } from 'react' import { useNavigate, useParams } from 'react-router-dom' import { saveRongdeshengStore, useRongdeshengStore } from '@/store/rongdeshengStore' +import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext' const starName = 'rongdesheng' const place = 'place2' @@ -23,11 +26,12 @@ const RongdeshengPlace2 = () => { const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useRongdeshengStore() - const persistedVisited = useMemo( - () => new Set(getVisitedExhibits(place) as (1 | 2 | 3 | 4 | 5)[]), - [getVisitedExhibits] - ) - const [visited, setVisited] = useState>(() => new Set(persistedVisited)) + // 获取碎片动画触发方法 + const { triggerPuzzleAnimation } = usePuzzleAnimation() + + // 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码 + const visitedExhibits = getVisitedExhibits(place) + const visited = new Set(visitedExhibits) const total = 5 const progressCount = visited.size @@ -60,6 +64,8 @@ const RongdeshengPlace2 = () => { const [quiz4Visible, setQuiz4Visible] = useState(false) const [guide5BeforeVisible, setGuide5BeforeVisible] = useState(false) const [guide5BeforeStep, setGuide5BeforeStep] = useState(0) + const [quiz5Visible, setQuiz5Visible] = useState(false) + const [quiz5SuccessVisible, setQuiz5SuccessVisible] = useState(false) const [guide5AfterVisible, setGuide5AfterVisible] = useState(false) const [guide5AfterStep, setGuide5AfterStep] = useState(0) @@ -98,22 +104,15 @@ const RongdeshengPlace2 = () => { setGuide1AfterVideoVisible(true) } if (id === 2) { - setVisited((prev) => { - const next = new Set(prev) - if (!next.has(2)) { - next.add(2) - markExhibitVisited(place, 2) - saveRongdeshengStore() - } - return next - }) + markExhibitVisited(place, 2) + // 触发碎片4获取动画 + triggerPuzzleAnimation(4) } if (id === 4) { setQuiz4Visible(true) } if (id === 5) { - setGuide5AfterStep(0) - setGuide5AfterVisible(true) + setQuiz5Visible(true) } } @@ -197,15 +196,11 @@ const RongdeshengPlace2 = () => { } // 其余展品先保持关闭即完成 - setVisited((prev) => { - const next = new Set(prev) - if (!next.has(closingId)) { - next.add(closingId) - markExhibitVisited(place, closingId) - saveRongdeshengStore() - } - return next - }) + markExhibitVisited(place, closingId) + // 触发碎片5获取动画(展品3 -> 碎片5) + if (closingId === 3) { + triggerPuzzleAnimation(5) + } }} /> @@ -267,7 +262,7 @@ const RongdeshengPlace2 = () => { speaker="student" audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_07.mp3')} > - 哇,这是福新面粉公司的老照片!荣德生先生为什么选择办面粉,面粉厂又为什么在上海创办?福新面粉厂后来发展得怎么样? + 哇,这是福新面粉公司的老照片!荣德生先生为什么选择办面粉,面粉厂又为什么在上海创办?福新面粉厂后来发展得怎么样? { > - 目睹洋面粉倾销的荣先生与兄长一起在无锡创办了保兴面粉厂,1912年,荣氏兄弟又看准了上海的区位优势——交通便利、人口密集、市场需求大,于是在上海创办了福新面粉厂,引进了当时最先进的生产设备。 + 目睹洋面粉倾销的荣先生先是与兄长一起在无锡创办了保兴面粉厂,1912年,荣氏兄弟又看准了上海的区位优势——交通便利、人口密集、市场需求大,于是在上海创办了福新面粉厂,引进了当时最先进的生产设备。 - + 之后他们趁热打铁,又在上海接连创办了福新二厂、三厂,还收买或租办了中兴面粉厂等,改为福新四厂、六厂,形成了规模化的面粉生产体系。短短几年间,福新面粉厂的产量就占据了上海面粉市场的半壁江山。 @@ -320,12 +318,15 @@ const RongdeshengPlace2 = () => { - 是的!1915年,他与兄长在上海招股创办了申新纺织无限公司,并开办申新一厂。他们之所以选择纺织业,是因为当时中国的棉纱市场被外资垄断,老百姓的棉布多依赖进口。 + 是的!荣德生先生深知,民族工业要强大,不能只靠一个行业。1915年,他与兄长在上海招股创办了申新纺织无限公司,并开办申新一厂。无限公司承担无限责任,荣氏将全部身家与振兴民族纺织业牢牢绑定。 - - 申新纺织公司的创办,就是要打破这种垄断局面,为国人生产品质优良的棉纱、棉布。 + + 他们之所以选择纺织业,是因为当时中国的棉纱市场被外资垄断,老百姓穿的棉布大多依赖进口。申新纺织公司的创办,就是要打破这种垄断局面,为国人生产优质的棉纱、棉布。 @@ -349,15 +350,9 @@ const RongdeshengPlace2 = () => { }} onSuccessContinue={() => { setQuiz4Visible(false) - setVisited((prev) => { - const next = new Set(prev) - if (!next.has(4)) { - next.add(4) - markExhibitVisited(place, 4) - saveRongdeshengStore() - } - return next - }) + markExhibitVisited(place, 4) + // 触发碎片6获取动画(展品4 -> 碎片6) + triggerPuzzleAnimation(6) }} /> @@ -379,19 +374,43 @@ const RongdeshengPlace2 = () => { }} onSuccessContinue={() => { setQuiz1Visible(false) - setVisited((prev) => { - const next = new Set(prev) - if (!next.has(1)) { - next.add(1) - markExhibitVisited(place, 1) - saveRongdeshengStore() - } - return next - }) + markExhibitVisited(place, 1) + // 触发碎片3获取动画(展品1 -> 碎片3) + triggerPuzzleAnimation(3) }} /> - {/* 展品5:打开即对话 + 视频后对话并标记完成 */} + { + if (!isCorrect) return + setQuiz5Visible(false) + setQuiz5SuccessVisible(true) + }} + /> + + { + setQuiz5SuccessVisible(false) + setGuide5AfterStep(0) + setGuide5AfterVisible(true) + }} + /> + + {/* 展品5:打开即对话 + 视频后答题,答对后显示对话并标记完成 */} { onVisibleChange={(v) => { setGuide5AfterVisible(v) if (!v) { - setVisited((prev) => { - const next = new Set(prev) - if (!next.has(5)) { - next.add(5) - markExhibitVisited(place, 5) - saveRongdeshengStore() - } - return next - }) + markExhibitVisited(place, 5) + // 触发碎片7获取动画(展品5 -> 碎片7) + triggerPuzzleAnimation(7) } }} currentStep={guide5AfterStep} @@ -439,14 +452,20 @@ const RongdeshengPlace2 = () => { > 在荣德生先生推行的以“劳资双方利益要兼顾”为出发点的劳工自治区管理模式下,工人在企业里得到尊重和关怀,干事的积极性自然高涨。他们不仅严格把控生产环节,还主动参与技术改进。 - + 再加上荣先生坚持每天清晨用前一天的面粉做疙瘩汤亲自检验品质的严谨态度,形成了“人文管理+品质把控”的双重保障。 - + 这种立足创新、聚焦工人福利的全方位关照,让荣氏产品质量远超同行,成功抵御了洋货倾销,也让“兵船”牌面粉和“人钟牌”棉纱成为行业标准。 diff --git a/src/pages/rongdesheng/place/place4.tsx b/src/pages/rongdesheng/place/place4.tsx index 07cbac1..4b5df6b 100644 --- a/src/pages/rongdesheng/place/place4.tsx +++ b/src/pages/rongdesheng/place/place4.tsx @@ -4,15 +4,18 @@ import { GuideSteps, GuideStepsItem, Image, + SingleChoiceQuizPanel, Title, Video, } from '@/components' import Cursor from '@/components/cursor' import { useCursorController } from '@/hooks/useCursorController' import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils' -import { useMemo, useState } from 'react' +import { useState } from 'react' import { useNavigate, useParams } from 'react-router-dom' import { saveRongdeshengStore, useRongdeshengStore } from '@/store/rongdeshengStore' +import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext' +import { setCourseTimeProgress } from '@/lib/utils/api-common' const starName = 'rongdesheng' const place = 'place4' @@ -23,11 +26,12 @@ const RongdeshengPlace4 = () => { const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useRongdeshengStore() - const persistedVisited = useMemo( - () => new Set(getVisitedExhibits(place) as (1 | 2 | 3 | 4)[]), - [getVisitedExhibits] - ) - const [visited, setVisited] = useState>(() => new Set(persistedVisited)) + // 获取碎片动画触发方法 + const { triggerPuzzleAnimation } = usePuzzleAnimation() + + // 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码 + const visitedExhibits = getVisitedExhibits(place) + const visited = new Set(visitedExhibits) const total = 4 const progressCount = visited.size @@ -55,6 +59,7 @@ const RongdeshengPlace4 = () => { const [guide1AfterVideoStep, setGuide1AfterVideoStep] = useState(0) const [guide2Visible, setGuide2Visible] = useState(false) const [guide2Step, setGuide2Step] = useState(0) + const [quiz2Visible, setQuiz2Visible] = useState(false) const [guide3Visible, setGuide3Visible] = useState(false) const [guide3Step, setGuide3Step] = useState(0) const [guide4Visible, setGuide4Visible] = useState(false) @@ -156,6 +161,7 @@ const RongdeshengPlace4 = () => { onClick={() => { setLastVisitedPlace('place4') saveRongdeshengStore() + setCourseTimeProgress(19) navigate(`/${moduleName}/${starName}/home`) }} /> @@ -169,15 +175,20 @@ const RongdeshengPlace4 = () => { const closingId = activeExhibitId setActiveExhibitId(null) - // 展品1在视频后对话结束再改状态;2/3/4关闭展品即改状态 - if (closingId === 2 || closingId === 3 || closingId === 4) { - setVisited((prev) => { - const next = new Set(prev) - next.add(closingId) - markExhibitVisited(place, closingId) - saveRongdeshengStore() - return next - }) + // 展品1在视频后对话结束再改状态;展品2答题后再改状态;3/4关闭展品即改状态 + if (closingId === 2) { + setQuiz2Visible(true) + } + + if (closingId === 3 || closingId === 4) { + markExhibitVisited(place, closingId) + + // 触发碎片获取动画(展品3 -> 碎片13,展品4 -> 碎片14) + if (closingId === 3) { + triggerPuzzleAnimation(13) + } else if (closingId === 4) { + triggerPuzzleAnimation(14) + } if (closingId === 4) { setShowEndingVideo(true) @@ -242,15 +253,9 @@ const RongdeshengPlace4 = () => { onVisibleChange={(v) => { setGuide1AfterVideoVisible(v) if (!v) { - setVisited((prev) => { - const next = new Set(prev) - if (!next.has(1)) { - next.add(1) - markExhibitVisited(place, 1) - saveRongdeshengStore() - } - return next - }) + markExhibitVisited(place, 1) + // 触发碎片11获取动画(展品1 -> 碎片11) + triggerPuzzleAnimation(11) } }} currentStep={guide1AfterVideoStep} @@ -284,15 +289,51 @@ const RongdeshengPlace4 = () => { 1948年,解放战争接近尾声,很多实业家纷纷撤离大陆,前往台湾或海外。但荣德生先生专程从无锡来到上海,发表声明,明确表态绝不离开大陆。他在给海外亲友的信中写道:“民族工业的根基在大陆,离开这片土地,实业救国便成空谈。” - + 他不仅自己留下,还力排众议,阻止了荣氏企业迁台的计划,保住了大量的工厂设备、技术人才和生产资料。这份坚守,为新中国成立后的民族工业留存了重要力量,也体现了一位民族实业家的家国情怀。 + { + if (value === 'A') { + return '荣德生作为民族实业家,毕生以实业救国、报国惠民为初心,其决策的核心是家国大义,而非个人财产得失,因此担心财产损失并非他阻止企业迁台的原因。请你再想想吧!' + } + if (value === 'B') { + return '荣德生阻止企业迁台是基于自己对民族工业发展的深刻认知与坚定的爱国信念,是他独立且主动的抉择,并非受家人意愿的影响。请你再想想吧!' + } + }} + successAlertProps={{ + content: + '荣德生毕生信奉“实业救国”,他认为民族工业的根基在大陆,对新中国的经济发展充满希望,希望能继续为国家和人民贡献力量,这是他坚守的核心原因。', + }} + onSubmit={(_value, isCorrect) => { + if (!isCorrect) return + }} + onSuccessContinue={() => { + setQuiz2Visible(false) + markExhibitVisited(place, 2) + // 触发碎片12获取动画(展品2 -> 碎片12) + triggerPuzzleAnimation(12) + }} + /> +