From b348780eaaebc3bfb426931129f7094e125fff96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=84=AD=E6=B2=9B=E8=BB=92?= Date: Sun, 21 Sep 2025 01:36:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E8=A9=9E=E5=8D=A1?= =?UTF-8?q?=E5=84=B2=E5=AD=98=E5=8A=9F=E8=83=BD=E6=95=B4=E5=90=88=E4=B8=A6?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=81=87=E8=B3=87=E6=96=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🎯 主要功能: - 統一前後端API欄位名稱(word/translation/definition) - 移除所有假資料生成函數,改用真實API調用 - 修正FlashcardForm和相關組件的欄位映射 🔧 技術修正: - 前端handleSaveWord函數使用正確的API欄位 - flashcardsService interface與後端API完全匹配 - handleAnalyzeSentence改為調用真實的analyze-sentence API 📝 代碼清理: - 移除generateMockAnalysis函數(~150行代碼) - 移除getRandomTranslation、getRandomPartOfSpeech等工具函數 - 清理所有mock相關的註釋和變數 ✅ 功能驗證: - 後端API正常運行(localhost:5000) - 前端Portal彈窗樣式完美 - 詞卡儲存功能完整可用 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- frontend/app/flashcards/page.tsx | 5 +- frontend/app/generate/page.tsx | 198 +++++--------------------- frontend/components/FlashcardForm.tsx | 17 +-- frontend/lib/services/flashcards.ts | 5 +- 4 files changed, 47 insertions(+), 178 deletions(-) diff --git a/frontend/app/flashcards/page.tsx b/frontend/app/flashcards/page.tsx index 48a42f4..a65f34b 100644 --- a/frontend/app/flashcards/page.tsx +++ b/frontend/app/flashcards/page.tsx @@ -876,8 +876,9 @@ function FlashcardsContent() { initialData={editingCard ? { id: editingCard.id, cardSetId: editingCard.cardSet ? cardSets.find(cs => cs.name === editingCard.cardSet.name)?.id || cardSets[0]?.id : cardSets[0]?.id, - english: editingCard.word, - chinese: editingCard.translation, + word: editingCard.word, + translation: editingCard.translation, + definition: editingCard.definition, pronunciation: editingCard.pronunciation, partOfSpeech: editingCard.partOfSpeech, example: editingCard.example, diff --git a/frontend/app/generate/page.tsx b/frontend/app/generate/page.tsx index 08d6c20..ced9612 100644 --- a/frontend/app/generate/page.tsx +++ b/frontend/app/generate/page.tsx @@ -20,9 +20,9 @@ function GenerateContent() { const [isPremium] = useState(true) - // 處理句子分析 - 使用假數據進行快速測試 + // 處理句子分析 - 使用真實API const handleAnalyzeSentence = async () => { - console.log('🚀 handleAnalyzeSentence 被調用 (假數據模式)') + console.log('🚀 handleAnalyzeSentence 被調用 (真實API模式)') console.log('📝 輸入文本:', textInput) if (!textInput.trim()) { @@ -33,181 +33,46 @@ function GenerateContent() { setIsAnalyzing(true) try { - // 模擬API延遲 - await new Promise(resolve => setTimeout(resolve, 1500)) + const response = await fetch('http://localhost:5000/api/ai/analyze-sentence', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` + }, + body: JSON.stringify({ + text: textInput + }) + }) - // 生成假的分析數據 - const mockAnalysis = generateMockAnalysis(textInput) + if (response.ok) { + const result = await response.json() + console.log('✅ API分析完成:', result) - setSentenceAnalysis(mockAnalysis.wordAnalysis) - setSentenceMeaning(mockAnalysis.sentenceTranslation) - setGrammarCorrection(mockAnalysis.grammarCorrection) - setFinalText(mockAnalysis.finalText) - setShowAnalysisView(true) - setUsageCount(prev => prev + 1) - - console.log('✅ 假數據分析完成:', mockAnalysis) + if (result.success) { + setSentenceAnalysis(result.data.wordAnalysis || {}) + setSentenceMeaning(result.data.sentenceTranslation || '') + setGrammarCorrection(result.data.grammarCorrection || null) + setFinalText(result.data.finalText || textInput) + setShowAnalysisView(true) + setUsageCount(prev => prev + 1) + } else { + throw new Error(result.error || '分析失敗') + } + } else { + throw new Error(`API錯誤: ${response.status}`) + } } catch (error) { - console.error('Error in mock analysis:', error) + console.error('Error in real API analysis:', error) alert(`分析句子時發生錯誤: ${error instanceof Error ? error.message : '未知錯誤'}`) } finally { setIsAnalyzing(false) } } - // 生成假的分析數據 - const generateMockAnalysis = (inputText: string) => { - const words = inputText.toLowerCase().split(/\s+/).filter(word => - word.length > 2 && /^[a-z]+$/.test(word.replace(/[.,!?;:]/g, '')) - ) - const wordAnalysis: any = {} - words.forEach((word, index) => { - const cleanWord = word.replace(/[.,!?;:]/g, '') - const isHighValue = index % 3 === 0 // 每3個詞中有1個高價值 - const isPhrase = cleanWord.length > 6 // 長詞視為片語 - wordAnalysis[cleanWord] = { - word: cleanWord, - translation: getRandomTranslation(cleanWord), - definition: `Definition of ${cleanWord} - a common English word`, - partOfSpeech: getRandomPartOfSpeech(), - pronunciation: `/${cleanWord}/`, - isHighValue: isHighValue, - isPhrase: isPhrase, - difficultyLevel: getRandomDifficulty(), - synonyms: [getRandomSynonym(cleanWord), getRandomSynonym(cleanWord)], - learningPriority: isHighValue ? 'high' : 'medium' - } - }) - return { - wordAnalysis, - sentenceTranslation: `這是「${inputText}」的中文翻譯。`, - grammarCorrection: { - hasErrors: Math.random() > 0.7, // 30%機率有語法錯誤 - correctedText: inputText, - originalText: inputText - }, - finalText: inputText - } - } - - // 輔助函數 - 改進翻譯生成 - const getRandomTranslation = (word: string) => { - // 常見英文單字的實際翻譯 - const commonTranslations: {[key: string]: string} = { - 'hello': '你好', - 'how': '如何', - 'are': '是', - 'you': '你', - 'today': '今天', - 'good': '好的', - 'morning': '早晨', - 'evening': '晚上', - 'thank': '謝謝', - 'please': '請', - 'sorry': '抱歉', - 'love': '愛', - 'like': '喜歡', - 'want': '想要', - 'need': '需要', - 'think': '思考', - 'know': '知道', - 'see': '看見', - 'go': '去', - 'come': '來', - 'get': '得到', - 'make': '製作', - 'take': '拿取', - 'give': '給予', - 'find': '找到', - 'work': '工作', - 'feel': '感覺', - 'become': '成為', - 'leave': '離開', - 'put': '放置', - 'mean': '意思', - 'keep': '保持', - 'let': '讓', - 'begin': '開始', - 'seem': '似乎', - 'help': '幫助', - 'talk': '談話', - 'turn': '轉向', - 'start': '開始', - 'show': '顯示', - 'hear': '聽見', - 'play': '玩耍', - 'run': '跑步', - 'move': '移動', - 'live': '生活', - 'believe': '相信', - 'bring': '帶來', - 'happen': '發生', - 'write': '寫作', - 'provide': '提供', - 'sit': '坐下', - 'stand': '站立', - 'lose': '失去', - 'pay': '付費', - 'meet': '遇見', - 'include': '包含', - 'continue': '繼續', - 'set': '設置', - 'learn': '學習', - 'change': '改變', - 'lead': '領導', - 'understand': '理解', - 'watch': '觀看', - 'follow': '跟隨', - 'stop': '停止', - 'create': '創造', - 'speak': '說話', - 'read': '閱讀', - 'allow': '允許', - 'add': '添加', - 'spend': '花費', - 'grow': '成長', - 'open': '打開', - 'walk': '走路', - 'win': '獲勝', - 'offer': '提供', - 'remember': '記住', - 'consider': '考慮', - 'appear': '出現', - 'buy': '購買', - 'wait': '等待', - 'serve': '服務', - 'die': '死亡', - 'send': '發送', - 'expect': '期待', - 'build': '建造', - 'stay': '停留', - 'fall': '跌倒', - 'cut': '切割', - 'reach': '到達', - 'kill': '殺死', - 'remain': '保持' - } - - return commonTranslations[word.toLowerCase()] || `${word}的翻譯` - } - - const getRandomPartOfSpeech = () => { - const parts = ['noun', 'verb', 'adjective', 'adverb', 'preposition'] - return parts[Math.floor(Math.random() * parts.length)] - } - - const getRandomDifficulty = () => { - const levels = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2'] - return levels[Math.floor(Math.random() * levels.length)] - } - - const getRandomSynonym = (word: string) => { - return `synonym_${word}_${Math.floor(Math.random() * 10)}` - } const handleAcceptCorrection = () => { if (grammarCorrection?.correctedText) { @@ -225,8 +90,9 @@ function GenerateContent() { const handleSaveWord = async (word: string, analysis: any) => { try { const cardData = { - english: word, // 修正API欄位名稱 - chinese: analysis.translation || analysis.Translation || '', + word: word, + translation: analysis.translation || analysis.Translation || '', + definition: analysis.definition || analysis.Definition || '', pronunciation: analysis.pronunciation || analysis.Pronunciation || `/${word}/`, partOfSpeech: analysis.partOfSpeech || analysis.PartOfSpeech || 'unknown', example: `Example sentence with ${word}.` // 提供預設例句 diff --git a/frontend/components/FlashcardForm.tsx b/frontend/components/FlashcardForm.tsx index 5ec9a90..6944b80 100644 --- a/frontend/components/FlashcardForm.tsx +++ b/frontend/components/FlashcardForm.tsx @@ -30,8 +30,9 @@ export function FlashcardForm({ cardSets, initialData, isEdit = false, onSuccess const [formData, setFormData] = useState({ cardSetId: getDefaultCardSetId(), - english: initialData?.english || '', - chinese: initialData?.chinese || '', + word: initialData?.word || '', + translation: initialData?.translation || '', + definition: initialData?.definition || '', pronunciation: initialData?.pronunciation || '', partOfSpeech: initialData?.partOfSpeech || '名詞', example: initialData?.example || '', @@ -158,16 +159,16 @@ export function FlashcardForm({ cardSets, initialData, isEdit = false, onSuccess
handleChange('english', e.target.value)} + value={formData.word} + onChange={(e) => handleChange('word', e.target.value)} className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent" placeholder="例如:negotiate" required /> - {formData.english && ( + {formData.word && (
@@ -182,8 +183,8 @@ export function FlashcardForm({ cardSets, initialData, isEdit = false, onSuccess handleChange('chinese', e.target.value)} + value={formData.translation} + onChange={(e) => handleChange('translation', e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent" placeholder="例如:談判,協商" required diff --git a/frontend/lib/services/flashcards.ts b/frontend/lib/services/flashcards.ts index f00f1ce..75c72a8 100644 --- a/frontend/lib/services/flashcards.ts +++ b/frontend/lib/services/flashcards.ts @@ -42,8 +42,9 @@ export interface CreateCardSetRequest { export interface CreateFlashcardRequest { cardSetId?: string; - english: string; - chinese: string; + word: string; + translation: string; + definition: string; pronunciation: string; partOfSpeech: string; example: string;