feat:修改英文双引号

This commit is contained in:
kevinzhu committed 2026-04-30 09:17:31 +08:00
commit 4a2be3b4d0
1351 files changed
+56547

No files matched your search

+316
View File
@@ -0,0 +1,316 @@
import {
ExhibitOverlay,
GuideSteps,
GuideStepsItem,
Image,
StudyGuideModal,
Title,
SelectFillQuizPanel,
Video,
type VideoRef,
} from '@/components'
import { useNavigate, useParams } from 'react-router-dom'
import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils'
import { useMemo, useRef, useState } from 'react'
import Cursor from '@/components/cursor'
import { useCursorController } from '@/hooks/useCursorController'
import SuccessAlert from '@/components/layout/alert/success-alert'
import ScaledPortal from '@/components/layout/ScaledPortal'
import type { SelectFillResult } from '@/components/layout/quiz'
import { saveMaozedongStore, useMaozedongStore } from '@/store/maozedongStore'
import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
// starName 固定为 maozedong
const starName = 'maozedong'
// 展品 ID 与资源路径映射,ID 用来做唯一标识
const exhibitSrcMap: Record<1 | 2, string> = {
1: 'maozedong/place1/场景1展品1',
2: 'maozedong/place1/场景1展品2',
}
const MaozedongPlace1 = () => {
const navigate = useNavigate()
const { moduleName } = useParams<{
moduleName: string
}>()
// 获取碎片动画触发方法
const { triggerPuzzleAnimation } = usePuzzleAnimation()
// 接入持久化:记录本场景 place1 的已访问展品与断点
const markVisited = useMaozedongStore((s) => s.markExhibitVisited)
const getVisited = useMaozedongStore((s) => s.getVisitedExhibits)
const setLastVisitedPlace = useMaozedongStore((s) => s.setLastVisitedPlace)
const persistedVisited = useMemo(() => new Set(getVisited('place1') as (1 | 2)[]), [getVisited])
// 学习引导弹窗
const [showGuideModal, setShowGuideModal] = useState(true)
// 对话结束后的学习引导弹窗
const [showAfterDialogGuide, setShowAfterDialogGuide] = useState(false)
// 展品2的对话引导状态
const [guideVisible2, setGuideVisible2] = useState(false)
const [guideStep2, setGuideStep2] = useState(0)
// 当前激活的展品 ID
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | null>(null)
// 已访问的展品(初始化自 store;随后每次关闭展品时写回)
const [visited, setVisited] = useState<Set<1 | 2>>(() => new Set(persistedVisited))
// 展品2关闭后播放的视频 + 选择填空题
const [showVideo2, setShowVideo2] = useState(false)
const video2Ref = useRef<VideoRef>(null)
const [quizVisible, setQuizVisible] = useState(false)
const [successAlertVisible, setSuccessAlertVisible] = useState(false)
// Cursor 顺序控制:1 -> 2
const { isEnabled } = useCursorController({
initialEnabled: [2],
dependencies: {
1: [2],
},
visited,
})
const handleOpenExhibit = (id: 1 | 2) => {
setActiveExhibitId(id)
// 根据展品ID显示对应的对话
if (id === 1) {
setGuideVisible2(true)
setGuideStep2(0)
}
}
// 监听展品2对话引导步骤变化
const handleGuideStepChange2 = (step: number) => {
setGuideStep2(step)
}
// 监听展品2对话引导完成
const handleGuideVisibleChange2 = (visible: boolean) => {
setGuideVisible2(visible)
}
const progressCount = visited.size
const total = 2
const completed = progressCount >= total
const subtitle = completed ? '已完成' : `${progressCount}/${total}`
if (!moduleName) return null
return (
<div
className="w-full h-full bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: getBackgroundImage(`${starName}/place1/场景1`),
}}
>
{/* 标题 */}
<Title
title="探索方向·确立信仰"
subtitle={subtitle}
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-106 bottom-150"
onClick={() => handleOpenExhibit(1)}
read={visited.has(1)}
disabled={!isEnabled(1)}
/>
{/* 光标2 */}
<Cursor
className="absolute left-170 bottom-135"
onClick={() => handleOpenExhibit(2)}
read={visited.has(2)}
disabled={!isEnabled(2)}
/>
{/* 继续前进 - 仅在完成所有展品后显示 */}
{completed && (
<Image
className="absolute bottom-0 right-140"
src={getStaticImage(`${starName}/place1/场景1继续前进`)}
button
onClick={() => {
setLastVisitedPlace('place2')
saveMaozedongStore()
navigate(`/${moduleName}/${starName}/place2`)
}}
/>
)}
{/* 展品2的对话引导 */}
<GuideSteps
visible={guideVisible2}
onVisibleChange={handleGuideVisibleChange2}
currentStep={guideStep2}
onStepChange={handleGuideStepChange2}
showMask={false}
highlightSelectors={[]}
zIndex={10001}
>
<GuideStepsItem
speaker="student"
audioSrc={getPublicPath('audio/maozedong/mp3_mzd_03.mp3')}
>
那参加中共一大时,毛主席对未来有什么想法吗?
</GuideStepsItem>
<GuideStepsItem speaker="guide" audioSrc={getPublicPath('audio/maozedong/mp3_mzd_04.mp3')}>
当时全国只有50多个党员,毛主席所在的湖南也才几个。但他觉得,重要的是把马克思主义和中国的实际情况结合起来。就像种田,种子再好,也得看土壤——中国的“土壤”就是农民占大多数,这是和苏联不一样的地方。
</GuideStepsItem>
</GuideSteps>
{/* 展品遮罩层 */}
<ExhibitOverlay
visible={activeExhibitId !== null}
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ''}
onOpen={() => {
if (activeExhibitId === 2) {
triggerPuzzleAnimation(1)
}
if (activeExhibitId === 1) {
triggerPuzzleAnimation(2)
}
}}
onClose={() => {
if (activeExhibitId === 2) {
setActiveExhibitId(null)
setShowVideo2(true)
return
}
if (activeExhibitId !== null) {
setVisited((prev) => {
const next = new Set(prev)
next.add(activeExhibitId)
// 写回持久化
markVisited('place1', activeExhibitId)
return next
})
}
setActiveExhibitId(null)
}}
/>
{/* 展品2关闭后播放的视频 */}
{showVideo2 && (
<Video
ref={video2Ref}
src={getPublicPath('video/maozedong/mzd_00.mp4')}
className="fixed inset-0 z-20000"
autoPlay
playsInline
showSkipButton
onEnded={() => {
setShowVideo2(false)
setQuizVisible(true)
}}
onSkip={() => {
setShowVideo2(false)
setQuizVisible(true)
}}
/>
)}
{/* 展品2对话结束后的选择填空题 + 成功提示(使用 ScaledPortal) */}
<ScaledPortal enabled={quizVisible || successAlertVisible} zIndex={10050}>
<>
<SelectFillQuizPanel
visible={quizVisible}
titlePrefix="【填空】毛泽东主席认为,到了"
titleSuffix="年夏天,在理论上,而且在某种程度的行动上,他已成为一个马克思主义者了。"
blanks={[{ id: 'd1' }, { id: 'd2' }, { id: 'd3' }, { id: 'd4' }]}
options={[
{ value: '0', label: '0' },
{ value: '1', label: '1' },
{ value: '2', label: '2' },
{ value: '3', label: '3' },
{ value: '4', label: '4' },
{ value: '5', label: '5' },
{ value: '6', label: '6' },
{ value: '7', label: '7' },
{ value: '8', label: '8' },
{ value: '9', label: '9' },
]}
correctAnswers={{
d1: '1',
d2: '9',
d3: '2',
d4: '0',
}}
errorMessage="1920年5月5日到7月初,在上海居住的2个月期间,毛主席的思想完成了向马克思主义者的转变。请你再想想吧!"
onSubmit={(_results: SelectFillResult[], isCorrect: boolean) => {
if (isCorrect) {
setQuizVisible(false)
setSuccessAlertVisible(true)
}
}}
onError={(_error: string) => {
// 错误时仅提示,再让学生重做
}}
submitText="确定"
zIndex={10000}
/>
<SuccessAlert
visible={successAlertVisible}
onContinue={() => {
setSuccessAlertVisible(false)
setVisited((prev) => {
const next = new Set(prev)
next.add(2)
markVisited('place1', 2)
return next
})
}}
/>
</>
</ScaledPortal>
{/* 学习引导词 */}
<StudyGuideModal
visible={showGuideModal}
audioSrc={getPublicPath('audio/maozedong/mp3_mzd_02.mp3')}
onContinue={() => {
setShowGuideModal(false)
setShowAfterDialogGuide(true)
}}
ailgn="left"
>
1.场景中出现动态光点的展品表示未浏览,可通过点击光点来了解展品背后的故事,通过滚轮可以放大展品,浏览展品后蓝色动态光点会变为已浏览状态。
{'\n'}
2.在学习过程中会掉落拼图碎片,拼图碎片会自动收入拼图盒中,拼图盒可以点开查看当前拼图收集情况,点击单个拼图可以回到获得拼图的场景再次学习。
{'\n'}
3.所有动态光点的展品全部学习后才能进入下一个场景学习。
</StudyGuideModal>
{/* 对话结束后的学习引导 */}
<StudyGuideModal
visible={showAfterDialogGuide}
audioSrc={getPublicPath('audio/maozedong/mp3_mzd_01.mp3')}
onContinue={() => setShowAfterDialogGuide(false)}
firstLineIndent="2em"
>
{`同学你好,欢迎来到“走进故居·对话”学习模块!
此刻你正处在毛泽东旧居陈列馆里,接下来请跟着时光的脚步,一同回望毛泽东主席人生中4段风云激荡的岁月:摸索方向 确立信仰、播撒红色火种、吹响合作号角、唤起工农群众。准备好推开这扇门,走进那段滚烫的峥嵘岁月了吗?`}
</StudyGuideModal>
</div>
)
}
export default MaozedongPlace1