96 lines
2.9 KiB
TypeScript
96 lines
2.9 KiB
TypeScript
import React, { useState, useCallback, memo } from 'react'
|
||
import {
|
||
SpeakingTestContainer
|
||
} from '@/components/review/shared'
|
||
import { BaseReviewProps } from '@/types/review'
|
||
|
||
interface SentenceSpeakingTestProps extends BaseReviewProps {
|
||
exampleImage?: string
|
||
onImageClick?: (image: string) => void
|
||
}
|
||
|
||
const SentenceSpeakingTestComponent: React.FC<SentenceSpeakingTestProps> = ({
|
||
cardData,
|
||
exampleImage,
|
||
onAnswer,
|
||
onReportError,
|
||
onImageClick,
|
||
disabled = false
|
||
}) => {
|
||
const [showResult, setShowResult] = useState(false)
|
||
|
||
// 判斷是否已答題(錄音提交後設定 hasAnswered = true)
|
||
const hasAnswered = showResult
|
||
|
||
const handleRecordingComplete = useCallback(() => {
|
||
if (disabled || showResult) return
|
||
setShowResult(true)
|
||
onAnswer(cardData.example) // 語音測驗通常都算正確
|
||
}, [disabled, showResult, cardData.example, onAnswer])
|
||
|
||
// 提示區域
|
||
const promptArea = (
|
||
<div className="text-center">
|
||
<p className="text-lg text-gray-700 text-left mb-4">
|
||
請看例句圖片並大聲說出完整的例句:
|
||
</p>
|
||
{exampleImage && (
|
||
<div className="bg-gray-50 rounded-lg p-4">
|
||
<img
|
||
src={exampleImage}
|
||
alt="Example illustration"
|
||
className="w-full max-w-md mx-auto rounded-lg cursor-pointer"
|
||
onClick={() => onImageClick?.(exampleImage)}
|
||
/>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
|
||
// 錄音區域
|
||
const recordingArea = (
|
||
<div className="w-full text-center">
|
||
<div className="bg-gray-50 rounded-lg p-6 border border-gray-200">
|
||
<p className="text-gray-600 mb-4">請點擊錄音按鈕開始錄製</p>
|
||
<button
|
||
onClick={handleRecordingComplete}
|
||
disabled={disabled || showResult}
|
||
className="bg-red-500 hover:bg-red-600 text-white px-6 py-3 rounded-full disabled:opacity-50 disabled:cursor-not-allowed"
|
||
>
|
||
🎤 開始錄音
|
||
</button>
|
||
<p className="text-sm text-gray-500 mt-2">
|
||
目標例句:{cardData.example}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
)
|
||
|
||
// 結果顯示區域
|
||
const resultArea = showResult ? (
|
||
<div className="p-6 rounded-lg bg-blue-50 border border-blue-200 w-full">
|
||
<p className="text-blue-700 text-left text-xl font-semibold mb-2">
|
||
錄音完成!
|
||
</p>
|
||
<p className="text-gray-600 text-left">
|
||
系統正在評估你的發音...
|
||
</p>
|
||
</div>
|
||
) : null
|
||
|
||
return (
|
||
<SpeakingTestContainer
|
||
cardData={cardData}
|
||
testTitle="例句口說"
|
||
promptArea={promptArea}
|
||
recordingArea={recordingArea}
|
||
resultArea={resultArea}
|
||
onAnswer={onAnswer}
|
||
onReportError={onReportError}
|
||
disabled={disabled}
|
||
/>
|
||
)
|
||
}
|
||
|
||
export const SentenceSpeakingTest = memo(SentenceSpeakingTestComponent)
|
||
SentenceSpeakingTest.displayName = 'SentenceSpeakingTest' |