feat: update residence learning flows
Commit non-media application and asset changes while leaving audio, video, and environment files out of version control. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
68ba22a33f
commit
6d40e19346
184 files changed
+7663
-2203
No files matched your search
@@ -38,7 +38,7 @@ const RongdeshengHome = () => {
|
||||
className="absolute right-37 top-22"
|
||||
src={getStaticImage('components/革命先驱故居-返回')}
|
||||
button
|
||||
onClick={() => navigate(`/entrance/${moduleName}`)}
|
||||
onClick={() => navigate(`/${moduleName}`)}
|
||||
/>
|
||||
|
||||
<div className="absolute right-20 top-75 flex flex-col space-y-10">
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
import { SuccessAlert } from '@/components/layout'
|
||||
import { MultipleChoiceQuizPanel, SingleChoiceQuizPanel } from '@/components/layout/quiz'
|
||||
import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils'
|
||||
import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
|
||||
import { setCourseTimeProgress } from '@/lib/utils/api-common'
|
||||
|
||||
const starName = 'rongdesheng'
|
||||
|
||||
@@ -73,7 +75,7 @@ const QUIZ_ANCHORS: QuizAnchor[] = [
|
||||
},
|
||||
{
|
||||
id: 'quiz-2',
|
||||
time: 60 + 51,
|
||||
time: 60 + 52,
|
||||
type: 'single',
|
||||
title: '【单选】荣德生的企业虽多次遭战火摧毁,但始终浴火重生,这体现了他哪种宝贵品质?',
|
||||
options: [
|
||||
@@ -133,7 +135,7 @@ const QUIZ_ANCHORS: QuizAnchor[] = [
|
||||
},
|
||||
{
|
||||
id: 'quiz-5',
|
||||
time: 5 * 60 + 22,
|
||||
time: 5 * 60 + 23,
|
||||
type: 'single',
|
||||
title:
|
||||
'【单选】荣德生先生提出的“崇俭立国”主张,以及造桥铺路、创办学校等善举,体现了他怎样的财富观?',
|
||||
@@ -160,6 +162,9 @@ const RongdeshengInternalization = () => {
|
||||
const navigate = useNavigate()
|
||||
const { moduleName } = useParams<{ moduleName: string }>()
|
||||
|
||||
// 获取碎片动画触发方法
|
||||
const { triggerPuzzleAnimation } = usePuzzleAnimation()
|
||||
|
||||
const [dragQuizValues, setDragQuizValues] = useState<Record<string, DragFillResult[]>>({})
|
||||
const [dragQuizErrors, setDragQuizErrors] = useState<Record<string, string>>({})
|
||||
const [narrationVisible, setNarrationVisible] = useState(true)
|
||||
@@ -303,6 +308,10 @@ const RongdeshengInternalization = () => {
|
||||
setActiveSuccess(item.id)
|
||||
setSuccessContent(option?.explanation || item.successContent || '')
|
||||
setResumeCallback(() => resume)
|
||||
// 答题成功后触发碎片获取动画
|
||||
// quiz-2 -> 碎片15(坚韧不拔),quiz-5 -> 碎片16(社会责任担当)
|
||||
if (item.id === 'quiz-2') triggerPuzzleAnimation(15)
|
||||
if (item.id === 'quiz-5') triggerPuzzleAnimation(16)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -369,7 +378,14 @@ const RongdeshengInternalization = () => {
|
||||
),
|
||||
}
|
||||
})
|
||||
}, [activeSuccess, dragQuizErrors, dragQuizValues, resumeCallback, successContent])
|
||||
}, [
|
||||
activeSuccess,
|
||||
dragQuizErrors,
|
||||
dragQuizValues,
|
||||
resumeCallback,
|
||||
successContent,
|
||||
triggerPuzzleAnimation,
|
||||
])
|
||||
|
||||
if (!moduleName) return null
|
||||
|
||||
@@ -411,18 +427,21 @@ const RongdeshengInternalization = () => {
|
||||
>
|
||||
<GuideStepsItem
|
||||
speaker="student"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_33.mp3')}
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_33_1.mp3')}
|
||||
>
|
||||
小沪,跟着你走完荣德生旧居的学习之旅,我真的被这位民族实业家的一生打动了!
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="student">
|
||||
<GuideStepsItem
|
||||
speaker="student"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_33_2.mp3')}
|
||||
>
|
||||
从钱庄学徒到“面粉大王”“纺织巨子”,从抗战守节到坚守大陆留存工业火种,他用一生践行实业救国的初心,那在新时代,我们该如何理解和传承荣先生的这份实业精神呢?
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_34.mp3')}
|
||||
>
|
||||
针对你的这个问题,今天我们有幸邀请到了上海社会科学院经济研究所博士后,经济史研究室研究人员徐晶老师。关于荣德生先生的实业精神与新时代的传承,我想徐老师会给我们带来深刻的解读!
|
||||
针对你的这个问题,今天我们有幸邀请到了上海社会科学院经济研究所博士后,经济史研究室研究人员徐昂老师。关于荣德生先生的实业精神与新时代的传承,我想徐老师会给我们带来深刻的解读!
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
|
||||
@@ -455,11 +474,14 @@ const RongdeshengInternalization = () => {
|
||||
>
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_35.mp3')}
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_35_1.mp3')}
|
||||
>
|
||||
荣德生先生用一生诠释了“实业救国、经济报国”的初心——从钱庄学徒到实业巨子,他扎根民族大地,以面粉和棉纱筑牢国家经济根基;从战火守节到坚守大陆,他始终与国家同呼吸、共命运;
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_35_2.mp3')}
|
||||
>
|
||||
从创办学校到造桥铺路,他用责任与担当诠释了企业家的社会价值。你不妨把这份触动凝练成几句心里话,既总结学习的收获,也想想未来该如何践行这份实业精神,用自己的行动传承这份初心吧!
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
@@ -482,6 +504,7 @@ const RongdeshengInternalization = () => {
|
||||
animationSpeed={200}
|
||||
onContinue={() => {
|
||||
setShowEnding(false)
|
||||
setCourseTimeProgress(21)
|
||||
navigate('/menu')
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -10,9 +10,10 @@ import {
|
||||
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 { useRongdeshengStore, saveRongdeshengStore } from '@/store/rongdeshengStore'
|
||||
import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
|
||||
|
||||
const starName = 'rongdesheng'
|
||||
const place = 'place1'
|
||||
@@ -23,11 +24,12 @@ const RongdeshengPlace1 = () => {
|
||||
|
||||
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useRongdeshengStore()
|
||||
|
||||
const persistedVisited = useMemo(
|
||||
() => new Set(getVisitedExhibits(place) as (1 | 2)[]),
|
||||
[getVisitedExhibits]
|
||||
)
|
||||
const [visited, setVisited] = useState<Set<1 | 2>>(() => new Set(persistedVisited))
|
||||
// 获取碎片动画触发方法
|
||||
const { triggerPuzzleAnimation } = usePuzzleAnimation()
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place)
|
||||
const visited = new Set(visitedExhibits)
|
||||
|
||||
const total = 2
|
||||
const progressCount = visited.size
|
||||
@@ -79,16 +81,6 @@ const RongdeshengPlace1 = () => {
|
||||
setGuide1Visible(false)
|
||||
}
|
||||
|
||||
const markVisited = (id: 1 | 2) => {
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev)
|
||||
next.add(id)
|
||||
markExhibitVisited(place, id)
|
||||
saveRongdeshengStore()
|
||||
return next
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full h-full bg-cover bg-center bg-no-repeat"
|
||||
@@ -97,7 +89,7 @@ const RongdeshengPlace1 = () => {
|
||||
}}
|
||||
>
|
||||
<Title
|
||||
title="金融启蒙·资本助基"
|
||||
title="金融启蒙·资本筑基"
|
||||
subtitle={subtitle}
|
||||
className="absolute top-10 left-1/2 -translate-x-1/2"
|
||||
/>
|
||||
@@ -139,9 +131,21 @@ const RongdeshengPlace1 = () => {
|
||||
|
||||
<ExhibitOverlay
|
||||
visible={activeExhibitId !== null}
|
||||
onOpen={() => {
|
||||
if (activeExhibitId === 1) {
|
||||
triggerPuzzleAnimation(1)
|
||||
}
|
||||
if (activeExhibitId === 2) {
|
||||
triggerPuzzleAnimation(2)
|
||||
}
|
||||
}}
|
||||
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ''}
|
||||
onClose={() => {
|
||||
setActiveExhibitId(null)
|
||||
if (activeExhibitId === 2) {
|
||||
setQuizVisible(true)
|
||||
return
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -151,7 +155,7 @@ const RongdeshengPlace1 = () => {
|
||||
onVisibleChange={(nextVisible) => {
|
||||
setGuide1Visible(nextVisible)
|
||||
if (!nextVisible) {
|
||||
markVisited(1)
|
||||
markExhibitVisited(place, 1)
|
||||
}
|
||||
}}
|
||||
currentStep={guide1Step}
|
||||
@@ -168,14 +172,20 @@ const RongdeshengPlace1 = () => {
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_04.mp3')}
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_04_1.mp3')}
|
||||
>
|
||||
如你所言,1875年,荣德生先生出生于无锡的商贾之家。凭借着殷实的家境,荣先生从小便由老先生教学,九岁的时候被送到私塾念书,毕业以后,就正式开始学习经商的道路。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_04_2.mp3')}
|
||||
>
|
||||
想要做生意,第一要走南闯北,第二要胆识过人。所以1889年,十几岁的荣先生离开无锡老家,来到上海通顺钱庄学做生意。当时的钱庄学徒要从打扫、记账、珠算等基础活干起,每天天不亮就起床,深夜才休息。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_04_3.mp3')}
|
||||
>
|
||||
荣先生格外刻苦,不仅熟练掌握了珠算、记账、汇兑等核心技能,还学会了察言观色、与人打交道的本事,也从繁重琐碎的钱业学习中逐渐培养起了稳健踏实的办事风格,为他后来创办自己的广生钱庄打下了坚实基础。
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
@@ -185,9 +195,6 @@ const RongdeshengPlace1 = () => {
|
||||
visible={guide2Visible}
|
||||
onVisibleChange={(nextVisible) => {
|
||||
setGuide2Visible(nextVisible)
|
||||
if (!nextVisible) {
|
||||
setQuizVisible(true)
|
||||
}
|
||||
}}
|
||||
currentStep={guide2Step}
|
||||
onStepChange={setGuide2Step}
|
||||
@@ -203,17 +210,26 @@ const RongdeshengPlace1 = () => {
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_06.mp3')}
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_06_1.mp3')}
|
||||
>
|
||||
1896年,荣德生先生与兄长荣宗敬和父亲与人合伙在上海开设广生钱庄,后在无锡设立分庄,这是荣氏兄弟携手创业的起点。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_06_2.mp3')}
|
||||
>
|
||||
广生钱庄开展的“收茧款项”就是他们的创新之举——利用钱庄的汇兑优势,在上海与无锡之间开展蚕茧收购业务,把无锡的蚕茧运到上海销售,再将货款通过钱庄汇回,赚取了可观的利润。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_06_3.mp3')}
|
||||
>
|
||||
广生钱庄后来转为荣家独资经营,成为荣家资本原始积累的重要来源,就像一座桥梁,连接起他后来的实业梦想。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_06_4.mp3')}
|
||||
>
|
||||
不过可惜的是,因为关联商号卷入投机活动失败,广生钱庄最终歇业了,但这段金融从业经历让荣德生摸清了市场规律,积累了宝贵的管理经验,为之后创办面粉厂、纺织厂埋下了伏笔。
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
@@ -238,7 +254,7 @@ const RongdeshengPlace1 = () => {
|
||||
successAlertProps={{
|
||||
title: '恭喜你,选择正确!',
|
||||
content:
|
||||
'1896年广生钱庄创办,荣宗敬任上海总庄经理,荣德生任无锡分庄经理,这是荣氏兄弟携手创业的起点',
|
||||
'1896年广生钱庄创办,荣宗敬任上海总庄经理,荣德生任无锡分庄经理,这是荣氏兄弟携手创业的起点。',
|
||||
}}
|
||||
onSubmit={(_value, isCorrect) => {
|
||||
if (!isCorrect) return
|
||||
@@ -246,7 +262,7 @@ const RongdeshengPlace1 = () => {
|
||||
}}
|
||||
onSuccessContinue={() => {
|
||||
setQuizVisible(false)
|
||||
markVisited(2)
|
||||
markExhibitVisited(place, 2)
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils'
|
||||
import { useMemo, 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 = 'place3'
|
||||
@@ -23,11 +24,12 @@ const RongdeshengPlace3 = () => {
|
||||
|
||||
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useRongdeshengStore()
|
||||
|
||||
const persistedVisited = useMemo(
|
||||
() => new Set(getVisitedExhibits(place) as (1 | 2 | 3)[]),
|
||||
[getVisitedExhibits]
|
||||
)
|
||||
const [visited, setVisited] = useState<Set<1 | 2 | 3>>(() => new Set(persistedVisited))
|
||||
// 获取碎片动画触发方法
|
||||
const { triggerPuzzleAnimation } = usePuzzleAnimation()
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place)
|
||||
const visited = new Set(visitedExhibits)
|
||||
|
||||
const total = 3
|
||||
const progressCount = visited.size
|
||||
@@ -144,6 +146,14 @@ const RongdeshengPlace3 = () => {
|
||||
<ExhibitOverlay
|
||||
visible={activeExhibitId !== null}
|
||||
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ''}
|
||||
onOpen={() => {
|
||||
if (activeExhibitId === 1) {
|
||||
triggerPuzzleAnimation(8)
|
||||
}
|
||||
if (activeExhibitId === 2) {
|
||||
triggerPuzzleAnimation(9)
|
||||
}
|
||||
}}
|
||||
onClose={() => {
|
||||
if (activeExhibitId === null) return
|
||||
const closingId = activeExhibitId
|
||||
@@ -151,13 +161,13 @@ const RongdeshengPlace3 = () => {
|
||||
|
||||
// 展品1、2:关闭展品后直接完成
|
||||
if (closingId === 1 || closingId === 2) {
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev)
|
||||
next.add(closingId)
|
||||
markExhibitVisited(place, closingId)
|
||||
saveRongdeshengStore()
|
||||
return next
|
||||
})
|
||||
markExhibitVisited(place, closingId)
|
||||
// 触发碎片获取动画(展品1 -> 碎片8,展品2 -> 碎片9)
|
||||
// if (closingId === 1) {
|
||||
// triggerPuzzleAnimation(8)
|
||||
// } else if (closingId === 2) {
|
||||
// triggerPuzzleAnimation(9)
|
||||
// }
|
||||
}
|
||||
|
||||
if (closingId === 1) {
|
||||
@@ -200,14 +210,20 @@ const RongdeshengPlace3 = () => {
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_16.mp3')}
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_16_1.mp3')}
|
||||
>
|
||||
1937年抗日战争全面爆发,日军的炮火轰炸了荣氏在上海、无锡的多家工厂,设备被毁,生产停滞。为了保住民族工业的火种,支援抗战,荣德生做出了艰难的决定——将工厂内迁。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_16_2.mp3')}
|
||||
>
|
||||
迁厂要从上海、无锡出发,穿越日军的封锁线,沿着崎岖的道路前往重庆、成都等地,路途遥远且危险重重。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_16_3.mp3')}
|
||||
>
|
||||
很多设备在运输过程中遭到损坏,不少工人也付出了生命的代价,但他们始终没有放弃,因为荣德生知道,后方的生产关乎前线的军需和民众的生计。
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
@@ -229,11 +245,14 @@ const RongdeshengPlace3 = () => {
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_18.mp3')}
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_18_1.mp3')}
|
||||
>
|
||||
是的!内迁后的重庆申新纱厂、茂新面粉厂等,在简陋的条件下迅速恢复生产。虽然产量不及战前,但从未中断。生产的面粉、棉纱,一部分供应给前线的抗日军队,制作军装、被褥、急救包;另一部分供应给后方民众,保障基本生活需求。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_18_2.mp3')}
|
||||
>
|
||||
这篇报道,就见证了荣氏企业用实业支援抗战的壮举,为抗日战争的物资供应提供了有力支持,成为民族救亡运动中不可或缺的力量。
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
@@ -255,11 +274,14 @@ const RongdeshengPlace3 = () => {
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_20.mp3')}
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_20_1.mp3')}
|
||||
>
|
||||
这发生于1941年太平洋战争爆发、上海公共租界沦陷之后。日军以经济提携为名,通过行政命令强迫中国企业交由日本公司或个人管理,企图将上海的民族工业纳入其战时经济体系。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem speaker="guide">
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_20_2.mp3')}
|
||||
>
|
||||
日商丰田纱厂多次上门威逼利诱,想收购上海申新一、八厂;汪伪政权的外交部长也出面邀谈,企图逼迫他妥协。但荣德生始终坚守民族底线,不合作、不妥协。
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
@@ -282,7 +304,7 @@ const RongdeshengPlace3 = () => {
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_21.mp3')}
|
||||
>
|
||||
生死存亡之际,他用行动诠释了“民族气节”四个字。即便工厂被占据,也绝不与侵略者合作。
|
||||
在那个生死存亡的时刻,他用自己的行动诠释了“民族气节”四个字,即便工厂被日军占据,也绝不与侵略者合作。
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
|
||||
@@ -315,10 +337,10 @@ const RongdeshengPlace3 = () => {
|
||||
<SingleChoiceQuizPanel
|
||||
visible={quiz3Visible}
|
||||
zIndex={10001}
|
||||
title="【单选】面对威逼利诱与占领压力,荣德生坚持的原则最能体现为:"
|
||||
title="【单选】荣德生先生题写“心正思无邪 意诚言必中”,背后体现的核心精神是:"
|
||||
options={[
|
||||
{ value: 'A', label: 'A. 民族气节,不合作、不妥协' },
|
||||
{ value: 'B', label: 'B. 权宜之计,暂时让渡控制权' },
|
||||
{ value: 'A', label: 'A. 面对日军威逼利诱,坚守民族气节绝不妥协' },
|
||||
{ value: 'B', label: 'B. 告诫自己做好企业管理,把控产品品质' },
|
||||
]}
|
||||
correctAnswer="A"
|
||||
errorMessage="这句题字是荣德生在民族危亡之际,表达坚守民族底线的心声,并非单纯针对企业管理。请你再想想吧!"
|
||||
@@ -332,15 +354,9 @@ const RongdeshengPlace3 = () => {
|
||||
onSuccessContinue={() => {
|
||||
setQuiz3Visible(false)
|
||||
// 展品3:答题成功后才算完成,更新 cursor 状态
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev)
|
||||
if (!next.has(3)) {
|
||||
next.add(3)
|
||||
markExhibitVisited(place, 3)
|
||||
saveRongdeshengStore()
|
||||
}
|
||||
return next
|
||||
})
|
||||
markExhibitVisited(place, 3)
|
||||
// 触发碎片10获取动画(展品3 -> 碎片10)
|
||||
triggerPuzzleAnimation(10)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { VideoBubbles } from './components/VideoBubbles'
|
||||
import { HINT_AUDIOS, HINT_TEXTS } from './constants'
|
||||
import { MODULES, getSceneTitle } from './modules.data'
|
||||
import type { ModuleId, ModuleState, VideoPlayingState } from './types'
|
||||
import { setCourseTimeProgress } from '@/lib/utils/api-common'
|
||||
|
||||
const RongdeshengReflection = () => {
|
||||
const navigate = useNavigate()
|
||||
@@ -28,8 +29,10 @@ const RongdeshengReflection = () => {
|
||||
const markReflectionFinalCompleted = useRongdeshengStore((s) => s.markReflectionFinalCompleted)
|
||||
const markReflectionGuideShown = useRongdeshengStore((s) => s.markReflectionGuideShown)
|
||||
|
||||
const [showOpeningVideo, setShowOpeningVideo] = useState(true)
|
||||
|
||||
// 学习引导弹窗:第一次进入时显示
|
||||
const [showGuide, setShowGuide] = useState(!persistedGuideShown)
|
||||
const [showGuide, setShowGuide] = useState(false)
|
||||
|
||||
// 当前选中的模块ID:默认选中第一个未完成的模块(断点续学)
|
||||
const [currentModuleId, setCurrentModuleId] = useState<ModuleId>(() => {
|
||||
@@ -260,6 +263,36 @@ const RongdeshengReflection = () => {
|
||||
),
|
||||
}}
|
||||
>
|
||||
{showOpeningVideo && (
|
||||
<div className="fixed inset-0 z-10000 w-full h-full bg-black">
|
||||
<Video
|
||||
src={getPublicPath('video/reflection/rongdesheng_tiwu_00.mp4')}
|
||||
autoPlay
|
||||
playsInline
|
||||
showSkipButton
|
||||
skipText="跳过"
|
||||
onEnded={() => {
|
||||
setShowOpeningVideo(false)
|
||||
if (!persistedGuideShown) {
|
||||
setShowGuide(true)
|
||||
}
|
||||
}}
|
||||
onSkip={() => {
|
||||
setShowOpeningVideo(false)
|
||||
if (!persistedGuideShown) {
|
||||
setShowGuide(true)
|
||||
}
|
||||
}}
|
||||
onError={() => {
|
||||
setShowOpeningVideo(false)
|
||||
if (!persistedGuideShown) {
|
||||
setShowGuide(true)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 返回按钮 */}
|
||||
<Image
|
||||
className="absolute right-5 top-5 z-10"
|
||||
@@ -337,6 +370,7 @@ const RongdeshengReflection = () => {
|
||||
finalVideoStage={finalVideoStage}
|
||||
onStart={() => setFinalVideoStage('playing')}
|
||||
onVideoEnded={() => {
|
||||
setCourseTimeProgress(20)
|
||||
setFinalVideoStage('ended')
|
||||
markReflectionFinalCompleted()
|
||||
}}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getPublicPath } from '@/lib/utils'
|
||||
import type { Module } from './types'
|
||||
|
||||
export const MODULES: Module[] = [
|
||||
@@ -6,24 +7,34 @@ export const MODULES: Module[] = [
|
||||
title: '建厂拓业',
|
||||
backgroundImage: 'rongdesheng/reflection/场景1选中',
|
||||
question:
|
||||
'1915年,荣氏兄弟计划在上海创办申新纱厂,却面临三大难题:租界建厂受列强限制、资金短缺、原料运输不便。上海作为远东商贸中心,机遇与挑战并存,如何在这里建起属于中国人的纱厂?',
|
||||
'1915年,荣氏兄弟计划在上海创办申新纱厂,却面临三大难题:租界建厂受列强限制、资金短缺、原料运输不便。上海作为远东商贸中心,机遇与挑战并存,如何在这里建起属于中国人的纱厂?',
|
||||
options: [
|
||||
{ id: 'opt1-1', text: '创办申新纱厂', isCorrect: true, videoResourceId: 'video1-1' },
|
||||
{ id: 'opt1-2', text: '引进国外先进设备', isCorrect: true, videoResourceId: 'video1-2' },
|
||||
{ id: 'opt1-3', text: '建立工会组织', isCorrect: false },
|
||||
{
|
||||
id: 'opt1-1',
|
||||
text: '选址上海杨树浦(临黄浦江,便于原料运输)',
|
||||
isCorrect: true,
|
||||
videoResourceId: 'video1-1',
|
||||
},
|
||||
{
|
||||
id: 'opt1-2',
|
||||
text: '联合无锡帮华商与实业同仁招股筹款',
|
||||
isCorrect: true,
|
||||
videoResourceId: 'video1-2',
|
||||
},
|
||||
{ id: 'opt1-3', text: '依附租界洋行,换取建厂便利与资金支持', isCorrect: false },
|
||||
],
|
||||
videoResources: [
|
||||
{
|
||||
id: 'video1-1',
|
||||
thumbnail: 'rongdesheng/reflection/视频1-1',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_1.mp4',
|
||||
videoSrc: getPublicPath('video/rongdesheng/rongdesheng_2_1.mp4'),
|
||||
position: { top: '16%', left: '8%' },
|
||||
flipY: false,
|
||||
},
|
||||
{
|
||||
id: 'video1-2',
|
||||
thumbnail: 'rongdesheng/reflection/视频1-2',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_2.mp4',
|
||||
videoSrc: getPublicPath('video/rongdesheng/rongdesheng_2_2.mp4'),
|
||||
position: { top: '25%', left: '29%' },
|
||||
flipY: true,
|
||||
},
|
||||
@@ -32,7 +43,7 @@ export const MODULES: Module[] = [
|
||||
'择址临港、凝聚华商力量,申新在上海扎根生长,成为民族工业冲破列强封锁的一面旗帜!',
|
||||
resultImage: 'rongdesheng/reflection/场景1结算',
|
||||
wrongHint:
|
||||
'荣氏兄弟坚守"实业救国"初心,拒绝洋行干涉企业经营,不愿民族产业沦为外资附庸!试试其他方案吧~',
|
||||
'荣氏兄弟坚守“实业救国”初心,拒绝洋行干涉企业经营,不愿民族产业沦为外资附庸!试试其他方案吧~',
|
||||
audio: { wrongHint: '' },
|
||||
},
|
||||
{
|
||||
@@ -60,13 +71,13 @@ export const MODULES: Module[] = [
|
||||
{
|
||||
id: 'video2-1',
|
||||
thumbnail: 'rongdesheng/reflection/视频2-1',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_3.mp4',
|
||||
videoSrc: getPublicPath('video/rongdesheng/rongdesheng_2_3.mp4'),
|
||||
position: { top: '14%', left: '11%' },
|
||||
},
|
||||
{
|
||||
id: 'video2-2',
|
||||
thumbnail: 'rongdesheng/reflection/视频2-2',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_4.mp4',
|
||||
videoSrc: getPublicPath('video/rongdesheng/rongdesheng_2_4.mp4'),
|
||||
position: { top: '21%', left: '22.2%' },
|
||||
flipY: true,
|
||||
},
|
||||
@@ -103,13 +114,13 @@ export const MODULES: Module[] = [
|
||||
{
|
||||
id: 'video3-1',
|
||||
thumbnail: 'rongdesheng/reflection/视频3-1',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_5.mp4',
|
||||
videoSrc: getPublicPath('video/rongdesheng/rongdesheng_2_5.mp4'),
|
||||
position: { top: '15%', left: '44%' },
|
||||
},
|
||||
{
|
||||
id: 'video3-2',
|
||||
thumbnail: 'rongdesheng/reflection/视频3-2',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_6.mp4',
|
||||
videoSrc: getPublicPath('video/rongdesheng/rongdesheng_2_6.mp4'),
|
||||
position: { bottom: '50%', left: '55%' },
|
||||
flipY: true,
|
||||
},
|
||||
@@ -144,14 +155,14 @@ export const MODULES: Module[] = [
|
||||
{
|
||||
id: 'video4-1',
|
||||
thumbnail: 'rongdesheng/reflection/视频4-1',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_7.mp4',
|
||||
videoSrc: getPublicPath('video/rongdesheng/rongdesheng_2_7.mp4'),
|
||||
position: { top: '42%', left: '41%' },
|
||||
flipY: true,
|
||||
},
|
||||
{
|
||||
id: 'video4-2',
|
||||
thumbnail: 'rongdesheng/reflection/视频4-2',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_8.mp4',
|
||||
videoSrc: getPublicPath('video/rongdesheng/rongdesheng_2_8.mp4'),
|
||||
position: { bottom: '26%', right: '44%' },
|
||||
},
|
||||
],
|
||||
@@ -189,13 +200,13 @@ export const MODULES: Module[] = [
|
||||
{
|
||||
id: 'video5-1',
|
||||
thumbnail: 'rongdesheng/reflection/视频5-1',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_10.mp4',
|
||||
videoSrc: getPublicPath('video/rongdesheng/rongdesheng_2_10.mp4'),
|
||||
position: { top: '25%', left: '42%' },
|
||||
},
|
||||
{
|
||||
id: 'video5-2',
|
||||
thumbnail: 'rongdesheng/reflection/视频5-2',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_9.mp4',
|
||||
videoSrc: getPublicPath('video/rongdesheng/rongdesheng_2_9.mp4'),
|
||||
position: { bottom: '50%', left: '55%' },
|
||||
flipY: true,
|
||||
},
|
||||
@@ -209,7 +220,7 @@ export const MODULES: Module[] = [
|
||||
},
|
||||
]
|
||||
|
||||
export const FINAL_VIDEO_SRC = '/video/rongdesheng/rongdesheng_2_11.mp4'
|
||||
export const FINAL_VIDEO_SRC = getPublicPath('video/rongdesheng/rongdesheng_2_11.mp4')
|
||||
|
||||
const SCENE_NUMBER_MAP: Record<Module['id'], string> = {
|
||||
scene1: '一',
|
||||
|
||||
Reference in new issue
Block a user