126 lines
3.8 KiB
TypeScript
126 lines
3.8 KiB
TypeScript
// 模擬真實API數據結構
|
|
import apiSeeds from './api_seeds.json'
|
|
|
|
// API響應接口 (匹配真實API結構 + 同義詞擴展)
|
|
export interface ApiFlashcard {
|
|
id: string
|
|
word: string
|
|
translation: string
|
|
definition: string
|
|
partOfSpeech: string
|
|
pronunciation: string
|
|
example: string
|
|
exampleTranslation: string
|
|
isFavorite: boolean
|
|
difficultyLevelNumeric: number
|
|
cefr: string
|
|
createdAt: string
|
|
updatedAt: string
|
|
hasExampleImage: boolean
|
|
primaryImageUrl: string | null
|
|
// 添加同義詞支持
|
|
synonyms?: string[]
|
|
}
|
|
|
|
// 前端狀態擴展接口 (延遲計數系統)
|
|
export interface CardState extends ApiFlashcard {
|
|
// 延遲計數欄位
|
|
skipCount: number // 跳過次數
|
|
wrongCount: number // 答錯次數
|
|
isCompleted: boolean // 是否已完成
|
|
originalOrder: number // 原始順序
|
|
}
|
|
|
|
// 測驗項目接口 (線性流程核心)
|
|
export interface QuizItem {
|
|
id: string // 測驗項目ID
|
|
cardId: string // 所屬詞卡ID
|
|
quizType: 'flip-card' | 'vocab-choice' | 'sentence-speaking' // 測驗類型
|
|
isCompleted: boolean // 個別測驗完成狀態
|
|
skipCount: number // 跳過次數
|
|
wrongCount: number // 答錯次數
|
|
order: number // 序列順序
|
|
cardData: CardState // 詞卡數據引用
|
|
}
|
|
|
|
|
|
// 為詞卡添加延遲計數狀態
|
|
const addStateFields = (flashcard: ApiFlashcard, index: number): CardState => ({
|
|
...flashcard,
|
|
skipCount: 0,
|
|
wrongCount: 0,
|
|
isCompleted: false,
|
|
originalOrder: index
|
|
})
|
|
|
|
// 提取詞卡數據 (方便組件使用)
|
|
export const SIMPLE_CARDS = (apiSeeds as any).data.flashcards.map(addStateFields)
|
|
|
|
// 生成線性測驗項目序列
|
|
export const generateQuizItems = (cards: CardState[]): QuizItem[] => {
|
|
const quizItems: QuizItem[] = []
|
|
let order = 0
|
|
|
|
cards.forEach((card) => {
|
|
// 為每張詞卡生成兩個測驗項目:先翻卡記憶,再詞彙選擇
|
|
const flipCardQuiz: QuizItem = {
|
|
id: `${card.id}-flip-card`,
|
|
cardId: card.id,
|
|
quizType: 'flip-card',
|
|
isCompleted: false,
|
|
skipCount: 0,
|
|
wrongCount: 0,
|
|
order: order++,
|
|
cardData: card
|
|
}
|
|
|
|
const vocabChoiceQuiz: QuizItem = {
|
|
id: `${card.id}-vocab-choice`,
|
|
cardId: card.id,
|
|
quizType: 'vocab-choice',
|
|
isCompleted: false,
|
|
skipCount: 0,
|
|
wrongCount: 0,
|
|
order: order++,
|
|
cardData: card
|
|
}
|
|
|
|
quizItems.push(flipCardQuiz, vocabChoiceQuiz)
|
|
})
|
|
|
|
return quizItems
|
|
}
|
|
|
|
// 測驗項目優先級排序 (修正後的延遲計數系統)
|
|
export const sortQuizItemsByPriority = (quizItems: QuizItem[]): QuizItem[] => {
|
|
return quizItems.sort((a, b) => {
|
|
// 1. 已完成的測驗項目排到最後
|
|
if (a.isCompleted && !b.isCompleted) return 1
|
|
if (!a.isCompleted && b.isCompleted) return -1
|
|
|
|
// 2. 未完成項目按延遲分數排序 (修正:延遲分數低的排前面)
|
|
const aDelayScore = a.skipCount + a.wrongCount
|
|
const bDelayScore = b.skipCount + b.wrongCount
|
|
|
|
if (aDelayScore !== bDelayScore) {
|
|
return aDelayScore - bDelayScore // 修正:延遲分數低的排前面,保持線性順序
|
|
}
|
|
|
|
// 3. 延遲分數相同時按原始順序
|
|
return a.order - b.order
|
|
})
|
|
}
|
|
|
|
|
|
// 生成詞彙選擇測驗選項
|
|
export const generateVocabOptions = (correctWord: string, allCards: CardState[]): string[] => {
|
|
const allWords = allCards.map(card => card.word).filter(word => word !== correctWord)
|
|
const shuffledWords = allWords.sort(() => Math.random() - 0.5)
|
|
const distractors = shuffledWords.slice(0, 3)
|
|
const options = [correctWord, ...distractors]
|
|
return options.sort(() => Math.random() - 0.5)
|
|
}
|
|
|
|
// 初始化測驗項目列表
|
|
export const INITIAL_TEST_ITEMS = generateQuizItems(SIMPLE_CARDS)
|