fix: 修复开发/测试包模拟进度被易班同步覆盖的问题
进度模拟面板写入 puzzleStore 后,App 启动时易班同步会将其覆盖; 现在在同步完成后按 sessionStorage 中保存的模式重放模拟进度, 确保测试包的"到达某故居/全流程完成"调试入口生效。 同时纳入近期未提交的页面、组件、资源与环境变量改动。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
6d40e19346
commit
49ecabbf3a
155 files changed
+1956
-724
No files matched your search
@@ -4,7 +4,8 @@ import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { Image } from '@/components'
|
||||
import { usePuzzleStore } from '@/store/puzzleStore'
|
||||
import { getResidenceStageUnlocks, isResidenceUnlocked } from '@/config/progression'
|
||||
import { getUserDefine } from '@/lib/utils/api-common'
|
||||
import { getUserDefineResult } from '@/lib/utils/api-common'
|
||||
import { showYibanSyncErrorPrompt, showYibanUnauthenticatedPrompt } from '@/components'
|
||||
import {
|
||||
useHuangyanpeiStore,
|
||||
HUANGYANPEI_LESSON_ID,
|
||||
@@ -37,28 +38,63 @@ const Huangyanpei = () => {
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
try {
|
||||
const raw = await getUserDefine(HUANGYANPEI_LESSON_ID, HUANGYANPEI_KEY)
|
||||
if (!raw) return
|
||||
const result = await getUserDefineResult(HUANGYANPEI_LESSON_ID, HUANGYANPEI_KEY)
|
||||
|
||||
const data = JSON.parse(raw) as {
|
||||
if (result.status !== 'success') {
|
||||
useHuangyanpeiStore.setState((prev) => ({
|
||||
...prev,
|
||||
visitedExhibits: {},
|
||||
lastVisitedPlace: null,
|
||||
reflectionRoute: 'reflection',
|
||||
shownReflectionCompletionGuides: {},
|
||||
reflectionScene2Choice: null,
|
||||
}))
|
||||
|
||||
if (result.status === 'unauthenticated') {
|
||||
showYibanUnauthenticatedPrompt()
|
||||
} else if (result.status === 'error') {
|
||||
showYibanSyncErrorPrompt()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const data = JSON.parse(result.content) as {
|
||||
visitedExhibits?: Record<string, number[]>
|
||||
lastVisitedPlace?: string | null
|
||||
reflectionRoute?: string
|
||||
shownReflectionCompletionGuides?: Record<string, boolean>
|
||||
reflectionScene2Choice?: '铁工科' | '商科' | null
|
||||
}
|
||||
|
||||
const validReflectionRoute =
|
||||
data.reflectionRoute &&
|
||||
['reflection', 'reflection2', 'reflection3', 'reflection4'].includes(data.reflectionRoute)
|
||||
? data.reflectionRoute
|
||||
: 'reflection'
|
||||
|
||||
useHuangyanpeiStore.setState((prev) => ({
|
||||
...prev,
|
||||
lastVisitedPlace:
|
||||
data.lastVisitedPlace !== undefined ? data.lastVisitedPlace : prev.lastVisitedPlace,
|
||||
reflectionRoute:
|
||||
data.reflectionRoute &&
|
||||
['reflection', 'reflection2', 'reflection3', 'reflection4'].includes(
|
||||
data.reflectionRoute
|
||||
)
|
||||
? data.reflectionRoute
|
||||
: (prev.reflectionRoute ?? 'reflection'),
|
||||
visitedExhibits: Object.fromEntries(
|
||||
Object.entries(data.visitedExhibits ?? {}).map(([place, exhibits]) => [
|
||||
place,
|
||||
new Set(Array.isArray(exhibits) ? exhibits : []),
|
||||
])
|
||||
),
|
||||
lastVisitedPlace: data.lastVisitedPlace ?? null,
|
||||
reflectionRoute: validReflectionRoute,
|
||||
shownReflectionCompletionGuides: data.shownReflectionCompletionGuides ?? {},
|
||||
reflectionScene2Choice: data.reflectionScene2Choice ?? null,
|
||||
}))
|
||||
} catch (error) {
|
||||
useHuangyanpeiStore.setState((prev) => ({
|
||||
...prev,
|
||||
visitedExhibits: {},
|
||||
lastVisitedPlace: null,
|
||||
reflectionRoute: 'reflection',
|
||||
shownReflectionCompletionGuides: {},
|
||||
reflectionScene2Choice: null,
|
||||
}))
|
||||
showYibanSyncErrorPrompt()
|
||||
console.error('Failed to load Huangyanpei store', error)
|
||||
}
|
||||
})()
|
||||
|
||||
@@ -546,7 +546,7 @@ const HuangyanpeiInternalization = () => {
|
||||
onContinue={() => {
|
||||
setShowStudyGuide(false)
|
||||
setCourseTimeProgress(12)
|
||||
navigate('/menu')
|
||||
navigate('/learning-map', { state: { completedResidence: 'huangyanpei' } })
|
||||
}}
|
||||
audioSrc={getPublicPath('audio/huangyanpei/mp3_huangyanpei_31.mp3')}
|
||||
/>
|
||||
|
||||
@@ -345,7 +345,7 @@ const HuangyanpeiPlace3 = () => {
|
||||
speaker="student"
|
||||
audioSrc={getPublicPath('audio/huangyanpei/mp3_huangyanpei_07.mp3')}
|
||||
>
|
||||
这所川沙小学堂就是黄炎培先生“教育救国”的第一个试验田吧?我们现在学习的课程有专业课、实习实训课都提倡技能培养,当年他是不是也很注重动手能力呀?
|
||||
这所川沙小学堂就是黄炎培先生“教育救国”的第一个试验田吧?我们现在的专业课、实训课都提倡技能培养,当年他是不是也很注重动手能力呀?
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
|
||||
@@ -342,7 +342,7 @@ const HuangyanpeiPlace4 = () => {
|
||||
{/* 题目 */}
|
||||
<SingleChoiceQuizPanel
|
||||
visible={showQuiz}
|
||||
title={`【单选】中华职业学校首批招生78人,凭借____口号和“免学费、包分配”的优待,开创了中国近代职业教育的先河。`}
|
||||
title={`【单选】中华职业学校首批招生78人,凭借____口号和“免学费、包分配”的优待,开创了中国近代职业教育的先河。`}
|
||||
options={[
|
||||
{ value: 'A', label: 'A. 学而优则仕' },
|
||||
{ value: 'B', label: 'B. 双手万能、手脑并用' },
|
||||
|
||||
@@ -150,13 +150,13 @@ const HuangyanpeiPlace6 = () => {
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/huangyanpei/mp3_huangyanpei_21_2.mp3')}
|
||||
>
|
||||
第一次把”中等专业学校”正式纳入国家学制,让职业教育有了明确的国家定位。1952年,他又推动建立了上海机械学校、南京航空学校等五所中专,这些学校为国家”一五”计划输送了6.3万名技术工人,为工业发展提供了坚实的人才支撑。
|
||||
第一次把“中等专业学校”正式纳入国家学制,让职业教育有了明确的国家定位。1952年,他又推动建立了上海机械学校、南京航空学校等五所中专,这些学校为国家“一五”计划输送了6.3万名技术工人,为工业发展提供了坚实的人才支撑。
|
||||
</GuideStepsItem>
|
||||
<GuideStepsItem
|
||||
speaker="guide"
|
||||
audioSrc={getPublicPath('audio/huangyanpei/mp3_huangyanpei_21_3.mp3')}
|
||||
>
|
||||
黄炎培先生曾说过:”我要把最后一滴血,滴在职业教育的讲台上。”他用一生践行了这句话,而我们现在中职教育强调的”工学结合、德技并修”,正是对他”手脑并用、注重实践”理念的传承和发展。
|
||||
黄炎培先生曾说过:“我要把最后一滴血,滴在职业教育的讲台上。”他用一生践行了这句话,而我们现在中职教育强调的“工学结合、德技并修”,正是对他“手脑并用、注重实践”理念的传承和发展。
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ const HuangyanpeiReflectionScene2 = () => {
|
||||
<SuccessAlert
|
||||
visible={showQuizSuccess}
|
||||
title=""
|
||||
content="恭喜你!选择正确。不愧是从川沙小学堂走出来!"
|
||||
content="恭喜你!选择正确。不愧是从川沙小学堂走出来!"
|
||||
onClose={() => {
|
||||
setShowQuizSuccess(false)
|
||||
}}
|
||||
@@ -246,7 +246,7 @@ const HuangyanpeiReflectionScene2 = () => {
|
||||
onContinue={handleCompleteGuideContinue}
|
||||
verticalAlign="center"
|
||||
>
|
||||
第二关完成!“职业方向锁定!”去新学校了解你的专业吧。
|
||||
第二关完成!职业方向锁定!去新学校了解你的专业吧。
|
||||
</StudyGuideModal>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -175,7 +175,7 @@ const ChoiceDialog2 = ({ visible = true, onClose, onSelect }: ChoiceDialog2Props
|
||||
<SuccessAlert
|
||||
visible={showSuccessAlert}
|
||||
title="选得好!"
|
||||
content="不管是做工匠还是做买卖,学好本事就能报效国家,这个选择会影响你在中华职业学校的专业哦!"
|
||||
content="不管是做工匠还是做买卖,学好本事就能报效国家,这个选择会影响你在中华职业学校的专业哦!"
|
||||
onClose={handleSuccessAlertContinue}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -114,7 +114,7 @@ const Scene2QuizDialog = ({ visible = true, onClose, onComplete }: Scene2QuizDia
|
||||
<div className="flex-1">
|
||||
<div className="text-5xl font-bold mb-6 -ml-[26px]">【单选】</div>
|
||||
<div className="text-4xl leading-relaxed">
|
||||
杨斯盛先生近期监工浦东中学建设,有一份上月的工人工钱支出表需要核对,你来告诉我答案吧?
|
||||
杨斯盛先生近期监工浦东中学建设,有一份上月的工人工钱支出表需要核对,你来告诉我答案吧!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useHuangyanpeiStore } from '@/store/huangyanpeiStore'
|
||||
import { setCourseTimeProgress } from '@/lib/utils/api-common'
|
||||
import { useCursorController } from '@/hooks/useCursorController'
|
||||
import { saveHuangyanpeiStore } from '@/store/huangyanpeiStore'
|
||||
import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
|
||||
|
||||
const starName = 'huangyanpei'
|
||||
|
||||
@@ -20,6 +21,8 @@ const HuangyanpeiReflectionScene3 = () => {
|
||||
moduleName: string
|
||||
}>()
|
||||
|
||||
const { triggerPuzzleAnimation } = usePuzzleAnimation()
|
||||
|
||||
//喇叭考题弹窗
|
||||
const [showQuizDialog, setShowQuizDialog] = useState(false)
|
||||
|
||||
@@ -252,6 +255,7 @@ const HuangyanpeiReflectionScene3 = () => {
|
||||
onComplete={() => {
|
||||
setShowQuizDialog(false)
|
||||
markReflectionCursorVisited(scene, 3)
|
||||
triggerPuzzleAnimation(15)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -8,10 +8,9 @@ import TowelSalesDialog from './scene4/TowelSalesDialog'
|
||||
import type { VideoRef } from '@/components/video'
|
||||
import { QuizDialog } from './components'
|
||||
import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
|
||||
import { useHuangyanpeiStore } from '@/store/huangyanpeiStore'
|
||||
import { useHuangyanpeiStore, saveHuangyanpeiStore } from '@/store/huangyanpeiStore'
|
||||
import { setCourseTimeProgress } from '@/lib/utils/api-common'
|
||||
import { useCursorController } from '@/hooks/useCursorController'
|
||||
import { saveHuangyanpeiStore } from '@/store/huangyanpeiStore'
|
||||
|
||||
const starName = 'huangyanpei'
|
||||
|
||||
@@ -186,7 +185,6 @@ const HuangyanpeiReflectionScene4 = () => {
|
||||
onComplete={() => {
|
||||
// 完成拖拽后更新visited状态
|
||||
markReflectionCursorVisited(scene, 2)
|
||||
triggerPuzzleAnimation(15)
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -238,6 +236,7 @@ const HuangyanpeiReflectionScene4 = () => {
|
||||
onComplete={() => {
|
||||
setShowQuizDialog(false)
|
||||
markReflectionCursorVisited(scene, 3)
|
||||
triggerPuzzleAnimation(15)
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -91,19 +91,19 @@ const TowelSalesDialog = ({ visible = true, onClose, onComplete }: TowelSalesDia
|
||||
() => [
|
||||
{
|
||||
value: 'option1',
|
||||
label: 'A.这是中华职校生产的国货,质量比洋货还好!',
|
||||
label: 'A.这是中华职校生产的国货,质量比洋货还好!',
|
||||
},
|
||||
{
|
||||
value: 'option2',
|
||||
label: 'B.价格便宜,量大从优,诚信经营绝不掺假',
|
||||
label: 'B.价格便宜,量大从优,诚信经营绝不掺假。',
|
||||
},
|
||||
{
|
||||
value: 'option3',
|
||||
label: 'C.买国货就是支持实业救国,为国家出一份力!',
|
||||
label: 'C.买国货就是支持实业救国,为国家出一份力!',
|
||||
},
|
||||
{
|
||||
value: 'option4',
|
||||
label: 'D.我只管赚钱,东西好不好不管哦',
|
||||
label: 'D.我只管赚钱,东西好不好不管哦。',
|
||||
},
|
||||
],
|
||||
[]
|
||||
|
||||
Reference in new issue
Block a user