dramaling-vocab-learning/前端圖片URL處理機制詳解.md

220 lines
6.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 前端圖片 URL 處理機制詳解
## 📋 概述
本文檔詳細解釋 DramaLing 前端如何處理詞卡圖片 URL以及為什麼不同格式的 URL 都能正常運作。
## 🎯 核心工具:`flashcardUtils.ts`
### 檔案位置
```
frontend/lib/utils/flashcardUtils.ts
```
### 檔案目的
**統一管理詞卡相關的顯示和處理邏輯** - 避免在各個元件中重複寫相同的處理代碼
---
## 📝 核心函數詳細解析
### 1. **圖片 URL 處理 - `getFlashcardImageUrl()`**
**位置**: 第 77-99 行
**用途**: 智能處理詞卡圖片 URL支援多種格式和來源
#### 處理邏輯流程:
```typescript
export const getFlashcardImageUrl = (flashcard: any): string | null => {
// 第一優先:檢查 primaryImageUrl
if (flashcard.primaryImageUrl) {
// 判斷是相對路徑還是完整 URL
if (flashcard.primaryImageUrl.startsWith('/')) {
// 相對路徑:拼接後端基礎 URL
return `${API_CONFIG.BASE_URL}${flashcard.primaryImageUrl}`
}
// 完整 URL直接使用
return flashcard.primaryImageUrl
}
// 第二優先:檢查 exampleImages 陣列
if (flashcard.exampleImages && flashcard.exampleImages.length > 0) {
// 尋找標記為主要的圖片
const primaryImage = flashcard.exampleImages.find((img: any) => img.isPrimary)
if (primaryImage) {
const imageUrl = primaryImage.imageUrl
return imageUrl?.startsWith('/') ? `${API_CONFIG.BASE_URL}${imageUrl}` : imageUrl
}
// 沒有主要圖片,使用第一張
const firstImageUrl = flashcard.exampleImages[0].imageUrl
return firstImageUrl?.startsWith('/') ? `${API_CONFIG.BASE_URL}${firstImageUrl}` : firstImageUrl
}
// 都沒有圖片
return null
}
```
#### 支援的 URL 格式:
| 格式類型 | 範例 | 處理方式 |
|---------|------|----------|
| **Google Cloud Storage** | `https://storage.googleapis.com/dramaling-images/examples/file.png` | 直接使用完整 URL |
| **本地服務** | `http://localhost:5008/images/examples/file.png` | 直接使用完整 URL |
| **相對路徑** | `/images/examples/file.png` | 拼接為 `http://localhost:5008/images/examples/file.png` |
---
### 2. **其他工具函數**
#### **詞性顯示 - `getPartOfSpeechDisplay()`**
```typescript
// 輸入:"noun" → 輸出:"n."
// 輸入:"adjective" → 輸出:"adj."
// 輸入:"preposition/adverb" → 輸出:"prep./adv." (支援複合詞性)
```
#### **CEFR 等級顏色 - `getCEFRColor()`**
```typescript
// A1 → "bg-green-100 text-green-700 border-green-200" (綠色)
// B1 → "bg-yellow-100 text-yellow-700 border-yellow-200" (黃色)
// C2 → "bg-purple-100 text-purple-700 border-purple-200" (紫色)
```
#### **熟練度處理**
- **`getMasteryColor()`**: 根據數字返回顏色 (90+→綠色, <50紅色)
- **`getMasteryText()`**: 轉換為中文 (90+→"精通", <50→"學習中")
#### **日期格式化**
- **`formatNextReviewDate()`**: 複習時間 (過期→"需要複習", 明天→"明天")
- **`formatCreatedDate()`**: 台灣日期格式 ("2024/1/15")
#### **統計計算 - `calculateFlashcardStats()`**
```typescript
{
total: 總詞卡數,
mastered: 精通詞卡數 (熟練度 80),
learning: 學習中詞卡數 (熟練度 40-79),
new: 新詞卡數 (熟練度 < 40),
favorites: 收藏詞卡數,
masteryPercentage: 精通百分比
}
```
---
## 🔍 實際運作流程
### API 回應格式
**目前狀態(修復後):**
- `/api/flashcards`: 回傳完整 Google Cloud Storage URL
- `/api/flashcards/{id}`: 回傳完整 Google Cloud Storage URL
```json
{
"primaryImageUrl": "https://storage.googleapis.com/dramaling-images/examples/b2bb23b8-16dd-44b2-bf64-34c468f2d362_e6498ba6-742b-473f-93b6-f4b58c3dd3e9.png"
}
```
### 前端處理流程
1. **接收 API 回應** 取得 `primaryImageUrl`
2. **呼叫 `getFlashcardImageUrl()`** 檢查 URL 格式
3. **格式判斷**
- 完整 URL (`https://storage.googleapis.com/...`) 直接使用
- 相對路徑 (`/images/...`) 拼接後端域名目前不會發生
4. **元件渲染** `<img src={imageUrl} />`
---
## 🎯 設計優勢
### **1. 兼容性設計**
- 支援多種 URL 格式相對路徑/完整 URL
- 可以無縫切換本地/雲端儲存
- 向後兼容舊版 API
### **2. 防禦性編程**
- 多重備用方案primaryImageUrl exampleImages null
- 自動處理路徑拼接邏輯
- 避免因 API 格式變更導致圖片顯示失敗
### **3. 集中化管理**
- 所有圖片 URL 處理邏輯集中在一個函數
- 修改邏輯時只需要改一個地方
- 多個元件可以重用相同邏輯
### **4. 模組化架構**
- 每個功能都有專門的工具函數
- 易於測試和維護
- 符合 DRY (Don't Repeat Yourself) 原則
---
## 🔧 使用範例
### 在元件中使用
```typescript
// FlashcardCard.tsx
import { getFlashcardImageUrl, getCEFRColor, getPartOfSpeechDisplay } from '@/lib/utils/flashcardUtils'
// 取得圖片 URL
const imageUrl = getFlashcardImageUrl(flashcard)
// 結果https://storage.googleapis.com/dramaling-images/examples/file.png
// 取得 CEFR 顏色
const cefrClasses = getCEFRColor(flashcard.cefr)
// 結果:"bg-blue-100 text-blue-700 border-blue-200"
// 取得詞性簡寫
const partOfSpeech = getPartOfSpeechDisplay(flashcard.partOfSpeech)
// 結果:"n."
// 在 JSX 中使用
<img src={imageUrl} alt="example" />
<span className={cefrClasses}>{flashcard.cefr}</span>
<span>{partOfSpeech}</span>
```
---
## 📊 問題解答
### Q: 為什麼不同格式的 URL 都能正常運作?
**A**: 前端設計了智能兼容性處理
1. **完整 URL** 直接使用目前的情況
2. **相對路徝** 自動拼接後端域名向後兼容
3. **多重備用** primaryImageUrl 失敗時使用 exampleImages
### Q: 這樣的設計有什麼好處?
**A**:
- **彈性切換**可以在本地開發和雲端部署間切換
- **向後兼容**支援舊版 API 格式
- **錯誤處理**多重備用方案確保圖片顯示
- **維護性**集中化管理易於修改
### Q: 目前系統的實際運作狀況?
**A**:
- 後端統一回傳完整的 Google Cloud Storage URLs
- 前端接收到完整 URL直接使用不進入相對路徑處理邏輯
- 圖片正常顯示系統運作正常
---
## 📈 總結
`flashcardUtils.ts` 是一個設計良好的工具函數庫實現了
- **統一化**所有詞卡相關的顯示邏輯集中管理
- **兼容性**支援多種 URL 格式和資料來源
- **可維護性**模組化設計易於擴展和修改
- **可靠性**防禦性編程確保系統穩定運作
這種設計確保了前端系統的健壯性和可維護性是現代前端架構的最佳實務範例