695 lines
18 KiB
Markdown
695 lines
18 KiB
Markdown
# 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<ApiResponse<Flashcard>>('/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<any>(null) // 完整詞彙分析
|
||
const [sentenceMeaning, setSentenceMeaning] = useState('') // 句子翻譯
|
||
const [grammarCorrection, setGrammarCorrection] = useState<any>(null) // 語法修正
|
||
const [finalText, setFinalText] = useState('') // 最終文本
|
||
|
||
// ClickableTextV2狀態
|
||
const [selectedWord, setSelectedWord] = useState<string | null>(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
|
||
<ClickableTextV2
|
||
analysis={sentenceAnalysis} // 傳遞給子組件
|
||
onSaveWord={handleSaveWord} // 回調函數
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
## 🚨 **當前架構問題分析**
|
||
|
||
### **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(
|
||
<div className="fixed z-50 bg-white rounded-xl shadow-lg w-96">
|
||
{/* 彈窗內容 */}
|
||
</div>,
|
||
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繼承問題
|
||
<div className="relative">
|
||
<div className="text-lg">可點擊文字</div>
|
||
<div className="fixed popup">彈窗</div> // 會繼承text-lg
|
||
</div>
|
||
|
||
// ✅ Portal方式 - 完全隔離
|
||
<div className="relative">
|
||
<div className="text-lg">可點擊文字</div>
|
||
</div>
|
||
{createPortal(
|
||
<div className="fixed popup">彈窗</div>, // 渲染到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生成功能,請參考本規格文件的相關章節,並遵循最佳實踐建議進行開發。 |