From d25ebe26835783051b04636d3e148dbcdeddfe13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=84=AD=E6=B2=9B=E8=BB=92?= Date: Thu, 25 Sep 2025 00:34:22 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E8=A9=9E=E5=8D=A1=E7=AE=A1=E7=90=86=E8=B3=87=E6=96=99=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=9C=96=E6=96=87=E6=AA=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 📊 完整的前端詞卡與例句圖片資料流程說明 **流程圖內容**: - ✅ 整體架構圖:從用戶訪問到圖片顯示的完整流程 - ✅ 詳細資料流程:8個關鍵階段的技術實現 - ✅ 後端資料處理:EF Core查詢 + Include圖片關聯 - ✅ 前端UI渲染:React組件和響應式圖片處理 **技術細節文檔**: - ✅ API資料結構:完整的JSON回應格式 - ✅ 圖片顯示邏輯:有圖/無圖的UI判斷 - ✅ 響應式設計:CSS處理各種螢幕尺寸 - ✅ 錯誤處理機制:網路和圖片載入失敗處理 **互動流程說明**: - ✅ 圖片生成流程:從點擊按鈕到顯示完成圖片 - ✅ 狀態管理:生成進度追蹤和UI更新 - ✅ 用戶體驗:2-3分鐘生成過程的完整體驗 **效能優化策略**: - ✅ 圖片懶載入和錯誤處理 - ✅ API快取和查詢優化 - ✅ 響應式圖片處理 (512x512 → CSS縮放) 包含完整的Mermaid流程圖和技術實現範例! 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- FRONTEND_FLASHCARD_DATA_FLOW_DIAGRAM.md | 533 ++++++++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100644 FRONTEND_FLASHCARD_DATA_FLOW_DIAGRAM.md diff --git a/FRONTEND_FLASHCARD_DATA_FLOW_DIAGRAM.md b/FRONTEND_FLASHCARD_DATA_FLOW_DIAGRAM.md new file mode 100644 index 0000000..272b603 --- /dev/null +++ b/FRONTEND_FLASHCARD_DATA_FLOW_DIAGRAM.md @@ -0,0 +1,533 @@ +# 前端詞卡管理資料流程圖 + +## 📋 **文檔概覽** + +本文檔詳細說明前端詞卡管理功能如何取得詞卡及其例句圖片,並顯示在用戶介面上的完整資料流程。 + +--- + +## 🏗️ **整體架構圖** + +```mermaid +graph TB + A[用戶訪問 /flashcards] --> B[FlashcardsContent 組件初始化] + B --> C[useEffect 觸發資料載入] + C --> D[flashcardsService.getFlashcards()] + D --> E[HTTP GET /api/flashcards] + E --> F[FlashcardsController.GetFlashcards()] + F --> G[EF Core 查詢 + Include 圖片關聯] + G --> H[資料庫查詢 flashcards + example_images] + H --> I[IImageStorageService.GetImageUrlAsync()] + I --> J[組裝回應資料] + J --> K[前端接收 flashcards 陣列] + K --> L[狀態更新 setFlashcards()] + L --> M[UI 重新渲染] + M --> N[FlashcardItem 組件渲染] + N --> O[圖片顯示邏輯判斷] + O --> P{有例句圖片?} + P -->|Yes| Q[顯示圖片 ] + P -->|No| R[顯示新增按鈕] + Q --> S[響應式圖片縮放] + R --> T[點擊觸發 handleGenerateExampleImage] +``` + +--- + +## 🔄 **詳細資料流程** + +### **第1階段:頁面初始化** + +#### **1.1 組件載入** +```typescript +// /frontend/app/flashcards/page.tsx +export default function FlashcardsPage() { + return ( + + + + ) +} + +function FlashcardsContent() { + const [searchState, searchActions] = useFlashcardSearch(activeTab) + + useEffect(() => { + loadTotalCounts() // 初始化資料載入 + }, []) +} +``` + +#### **1.2 資料載入觸發** +```mermaid +sequenceDiagram + participant UC as 用戶 + participant FC as FlashcardsContent + participant FS as flashcardsService + participant API as Backend API + + UC->>FC: 訪問 /flashcards + FC->>FC: useEffect 觸發 + FC->>FS: searchActions.refresh() + FS->>API: GET /api/flashcards +``` + +--- + +### **第2階段:後端資料處理** + +#### **2.1 API 端點處理** +```csharp +// FlashcardsController.GetFlashcards() +var query = _context.Flashcards + .Include(f => f.FlashcardExampleImages) // 載入圖片關聯 + .ThenInclude(fei => fei.ExampleImage) // 載入圖片詳情 + .Where(f => f.UserId == userId && !f.IsArchived) + .AsQueryable(); +``` + +#### **2.2 資料庫查詢流程** +```mermaid +graph LR + A[Flashcards Table] --> B[FlashcardExampleImages Table] + B --> C[ExampleImages Table] + A --> D[User Filter] + A --> E[Search Filter] + A --> F[CEFR Filter] + C --> G[Image URL Generation] + G --> H[完整 JSON 回應] +``` + +#### **2.3 圖片資料組裝** +```csharp +// 每個 flashcard 處理圖片關聯 +foreach (var flashcardImage in flashcard.FlashcardExampleImages) +{ + var imageUrl = await _imageStorageService.GetImageUrlAsync( + flashcardImage.ExampleImage.RelativePath + ); + + exampleImages.Add(new ExampleImageDto + { + Id = flashcardImage.ExampleImage.Id.ToString(), + ImageUrl = imageUrl, // 完整的 HTTP URL + IsPrimary = flashcardImage.IsPrimary, + QualityScore = flashcardImage.ExampleImage.QualityScore + }); +} +``` + +--- + +### **第3階段:前端資料接收與處理** + +#### **3.1 API 回應結構** +```json +{ + "success": true, + "data": { + "flashcards": [ + { + "id": "94c32b17-53a7-4de5-9bfc-f6d4f2dc1368", + "word": "up", + "translation": "出", + "example": "He brought the issue up in the meeting.", + + // 新增的圖片相關欄位 + "exampleImages": [ + { + "id": "d96d3330-7814-45e1-9ac6-801c8ca32ee7", + "imageUrl": "https://localhost:5008/images/examples/xxx.png", + "isPrimary": true, + "qualityScore": 0.95, + "fileSize": 190000 + } + ], + "hasExampleImage": true, + "primaryImageUrl": "https://localhost:5008/images/examples/xxx.png" + } + ] + } +} +``` + +#### **3.2 前端狀態更新流程** +```mermaid +graph TD + A[API 回應接收] --> B[解構 flashcards 陣列] + B --> C[更新 React 狀態] + C --> D[觸發組件重新渲染] + D --> E[FlashcardItem 組件 map 渲染] + E --> F[個別詞卡資料傳入] + F --> G[圖片顯示邏輯判斷] +``` + +--- + +### **第4階段:UI 渲染與圖片顯示** + +#### **4.1 詞卡項目渲染** +```typescript +// FlashcardItem 組件 +function FlashcardItem({ card, ... }) { + return ( +
+ {/* 圖片區域 - 響應式設計 */} +
+ {hasExampleImage(card) ? ( + // 顯示圖片 + {`${card.word} + ) : ( + // 顯示新增按鈕 +
onGenerateExampleImage(card)}> + 新增例句圖 +
+ )} +
+ + {/* 詞卡資訊 */} +
+

