From 1dd8afcce7eb5419eb30cdda78ecd8b894279a4f Mon Sep 17 00:00:00 2001 From: join_zhong Date: Mon, 2 Feb 2026 00:03:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E6=95=8F=E6=84=9F?= =?UTF-8?q?=E8=AF=8D=E5=A4=84=E7=90=86=E5=92=8C=E6=9B=B4=E6=96=B0=E6=8B=96?= =?UTF-8?q?=E6=8B=BD=E9=A2=98=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=8F=90=E5=8D=87?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 清理敏感词处理函数中的多余空行,提升代码整洁性。 - 在 HuangyanpeiPlace3 组件中引入拖拽题功能,增强互动性。 - 添加拖拽题的成功和错误提示逻辑,优化用户填写体验。 - 使用 useMemo 优化已访问展品的状态管理,提升性能。 --- src/lib/utils/sensitiveWords.ts | 19 ++- src/pages/huangyanpei/place/place3.tsx | 164 +++++++++++++++++++++---- 2 files changed, 150 insertions(+), 33 deletions(-) diff --git a/src/lib/utils/sensitiveWords.ts b/src/lib/utils/sensitiveWords.ts index feeebd8..910197f 100644 --- a/src/lib/utils/sensitiveWords.ts +++ b/src/lib/utils/sensitiveWords.ts @@ -19,10 +19,10 @@ const SENSITIVE_WORDS = [ */ export function containsSensitiveWord(text: string): boolean { if (!text) return false; - + const normalizedText = text.toLowerCase().trim(); - - return SENSITIVE_WORDS.some((word) => + + return SENSITIVE_WORDS.some((word) => normalizedText.includes(word.toLowerCase()) ); } @@ -34,14 +34,14 @@ export function containsSensitiveWord(text: string): boolean { */ export function filterSensitiveWords(text: string): string { if (!text) return text; - + let result = text; - + SENSITIVE_WORDS.forEach((word) => { const regex = new RegExp(word, "gi"); result = result.replace(regex, "*".repeat(word.length)); }); - + return result; } @@ -52,10 +52,9 @@ export function filterSensitiveWords(text: string): string { */ export function filterInput(input: string): string { if (!input) return input; - + // 先进行敏感词过滤 - let filtered = filterSensitiveWords(input); - + const filtered = filterSensitiveWords(input); + return filtered; } - diff --git a/src/pages/huangyanpei/place/place3.tsx b/src/pages/huangyanpei/place/place3.tsx index 63de4df..6c2b21e 100644 --- a/src/pages/huangyanpei/place/place3.tsx +++ b/src/pages/huangyanpei/place/place3.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, useMemo } from "react"; import { ExhibitOverlay, Title, @@ -7,8 +7,9 @@ import { GuideSteps, GuideStepsItem, SingleChoiceQuizPanel, + type DragFillResult, } from "@/components"; -import { SuccessAlert } from "@/components/layout"; +import { SuccessAlert, DragQuizPanel } from "@/components/layout"; import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils"; import { useNavigate, useParams } from "react-router-dom"; import { useCursorController } from "@/hooks/useCursorController"; @@ -16,6 +17,7 @@ import { usePuzzleAnimation } from "@/contexts/PuzzleAnimationContext"; import { useHuangyanpeiStore } from "@/store/huangyanpeiStore"; const starName = "huangyanpei"; +const place = "place3"; const exhibitSrcMap: Record<1 | 2 | 3, string> = { 1: "huangyanpei/place3/5_005", @@ -23,6 +25,31 @@ const exhibitSrcMap: Record<1 | 2 | 3, string> = { 3: "huangyanpei/place3/5_007", }; +// 常量定义 +const FIRST_QUIZ_SUCCESS_CONTENT = + '1903年,黄炎培将"观澜书院"改制为"川沙小学堂",自任校长,率先打破传统经学模式,在课程表中首次列入算术、格致(近代自然科学)、体操和乡土调查四门新学科,并实行"半日授课、半日实习"的教学方式,开创了近代中国小学融入科学、体育与实践教育的新风。'; + +const DRAG_QUIZ_SUCCESS_CONTENT = "恭喜你,选择正确!"; + +const DRAG_QUIZ_ERROR_MESSAGE = + "艺术不是黄炎培先生在川沙小学堂办学期间强调的重点。请你再想想吧!"; + +const DRAG_QUIZ_OPTIONS = [ + { value: "读书", label: "读书" }, + { value: "做手工", label: "做手工" }, + { value: "学艺术", label: "学艺术" }, +]; + +const DRAG_QUIZ_BLANKS = [ + { id: "blank-1", label: "" }, + { id: "blank-2", label: "" }, +]; + +const CORRECT_ANSWERS = { + blank1: "读书", + blank2: "做手工", +} as const; + const HuangyanpeiPlace3 = () => { const navigate = useNavigate(); const { moduleName } = useParams<{ @@ -36,12 +63,13 @@ const HuangyanpeiPlace3 = () => { ); // 使用持久化的访问状态 - const place = "place3"; const { markExhibitVisited, getVisitedExhibits } = useHuangyanpeiStore(); - // 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码 + // 从 store 获取已访问的展品 const visitedExhibits = getVisitedExhibits(place); - const visited = new Set(visitedExhibits); + + // 使用 useMemo 优化 visited Set,避免每次渲染都重新创建 + const visited = useMemo(() => new Set(visitedExhibits), [visitedExhibits]); const { isEnabled } = useCursorController({ initialEnabled: [3], @@ -54,8 +82,16 @@ const HuangyanpeiPlace3 = () => { const [guide3Step, setGuide3Step] = useState(0); // 光标3的题目显示状态 const [showQuiz3, setShowQuiz3] = useState(false); + // 拖拽题显示状态 + const [showDragQuiz, setShowDragQuiz] = useState(false); + // 拖拽题的值 + const [dragQuizValue, setDragQuizValue] = useState([]); + // 拖拽题错误提示 + const [dragQuizError, setDragQuizError] = useState(""); // 成功提示状态 const [showSuccessAlert, setShowSuccessAlert] = useState(false); + // 成功提示内容 + const [successAlertContent, setSuccessAlertContent] = useState(""); // 光标1、2的引导对话状态(在两者都浏览后显示) const [guide12Visible, setGuide12Visible] = useState(false); @@ -91,6 +127,58 @@ const HuangyanpeiPlace3 = () => { } }; + // 处理第一题成功提示继续 + const handleFirstQuizSuccessContinue = () => { + setShowSuccessAlert(false); + // 显示拖拽题 + setShowDragQuiz(true); + }; + + // 处理拖拽题变化 + const handleDragQuizChange = (value: DragFillResult[]) => { + setDragQuizValue(value); + setDragQuizError(""); + + // 检查是否有错误选项(学艺术)- 立即检查并显示错误 + const hasInvalidOption = value.some((v) => v.optionValue === "学艺术"); + if (hasInvalidOption) { + setDragQuizError(DRAG_QUIZ_ERROR_MESSAGE); + return; + } + + // 检查是否所有空白都已填充 + if (value.length < 2) return; + + // 验证答案:第一个空白应该是"读书",第二个空白应该是"做手工" + const blank1 = value.find((v) => v.blankId === "blank-1"); + const blank2 = value.find((v) => v.blankId === "blank-2"); + + // 验证正确答案 + const isCorrect = + blank1?.optionValue === CORRECT_ANSWERS.blank1 && + blank2?.optionValue === CORRECT_ANSWERS.blank2; + + if (isCorrect) { + // 答题成功 + setShowDragQuiz(false); + setSuccessAlertContent(DRAG_QUIZ_SUCCESS_CONTENT); + setShowSuccessAlert(true); + } else { + // 答题失败 + setDragQuizError("请再想想,确保选择正确的答案。"); + } + }; + + // 处理拖拽题成功提示继续 + const handleDragQuizSuccessContinue = () => { + setShowSuccessAlert(false); + setSuccessAlertContent(""); + setDragQuizValue([]); + setDragQuizError(""); + // 标记展品已访问 + markExhibitVisited(place, 3); + }; + // 监听visited变化,当光标1、2都浏览后显示引导对话 useEffect(() => { if (visited.has(1) && visited.has(2) && !guide12Shown) { @@ -171,14 +259,13 @@ const HuangyanpeiPlace3 = () => { visible={activeExhibitId !== null} src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ""} onOpen={() => { - if (activeExhibitId === 3) { - triggerPuzzleAnimation(3); - } - if (activeExhibitId === 2) { - triggerPuzzleAnimation(4); - } - if (activeExhibitId === 1) { - triggerPuzzleAnimation(5); + const animationMap: Record<1 | 2 | 3, number> = { + 3: 3, + 2: 4, + 1: 5, + }; + if (activeExhibitId) { + triggerPuzzleAnimation(animationMap[activeExhibitId]); } }} onClose={() => { @@ -239,20 +326,51 @@ const HuangyanpeiPlace3 = () => { { value: "D", label: "D. 绘画、声乐、舞蹈、戏剧" }, ]} correctAnswer="B" - errorMessage="1903年,黄炎培将“观澜书院”改制为“川沙小学堂”,自任校长,率先打破传统经学模式,在课程表中首次列入算术、格致(近代自然科学)、体操和乡土调查四门新学科,并实行“半日授课、半日实习”的教学方式,开创了近代中国小学融入科学、体育与实践教育的新风。" + errorMessage='1903年,黄炎培将"观澜书院"改制为"川沙小学堂",自任校长,率先打破传统经学模式,在课程表中首次列入算术、格致(近代自然科学)、体操和乡土调查四门新学科,并实行"半日授课、半日实习"的教学方式,开创了近代中国小学融入科学、体育与实践教育的新风。' + onWrongSelect={(value) => { + switch (value) { + case "A": + return "经学、策论、八股文是传统书院的旧式课程,而川沙小学堂正是要打破这种传统模式。请你再想想吧!"; + case "C": + return "外语、逻辑、演讲、社交这些科目虽然也很重要,但不是川沙小学堂首次引入的突破性学科。请你再想想吧!"; + case "D": + return "绘画、声乐、舞蹈、戏剧属于艺术类课程,不是川沙小学堂在1903年首次开设的突破性学科。请你再想想吧!"; + default: + return "这不是川沙小学堂开设的具有突破性的学科。请你再想想吧!"; + } + }} onSubmit={handleQuiz3Submit} /> - {/* 成功提示 */} + {/* 第一题成功提示 */} { - setShowSuccessAlert(false); - markExhibitVisited(place, 3); - }} - onClose={() => { - setShowSuccessAlert(false); - }} + visible={showSuccessAlert && !showDragQuiz && !successAlertContent} + content={FIRST_QUIZ_SUCCESS_CONTENT} + onContinue={handleFirstQuizSuccessContinue} + onClose={handleFirstQuizSuccessContinue} + /> + + {/* 拖拽题 */} + + + {/* 拖拽题成功提示 */} + {/* 光标1、2的引导对话 */}