Files
shanghai-residences/src/components/layout/quiz/SingleChoiceQuizPanel.tsx
T
zhongyidian b91f2430c6 feat: 添加选择填空题和多选题组件,增强学习互动性
- 在 quiz 组件中引入 SelectFillQuizPanel 和 MultipleChoiceQuizPanel,丰富用户的学习体验。
- 更新 Maozedong 相关页面,增加新的选择填空题和多选题,提升互动性。
- 优化状态管理,确保题目完成后的反馈逻辑流畅。
- 更新 README 文档,详细说明新组件的功能和使用示例。
2026-02-27 17:50:47 +08:00

143 lines
4.1 KiB
TypeScript

import { useEffect, useState } from "react";
import { SingleChoice } from "@/components";
import type { SingleChoiceQuizPanelProps } from "./types";
/**
* 单选题面板组件
*
* 内部管理选择状态,点击选项后自动提交并验证答案
*
* @example
* ```tsx
* <SingleChoiceQuizPanel
* visible={true}
* title="这是一道单选题"
* options={[
* { value: "A", label: "选项A" },
* { value: "B", label: "选项B" },
* ]}
* correctAnswer="B"
* errorMessage="再想一想,注意题干中的时间和提示信息哦~"
* onSubmit={(value, isCorrect) => {
* console.log("用户选择:", value, "是否正确:", isCorrect);
* }}
* onError={(error) => {
* console.log("错误信息:", error);
* }}
* />
* ```
*/
const SingleChoiceQuizPanel = ({
visible,
zIndex = 10000,
title,
options,
correctAnswer,
errorMessage,
onSubmit,
onWrongSelect,
onError,
disabled = false,
}: SingleChoiceQuizPanelProps) => {
const [selectedValue, setSelectedValue] = useState<string>("");
const [error, setError] = useState<string>("");
// 当窗口关闭时,重置内部状态
useEffect(() => {
if (!visible) {
setSelectedValue("");
setError("");
}
}, [visible]);
if (!visible) return null;
// 处理选项点击
const handleOptionClick = (value: string) => {
if (disabled) return;
setSelectedValue(value);
setError("");
// 验证答案
const isCorrect =
correctAnswer !== undefined ? value === correctAnswer : undefined;
// 如果提供了正确答案,进行验证
if (correctAnswer !== undefined) {
if (isCorrect) {
// 答题成功:清空状态,执行 onSubmit(外部控制关闭窗口)
setSelectedValue("");
setError("");
onSubmit?.(value, true);
} else {
// 答题失败:清空状态,执行 onError,允许重新选择
setSelectedValue("");
const customError = onWrongSelect?.(value);
const errorMsg =
customError || errorMessage || "很抱歉,你选错了!请你再思考一下吧!";
setError(errorMsg);
onError?.(errorMsg);
}
} else {
// 没有提供正确答案,直接提交
setSelectedValue("");
setError("");
onSubmit?.(value, true);
}
};
return (
<div
className="fixed inset-0 flex items-center justify-center bg-black/40"
style={{ zIndex }}
>
<div className="relative w-[1728px] h-[972px] flex flex-col items-center justify-center">
{/* 题干卡片 */}
<div className="w-[1700px] rounded-[32px] bg-black px-20 py-8 shadow-[0_20px_60px_rgba(240,190,117,0.8)] border-4 border-[#d4c5a9]">
<div className="text-[34px] leading-relaxed text-white">{title}</div>
{/* 答案反馈 */}
{error && (
<div className="mt-5 text-center text-[26px] text-[#fcbe53]">
{error}
</div>
)}
</div>
{/* 选项区域 */}
<div className="mt-16">
<SingleChoice
options={options}
value={selectedValue}
disabled={disabled}
renderOption={(option, isSelected) => (
<div
key={option.value}
className="mb-6 cursor-pointer"
onClick={() => handleOptionClick(option.value)}
>
<div
className={`w-[1500px] flex items-center p-4 text-[28px] leading-relaxed rounded-tr-[50px] rounded-bl-4xl transition-all duration-150 ${
disabled
? "opacity-60 cursor-not-allowed"
: "cursor-pointer"
} ${
isSelected
? "bg-[#d4a574] text-[#5c3e2e]"
: "bg-[#d7b888] text-[#5c3e2e] hover:bg-gradient-to-r from-[#ddf3e1] to-[#fbdd97]"
}`}
>
{option.label}
</div>
</div>
)}
/>
</div>
</div>
</div>
);
};
export default SingleChoiceQuizPanel;