feat: 优化敏感词处理和更新拖拽题功能,提升用户体验
- 清理敏感词处理函数中的多余空行,提升代码整洁性。 - 在 HuangyanpeiPlace3 组件中引入拖拽题功能,增强互动性。 - 添加拖拽题的成功和错误提示逻辑,优化用户填写体验。 - 使用 useMemo 优化已访问展品的状态管理,提升性能。
This commit is contained in:
1 parent
45dd648d21
commit
1dd8afcce7
2 files changed
+150
-33
No files matched your search
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<DragFillResult[]>([]);
|
||||
// 拖拽题错误提示
|
||||
const [dragQuizError, setDragQuizError] = useState<string>("");
|
||||
// 成功提示状态
|
||||
const [showSuccessAlert, setShowSuccessAlert] = useState(false);
|
||||
// 成功提示内容
|
||||
const [successAlertContent, setSuccessAlertContent] = useState<string>("");
|
||||
|
||||
// 光标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}
|
||||
/>
|
||||
|
||||
{/* 成功提示 */}
|
||||
{/* 第一题成功提示 */}
|
||||
<SuccessAlert
|
||||
visible={showSuccessAlert}
|
||||
onContinue={() => {
|
||||
setShowSuccessAlert(false);
|
||||
markExhibitVisited(place, 3);
|
||||
}}
|
||||
onClose={() => {
|
||||
setShowSuccessAlert(false);
|
||||
}}
|
||||
visible={showSuccessAlert && !showDragQuiz && !successAlertContent}
|
||||
content={FIRST_QUIZ_SUCCESS_CONTENT}
|
||||
onContinue={handleFirstQuizSuccessContinue}
|
||||
onClose={handleFirstQuizSuccessContinue}
|
||||
/>
|
||||
|
||||
{/* 拖拽题 */}
|
||||
<DragQuizPanel
|
||||
visible={showDragQuiz}
|
||||
zIndex={10000}
|
||||
title=""
|
||||
questionPrefix='【拖拽】黄炎培在"川沙小学堂"实行"半天'
|
||||
questionSuffix="的模式,特别注重实践。"
|
||||
options={DRAG_QUIZ_OPTIONS}
|
||||
blanks={DRAG_QUIZ_BLANKS}
|
||||
blankSeparators={["、"]}
|
||||
value={dragQuizValue}
|
||||
error={dragQuizError}
|
||||
onChange={handleDragQuizChange}
|
||||
/>
|
||||
|
||||
{/* 拖拽题成功提示 */}
|
||||
<SuccessAlert
|
||||
visible={showSuccessAlert && !showDragQuiz && !!successAlertContent}
|
||||
content={successAlertContent}
|
||||
onContinue={handleDragQuizSuccessContinue}
|
||||
onClose={handleDragQuizSuccessContinue}
|
||||
/>
|
||||
|
||||
{/* 光标1、2的引导对话 */}
|
||||
|
||||
Reference in new issue
Block a user