ux: 優化學習頁面用戶體驗和互動設計
- 修正翻卡模式卡片翻轉動畫和版面配置 - 改善選擇題模式答案顯示和回饋機制 - 優化語音錄音組件狀態管理 - 加強用戶交互體驗和視覺回饋 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
da85bb8f42
commit
31e3fe9fa8
File diff suppressed because it is too large
Load Diff
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { useState, useRef, useCallback, useEffect } from 'react';
|
import { useState, useRef, useCallback, useEffect } from 'react';
|
||||||
import { Mic, Square, Play, Upload } from 'lucide-react';
|
import { Mic, Square, Play, Upload } from 'lucide-react';
|
||||||
|
import AudioPlayer from './AudioPlayer';
|
||||||
|
|
||||||
export interface PronunciationScore {
|
export interface PronunciationScore {
|
||||||
overall: number;
|
overall: number;
|
||||||
|
|
@ -21,6 +22,8 @@ export interface PhonemeScore {
|
||||||
|
|
||||||
export interface VoiceRecorderProps {
|
export interface VoiceRecorderProps {
|
||||||
targetText: string;
|
targetText: string;
|
||||||
|
targetTranslation?: string;
|
||||||
|
exampleImage?: string;
|
||||||
onScoreReceived?: (score: PronunciationScore) => void;
|
onScoreReceived?: (score: PronunciationScore) => void;
|
||||||
onRecordingComplete?: (audioBlob: Blob) => void;
|
onRecordingComplete?: (audioBlob: Blob) => void;
|
||||||
maxDuration?: number;
|
maxDuration?: number;
|
||||||
|
|
@ -30,6 +33,8 @@ export interface VoiceRecorderProps {
|
||||||
|
|
||||||
export default function VoiceRecorder({
|
export default function VoiceRecorder({
|
||||||
targetText,
|
targetText,
|
||||||
|
targetTranslation,
|
||||||
|
exampleImage,
|
||||||
onScoreReceived,
|
onScoreReceived,
|
||||||
onRecordingComplete,
|
onRecordingComplete,
|
||||||
maxDuration = 30, // 30 seconds default
|
maxDuration = 30, // 30 seconds default
|
||||||
|
|
@ -233,20 +238,44 @@ export default function VoiceRecorder({
|
||||||
}, [audioUrl]);
|
}, [audioUrl]);
|
||||||
|
|
||||||
return (
|
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} />
|
<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">
|
<div className="mb-6">
|
||||||
<h3 className="text-lg font-semibold mb-2">請朗讀以下內容:</h3>
|
<div className="p-4 bg-gray-50 rounded-lg">
|
||||||
<p className="text-2xl font-medium text-gray-800 p-4 bg-blue-50 rounded-lg">
|
<div className="flex items-start justify-between gap-3">
|
||||||
{targetText}
|
<div className="flex-1">
|
||||||
</p>
|
<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>
|
||||||
|
|
||||||
{/* 錄音控制區 */}
|
{/* 錄音控制區 */}
|
||||||
<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
|
<button
|
||||||
onClick={isRecording ? stopRecording : startRecording}
|
onClick={isRecording ? stopRecording : startRecording}
|
||||||
|
|
@ -360,6 +389,7 @@ export default function VoiceRecorder({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue