diff --git a/src/components/layout/quiz/SingleChoiceQuizPanel.tsx b/src/components/layout/quiz/SingleChoiceQuizPanel.tsx
index 8b69f19..50459d7 100644
--- a/src/components/layout/quiz/SingleChoiceQuizPanel.tsx
+++ b/src/components/layout/quiz/SingleChoiceQuizPanel.tsx
@@ -36,6 +36,7 @@ const SingleChoiceQuizPanel = ({
correctAnswer,
errorMessage,
onSubmit,
+ onWrongSelect,
onError,
disabled = false,
}: SingleChoiceQuizPanelProps) => {
@@ -65,7 +66,9 @@ const SingleChoiceQuizPanel = ({
} else {
// 答题失败:清空状态,执行 onError,允许重新选择
setSelectedValue("");
- const errorMsg = errorMessage || "很抱歉,你选错了!请你再思考一下吧!";
+ const customError = onWrongSelect?.(value);
+ const errorMsg =
+ customError || errorMessage || "很抱歉,你选错了!请你再思考一下吧!";
setError(errorMsg);
onError?.(errorMsg);
}
diff --git a/src/components/layout/quiz/types.ts b/src/components/layout/quiz/types.ts
index 7a308ec..32d22cc 100644
--- a/src/components/layout/quiz/types.ts
+++ b/src/components/layout/quiz/types.ts
@@ -45,6 +45,8 @@ export interface SingleChoiceQuizPanelProps {
errorMessage?: string;
/** 提交回调,返回用户选择和是否正确 */
onSubmit?: (value: string, isCorrect: boolean) => void;
+ /** 答错时回调,可返回自定义解析文案覆盖默认 errorMessage */
+ onWrongSelect?: (value: string) => string | void;
/** 错误回调(可选) */
onError?: (error: string) => void;
/** 是否禁用 */
diff --git a/src/pages/chenyun/internalization.tsx b/src/pages/chenyun/internalization.tsx
index aecf87d..08f34ff 100644
--- a/src/pages/chenyun/internalization.tsx
+++ b/src/pages/chenyun/internalization.tsx
@@ -30,6 +30,7 @@ type QuizAnchor = {
options: QuizOption[];
// 拖拽题专用
blanks?: { id: string; label: string }[];
+ explanation?: string;
};
// 星名固定为 chenyun
@@ -48,11 +49,11 @@ const QUIZ_ANCHORS: QuizAnchor[] = [
{
value: "B",
label: "鸟笼比喻",
- explanation: "鸟笼比喻是正确的处理好计划与市场的关系的一个发展阶段",
},
{ value: "C", label: "正确的处理好计划与市场的关系", isCorrect: true },
{ value: "D", label: "经济建设的目的论", isCorrect: true },
],
+ explanation: "鸟笼比喻是正确的处理好计划与市场的关系的一个发展阶段",
},
{
id: "quiz-2",
@@ -234,7 +235,9 @@ const ChenyunInternalization = () => {
label: `${opt.value}. ${opt.label}`,
}))}
correctAnswer={correctAnswers}
- errorMessage="再思考一下,注意选择所有正确答案。"
+ errorMessage={
+ item.explanation || "再思考一下,注意选择所有正确答案。"
+ }
onSubmit={(values: string[], isCorrect: boolean) => {
if (isCorrect) {
setActiveSuccess(item.id);
diff --git a/src/pages/huangyanpei/internalization.tsx b/src/pages/huangyanpei/internalization.tsx
index 76f526e..8a7ced9 100644
--- a/src/pages/huangyanpei/internalization.tsx
+++ b/src/pages/huangyanpei/internalization.tsx
@@ -29,6 +29,7 @@ type QuizAnchor = {
type: "single" | "multiple" | "drag";
options: QuizOption[];
blanks?: { id: string; label: string }[];
+ explanation?: string;
};
const starName = "huangyanpei";
@@ -47,12 +48,12 @@ const QUIZ_ANCHORS: QuizAnchor[] = [
{
value: "B",
label: "自动控制",
- explanation:
- "这一工程理论框架成形于20世纪中叶,70年代实现集中控制,新中国成立前,没有相关学科基础、设备与技术条件",
},
{ value: "C", label: "木工", isCorrect: true },
{ value: "D", label: "纽扣科", isCorrect: true },
],
+ explanation:
+ "自动控制”这一工程理论框架成形于20世纪中叶,70年代实现集中控制,在民国时期,没有相关学科基础、设备与技术条件",
},
{
id: "quiz-2",
@@ -75,7 +76,6 @@ const QUIZ_ANCHORS: QuizAnchor[] = [
{
value: "C",
label: "学习四书五经、八股文",
- explanation: "黄炎培认为教育不能只谈书本、脱离实际",
},
{
value: "D",
@@ -83,6 +83,7 @@ const QUIZ_ANCHORS: QuizAnchor[] = [
isCorrect: true,
},
],
+ explanation: "黄炎培认为教育不能只谈书本、脱离实际",
},
{
id: "quiz-3",
@@ -125,10 +126,10 @@ const QUIZ_ANCHORS: QuizAnchor[] = [
{
value: "D",
label: "美术设计与制作→影像与影视技术",
- explanation:
- "两个专业为不同技能方向的学习,前者为平面绘画等,后者以视频为主",
},
],
+ explanation:
+ "美术设计与制作专业侧重于平面设计,而影像与影视技术专业侧重于影视特效后期,两者的专业核心课程涉及的软硬件也有差异",
},
];
@@ -286,7 +287,9 @@ const HuangyanpeiInternalization = () => {
label: `${opt.value}. ${opt.label}`,
}))}
correctAnswer={correctAnswers}
- errorMessage="再思考一下,注意选择所有正确答案。"
+ errorMessage={
+ item.explanation || "再思考一下,注意选择所有正确答案。"
+ }
onSubmit={(values: string[], isCorrect: boolean) => {
if (isCorrect) {
setActiveSuccess(item.id);
diff --git a/src/pages/huangyanpei/place/place3.tsx b/src/pages/huangyanpei/place/place3.tsx
index 6b958c4..c8dc724 100644
--- a/src/pages/huangyanpei/place/place3.tsx
+++ b/src/pages/huangyanpei/place/place3.tsx
@@ -9,6 +9,7 @@ import {
GuideStepsItem,
SingleChoiceQuizPanel,
} from "@/components";
+import { SuccessAlert } from "@/components/layout";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { useNavigate, useParams } from "react-router-dom";
@@ -38,6 +39,8 @@ const HuangyanpeiPlace3 = () => {
const [guide3Step, setGuide3Step] = useState(0);
// 光标3的题目显示状态
const [showQuiz3, setShowQuiz3] = useState(false);
+ // 成功提示状态
+ const [showSuccessAlert, setShowSuccessAlert] = useState(false);
// 光标1、2的引导对话状态(在两者都浏览后显示)
const [guide12Visible, setGuide12Visible] = useState(false);
@@ -69,7 +72,7 @@ const HuangyanpeiPlace3 = () => {
const handleQuiz3Submit = (value: string, isCorrect: boolean) => {
if (isCorrect) {
setShowQuiz3(false);
- setVisited((prev) => prev.add(3));
+ setShowSuccessAlert(true);
}
};
@@ -212,6 +215,22 @@ const HuangyanpeiPlace3 = () => {
onSubmit={handleQuiz3Submit}
/>
+ {/* 成功提示 */}
+ {
+ setShowSuccessAlert(false);
+ setVisited((prev) => {
+ const next = new Set(prev);
+ next.add(3);
+ return next;
+ });
+ }}
+ onClose={() => {
+ setShowSuccessAlert(false);
+ }}
+ />
+
{/* 光标1、2的引导对话 */}
{
const [showVideo, setShowVideo] = useState(false);
// 题目显示状态
const [showQuiz, setShowQuiz] = useState(false);
+ // 成功提示状态
+ const [showSuccessAlert, setShowSuccessAlert] = useState(false);
const videoRef = useRef(null);
const handleOpenExhibit = (index: 1 | 2 | 3 | 4 | 5) => {
@@ -62,6 +65,7 @@ const HuangyanpeiPlace4 = () => {
const handleQuizSubmit = (value: string, isCorrect: boolean) => {
if (isCorrect) {
setShowQuiz(false);
+ setShowSuccessAlert(true);
}
};
@@ -225,6 +229,17 @@ const HuangyanpeiPlace4 = () => {
zIndex={10001}
/>
+ {/* 成功提示 */}
+ {
+ setShowSuccessAlert(false);
+ }}
+ onClose={() => {
+ setShowSuccessAlert(false);
+ }}
+ />
+
{/* 学习引导(进入场景即显示,与 place1 保持一致) */}
{
新中国成立后,黄炎培先生还继续从事教育工作吗?我们现在的中职教育体系,是不是也有他当年的理念呀?
- 当然啦!1949年10月19日,71岁的黄炎培出任政务院副总理兼轻工业部部长,依然没有放下他毕生追求的职业教育事业。他把中华职教社的骨干力量全部调到北京,亲自起草《关于改革学制的决定》,第一次把"中等专业学校"正式纳入国家学制,让职业教育有了明确的国家定位。
+ 当然啦!1949年10月19日,71岁的黄炎培出任政务院副总理兼轻工业部部长,依然没有放下他毕生追求的职业教育事业。他把中华职教社的骨干力量全部调到北京,亲自起草《关于改革学制的决定》...
- 1952年,他又推动建立了上海机械学校、南京航空学校等五所中专,这些学校为国家"一五"计划输送了6.3万名技术工人,为工业发展提供了坚实的人才支撑。
+ 第一次把"中等专业学校"正式纳入国家学制,让职业教育有了明确的国家定位。1952年,他又推动建立了上海机械学校、南京航空学校等五所中专,这些学校为国家"一五"计划输送了6.3万名技术工人,为工业发展提供了坚实的人才支撑。
黄炎培先生曾说过:"我要把最后一滴血,滴在职业教育的讲台上。"他用一生践行了这句话,而我们现在中职教育强调的"工学结合、德技并修",正是对他"手脑并用、注重实践"理念的传承和发展。
diff --git a/src/pages/luxun/internalization.tsx b/src/pages/luxun/internalization.tsx
index 9035c60..c22d1de 100644
--- a/src/pages/luxun/internalization.tsx
+++ b/src/pages/luxun/internalization.tsx
@@ -30,6 +30,7 @@ type QuizAnchor = {
prompt: string;
type: "single" | "multiple";
options: QuizOption[];
+ explanation?: string;
};
const starName = "luxun";
@@ -62,9 +63,10 @@ const QUIZ_ANCHORS: QuizAnchor[] = [
{
value: "D",
label: "让青年都去参军",
- explanation: "鲁迅先生还是在文学和思想领域培养青年一代,传递思想的火苗",
},
],
+ explanation:
+ "虽然鲁迅热爱革命,但他主要通过文学社团、木刻艺术和纪念文章来教育和启发青年,而不是通过参军来培养青年。",
},
{
id: "quiz-3",
@@ -189,7 +191,9 @@ const LuxunInternalization = () => {
label: `${opt.value}. ${opt.label}`,
}))}
correctAnswer={correctAnswers}
- errorMessage="再思考一下,注意选择所有正确答案。"
+ errorMessage={
+ item.explanation || "再思考一下,注意选择所有正确答案。"
+ }
onSubmit={(values: string[], isCorrect: boolean) => {
if (isCorrect) {
setActiveSuccess(item.id);
diff --git a/src/pages/luxun/place/place1.tsx b/src/pages/luxun/place/place1.tsx
index f54c76f..9fb8a6e 100644
--- a/src/pages/luxun/place/place1.tsx
+++ b/src/pages/luxun/place/place1.tsx
@@ -5,10 +5,11 @@ import {
Image,
Title,
Cursor,
+ StudyGuideModal,
} from "@/components";
import { useNavigate, useParams } from "react-router-dom";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
-import { useState } from "react";
+import { useState, useEffect } from "react";
// starName 固定为 luxun
const starName = "luxun";
@@ -25,8 +26,18 @@ const LuxunPlace1 = () => {
moduleName: string;
}>();
+ // 学习引导弹窗
+ const [showGuideModal, setShowGuideModal] = useState(true);
+ // 对话结束后的学习引导弹窗
+ const [showAfterDialogGuide, setShowAfterDialogGuide] = useState(false);
+ const [afterDialogGuideShown, setAfterDialogGuideShown] = useState(false);
// 引导对话显示状态
- const [visible, setVisible] = useState(true);
+ const [visible, setVisible] = useState(false);
+ // 浏览完展品后的对话显示状态
+ const [showAfterExhibitDialog, setShowAfterExhibitDialog] = useState(false);
+ const [afterExhibitDialogShown, setAfterExhibitDialogShown] = useState(false);
+ // 对话结束后是否显示前进按钮
+ const [showForwardButton, setShowForwardButton] = useState(false);
// 当前激活的展品 ID(只存 ID,不直接存路径,便于作为唯一标识)
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | null>(null);
// 已浏览展品的 ID 集合
@@ -39,6 +50,14 @@ const LuxunPlace1 = () => {
const progressCount = visited.size;
const total = 2;
const completed = progressCount >= total;
+
+ // 当两个展品都浏览完成后,自动显示对话
+ useEffect(() => {
+ if (completed && !afterExhibitDialogShown) {
+ setShowAfterExhibitDialog(true);
+ setAfterExhibitDialogShown(true);
+ }
+ }, [completed, afterExhibitDialogShown]);
const subtitle = completed
? "探索鲁迅故居一楼客厅(已完成)"
: `探索鲁迅故居一楼客厅(${progressCount}/${total})`;
@@ -85,8 +104,8 @@ const LuxunPlace1 = () => {
read={visited.has(2)}
/>
- {/* 继续前进 - 仅在完成所有展品后显示 */}
- {completed && (
+ {/* 继续前进 - 仅在完成所有展品且对话结束后显示 */}
+ {showForwardButton && (
{
{
if (activeExhibitId !== null) {
setVisited((prev) => {
@@ -113,19 +137,81 @@ const LuxunPlace1 = () => {
}}
/>
+ {/* 学习引导词 */}
+ {
+ setShowGuideModal(false);
+ setVisible(true);
+ }}
+ >
+ {`同学你好,欢迎来到走进故居-对话学习模块!
+进入鲁迅故居后,我们一起来了解鲁迅先生经历的5个阶段:"风雨前夕的思想淬炼"->"杂文集里的匕首与投枪"->"历史新编中的现实呐喊"->"为亡友编纂的精神丰碑"->"6平方米的红色庇护所";`}
+
+
{/* 引导 */}
{
+ setVisible(nextVisible);
+ if (!nextVisible && !afterDialogGuideShown) {
+ setAfterDialogGuideShown(true);
+ setShowAfterDialogGuide(true);
+ }
+ }}
showMask={true}
highlightSelectors={[]}
>
-
- 同学你好,欢迎来到走进故居-对话学习模块!
- 进入鲁迅故居纪念馆后,我们一起来了解鲁迅先生的思想历程和文学成就。
+
+ 小沪!我知道鲁迅是1933年搬到大陆新村9号的,那在这之前他为什么会选择来到上海,又有哪些思想转变呢?
- 在这个场景中,我们将探索鲁迅故居一楼客厅,了解风雨前夕的思想淬炼。
+ 你问得很关键!而1927年是鲁迅思想的重要转折点,这一年他在广州亲历了"四一二"反革命政变,亲眼看见进步青年和革命志士倒在血泊中,曾经对"温和改革"的幻想彻底破灭。
+
+
+ 此前他写下《狂人日记》《阿Q正传》批判封建礼教,参加语丝社针砭时弊,但始终在"彷徨"中摸索救国之路。广州的血雨腥风,让他认清了反动派的真面目。
+
+
+
+ {/* 对话结束后的学习引导 */}
+ setShowAfterDialogGuide(false)}
+ ailgn="left"
+ >
+ 1、场景中出现动态光点的展品表示未浏览,可通过点击光点来了解展品背后的故事,通过滚轮可以放大展品,浏览展品后蓝色动态光点会变为已浏览状态;
+ {"\n"}
+ 2、在学习过程中会掉落拼图碎片,拼图碎片会自动收入拼图盒中,拼图盒可以点开查看当前拼图收集情况,点击单个拼图可以回到获得拼图的场景再次学习;
+ {"\n"}
+ 3、所有动态光点的展品全部学习后才能进入下一个场景学习;
+
+
+ {/* 浏览完展品后的对话 */}
+ {
+ setShowAfterExhibitDialog(nextVisible);
+ if (!nextVisible) {
+ setShowForwardButton(true);
+ }
+ }}
+ showMask={true}
+ highlightSelectors={[]}
+ >
+
+ 刚到上海的鲁迅住在哪里,又在做什么?
+
+
+ 1927年10月,鲁迅带着许广平来到上海,先住进了虹口区横浜路景云里。在景云里的两年半,他结识了冯雪峰等共产党员,参与发起中国左翼作家联盟,从"独战的英雄"转变为革命文艺战线的旗手。
+
+
+ 这段时间他写下《三闲集》《二心集》,表达他在当时历史条件下针对社会现象、文化问题和论战的看法与社会批判,此间还翻译了大量马列文艺理论著作,为此后的战斗打下了坚实思想基础。
+
+
+ 鲁迅在发起成立左联后,为躲避国民党当局监视,1930年5月,与许广平、周海婴迁居到拉摩斯公寓。1933年4月,受战火纷扰影响,为安全及健康计,同时也为了更方便开展革命文化工作,鲁迅以"内山书店职员"的名义,搬到了大陆新村9号。
+
+
+ 这个过程的确十分曲折。
diff --git a/src/pages/luxun/place/place2.tsx b/src/pages/luxun/place/place2.tsx
index 9848a2d..270002d 100644
--- a/src/pages/luxun/place/place2.tsx
+++ b/src/pages/luxun/place/place2.tsx
@@ -1,4 +1,12 @@
-import { ExhibitOverlay, Image, Title, Cursor } from "@/components";
+import {
+ ExhibitOverlay,
+ Image,
+ Title,
+ Cursor,
+ StudyGuideModal,
+ GuideSteps,
+ GuideStepsItem,
+} from "@/components";
import { useNavigate, useParams } from "react-router-dom";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { useState } from "react";
@@ -19,13 +27,57 @@ const LuxunPlace2 = () => {
moduleName: string;
}>();
+ // 学习引导弹窗
+ const [showGuideModal, setShowGuideModal] = useState(true);
+
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | 3 | null>(
null
);
const [visited, setVisited] = useState
>(new Set());
+ // 光标1的引导对话状态
+ const [guide1Visible, setGuide1Visible] = useState(false);
+ const [guide1Step, setGuide1Step] = useState(0);
+
+ // 光标2的引导对话状态
+ const [guide2Visible, setGuide2Visible] = useState(false);
+ const [guide2Step, setGuide2Step] = useState(0);
+
const handleOpenExhibit = (index: 1 | 2 | 3) => {
- setActiveExhibitId(index);
+ if (index === 1) {
+ // 光标1:点击时同时打开展品和引导对话
+ setActiveExhibitId(1);
+ setGuide1Visible(true);
+ setGuide1Step(0);
+ } else if (index === 2) {
+ // 光标2:点击时同时打开展品和引导对话
+ setActiveExhibitId(2);
+ setGuide2Visible(true);
+ setGuide2Step(0);
+ } else {
+ // 光标3:直接打开
+ setActiveExhibitId(index);
+ }
+ };
+
+ // 监听光标1的引导对话步骤变化
+ const handleGuide1StepChange = (step: number) => {
+ setGuide1Step(step);
+ };
+
+ // 监听光标1的引导对话关闭
+ const handleGuide1VisibleChange = (visible: boolean) => {
+ setGuide1Visible(visible);
+ };
+
+ // 监听光标2的引导对话步骤变化
+ const handleGuide2StepChange = (step: number) => {
+ setGuide2Step(step);
+ };
+
+ // 监听光标2的引导对话关闭
+ const handleGuide2VisibleChange = (visible: boolean) => {
+ setGuide2Visible(visible);
};
const progressCount = visited.size;
@@ -63,19 +115,21 @@ const LuxunPlace2 = () => {
}}
/>
- {/* 展品 */}
+ {/* 光标1 */}
handleOpenExhibit(1)}
read={visited.has(1)}
/>
+ {/* 光标2 */}
handleOpenExhibit(2)}
read={visited.has(2)}
/>
+ {/* 光标3 */}
{
{
if (activeExhibitId !== null) {
setVisited((prev) => {
@@ -108,6 +167,58 @@ const LuxunPlace2 = () => {
setActiveExhibitId(null);
}}
/>
+
+ {/* 光标1的引导对话 */}
+
+
+ 小沪,这里的《且介亭杂文》就是鲁迅先生在大陆新村9号创作的吧,有人将先生的杂文评价为"匕首与投枪",《且介亭杂文》中有具体体现吗?
+
+
+ 当然有!20世纪30年代,国民党"文化围剿"愈烈,进步思想遭严控,鲁迅在大陆新村9号创作出《且介亭杂文》等集子。这些杂文聚焦文化乱象与社会沉疴,篇幅精炼却直击要害,像匕首般刺穿虚伪表象,似投枪般直戳问题核心...
+
+
+ 用锋利文字与黑暗势力对峙,成为那个时代最具战斗力的思想武器,"匕首与投枪"的赞誉恰是其战斗性的生动写照。
+
+
+
+ {/* 光标2的引导对话 */}
+
+
+ 那《南腔北调集》这个名字又有什么深意呢?
+
+
+ 这是鲁迅的自嘲,他曾说"我不会说绵软的苏白,不会打响亮的京腔,不入调,不入流",所以戏称自己的文章是"南腔北调"。这本书里的《〈守常全集》题记》,赞美了李大钊同志,说"他的遗文却将永驻,因为这是先驱者的遗产,革命史上的丰碑。"
+
+
+
+ {/* 学习引导 */}
+ setShowGuideModal(false)}
+ ailgn="left"
+ >
+ 1、场景中出现动态光点的展品表示未浏览,可通过点击光点来了解展品背后的故事,通过滚轮可以放大展品,浏览展品后蓝色动态光点会变为已浏览状态;
+ {"\n"}
+ 2、在学习过程中会掉落拼图碎片,拼图碎片会自动收入拼图盒中,拼图盒可以点开查看当前拼图收集情况,点击单个拼图可以回到获得拼图的场景再次学习;
+ {"\n"}
+ 3、所有动态光点的展品全部学习后才能进入下一个场景学习;
+
);
};
diff --git a/src/pages/luxun/place/place3.tsx b/src/pages/luxun/place/place3.tsx
index 39ba363..b3b41c8 100644
--- a/src/pages/luxun/place/place3.tsx
+++ b/src/pages/luxun/place/place3.tsx
@@ -1,7 +1,17 @@
-import { ExhibitOverlay, Image, Title, Cursor } from "@/components";
+import {
+ ExhibitOverlay,
+ Image,
+ Title,
+ Cursor,
+ StudyGuideModal,
+ GuideSteps,
+ GuideStepsItem,
+ SingleChoiceQuizPanel,
+} from "@/components";
import { useNavigate, useParams } from "react-router-dom";
import { getBackgroundImage, getStaticImage } from "@/lib/utils";
import { useState } from "react";
+import SuccessAlert from "@/components/layout/alert/success-alert";
// starName 固定为 luxun
const starName = "luxun";
@@ -15,19 +25,84 @@ const exhibitSrcMap: Record<1 | 2 | 3 | 4 | 5, string> = {
5: "luxun/place3/10",
};
+const quiz2DefaultError = "再想一想,注意题干中的时间和提示信息哦~";
+const quiz2Explanations: Record