# AI生成畫面前端程式碼規格 ## 📋 **概述** 本文件詳細說明DramaLing AI生成功能的前端程式碼架構、API調用、資料流程,以及如何理解和維護相關程式碼。 --- ## 🏗️ **檔案架構圖** ### **1. 核心檔案結構** ``` frontend/ ├── app/generate/ │ └── page.tsx # 🎯 主分析頁面 ├── components/ │ ├── ClickableTextV2.tsx # 🔍 可點擊詞彙組件 │ ├── Navigation.tsx # 🧭 導航組件 │ └── ProtectedRoute.tsx # 🔒 路由保護組件 └── lib/services/ └── flashcards.ts # 💾 詞卡服務層 ``` ### **2. 依賴關係圖** ``` page.tsx ├── imports Navigation.tsx ├── imports ProtectedRoute.tsx ├── imports ClickableTextV2.tsx └── imports flashcardsService ``` --- ## 🔄 **API調用架構** ### **1. 主分析頁面 (`/app/generate/page.tsx`)** #### **調用的API端點**: ```typescript POST /api/ai/analyze-sentence ``` #### **調用位置**: ```typescript // 第40行 - handleAnalyzeSentence函數 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({ inputText: textInput, userLevel: userLevel, // 個人化重點學習範圍 analysisMode: 'full' }) }) ``` #### **API回傳資料格式**: ```json { "success": true, "data": { "analysisId": "guid", "userLevel": "A2", "highValueCriteria": "B1-B2", "wordAnalysis": { "bonus": { "word": "bonus", "translation": "獎金", "definition": "額外給予的金錢", "partOfSpeech": "noun", "pronunciation": "/ˈboʊnəs/", "isHighValue": true, "difficultyLevel": "B1", "synonyms": ["reward", "incentive"], "example": "She received a year-end bonus.", "exampleTranslation": "她獲得了年終獎金。" } }, "sentenceMeaning": { "translation": "公司提供了獎金。" }, "grammarCorrection": { /*...*/ }, "highValueWords": ["bonus", "offered"] } } ``` ### **2. 可點擊詞彙組件 (`/components/ClickableTextV2.tsx`)** #### **調用的API端點**: ```typescript POST /api/ai/query-word ``` #### **調用位置有兩處**: ##### **位置1: handleCostConfirm函數 (第245行)** ```typescript const response = await fetch('http://localhost:5000/api/ai/query-word', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ word: showCostConfirm.word, sentence: text, analysisId: null }) }) ``` ##### **位置2: queryWordWithAI函數 (第303行)** ```typescript const response = await fetch('http://localhost:5000/api/ai/query-word', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ word: word, sentence: text, analysisId: null }) }) ``` #### **觸發條件**: - 用戶點擊詞彙時,如果該詞彙不在`analysis`物件中 - 用戶確認付費查詢詞彙時 ### **3. 詞卡服務 (`/lib/services/flashcards.ts`)** #### **調用的API端點**: ```typescript POST /api/flashcards // 創建詞卡 GET /api/flashcards // 查詢詞卡 GET /api/cardsets // 查詢詞卡組 ``` #### **調用方式**: ```typescript // 透過flashcardsService.createFlashcard()間接調用 await this.makeRequest>('/flashcards', { method: 'POST', body: JSON.stringify(data), }); ``` --- ## 📊 **資料流程架構** ### **1. 完整用戶操作流程** ```mermaid graph TD A[用戶輸入句子] --> B[點擊分析按鈕] B --> C[調用 analyze-sentence API] C --> D[接收完整詞彙分析資料] D --> E[顯示可點擊文字] E --> F[用戶點擊詞彙] F --> G{詞彙在analysis中?} G -->|是| H[直接顯示Portal彈窗] G -->|否| I[調用 query-word API] I --> J[覆蓋原有資料] J --> K[顯示Portal彈窗] H --> L[點擊保存詞卡] K --> L L --> M[調用 flashcards API] ``` ### **2. 狀態管理流程** ```typescript // 主頁面狀態 const [sentenceAnalysis, setSentenceAnalysis] = useState(null) // 完整詞彙分析 const [sentenceMeaning, setSentenceMeaning] = useState('') // 句子翻譯 const [grammarCorrection, setGrammarCorrection] = useState(null) // 語法修正 const [finalText, setFinalText] = useState('') // 最終文本 // ClickableTextV2狀態 const [selectedWord, setSelectedWord] = useState(null) // 選中詞彙 const [popupPosition, setPopupPosition] = useState({...}) // 彈窗位置 const [mounted, setMounted] = useState(false) // Portal渲染狀態 ``` ### **3. 資料傳遞路徑** ``` API回應 → setSentenceAnalysis → analysis prop → ClickableTextV2 → Portal彈窗 ``` --- ## 🎯 **組件職責分析** ### **1. `/app/generate/page.tsx` - 主分析頁面** #### **核心職責**: - 🎯 **句子分析觸發器** - 調用AI分析API - 📊 **資料狀態管理** - 管理分析結果和UI狀態 - 🎨 **UI佈局控制** - 控制分析前/後的畫面切換 - 🔧 **個人化設定** - 取得用戶程度設定 #### **關鍵函數**: ```typescript handleAnalyzeSentence() // 句子分析主函數 handleSaveWord() // 詞彙儲存函數 handleAcceptCorrection() // 語法修正處理 ``` #### **API依賴**: - `POST /api/ai/analyze-sentence` - 句子分析 - `flashcardsService.createFlashcard()` - 詞卡儲存 ### **2. `/components/ClickableTextV2.tsx` - 可點擊詞彙組件** #### **核心職責**: - 🖱️ **詞彙互動處理** - 處理詞彙點擊事件 - 🎨 **Portal彈窗管理** - 使用React Portal渲染彈窗 - 🔍 **詞彙資料查找** - 在analysis中查找或即時查詢 - 💾 **詞卡儲存整合** - 提供儲存到詞卡功能 #### **關鍵函數**: ```typescript handleWordClick() // 詞彙點擊處理 queryWordWithAI() // 即時詞彙查詢 getWordProperty() // 智能屬性讀取 VocabPopup() // Portal彈窗組件 ``` #### **API依賴**: - `POST /api/ai/query-word` - 即時詞彙查詢 #### **⚠️ 已知問題**: - 使用`query-word` API覆蓋了`analyze-sentence`的完整資料 - 導致例句和其他資料遺失 ### **3. `/components/Navigation.tsx` - 導航組件** #### **核心職責**: - 🧭 **頁面導航** - 提供網站主要頁面連結 - 👤 **用戶狀態顯示** - 顯示登入狀態 - ⚙️ **設定頁面入口** - 連結到用戶程度設定 #### **API依賴**:無直接API調用 ### **4. `/lib/services/flashcards.ts` - 詞卡服務層** #### **核心職責**: - 💾 **詞卡CRUD操作** - 創建、讀取、更新、刪除詞卡 - 🗂️ **詞卡組管理** - 管理詞卡分類 - 🔒 **API認證處理** - 自動添加JWT Token #### **API端點封裝**: ```typescript /api/flashcards // 詞卡CRUD /api/cardsets // 詞卡組管理 /api/cardsets/ensure-default // 確保預設詞卡組 ``` --- ## 🔍 **如何分析程式碼中的API調用** ### **1. 搜索技巧** #### **在VS Code或終端中**: ```bash # 搜索API調用 grep -r "fetch(" frontend/ grep -r "api/" frontend/ grep -r "localhost:5000" frontend/ # 搜索特定API端點 grep -r "analyze-sentence" frontend/ grep -r "query-word" frontend/ grep -r "flashcards" frontend/ ``` #### **在瀏覽器開發者工具中**: 1. **Network面板** - 查看實際API調用 2. **Console面板** - 查看調試輸出 3. **Application面板** - 查看localStorage資料 ### **2. 程式碼閱讀要點** #### **識別API調用的關鍵字**: ```typescript // 直接API調用 fetch('http://localhost:5000/api/...') await fetch(...) // 服務層調用 flashcardsService.createFlashcard() flashcardsService.getFlashcards() // 其他HTTP客戶端 axios.post(...) ``` #### **找到觸發條件**: ```typescript // 用戶事件觸發 onClick={handleAnalyzeSentence} onClick={(e) => handleWordClick(word, e)} // 狀態變化觸發 useEffect(() => { /* API調用 */ }, [dependency]) ``` ### **3. 資料流追蹤** #### **API回應到狀態**: ```typescript const result = await response.json() setSentenceAnalysis(result.data.WordAnalysis) // 儲存到狀態 ``` #### **狀態到組件**: ```typescript ``` --- ## 🚨 **當前架構問題分析** ### **1. API調用衝突問題** #### **問題描述**: - **主頁面** 調用 `analyze-sentence` API → 取得完整詞彙資料(包含例句) - **詞彙組件** 調用 `query-word` API → 取得簡化資料(無例句) - **結果** → 好資料被壞資料覆蓋 #### **程式碼位置**: ```typescript // ✅ 正確的API (page.tsx:40) POST /api/ai/analyze-sentence → 完整資料 // ❌ 問題的API (ClickableTextV2.tsx:245, 303) POST /api/ai/query-word → 簡化資料 ``` #### **觸發條件**: ```typescript // ClickableTextV2.tsx:221 if (wordAnalysis) { // 使用預存資料 ✅ } else { // 調用 query-word API ❌ await queryWordWithAI(cleanWord, position) } ``` ### **2. 資料不一致問題** #### **analyze-sentence 回傳**: ```json { "example": "She received a year-end bonus for her hard work.", "exampleTranslation": "她因為努力工作獲得了年終獎金。", "synonyms": ["reward", "incentive", "extra pay"] } ``` #### **query-word 回傳**: ```json { "example": null, "exampleTranslation": null, "synonyms": [] } ``` --- ## 🎨 **UI組件架構** ### **1. Portal彈窗系統** #### **技術實現**: ```typescript import { createPortal } from 'react-dom' const VocabPopup = () => { if (!selectedWord || !analysis?.[selectedWord] || !mounted) return null return createPortal(
{/* 彈窗內容 */}
, document.body // 渲染到body,避免CSS繼承 ) } ``` #### **設計優勢**: - **完全脫離父級CSS繼承** - **響應式定位系統** - **詞卡風格一致性** ### **2. 個人化標記系統** #### **詞彙分類邏輯**: ```typescript const getWordClass = (word: string) => { const wordAnalysis = analysis?.[cleanWord] const isHighValue = getWordProperty(wordAnalysis, 'isHighValue') if (isHighValue) { return "bg-green-100 border-green-400 hover:bg-green-200" // 重點學習 } else { return "bg-blue-100 border-blue-300 hover:bg-blue-200" // 普通詞彙 } } ``` #### **視覺效果**: - **重點學習詞彙** → 綠色邊框 + ⭐ 標記 - **普通詞彙** → 藍色邊框 - **未分析詞彙** → 灰色虛線邊框 --- ## 📊 **狀態管理架構** ### **1. 主頁面狀態流** ```typescript // 分析階段 [textInput] → handleAnalyzeSentence() → [sentenceAnalysis] ↓ [sentenceMeaning] ↓ [grammarCorrection] // 顯示階段 [sentenceAnalysis] → ClickableTextV2 → Portal彈窗 ``` ### **2. 詞彙組件狀態流** ```typescript // 點擊階段 handleWordClick() → [selectedWord] + [popupPosition] ↓ VocabPopup() Portal渲染 // 儲存階段 handleSaveWord() → flashcardsService.createFlashcard() ``` ### **3. 個人化設定流** ```typescript localStorage.getItem('userEnglishLevel') → API請求 → 個人化結果 ``` --- ## 🔧 **關鍵技術實現** ### **1. Portal彈窗技術** #### **為什麼使用Portal**: ```typescript // ❌ 舊方式 - CSS繼承問題
可點擊文字
彈窗
// 會繼承text-lg
// ✅ Portal方式 - 完全隔離
可點擊文字
{createPortal(
彈窗
, // 渲染到body,不繼承 document.body )} ``` ### **2. 智能屬性讀取** #### **解決大小寫不一致**: ```typescript const getWordProperty = (wordData: any, propName: string) => { const variations = [ propName, // 原始 propName.toLowerCase(), // 小寫 propName.charAt(0).toUpperCase() + propName.slice(1) // 首字母大寫 ]; for (const variation of variations) { if (wordData[variation] !== undefined) { return wordData[variation]; } } } ``` ### **3. 個人化重點學習範圍** #### **前端整合**: ```typescript // 讀取用戶程度 const userLevel = localStorage.getItem('userEnglishLevel') || 'A2'; // 傳遞給API body: JSON.stringify({ inputText: textInput, userLevel: userLevel, // 個人化參數 analysisMode: 'full' }) // 顯示重點學習範圍 const getTargetRange = (level: string) => { const ranges = { 'A1': 'A2-B1', 'A2': 'B1-B2', 'B1': 'B2-C1', 'B2': 'C1-C2', 'C1': 'C2', 'C2': 'C2' }; return ranges[level] || 'B1-B2'; }; ``` --- ## 🛠️ **開發維護指南** ### **1. 如何添加新的API調用** #### **步驟**: 1. **選擇調用位置** - 頁面組件或服務層 2. **定義請求格式** - TypeScript介面 3. **處理回應資料** - 錯誤處理和狀態更新 4. **更新UI狀態** - 觸發重新渲染 #### **範例**: ```typescript // 1. 定義介面 interface NewApiRequest { input: string; options: object; } // 2. API調用 const callNewApi = async (data: NewApiRequest) => { try { const response = await fetch('/api/new-endpoint', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (response.ok) { const result = await response.json(); // 3. 更新狀態 setNewData(result.data); } } catch (error) { console.error('API調用失敗:', error); } }; ``` ### **2. 如何修改詞彙顯示邏輯** #### **修改位置**: ```typescript // 詞彙分類邏輯 ClickableTextV2.tsx → getWordClass() 函數 // 彈窗內容 ClickableTextV2.tsx → VocabPopup() 組件 // 屬性讀取 ClickableTextV2.tsx → getWordProperty() 函數 ``` ### **3. 如何添加新的詞彙屬性** #### **步驟**: 1. **後端API** - 確保API回傳新屬性 2. **前端介面** - 更新TypeScript介面 3. **屬性讀取** - 在`getWordProperty`中處理 4. **UI顯示** - 在Portal彈窗中顯示 --- ## 🔍 **問題診斷指南** ### **1. API調用問題** #### **檢查步驟**: ```typescript // 1. 檢查Network面板 // 瀏覽器 → F12 → Network → 查看API調用 // 2. 檢查Console輸出 console.log('API回應:', result); // 3. 檢查回應格式 console.log('詞彙資料:', result.data.WordAnalysis?.bonus); ``` #### **常見問題**: - **API端點錯誤** - 檢查URL是否正確 - **請求格式錯誤** - 檢查Content-Type和body - **認證問題** - 檢查JWT Token ### **2. 資料顯示問題** #### **檢查步驟**: ```typescript // 1. 檢查狀態 console.log('sentenceAnalysis:', sentenceAnalysis); // 2. 檢查組件接收 console.log('analysis prop:', analysis); // 3. 檢查屬性讀取 console.log('getWordProperty結果:', getWordProperty(wordData, 'example')); ``` ### **3. Portal彈窗問題** #### **檢查步驟**: ```typescript // 1. 檢查Portal渲染條件 console.log('selectedWord:', selectedWord); console.log('mounted:', mounted); // 2. 檢查彈窗位置 console.log('popupPosition:', popupPosition); // 3. 檢查CSS樣式 // 瀏覽器 → F12 → Elements → 檢查Portal元素 ``` --- ## 🚀 **最佳實踐建議** ### **1. API調用** - ✅ **統一使用服務層** - 避免直接在組件中調用API - ✅ **錯誤處理** - 每個API調用都要有try-catch - ✅ **loading狀態** - 提供用戶反饋 - ✅ **快取策略** - 避免重複調用相同API ### **2. 狀態管理** - ✅ **單一資料來源** - 避免狀態重複 - ✅ **明確的狀態型別** - 使用TypeScript介面 - ✅ **適當的狀態粒度** - 不要過度細分或合併 ### **3. 組件設計** - ✅ **職責單一** - 每個組件專注一個功能 - ✅ **Props介面** - 明確定義組件輸入 - ✅ **可重用性** - 組件應該可以在多處使用 --- ## 📝 **未來改進方向** ### **1. 統一API策略** - 合併`analyze-sentence`和`query-word`的功能 - 建立統一的詞彙分析端點 - 減少API調用複雜度 ### **2. 效能優化** - 實現詞彙分析結果快取 - 減少不必要的API調用 - 優化Portal渲染效能 ### **3. 用戶體驗提升** - 添加載入動畫 - 優化錯誤處理和用戶提示 - 增強響應式設計 --- **文件版本**: v1.0 **建立日期**: 2025-09-21 **維護團隊**: DramaLing開發團隊 --- ## 📞 **技術支援** 如需修改或擴展AI生成功能,請參考本規格文件的相關章節,並遵循最佳實踐建議進行開發。