{card.word}

+ {card.translation} +
+
+ ) +} +``` + +#### **4.2 圖片顯示判斷邏輯** +```mermaid +flowchart TD + A[FlashcardItem 渲染] --> B{檢查 card.hasExampleImage} + B -->|true| C[取得 card.primaryImageUrl] + B -->|false| D[顯示新增例句圖按鈕] + C --> E[設定 img src 屬性] + E --> F[瀏覽器載入圖片] + F --> G{圖片載入成功?} + G -->|成功| H[顯示 512x512 圖片] + G -->|失敗| I[顯示錯誤提示] + D --> J[用戶點擊生成按鈕] + J --> K[觸發 handleGenerateExampleImage] + H --> L[CSS 響應式縮放顯示] +``` + +--- + +## 🔧 **技術實現細節** + +### **前端服務層** +```typescript +// /frontend/lib/services/flashcards.ts +export const flashcardsService = { + async getFlashcards(): Promise { + const response = await fetch(`${API_URL}/api/flashcards`, { + headers: { + 'Authorization': `Bearer ${getToken()}`, + 'Content-Type': 'application/json' + } + }) + + return response.json() + } +} + +// 回應介面定義 +interface Flashcard { + id: string + word: string + translation: string + example: string + + // 圖片相關欄位 + exampleImages: ExampleImage[] + hasExampleImage: boolean + primaryImageUrl?: string +} + +interface ExampleImage { + id: string + imageUrl: string + isPrimary: boolean + qualityScore?: number + fileSize?: number +} +``` + +### **圖片顯示邏輯** +```typescript +// 當前實現 (將被取代) +const getExampleImage = (card: Flashcard): string | null => { + // 硬編碼映射 (舊方式) + const imageMap: {[key: string]: string} = { + 'evidence': '/images/examples/bring_up.png', + } + return imageMap[card.word?.toLowerCase()] || null +} + +// 新實現 (基於 API 資料) +const getExampleImage = (card: Flashcard): string | null => { + return card.primaryImageUrl || null +} + +const hasExampleImage = (card: Flashcard): boolean => { + return card.hasExampleImage +} +``` + +--- + +## 🖼️ **圖片載入和顯示流程** + +### **圖片 URL 生成過程** +```mermaid +sequenceDiagram + participant FE as 前端 + participant BE as 後端 API + participant DB as 資料庫 + participant FS as 檔案系統 + + FE->>BE: GET /api/flashcards + BE->>DB: 查詢 flashcards + images + DB-->>BE: 返回關聯資料 + BE->>FS: 檢查圖片檔案存在 + FS-->>BE: 確認檔案路徑 + BE->>BE: 生成完整 HTTP URL + BE-->>FE: 回應包含 imageUrl + FE->>FS: 瀏覽器請求圖片 + FS-->>FE: 返回 512x512 PNG 圖片 +``` + +### **響應式圖片顯示** +```css +/* 圖片容器響應式尺寸 */ +.example-image-container { + /* 手機 */ + width: 128px; /* w-32 */ + height: 80px; /* h-20 */ +} + +@media (min-width: 640px) { + .example-image-container { + /* 平板 */ + width: 160px; /* sm:w-40 */ + height: 96px; /* sm:h-24 */ + } +} + +@media (min-width: 768px) { + .example-image-container { + /* 桌面 */ + width: 192px; /* md:w-48 */ + height: 128px; /* md:h-32 */ + } +} + +/* 圖片本身處理 */ +.example-image { + width: 100%; + height: 100%; + object-fit: cover; /* 保持比例,裁切適應容器 */ + border-radius: 8px; +} +``` + +--- + +## ⚡ **效能優化策略** + +### **前端優化** +```typescript +// 圖片懶載入 +{`${card.word} + +// 錯誤處理 + { + e.target.style.display = 'none' + // 顯示備用內容 + }} +/> +``` + +### **後端優化** +```csharp +// 查詢優化 +var flashcards = await query + .AsNoTracking() // 只讀查詢優化 + .OrderByDescending(f => f.CreatedAt) + .ToListAsync(); + +// 圖片 URL 快取 (未來實現) +private readonly IMemoryCache _urlCache; +``` + +--- + +## 🎮 **用戶互動流程** + +### **圖片生成流程** +```mermaid +flowchart TD + A[用戶看到詞卡] --> B{是否有圖片?} + B -->|有| C[顯示 512x512 圖片] + B -->|無| D[顯示新增例句圖按鈕] + D --> E[用戶點擊按鈕] + E --> F[觸發 handleGenerateExampleImage] + F --> G[調用圖片生成 API] + G --> H[顯示生成進度] + H --> I[等待 2-3 分鐘] + I --> J[生成完成] + J --> K[自動刷新詞卡列表] + K --> L[新圖片顯示在詞卡中] +``` + +### **生成進度顯示** +```typescript +// 生成狀態管理 +const [generatingCards, setGeneratingCards] = useState>(new Set()) + +const handleGenerateExampleImage = async (card: Flashcard) => { + // 1. 標記為生成中 + setGeneratingCards(prev => new Set([...prev, card.id])) + + try { + // 2. 調用生成 API + const result = await imageGenerationService.generateImage(card.id) + + // 3. 輪詢進度 + await imageGenerationService.pollUntilComplete(result.requestId) + + // 4. 刷新資料 + await searchActions.refresh() + + // 5. 顯示成功訊息 + toast.success(`「${card.word}」的例句圖片生成完成!`) + } catch (error) { + toast.error(`圖片生成失敗: ${error.message}`) + } finally { + // 6. 移除生成中狀態 + setGeneratingCards(prev => { + const newSet = new Set(prev) + newSet.delete(card.id) + return newSet + }) + } +} +``` + +--- + +## 📊 **資料流轉換表** + +| 階段 | 資料格式 | 位置 | 範例 | +|------|----------|------|------| +| **資料庫** | 關聯表格 | `flashcard_example_images` | `{flashcard_id, example_image_id, is_primary}` | +| **EF Core** | 實體物件 | `Flashcard.FlashcardExampleImages` | `List` | +| **後端 API** | JSON 回應 | HTTP Response | `{hasExampleImage: true, primaryImageUrl: "https://..."}` | +| **前端狀態** | TypeScript 物件 | React State | `flashcards: Flashcard[]` | +| **UI 組件** | JSX 元素 | React Component | `` | +| **瀏覽器** | 實際圖片 | DOM | `512x512 PNG 圖片顯示` | + +--- + +## 🔍 **錯誤處理流程** + +### **API 層級錯誤** +```mermaid +graph TD + A[API 調用] --> B{網路狀態} + B -->|成功| C[解析 JSON] + B -->|失敗| D[顯示網路錯誤] + C --> E{success: true?} + E -->|Yes| F[正常資料流程] + E -->|No| G[顯示 API 錯誤訊息] + D --> H[重試機制] + G --> H + H --> I[用戶手動重新整理] +``` + +### **圖片載入錯誤** +```typescript +// 圖片載入失敗處理 +const handleImageError = (e: React.SyntheticEvent) => { + const target = e.target as HTMLImageElement + target.style.display = 'none' + + // 顯示備用內容 + target.parentElement!.innerHTML = ` +
+ + + + 圖片載入失敗 +
+ ` +} +``` + +--- + +## 🎯 **實際運作範例** + +### **情境1:有圖片的詞卡 (deal)** +``` +1. 用戶訪問詞卡頁面 +2. API 返回: hasExampleImage: true, primaryImageUrl: "https://localhost:5008/..." +3. React 渲染: +4. 瀏覽器載入: 512x512 PNG 圖片 (約190KB) +5. CSS 處理: 響應式縮放顯示在詞卡中 +``` + +### **情境2:無圖片的詞卡 (up)** +``` +1. 用戶訪問詞卡頁面 +2. API 返回: hasExampleImage: false, primaryImageUrl: null +3. React 渲染: 新增例句圖按鈕 +4. 用戶點擊: 觸發圖片生成流程 +5. 生成完成: 自動刷新並顯示新圖片 +``` + +--- + +## 🔮 **未來擴展規劃** + +### **前端增強功能** +- **圖片預覽**: 點擊圖片查看大圖 +- **多圖片支援**: 輪播顯示多張例句圖 +- **圖片編輯**: 刪除、重新生成功能 +- **批量生成**: 一次為多個詞卡生成圖片 + +### **效能優化** +- **圖片 CDN**: 雲端加速分發 +- **WebP 格式**: 更小的檔案大小 +- **預載入**: 預先載入即將顯示的圖片 +- **虛擬化**: 大量詞卡的效能優化 + +--- + +## 📈 **監控指標** + +### **前端效能** +- 頁面載入時間: 目標 < 2 秒 +- 圖片載入時間: 目標 < 1 秒 +- API 回應時間: 目標 < 500ms + +### **用戶體驗** +- 圖片顯示成功率: 目標 > 95% +- 生成成功率: 目標 > 90% +- 用戶滿意度: 目標 > 4.5/5 + +--- + +**文檔版本**: v1.0 +**建立日期**: 2025-09-24 +**最後更新**: 2025-09-24 +**相關文檔**: [前後端整合計劃](./EXAMPLE_IMAGE_FRONTEND_BACKEND_INTEGRATION_PLAN.md) \ No newline at end of file