docs: 新增前端詞卡管理資料流程圖文檔
📊 完整的前端詞卡與例句圖片資料流程說明 **流程圖內容**: - ✅ 整體架構圖:從用戶訪問到圖片顯示的完整流程 - ✅ 詳細資料流程: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 <noreply@anthropic.com>
This commit is contained in:
parent
4243528376
commit
d25ebe2683
|
|
@ -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[顯示圖片 <img>]
|
||||||
|
P -->|No| R[顯示新增按鈕]
|
||||||
|
Q --> S[響應式圖片縮放]
|
||||||
|
R --> T[點擊觸發 handleGenerateExampleImage]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 **詳細資料流程**
|
||||||
|
|
||||||
|
### **第1階段:頁面初始化**
|
||||||
|
|
||||||
|
#### **1.1 組件載入**
|
||||||
|
```typescript
|
||||||
|
// /frontend/app/flashcards/page.tsx
|
||||||
|
export default function FlashcardsPage() {
|
||||||
|
return (
|
||||||
|
<ProtectedRoute>
|
||||||
|
<FlashcardsContent />
|
||||||
|
</ProtectedRoute>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className="bg-white border rounded-lg">
|
||||||
|
{/* 圖片區域 - 響應式設計 */}
|
||||||
|
<div className="w-32 h-20 sm:w-40 sm:h-24 md:w-48 md:h-32">
|
||||||
|
{hasExampleImage(card) ? (
|
||||||
|
// 顯示圖片
|
||||||
|
<img
|
||||||
|
src={getExampleImage(card)}
|
||||||
|
alt={`${card.word} example`}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
// 顯示新增按鈕
|
||||||
|
<div onClick={() => onGenerateExampleImage(card)}>
|
||||||
|
<span>新增例句圖</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 詞卡資訊 */}
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3>{card.word}</h3>
|
||||||
|
<span>{card.translation}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **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<FlashcardsResponse> {
|
||||||
|
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
|
||||||
|
// 圖片懶載入
|
||||||
|
<img
|
||||||
|
src={card.primaryImageUrl}
|
||||||
|
loading="lazy" // 瀏覽器原生懶載入
|
||||||
|
alt={`${card.word} example`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
// 錯誤處理
|
||||||
|
<img
|
||||||
|
src={card.primaryImageUrl}
|
||||||
|
onError={(e) => {
|
||||||
|
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<Set<string>>(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<FlashcardExampleImage>` |
|
||||||
|
| **後端 API** | JSON 回應 | HTTP Response | `{hasExampleImage: true, primaryImageUrl: "https://..."}` |
|
||||||
|
| **前端狀態** | TypeScript 物件 | React State | `flashcards: Flashcard[]` |
|
||||||
|
| **UI 組件** | JSX 元素 | React Component | `<img src={card.primaryImageUrl} />` |
|
||||||
|
| **瀏覽器** | 實際圖片 | 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<HTMLImageElement>) => {
|
||||||
|
const target = e.target as HTMLImageElement
|
||||||
|
target.style.display = 'none'
|
||||||
|
|
||||||
|
// 顯示備用內容
|
||||||
|
target.parentElement!.innerHTML = `
|
||||||
|
<div class="text-gray-400 text-xs text-center">
|
||||||
|
<svg class="w-6 h-6 mx-auto mb-1">
|
||||||
|
<!-- 錯誤圖示 -->
|
||||||
|
</svg>
|
||||||
|
圖片載入失敗
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 **實際運作範例**
|
||||||
|
|
||||||
|
### **情境1:有圖片的詞卡 (deal)**
|
||||||
|
```
|
||||||
|
1. 用戶訪問詞卡頁面
|
||||||
|
2. API 返回: hasExampleImage: true, primaryImageUrl: "https://localhost:5008/..."
|
||||||
|
3. React 渲染: <img src="https://localhost:5008/..." />
|
||||||
|
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)
|
||||||
Loading…
Reference in New Issue