feat: 完整記錄詞彙選擇題設計規格 - 為階段2擴展做準備
## 技術實作規格增強 - 🎨 完整記錄您設計的 VocabChoiceTest 組件架構 - 📋 詳細的 ChoiceGrid 響應式網格設計 - 🎯 完整的 ChoiceOption 狀態樣式系統 - 🔧 三區域設計: 問題顯示/選項網格/結果顯示 ## 設計規格詳情 - 組件接口: VocabChoiceTestProps 完整定義 - 狀態管理: selectedAnswer + showResult 邏輯 - 樣式系統: 正確(綠)/錯誤(紅)/選中(藍)/默認(灰) - 響應式: grid-cols-1 sm:grid-cols-2 自適應布局 ## 階段2擴展準備 - ✅ 有完整設計規格可參考實作 - ✅ 有明確的組件分工和職責 - ✅ 有詳細的UI樣式和交互邏輯 - ✅ 受開發控制規範約束避免過度工程 為未來的階段2詞彙選擇功能提供完整的實作指南 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9307cb593d
commit
07a72da006
|
|
@ -94,25 +94,172 @@ const confidenceToScore = {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### **階段2: 詞彙選擇題 (計劃中)**
|
### **階段2: 詞彙選擇題 (已設計完成)**
|
||||||
|
|
||||||
|
**組件架構** (基於您的設計):
|
||||||
|
```typescript
|
||||||
|
interface VocabChoiceTestProps {
|
||||||
|
cardData: FlashcardData
|
||||||
|
options: string[] // 4選1選項
|
||||||
|
onAnswer: (answer: string) => void
|
||||||
|
onReportError: () => void
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// 組件分區設計
|
||||||
|
const VocabChoiceTest = () => {
|
||||||
|
const questionArea = // 問題顯示區域
|
||||||
|
const optionsArea = // 選項網格區域
|
||||||
|
const resultArea = // 結果顯示區域
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChoiceTestContainer
|
||||||
|
questionArea={questionArea}
|
||||||
|
optionsArea={optionsArea}
|
||||||
|
resultArea={resultArea}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**問題顯示區域設計**:
|
||||||
|
```typescript
|
||||||
|
const questionArea = (
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="bg-gray-50 rounded-lg p-6">
|
||||||
|
<h3 className="font-semibold text-gray-900 mb-3 text-left">定義</h3>
|
||||||
|
<p className="text-gray-700 text-left text-lg leading-relaxed">
|
||||||
|
{cardData.definition}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-lg text-gray-700 mt-4 text-left">
|
||||||
|
請選擇符合上述定義的英文詞彙:
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**狀態管理設計**:
|
||||||
|
```typescript
|
||||||
|
const [selectedAnswer, setSelectedAnswer] = useState<string | null>(null)
|
||||||
|
const [showResult, setShowResult] = useState(false)
|
||||||
|
|
||||||
|
// 答案驗證邏輯
|
||||||
|
const isCorrect = useMemo(() =>
|
||||||
|
selectedAnswer === cardData.word,
|
||||||
|
[selectedAnswer, cardData.word]
|
||||||
|
)
|
||||||
|
|
||||||
|
// 選擇處理
|
||||||
|
const handleAnswerSelect = useCallback((answer: string) => {
|
||||||
|
if (disabled || showResult) return
|
||||||
|
setSelectedAnswer(answer)
|
||||||
|
setShowResult(true)
|
||||||
|
onAnswer(answer)
|
||||||
|
}, [disabled, showResult, onAnswer])
|
||||||
|
```
|
||||||
|
|
||||||
**選項生成算法**:
|
**選項生成算法**:
|
||||||
```typescript
|
```typescript
|
||||||
// 暫時使用固定選項 (MVP階段)
|
// MVP階段: 固定選項
|
||||||
const mockOptions = ['apple', 'orange', 'banana', correctAnswer]
|
const generateSimpleOptions = (correctWord: string): string[] => {
|
||||||
const shuffledOptions = shuffle(mockOptions)
|
const fixedDistractors = ['apple', 'orange', 'banana']
|
||||||
```
|
return shuffle([correctWord, ...fixedDistractors])
|
||||||
|
}
|
||||||
|
|
||||||
**未來升級方案**:
|
// 階段3升級方案: 智能干擾項
|
||||||
```typescript
|
const generateSmartOptions = (correctWord: Flashcard, allWords: Flashcard[]): string[] => {
|
||||||
// 基於詞性和CEFR等級生成干擾項
|
|
||||||
const generateDistractors = (correctWord, allWords) => {
|
|
||||||
const samePOS = allWords.filter(w => w.partOfSpeech === correctWord.partOfSpeech)
|
const samePOS = allWords.filter(w => w.partOfSpeech === correctWord.partOfSpeech)
|
||||||
const sameCEFR = allWords.filter(w => w.cefr === correctWord.cefr)
|
const sameCEFR = allWords.filter(w => w.cefr === correctWord.cefr)
|
||||||
return selectRandom([...samePOS, ...sameCEFR], 3)
|
const distractors = selectRandom([...samePOS, ...sameCEFR], 3)
|
||||||
|
return shuffle([correctWord.word, ...distractors.map(w => w.word)])
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**ChoiceGrid組件設計** (您的完整設計):
|
||||||
|
```typescript
|
||||||
|
interface ChoiceGridProps {
|
||||||
|
options: string[] // 4個選項
|
||||||
|
selectedOption?: string | null
|
||||||
|
correctAnswer?: string
|
||||||
|
showResult?: boolean // 控制結果顯示
|
||||||
|
onSelect: (option: string) => void
|
||||||
|
disabled?: boolean
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 響應式網格布局
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||||
|
{options.map((option, index) => (
|
||||||
|
<ChoiceOption
|
||||||
|
key={`${option}-${index}`}
|
||||||
|
option={option}
|
||||||
|
index={index}
|
||||||
|
isSelected={selectedOption === option}
|
||||||
|
isCorrect={showResult && option === correctAnswer}
|
||||||
|
isIncorrect={showResult && option !== correctAnswer}
|
||||||
|
showResult={showResult}
|
||||||
|
onSelect={onSelect}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
**ChoiceOption樣式規格** (您的設計):
|
||||||
|
```typescript
|
||||||
|
// 選項按鈕狀態樣式
|
||||||
|
const getOptionStyles = (isSelected, isCorrect, isIncorrect, showResult) => {
|
||||||
|
if (showResult) {
|
||||||
|
if (isCorrect) return 'border-green-500 bg-green-50 text-green-700'
|
||||||
|
if (isIncorrect && isSelected) return 'border-red-500 bg-red-50 text-red-700'
|
||||||
|
return 'border-gray-200 bg-gray-50 text-gray-500'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSelected) return 'border-blue-500 bg-blue-50 text-blue-700'
|
||||||
|
return 'border-gray-200 hover:border-blue-300 hover:bg-blue-50'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按鈕基礎樣式
|
||||||
|
className={`p-4 text-center rounded-lg border-2 transition-all ${getOptionStyles()}`}
|
||||||
|
```
|
||||||
|
|
||||||
|
**UI組件層次設計**:
|
||||||
|
```typescript
|
||||||
|
// 您設計的完整組件結構
|
||||||
|
<ChoiceTestContainer> // 外層容器和錯誤處理
|
||||||
|
<TestHeader title="詞彙選擇" cefr={cardData.cefr} />
|
||||||
|
<QuestionArea> // 問題顯示區域
|
||||||
|
<div className="bg-gray-50 rounded-lg p-6">
|
||||||
|
<h3>定義</h3>
|
||||||
|
<p className="text-lg leading-relaxed">{definition}</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-lg mt-4">請選擇符合上述定義的英文詞彙:</p>
|
||||||
|
</QuestionArea>
|
||||||
|
<OptionsArea> // 選項區域
|
||||||
|
<ChoiceGrid // 響應式2x2網格
|
||||||
|
options={options}
|
||||||
|
selectedOption={selectedAnswer}
|
||||||
|
correctAnswer={cardData.word}
|
||||||
|
showResult={showResult}
|
||||||
|
onSelect={handleAnswerSelect}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</OptionsArea>
|
||||||
|
<ResultArea> // 結果顯示區域
|
||||||
|
{showResult && (
|
||||||
|
<TestResultDisplay
|
||||||
|
isCorrect={isCorrect}
|
||||||
|
correctAnswer={cardData.word}
|
||||||
|
userAnswer={selectedAnswer}
|
||||||
|
pronunciation={cardData.pronunciation}
|
||||||
|
example={cardData.example}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ResultArea>
|
||||||
|
</ChoiceTestContainer>
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎨 **UI/UX實作規格**
|
## 🎨 **UI/UX實作規格**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue