diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..edeafa9 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "mcp__chrome-devtools__click" + ] + } +} diff --git a/src/assets/images/sunzhongshan/__MACOSX/._reflection b/src/assets/images/sunzhongshan/__MACOSX/._reflection deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/._reflection and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/._.DS_Store b/src/assets/images/sunzhongshan/__MACOSX/reflection/._.DS_Store deleted file mode 100644 index a5b28df..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/._.DS_Store and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/._home b/src/assets/images/sunzhongshan/__MACOSX/reflection/._home deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/._home and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/._scene1 b/src/assets/images/sunzhongshan/__MACOSX/reflection/._scene1 deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/._scene1 and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/._scene2 b/src/assets/images/sunzhongshan/__MACOSX/reflection/._scene2 deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/._scene2 and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/._scene3 b/src/assets/images/sunzhongshan/__MACOSX/reflection/._scene3 deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/._scene3 and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/._关闭.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/._关闭.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/._关闭.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/._框.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/._框.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/._框.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/._线索提示.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/._线索提示.png deleted file mode 100644 index c607f24..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/._线索提示.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/._线索簿.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/._线索簿.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/._线索簿.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/._背包.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/._背包.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/._背包.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/._详情线索簿背景.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/._详情线索簿背景.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/._详情线索簿背景.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._场景1.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._场景1.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._场景1.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._场景2.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._场景2.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._场景2.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._场景3.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._场景3.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._场景3.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._按钮.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._按钮.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._按钮.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._背景.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._背景.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/home/._背景.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._bg.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._bg.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._bg.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索1-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索1-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索1-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索1.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索1.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索1.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索2-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索2-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索2-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索2.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索2.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索2.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索3-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索3-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索3-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索3.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索3.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索3.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索4-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索4-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索4-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索4.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索4.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._线索4.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._背包背景.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._背包背景.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene1/._背包背景.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._bg.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._bg.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._bg.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索1-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索1-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索1-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索1.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索1.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索1.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索2-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索2-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索2-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索2.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索2.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索2.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索3.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索3.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索3.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索4-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索4-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索4-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索4.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索4.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索4.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索5-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索5-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索5-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索5.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索5.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._线索5.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._背包背景.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._背包背景.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene2/._背包背景.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._bg.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._bg.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._bg.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._窃窃私语.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._窃窃私语.png deleted file mode 100644 index c607f24..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._窃窃私语.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索1-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索1-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索1-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索1.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索1.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索1.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索2-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索2-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索2-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索2.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索2.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索2.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索3-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索3-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索3-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索3.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索3.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索3.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索4-详情.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索4-详情.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索4-详情.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索4.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索4.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._线索4.png and /dev/null differ diff --git a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._背包背景.png b/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._背包背景.png deleted file mode 100644 index 0e41428..0000000 Binary files a/src/assets/images/sunzhongshan/__MACOSX/reflection/scene3/._背包背景.png and /dev/null differ diff --git a/src/components/layout/ScaledPortal.tsx b/src/components/layout/ScaledPortal.tsx new file mode 100644 index 0000000..1d1979a --- /dev/null +++ b/src/components/layout/ScaledPortal.tsx @@ -0,0 +1,59 @@ +import type { ReactNode } from "react"; +import { useEffect, useState } from "react"; +import { createPortal } from "react-dom"; +import { useScale } from "@/contexts/ScaleContext"; + +export interface ScaledPortalProps { + /** 是否启用(不启用则直接渲染 children) */ + enabled?: boolean; + /** z-index 层级(建议 > ExhibitOverlay 的 9999) */ + zIndex?: number; + /** 子元素 */ + children: ReactNode; +} + +/** + * ScaledPortal + * - 使用 Portal 渲染到 document.body,避免被其他 Portal/遮罩层挡住 + * - 使用 useScale 的 offset/scale 做视觉对齐(与 ExhibitOverlay 一致) + */ +const ScaledPortal = ({ + enabled = true, + zIndex = 10050, + children, +}: ScaledPortalProps) => { + const [mounted, setMounted] = useState(false); + const { scale, offsetX, offsetY, DESIGN_WIDTH, DESIGN_HEIGHT } = useScale(); + + useEffect(() => { + setMounted(true); + }, []); + + if (!enabled) return <>{children}; + if (!mounted) return null; + + const scaled = ( +
+ {children} +
+ ); + + return createPortal(scaled, document.body); +}; + +export default ScaledPortal; + + diff --git a/src/components/layout/index.ts b/src/components/layout/index.ts index ae615e2..193005b 100644 --- a/src/components/layout/index.ts +++ b/src/components/layout/index.ts @@ -1,6 +1,8 @@ export { default as ResidenceLayout } from "./ResidenceLayout"; export { default as ClueOverlay } from "../../pages/chenyun/reflection/clue"; export type { ClueOverlayProps } from "../../pages/chenyun/reflection/clue"; +export { default as ScaledPortal } from "./ScaledPortal"; +export type { ScaledPortalProps } from "./ScaledPortal"; export { default as Alert } from "./alert/title-alert"; export type { AlertProps } from "./alert/title-alert"; export { default as SuccessAlert } from "./alert/success-alert"; diff --git a/src/config/puzzleData.ts b/src/config/puzzleData.ts index cc7f432..3c81ec0 100644 --- a/src/config/puzzleData.ts +++ b/src/config/puzzleData.ts @@ -29,7 +29,7 @@ export const puzzleDataConfig: Record< }, // 碎片2 2: { - title: "陈云就读 青浦县乙种商业学校", + title: "陈云就读 青浦县立乙种商业学校", detailImage: "chenyun/details/2", route: "place2", }, diff --git a/src/pages/chenyun/internalization.tsx b/src/pages/chenyun/internalization.tsx index 3c21111..2005f35 100644 --- a/src/pages/chenyun/internalization.tsx +++ b/src/pages/chenyun/internalization.tsx @@ -7,7 +7,6 @@ import { type VideoRef, type DragFillResult, LetterToChenyun, - StudyGuideModal, EndingOverlay, } from "@/components"; import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils"; @@ -37,11 +36,15 @@ type QuizAnchor = { // 星名固定为 chenyun const starName = "chenyun"; +// 通用提示文案 +const MULTIPLE_DEFAULT_ERROR = + "你的选择还不够全面哦~请你再想想吧!"; + // 锚点数据:可根据实际视频时长调整 time const QUIZ_ANCHORS: QuizAnchor[] = [ { id: "quiz-1", - time: Math.ceil(5.52 * 60), //5.52分钟 + time: 60 * 5 + 52, //5.52分钟 title: "题目1", prompt: "陈云经济思想的核心内容指的是(\xa0\xa0\xa0\xa0)。", type: "multiple", @@ -54,11 +57,11 @@ const QUIZ_ANCHORS: QuizAnchor[] = [ { value: "C", label: "正确的处理好计划与市场的关系", isCorrect: true }, { value: "D", label: "经济建设的目的论", isCorrect: true }, ], - explanation: "鸟笼比喻是正确的处理好计划与市场的关系的一个发展阶段", + explanation: "鸟笼比喻是陈云提出的一个比喻。请你再想想吧!", }, { id: "quiz-2", - time: Math.ceil(10.01 * 60), + time: 60 * 10 + 1, //10.01分钟 title: "题目2", prompt: "陈云说过以下哪些话能着重体现出他的经济思想?", type: "multiple", @@ -76,6 +79,7 @@ const QUIZ_ANCHORS: QuizAnchor[] = [ isCorrect: true, }, ], + explanation: "你的选择还不够全面哦~请你再想想吧!", }, { id: "quiz-3", @@ -95,6 +99,39 @@ const QUIZ_ANCHORS: QuizAnchor[] = [ { id: "blank-3", label: "____" }, ], }, + { + id: "quiz-4", + time: 60 * 5 + 21, // 5:21 + title: "题目4", + prompt: "以下哪些陈云的生活小事值得我们学习?", + type: "multiple", + options: [ + { value: "A", label: "光盘行动,不浪费粮食", isCorrect: true }, + { value: "B", label: "打了 32 个补丁的棉坎肩他穿了 38 年", isCorrect: true }, + { value: "C", label: "2-3 厘米长的铅笔头仍然留用", isCorrect: true }, + { value: "D", label: "搓肥皂时关闭水龙头,节约用水", isCorrect: true }, + ], + explanation: MULTIPLE_DEFAULT_ERROR, + }, + { + id: "quiz-5", + time: 60 * 16 + 23, // 16:23 + title: "题目5", + prompt: "新时代的青年要从陈云身上学习哪些精神品质?", + type: "multiple", + options: [ + { value: "A", label: "谦虚谨慎,实事求是的工作作风", isCorrect: true }, + { value: "B", label: "勤俭朴素,艰苦奋斗的优良作风", isCorrect: true }, + { value: "C", label: "密切联系群众,关心群众生活", isCorrect: true }, + { + value: "D", + label: "只唯上的工作作风", + isCorrect: false, + explanation: "\"只唯上\"的工作作风与陈云同志实事求是的精神相悖。请你再想想吧!", + }, + ], + explanation: MULTIPLE_DEFAULT_ERROR, + }, ]; const ChenyunInternalization = () => { @@ -103,7 +140,6 @@ const ChenyunInternalization = () => { // 获取碎片动画触发方法 const { triggerPuzzleAnimation } = usePuzzleAnimation(); const [activeSuccess, setActiveSuccess] = useState(null); - const [successContent, setSuccessContent] = useState(""); const [resumeCallback, setResumeCallback] = useState<(() => void) | null>( null ); @@ -171,6 +207,7 @@ const ChenyunInternalization = () => { once: true, afterResume: "remove", render: ({ resume }) => { + // 拖拽题:处理选项拖拽与校验 const handleDragChange = (val: DragFillResult[]) => { setDragQuizValues((prev) => ({ ...prev, [item.id]: val })); setDragQuizErrors((prev) => ({ ...prev, [item.id]: "" })); @@ -187,13 +224,12 @@ const ChenyunInternalization = () => { if (isCorrect) { setActiveSuccess(item.id); - setSuccessContent("回答正确,请继续探索。"); setResumeCallback(() => resume); } else { setDragQuizErrors((prev) => ({ ...prev, [item.id]: - "再想一想,不唯上才是陈云的工作作风,不是只唯上!", + "这不是陈云的工作作风。请你再想想吧!", })); } }; @@ -217,10 +253,9 @@ const ChenyunInternalization = () => { { setActiveSuccess(null); - setSuccessContent(""); triggerPuzzleAnimation(16); setDragQuizValues((prev) => { const next = { ...prev }; @@ -261,13 +296,32 @@ const ChenyunInternalization = () => { label: `${opt.value}. ${opt.label}`, }))} correctAnswer={correctAnswers} - errorMessage={ - item.explanation || "再思考一下,注意选择所有正确答案。" - } + errorMessage={(selectedValues) => { + // 题目1:选到“鸟笼比喻(B)”给专属提示;否则未选全给“还不够全面”提示 + if (item.id === "quiz-1") { + if (selectedValues.includes("B")) { + return ( + item.explanation || + "鸟笼比喻是陈云提出的一个比喻。请你再想想吧!" + ); + } + return MULTIPLE_DEFAULT_ERROR; + } + // 题目5:选到“D”给专属提示;否则未选全给“还不够全面”提示 + if (item.id === "quiz-5") { + if (selectedValues.includes("D")) { + const pickedD = item.options.find( + (o) => o.value === "D" + ); + return pickedD?.explanation || MULTIPLE_DEFAULT_ERROR; + } + return MULTIPLE_DEFAULT_ERROR; + } + return item.explanation || MULTIPLE_DEFAULT_ERROR; + }} onSubmit={(values: string[], isCorrect: boolean) => { if (isCorrect) { setActiveSuccess(item.id); - setSuccessContent("回答正确,请继续探索。"); if (item.id === "quiz-1") triggerPuzzleAnimation(15); setResumeCallback(() => resume); } @@ -275,11 +329,10 @@ const ChenyunInternalization = () => { /> { setActiveSuccess(null); - setSuccessContent(""); const cb = resumeCallback; setResumeCallback(null); cb?.(); @@ -302,7 +355,6 @@ const ChenyunInternalization = () => { [ activeSuccess, resumeCallback, - successContent, dragQuizValues, dragQuizErrors, triggerPuzzleAnimation, diff --git a/src/pages/chenyun/place/place2.tsx b/src/pages/chenyun/place/place2.tsx index 7e36d32..797f0f4 100644 --- a/src/pages/chenyun/place/place2.tsx +++ b/src/pages/chenyun/place/place2.tsx @@ -171,13 +171,13 @@ const ChenyunPlace2 = () => { speaker="student" audioSrc={getPublicPath("audio/chenyun/mp3_chenyun_05.mp3")} > - 那陈云是怎么有机会到图中的青浦县乙种商业学校读书的呀? + 那陈云是怎么有机会到图中的青浦县立乙种商业学校读书的呀? - 因为家贫,初小读完的陈云便辍学在家。后在舅母亲戚的资助下,陈云才到青浦县乙种商业学校学习珠算和记账的,他在很短时间里就基本掌握了珠算,还初步学会写账,这为陈云日后从事经济工作打下了基础。 + 因为家贫,初小读完的陈云便辍学在家。后在舅母亲戚的资助下,陈云才到青浦县立乙种商业学校学习珠算和记账的,他在很短时间里就基本掌握了珠算,还初步学会写账,这为陈云日后从事经济工作打下了基础。 陈云在党内以“铁算盘”著称,他在经济工作中能熟练使用算盘,就得益于少年时在青浦县立乙种商业学校的学习。 @@ -198,13 +198,13 @@ const ChenyunPlace2 = () => { speaker="student" audioSrc={getPublicPath("audio/chenyun/mp3_chenyun_07.mp3")} > - 这里有一幅陈云提写的颜安小学,请问陈云在这所学校读过书吗? + 这里有一幅陈云题写的颜安小学,请问陈云在这所学校读过书吗? - 是的,陈云在青浦县乙种商业学校学习了一个多月后,又因经济困难不得不辍学回家。多亏当地知识分子、颜安小学校长杜枢,他免费保荐陈云入颜安小学高小部读书。 + 是的,陈云在青浦县立乙种商业学校学习了一个多月后,又因经济困难不得不辍学回家。多亏当地知识分子、颜安小学校长杜枢,他免费保荐陈云入颜安小学高小部读书。 颜安小学实行的新式教育开阔了陈云的视野,五四运动后,颜安小学师生积极投入反帝爱国运动,为陈云日后加入中国共产党埋下了思想的种子。 diff --git a/src/pages/chenyun/place/place4.tsx b/src/pages/chenyun/place/place4.tsx index 5f4741f..65aab0a 100644 --- a/src/pages/chenyun/place/place4.tsx +++ b/src/pages/chenyun/place/place4.tsx @@ -6,12 +6,15 @@ import { Cursor, GuideSteps, GuideStepsItem, + MultipleChoiceQuizPanel, } from "@/components"; import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils"; import { useNavigate, useParams } from "react-router-dom"; import { useChenyunStore } from "@/store/chenyunStore"; import { usePuzzleAnimation } from "@/contexts/PuzzleAnimationContext"; import { useCursorController } from "@/hooks/useCursorController"; +import SuccessAlert from "@/components/layout/alert/success-alert"; +import ScaledPortal from "@/components/layout/ScaledPortal"; // starName 固定为 chenyun const starName = "chenyun"; @@ -34,8 +37,7 @@ const ChenyunPlace4 = () => { // 使用持久化的访问状态 const place = "place4"; - const { isExhibitVisited, markExhibitVisited, getVisitedExhibits } = - useChenyunStore(); + const { markExhibitVisited, getVisitedExhibits } = useChenyunStore(); // 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码 const visitedExhibits = getVisitedExhibits(place); @@ -52,6 +54,10 @@ const ChenyunPlace4 = () => { const [guideVisible2, setGuideVisible2] = useState(false); const [guideStep2, setGuideStep2] = useState(0); + // 展品2关闭后的题目状态(多选) + const [quiz2Visible, setQuiz2Visible] = useState(false); + const [successAlert2Visible, setSuccessAlert2Visible] = useState(false); + const handleOpenExhibit = (index: 1 | 2) => { if (index === 1) { // 展品1:点击时同时打开展品和引导对话 @@ -148,6 +154,14 @@ const ChenyunPlace4 = () => { } }} onClose={() => { + // 展品2:关闭后先进入答题,答对后再标记已读 + if (activeExhibitId === 2) { + setActiveExhibitId(null); + setQuiz2Visible(true); + return; + } + + // 其他展品:关闭即标记已读 if (activeExhibitId !== null) { markExhibitVisited(place, activeExhibitId); } @@ -155,6 +169,62 @@ const ChenyunPlace4 = () => { }} /> + {/* 展品2的题目 + 成功提示(使用 ScaledPortal 保证层级与缩放) */} + + <> + { + // 选了 C:给出截图中的专属提示 + if (selectedValues.includes("C")) { + return "陈云反对“左”倾错误。请你再想想吧!"; + } + // 没选 C 但仍不正确:通常是“选不全” + return "你的选择还不够全面哦~请你再想想吧!"; + }} + onSubmit={(_values, isCorrect) => { + if (isCorrect) { + setQuiz2Visible(false); + setSuccessAlert2Visible(true); + } + }} + zIndex={10000} + /> + + { + setSuccessAlert2Visible(false); + markExhibitVisited(place, 2); + }} + /> + + + {/* 展品1的引导对话 */} = { 2: "chenyun/place5/场景5展品2", 3: "chenyun/place5/场景5展品3", }; -// 展品描述映射 -const exhibitDescriptionMap: Record<1 | 2 | 3, string> = { - 1: "", - 2: "延安时期因为国民党的封锁,陕甘宁边区经济困难。陈云转战陕甘宁边区主持财经工作,他遇到了很多经济难题。1944年新春伊始,陈云开始担任西北财经办事处副主任,工作转到财经方面,这是陈云以主要精力从事领导经济工作的开始。当时,陕甘宁边区在国民党严密封锁下,财经状况严峻,边区物价猛涨,老百姓日常生活必需的布匹和粮食十分缺乏。国民党顽固派封锁边区,企图“不让一粒粮、一尺布进入边区”,同时千方百计阻止边区食盐出口。", - 3: "", -}; +// 展品描述映射(目前未使用,后续如需在展品弹窗中显示可恢复) +// const exhibitDescriptionMap: Record<1 | 2 | 3, string> = { +// 1: "", +// 2: "延安时期因为国民党的封锁,陕甘宁边区经济困难。陈云转战陕甘宁边区主持财经工作,他遇到了很多经济难题。1944年新春伊始,陈云开始担任西北财经办事处副主任,工作转到财经方面,这是陈云以主要精力从事领导经济工作的开始。当时,陕甘宁边区在国民党严密封锁下,财经状况严峻,边区物价猛涨,老百姓日常生活必需的布匹和粮食十分缺乏。国民党顽固派封锁边区,企图“不让一粒粮、一尺布进入边区”,同时千方百计阻止边区食盐出口。", +// 3: "", +// }; const ChenyunPlace5 = () => { const navigate = useNavigate(); const { moduleName } = useParams<{ @@ -44,8 +47,7 @@ const ChenyunPlace5 = () => { // 使用持久化的访问状态 const place = "place5"; - const { isExhibitVisited, markExhibitVisited, getVisitedExhibits } = - useChenyunStore(); + const { markExhibitVisited, getVisitedExhibits } = useChenyunStore(); // 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码 const visitedExhibits = getVisitedExhibits(place); @@ -56,6 +58,8 @@ const ChenyunPlace5 = () => { visited, }); const [videoVisible3, setVideoVisible3] = useState(false); + const [quiz3Visible, setQuiz3Visible] = useState(false); + const [successAlert3Visible, setSuccessAlert3Visible] = useState(false); // 展品1引导对话 const [guideVisible, setGuideVisible] = useState(false); @@ -182,12 +186,59 @@ const ChenyunPlace5 = () => { showSkipButton onEnded={() => { setVideoVisible3(false); - markExhibitVisited(place, 3); - triggerPuzzleAnimation(8); + setQuiz3Visible(true); }} /> )} + {/* 展品3视频后的题目 + 成功提示(使用 ScaledPortal) */} + + <> + { + if (isCorrect) { + setQuiz3Visible(false); + setSuccessAlert3Visible(true); + } + }} + zIndex={10000} + /> + + { + setSuccessAlert3Visible(false); + markExhibitVisited(place, 3); + triggerPuzzleAnimation(8); + }} + /> + + + {/* 遮罩层 */} = { const QUIZ1_OPTIONS: SingleChoiceOption[] = [ { value: "A", - label: "A. 推动了淮海战役的胜利", + label: "A.推动了淮海战役的胜利", }, { value: "B", - label: "B. 有利于稳定人民的发行和地位", + label: "B.有利于稳定人民币的发行和地位", }, { value: "C", - label: "C. 巩固了新中国人民政权", + label: "C.巩固了新中国人民政权", }, ]; // 题目1各选项对应的错误提示(原来写在选项括号中的内容) const QUIZ1_ERROR_HINTS: Record = { - A: "淮海战役于1949年1月胜利结束,发生在这次“银元之战”前,不存在因果关系。", - C: "1949年6月新中国政权尚未建立。", + A: "淮海战役于1949年1月胜利结束,发生在这次“银元之战”前,不存在因果关系。请你再想想吧!", + C: "1949年6月新中国政权尚未建立。请你再想想吧", }; const ChenyunPlace7 = () => { @@ -169,7 +169,7 @@ const ChenyunPlace7 = () => { // 显示成功提示 setSuccessAlertVisible(true); } else { - setQuiz2Error("再想一想,石油当时不是主要调控对象~"); + setQuiz2Error("再想一想,石油当时不是主要调控对象。请你再想想吧!"); } }; @@ -285,7 +285,7 @@ const ChenyunPlace7 = () => { { // 显示成功提示 setSuccessAlertVisible(true); setSuccessAlertContent( - "“银元之战”属于当时人民政府整顿金融的重要举措,黄金、银元投机不利于人民币币值稳定和流通,因此“银元之战”打击贵金属投机活动,有利于稳定人民币的发行和地位" + "“银元之战”属于当时人民政府整顿金融的重要举措,黄金、银元投机不利于人民币币值稳定和流通,因此“银元之战”打击贵金属投机活动,有利于稳定人民币的发行和地位。" ); }} onWrongSelect={(val) => { @@ -356,8 +356,8 @@ const ChenyunPlace7 = () => { { // 使用持久化的访问状态 const place = "place8"; - const { isExhibitVisited, markExhibitVisited, getVisitedExhibits } = - useChenyunStore(); + const { markExhibitVisited, getVisitedExhibits } = useChenyunStore(); // 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码 const visitedExhibits = getVisitedExhibits(place); @@ -50,12 +53,21 @@ const ChenyunPlace8 = () => { const [guideStep, setGuideStep] = useState(0); const [guideVisible2, setGuideVisible2] = useState(false); const [guideStep2, setGuideStep2] = useState(0); + const [quizVisible, setQuizVisible] = useState(false); + const [successAlertVisible, setSuccessAlertVisible] = useState(false); + const [quizPassed, setQuizPassed] = useState(false); + // 关闭对话后出现的“统购统销”单选题 + const [closeQuizVisible, setCloseQuizVisible] = useState(false); + const [closeSuccessAlertVisible, setCloseSuccessAlertVisible] = + useState(false); + const [closeQuizPassed, setCloseQuizPassed] = useState(false); const handleOpenExhibit = (index: 1 | 2) => { setActiveExhibitId(index); if (index === 1) { setGuideVisible2(true); setGuideStep2(0); + setQuizPassed(false); } // 如果是展品2,显示对话引导 if (index === 2) { @@ -191,9 +203,23 @@ const ChenyunPlace8 = () => { setGuideVisible2(visible)} + onVisibleChange={(visible) => { + setGuideVisible2(visible); + // 关闭对话后,如果还未完成该题,则弹出题目 + if (!visible && !closeQuizPassed) { + setCloseQuizVisible(true); + } + }} currentStep={guideStep2} - onStepChange={(step) => setGuideStep2(step)} + onStepChange={(step) => { + // 在第 3 句(guideStep2 === 2)结束后,进入下一句前先弹题 + if (step === 3 && !quizPassed) { + setGuideStep2(2); + setQuizVisible(true); + return; + } + setGuideStep2(step); + }} showMask={false} highlightSelectors={[]} zIndex={10001} @@ -217,6 +243,77 @@ const ChenyunPlace8 = () => { 第一个五年计划纲要草案的重点是优先发展重工业,特别是156项重点工程。当时陈云重点抓重工业建设和粮食保障,通过“统购统销”稳定粮价,为重点工程项目筑牢根基,亲手铺就了我国工业化的起步之路。 + + {/* “这三个问题…”结束后的题目 + 成功提示(使用 ScaledPortal) */} + + <> + { + if (isCorrect) { + setQuizVisible(false); + setSuccessAlertVisible(true); + } + }} + zIndex={10000} + /> + + { + setSuccessAlertVisible(false); + setQuizPassed(true); + setGuideVisible2(true); + setGuideStep2(3); + }} + /> + + + + {/* 关闭对话后出现的题目(单选) + 成功提示(使用 ScaledPortal) */} + + <> + "这不是当时陈云稳定粮价的方法。请你再想想吧!"} + onSubmit={(_value, isCorrect) => { + if (!isCorrect) return; + setCloseQuizVisible(false); + setCloseSuccessAlertVisible(true); + }} + zIndex={10000} + /> + + { + setCloseSuccessAlertVisible(false); + // setCloseQuizPassed(true); + }} + /> + + ); }; diff --git a/src/pages/sunzhongshan/internalization.tsx b/src/pages/sunzhongshan/internalization.tsx index 034602f..3edb449 100644 --- a/src/pages/sunzhongshan/internalization.tsx +++ b/src/pages/sunzhongshan/internalization.tsx @@ -78,8 +78,114 @@ const QUIZ_ANCHORS: QuizAnchor[] = [ }, { id: "quiz-2", - time: 60 * 5 + 40, + time: 60 * 2, // 2分钟 title: "题目2", + prompt: + "【多选】新时代青年传承“振兴中华”的精神,能够具体落实为( )。", + type: "multiple", + options: [ + { + value: "A", + label: "专注学习,提升自身技能与素养", + isCorrect: true, + explanation: + "恭喜你,选择正确!青年通过专注学习、提升自身能力,用精益求精的精神服务于国家建设,才能成为“振兴中华”的践行者。", + }, + { + value: "B", + label: "秉持精益求精的精神,在专业领域深耕", + isCorrect: true, + explanation: + "恭喜你,选择正确!青年在自己的专业领域深耕细作,才能以扎实的能力为国家发展贡献力量。", + }, + { + value: "C", + label: "遇到困难不退缩,不因失败而灰心", + isCorrect: true, + explanation: + "恭喜你,选择正确!不因失败而灰心,也从不因困难而退缩,这种坚韧的态度是践行“振兴中华”过程中不可或缺的品质。", + }, + { + value: "D", + label: "放弃个人职业规划,直接投身革命事业", + isCorrect: false, + explanation: + "振兴中华要把个人理想融入国家命运,在自己的领域里成为“振兴中华”的践行者。请你再想想吧!", + }, + ], + }, + { + id: "quiz-3", + time: 60 * 4 + 14, // 4分14秒 + title: "题目3", + prompt: "【多选】新时代青年涵养“天下为公”的情怀能够具体落实为:", + type: "multiple", + options: [ + { + value: "A", + label: "做一名志愿者走进养老院服务老人", + isCorrect: true, + explanation: + "恭喜你,选择正确!对他人的关爱与奉献是传承“天下为公”情怀的具体行动哦!", + }, + { + value: "B", + label: "只专注于个人学业与职业发展,不参与公共事务", + isCorrect: false, + explanation: + "这与“天下为公”的情怀完全相悖。请你再想想吧!", + }, + { + value: "C", + label: "用自己所学的技能为企业、社区提供服务", + isCorrect: true, + explanation: + "恭喜你,选择正确!用自己所学的技能为企业、社区提供服务是青年将个人能力转化为公共服务、践行“天下为公”的重要方式。", + }, + { + value: "D", + label: "在日常生活中关心身边有需要的人", + isCorrect: true, + explanation: + "恭喜你,选择正确!“天下为公”的情怀不需要宏大叙事,从身边小事做起就是具体落实。", + }, + ], + explanation: "请再仔细分析一下题目,做出更全面的选择吧!", + }, + { + id: "quiz-4", + time: 60 * 5 + 40, + title: "题目4", + prompt: "【多选】新时代青年学习“与时俱进”的智慧能够具体落实为:", + type: "multiple", + options: [ + { + value: "A", + label: "主动学习新知识,关注科技等前沿领域", + isCorrect: true, + explanation: + "恭喜你,选择正确!主动学习新知识、适应新趋势正是青年与时俱进、紧跟时代步伐的具体表现。", + }, + { + value: "B", + label: "在工作中突破传统思维,用创新方法解决问题", + isCorrect: true, + explanation: + "恭喜你,选择正确!在工作中突破传统思维,用创新解决问题正是与时俱进精神在职业场景中的落地。", + }, + { + value: "C", + label: "固执己见,拒绝接受新事物和新变化", + isCorrect: false, + explanation: "这与“与时俱进”的智慧完全相悖。请你再想想吧!", + }, + ], + explanation: "请再仔细分析一下题目,做出更全面的选择吧!", + }, + { + id: "quiz-5", + time: 60 * 5 + 40, + title: "题目5", prompt: "【多选】新时代的青年要从孙中山先生身上学习哪些精神?", type: "multiple", options: [ @@ -106,7 +212,6 @@ const ChenyunInternalization = () => { const { triggerPuzzleAnimation } = usePuzzleAnimation(); const [activeSuccess, setActiveSuccess] = useState(null); - const [successContent, setSuccessContent] = useState(""); const [resumeCallback, setResumeCallback] = useState<(() => void) | null>( null ); @@ -137,10 +242,11 @@ const ChenyunInternalization = () => { // 组件卸载时清理背景音乐 useEffect(() => { + const audioEl = audioRef.current; return () => { - if (audioRef.current) { - audioRef.current.pause(); - audioRef.current.currentTime = 0; + if (audioEl) { + audioEl.pause(); + audioEl.currentTime = 0; } }; }, []); @@ -195,7 +301,6 @@ const ChenyunInternalization = () => { if (isCorrect) { setActiveSuccess(item.id); - setSuccessContent("回答正确,请继续探索。"); setResumeCallback(() => resume); } else { setDragQuizErrors((prev) => ({ @@ -214,7 +319,6 @@ const ChenyunInternalization = () => { if (isCorrect) { setActiveSuccess(item.id); - setSuccessContent("回答正确,请继续探索。"); setResumeCallback(() => resume); } else { setDragQuizErrors((prev) => ({ @@ -246,10 +350,9 @@ const ChenyunInternalization = () => { { setActiveSuccess(null); - setSuccessContent(""); setDragQuizValues((prev) => { const next = { ...prev }; delete next[item.id]; @@ -291,23 +394,78 @@ const ChenyunInternalization = () => { }))} correctAnswer={correctAnswers} errorMessage={ - item.explanation || "再想一想,确保选择了所有正确答案~" + item.id === "quiz-2" + ? (selectedValues: string[]) => { + const picked = new Set(selectedValues); + const pickedD = item.options.find( + (o) => o.value === "D" + ); + if (picked.has("D")) return pickedD?.explanation || ""; + return "请再仔细分析一下题目,做出更全面的选择吧!"; + } + : item.id === "quiz-3" + ? (selectedValues: string[]) => { + const picked = new Set(selectedValues); + const pickedB = item.options.find( + (o) => o.value === "B" + ); + if (picked.has("B")) return pickedB?.explanation || ""; + return ( + item.explanation || + "请再仔细分析一下题目,做出更全面的选择吧!" + ); + } + : item.id === "quiz-4" + ? (selectedValues: string[]) => { + const picked = new Set(selectedValues); + const pickedC = item.options.find( + (o) => o.value === "C" + ); + if (picked.has("C")) return pickedC?.explanation || ""; + return ( + item.explanation || + "请再仔细分析一下题目,做出更全面的选择吧!" + ); + } + : item.id === "quiz-5" + ? (selectedValues: string[]) => { + const picked = new Set(selectedValues); + const pickedC = item.options.find( + (o) => o.value === "C" + ); + if (picked.has("C")) return pickedC?.explanation || ""; + return ( + item.explanation || + "请再仔细分析一下题目,做出更全面的选择吧!" + ); + } + : item.explanation || + "再想一想,确保选择了所有正确答案~" } onSubmit={(values: string[], isCorrect: boolean) => { if (isCorrect) { setActiveSuccess(item.id); - setSuccessContent("回答正确,请继续探索。"); + // if (item.id === "quiz-2") { + // const picked = new Set(values); + // const content = item.options + // .filter((o) => picked.has(o.value)) + // .map((o) => o.explanation) + // .filter(Boolean) + // .join("\n"); + // setSuccessContent(content || "回答正确,请继续探索。"); + // } else { + // setSuccessContent("回答正确,请继续探索。"); + // } setResumeCallback(() => resume); } }} /> { setActiveSuccess(null); - setSuccessContent(""); const cb = resumeCallback; setResumeCallback(null); triggerPuzzleAnimation(16); @@ -331,7 +489,6 @@ const ChenyunInternalization = () => { [ activeSuccess, resumeCallback, - successContent, dragQuizValues, dragQuizErrors, triggerPuzzleAnimation,