Files
shanghai-residences/src/components/layout/quiz/SingleChoiceQuizPanel.tsx
T
zhongyidianandClaude Opus 4.7 6d40e19346 feat: update residence learning flows
Commit non-media application and asset changes while leaving audio, video, and environment files out of version control.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:15:26 +08:00

170 lines
5.2 KiB
TypeScript

import { useEffect, useState } from 'react'
import { SingleChoice } from '@/components'
import SuccessAlert from '@/components/layout/alert/success-alert'
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,
showSuccessAlert = false,
successAlertProps,
onSuccessContinue,
}: SingleChoiceQuizPanelProps) => {
const [selectedValue, setSelectedValue] = useState<string>('')
const [error, setError] = useState<string>('')
const [successAlertVisible, setSuccessAlertVisible] = useState<boolean>(false)
// 当窗口关闭时,重置内部状态
useEffect(() => {
if (!visible) {
setSelectedValue('')
setError('')
setSuccessAlertVisible(false)
}
}, [visible])
// 当成功弹窗显示时,隐藏题目面板,避免双层遮罩叠加
const shouldRenderQuizPanel = visible && !successAlertVisible
if (!visible) return null
// 约定:显式开启,或传入 title/content 任一文案,即启用答对成功弹窗
const hasSuccessAlertCopy =
Boolean(successAlertProps?.title) || Boolean(successAlertProps?.content)
const shouldShowSuccessAlert = showSuccessAlert || hasSuccessAlertCopy
const handleSuccessContinue = () => {
setSuccessAlertVisible(false)
onSuccessContinue?.()
}
// 处理选项点击
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)
if (shouldShowSuccessAlert) {
setSuccessAlertVisible(true)
}
} else {
// 答题失败:清空状态,执行 onError,允许重新选择
setSelectedValue('')
const customError = onWrongSelect?.(value)
const errorMsg = customError || errorMessage || '很抱歉,你选错了!请你再思考一下吧!'
setError(errorMsg)
onError?.(errorMsg)
}
} else {
// 没有提供正确答案,直接提交
setSelectedValue('')
setError('')
onSubmit?.(value, true)
}
}
return (
<>
{shouldRenderQuizPanel && (
<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-linear-to-r from-[#ddf3e1] to-[#fbdd97]'
}`}
>
{option.label}
</div>
</div>
)}
/>
</div>
</div>
</div>
)}
{shouldShowSuccessAlert && (
<SuccessAlert
{...successAlertProps}
visible={successAlertVisible}
onClose={handleSuccessContinue}
/>
)}
</>
)
}
export default SingleChoiceQuizPanel