123 lines
4.0 KiB
TypeScript
123 lines
4.0 KiB
TypeScript
// 熟悉度計算工具 - 與後端算法保持一致
|
|
|
|
/**
|
|
* 計算當前熟悉度(考慮記憶衰減)
|
|
* @param baseMastery 基礎熟悉度 (0-100)
|
|
* @param lastReviewDate 最後復習日期 (ISO格式)
|
|
* @returns 當前熟悉度 (0-100)
|
|
*/
|
|
export function calculateCurrentMastery(baseMastery: number, lastReviewDate: string): number {
|
|
const today = new Date();
|
|
const lastDate = new Date(lastReviewDate);
|
|
const daysSince = Math.floor((today.getTime() - lastDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
|
|
if (daysSince <= 0) return baseMastery;
|
|
|
|
// 應用記憶衰減(與後端一致的算法)
|
|
const decayRate = 0.05; // 每天5%衰減
|
|
const maxDecayDays = 30;
|
|
const effectiveDays = Math.min(daysSince, maxDecayDays);
|
|
const decayFactor = Math.pow(1 - decayRate, effectiveDays);
|
|
|
|
return Math.max(0, Math.floor(baseMastery * decayFactor));
|
|
}
|
|
|
|
/**
|
|
* 計算衰減程度
|
|
* @param baseMastery 基礎熟悉度
|
|
* @param currentMastery 當前熟悉度
|
|
* @returns 衰減量
|
|
*/
|
|
export function getDecayAmount(baseMastery: number, currentMastery: number): number {
|
|
return Math.max(0, baseMastery - currentMastery);
|
|
}
|
|
|
|
/**
|
|
* 根據學習者CEFR等級和詞彙CEFR等級決定可用的複習方式
|
|
* @param userCEFRLevel 學習者CEFR等級 (A1-C2)
|
|
* @param wordCEFRLevel 詞彙CEFR等級 (A1-C2)
|
|
* @returns 適合的複習題型列表
|
|
*/
|
|
export function getReviewTypesByDifficulty(userCEFRLevel: string, wordCEFRLevel: string): string[] {
|
|
// 即時轉換CEFR為數值進行計算
|
|
const userLevel = getCEFRToLevel(userCEFRLevel);
|
|
const wordLevel = getCEFRToLevel(wordCEFRLevel);
|
|
const difficulty = wordLevel - userLevel;
|
|
|
|
if (userCEFRLevel === 'A1') {
|
|
// A1學習者 - 統一基礎題型
|
|
return ['flip-memory', 'vocab-choice', 'vocab-listening'];
|
|
} else if (difficulty < -10) {
|
|
// 簡單詞彙 (學習者CEFR > 詞彙CEFR)
|
|
return ['sentence-reorder', 'sentence-fill'];
|
|
} else if (difficulty >= -10 && difficulty <= 10) {
|
|
// 適中詞彙 (學習者CEFR ≈ 詞彙CEFR)
|
|
return ['sentence-fill', 'sentence-reorder', 'sentence-speaking'];
|
|
} else {
|
|
// 困難詞彙 (學習者CEFR < 詞彙CEFR)
|
|
return ['flip-memory', 'vocab-choice'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 檢查是否為A1學習者
|
|
* @param userCEFRLevel 學習者CEFR等級
|
|
* @returns 是否為A1學習者
|
|
*/
|
|
export function isA1Learner(userCEFRLevel: string): boolean {
|
|
return userCEFRLevel === 'A1';
|
|
}
|
|
|
|
/**
|
|
* CEFR等級轉換為數值 (前端計算用)
|
|
* @param cefr CEFR等級字符串 (A1-C2)
|
|
* @returns 對應的數值 (20-95)
|
|
*/
|
|
export function getCEFRToLevel(cefr: string): number {
|
|
const mapping: { [key: string]: number } = {
|
|
'A1': 20, 'A2': 35, 'B1': 50, 'B2': 65, 'C1': 80, 'C2': 95
|
|
};
|
|
return mapping[cefr] || 50;
|
|
}
|
|
|
|
/**
|
|
* 數值轉換為CEFR等級 (前端顯示用)
|
|
* @param level 數值 (20-95)
|
|
* @returns CEFR等級字符串
|
|
*/
|
|
export function getLevelToCEFR(level: number): string {
|
|
if (level <= 20) return 'A1';
|
|
if (level <= 35) return 'A2';
|
|
if (level <= 50) return 'B1';
|
|
if (level <= 65) return 'B2';
|
|
if (level <= 80) return 'C1';
|
|
return 'C2';
|
|
}
|
|
|
|
/**
|
|
* 獲取熟悉度等級標籤
|
|
* @param masteryLevel 熟悉度 (0-100)
|
|
* @returns 等級標籤和顏色
|
|
*/
|
|
export function getMasteryLevelInfo(masteryLevel: number): { label: string; color: string; bgColor: string } {
|
|
if (masteryLevel >= 80) {
|
|
return { label: '熟練', color: 'text-green-700', bgColor: 'bg-green-100' };
|
|
} else if (masteryLevel >= 60) {
|
|
return { label: '良好', color: 'text-blue-700', bgColor: 'bg-blue-100' };
|
|
} else if (masteryLevel >= 40) {
|
|
return { label: '一般', color: 'text-yellow-700', bgColor: 'bg-yellow-100' };
|
|
} else {
|
|
return { label: '需加強', color: 'text-red-700', bgColor: 'bg-red-100' };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 計算複習進度百分比
|
|
* @param completed 已完成數量
|
|
* @param total 總數量
|
|
* @returns 進度百分比 (0-100)
|
|
*/
|
|
export function calculateProgress(completed: number, total: number): number {
|
|
if (total === 0) return 0;
|
|
return Math.round((completed / total) * 100);
|
|
} |