ux: 優化學習頁面用戶體驗和互動設計

- 修正翻卡模式卡片翻轉動畫和版面配置
- 改善選擇題模式答案顯示和回饋機制
- 優化語音錄音組件狀態管理
- 加強用戶交互體驗和視覺回饋

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
鄭沛軒 2025-09-19 17:44:39 +08:00
parent da85bb8f42
commit 31e3fe9fa8
2 changed files with 646 additions and 535 deletions

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
import { useState, useRef, useCallback, useEffect } from 'react';
import { Mic, Square, Play, Upload } from 'lucide-react';
import AudioPlayer from './AudioPlayer';
export interface PronunciationScore {
overall: number;
@ -21,6 +22,8 @@ export interface PhonemeScore {
export interface VoiceRecorderProps {
targetText: string;
targetTranslation?: string;
exampleImage?: string;
onScoreReceived?: (score: PronunciationScore) => void;
onRecordingComplete?: (audioBlob: Blob) => void;
maxDuration?: number;
@ -30,6 +33,8 @@ export interface VoiceRecorderProps {
export default function VoiceRecorder({
targetText,
targetTranslation,
exampleImage,
onScoreReceived,
onRecordingComplete,
maxDuration = 30, // 30 seconds default
@ -233,20 +238,44 @@ export default function VoiceRecorder({
}, [audioUrl]);
return (
<div className={`voice-recorder p-6 border-2 border-dashed border-gray-300 rounded-xl ${className}`}>
<div className={`voice-recorder ${className}`}>
{/* 隱藏的音頻元素 */}
<audio ref={audioRef} />
{/* Example Image */}
{exampleImage && (
<div className="mb-4">
<img
src={exampleImage}
alt="Example context"
className="w-full rounded-lg shadow-md cursor-pointer hover:shadow-lg transition-shadow"
style={{ maxHeight: '400px', objectFit: 'contain' }}
/>
<div className="text-xs text-gray-500 mt-2 text-center"></div>
</div>
)}
{/* 目標文字顯示 */}
<div className="text-center mb-6">
<h3 className="text-lg font-semibold mb-2"></h3>
<p className="text-2xl font-medium text-gray-800 p-4 bg-blue-50 rounded-lg">
{targetText}
</p>
<div className="mb-6">
<div className="p-4 bg-gray-50 rounded-lg">
<div className="flex items-start justify-between gap-3">
<div className="flex-1">
<div className="text-gray-800 text-lg mb-2">{targetText}</div>
{targetTranslation && (
<div className="text-gray-600 text-base">{targetTranslation}</div>
)}
</div>
<AudioPlayer
text={targetText}
className="flex-shrink-0 mt-1"
/>
</div>
</div>
</div>
{/* 錄音控制區 */}
<div className="flex flex-col items-center gap-4">
<div className="p-6 border-2 border-dashed border-gray-300 rounded-xl">
<div className="flex flex-col items-center gap-4">
{/* 錄音按鈕 */}
<button
onClick={isRecording ? stopRecording : startRecording}
@ -360,6 +389,7 @@ export default function VoiceRecorder({
)}
</div>
)}
</div>
</div>
</div>
);