update FRONTEND documentation
This commit is contained in:
@@ -51,7 +51,9 @@
|
||||
"Bash(TOKEN=\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzIiwidXNlcm5hbWUiOiJhZG1pbiIsImV4cCI6MTc2Mjk1ODUzOX0.S1JjFxVVmifdkN5F_dORt5jTRdTFN9MKJ8UJKuYacA8\")",
|
||||
"Bash(tree:*)",
|
||||
"Bash(done)",
|
||||
"Bash(git add:*)"
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(git push)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
511
FRONTEND_ANALYSIS.md
Normal file
511
FRONTEND_ANALYSIS.md
Normal file
@@ -0,0 +1,511 @@
|
||||
# Tool_OCR 前端項目結構分析
|
||||
|
||||
## 項目概況
|
||||
|
||||
- **項目名稱**: Tool_OCR Frontend
|
||||
- **框架**: React 19.2.0 (TypeScript)
|
||||
- **構建工具**: Vite 7.2.2
|
||||
- **樣式方案**: Tailwind CSS 4.1.17
|
||||
- **路由管理**: React Router v7.9.5
|
||||
- **狀態管理**: Zustand 5.0.8
|
||||
- **數據獲取**: TanStack React Query v5.90.7
|
||||
- **國際化**: i18next 25.6.2
|
||||
- **源碼行數**: ~2,700 行
|
||||
- **開發語言**: TypeScript
|
||||
- **運行端口**: 12011
|
||||
|
||||
---
|
||||
|
||||
## 前端技術棧詳解
|
||||
|
||||
### 核心框架
|
||||
- **React 19.2.0**: 最新版 React,用於構建用戶界面
|
||||
- 使用函數式組件
|
||||
- React Hooks 進行狀態和副作用管理
|
||||
- 嚴格模式(StrictMode)開啟
|
||||
|
||||
### 構建與開發
|
||||
- **Vite 7.2.2**: 快速的前端構建工具
|
||||
- 配置路徑別名 `@` → `./src`
|
||||
- 開發服務器代理設定(API 轉發到 localhost:12010)
|
||||
- TypeScript 支持
|
||||
|
||||
- **TypeScript ~5.9.3**: 類型檢查和增強開發體驗
|
||||
|
||||
- **ESLint + TypeScript ESLint**: 代碼質量檢查
|
||||
- React Hooks 規則檢查
|
||||
- React Refresh 支持
|
||||
|
||||
### 樣式設計
|
||||
|
||||
#### Tailwind CSS 4.1.17
|
||||
- **CSS 框架**: 完全採用 Tailwind CSS
|
||||
- **配置文件**: `/frontend/tailwind.config.js`
|
||||
|
||||
**主題配置 (HSL 色彩系統)**:
|
||||
```
|
||||
顏色變數 (CSS 自定義屬性):
|
||||
- --background / --foreground (背景/前景)
|
||||
- --primary / --primary-foreground (主色/主色前景)
|
||||
- --secondary / --secondary-foreground (次色)
|
||||
- --muted / --muted-foreground (灰色系)
|
||||
- --accent / --accent-foreground (強調色)
|
||||
- --destructive / --destructive-foreground (危險操作)
|
||||
- --card / --card-foreground (卡片)
|
||||
- --popover / --popover-foreground (彈出框)
|
||||
- --border / --input (邊框/輸入框)
|
||||
- --ring (焦點環)
|
||||
```
|
||||
|
||||
**亮色模式**:
|
||||
- 白色背景 (hsl(0 0% 100%))
|
||||
- 深色文字 (hsl(222.2 84% 4.9%))
|
||||
- 藍色主題 (hsl(221.2 83.2% 53.3%))
|
||||
|
||||
**暗色模式** (.dark class):
|
||||
- 深色背景 (hsl(222.2 84% 4.9%))
|
||||
- 淺色文字 (hsl(210 40% 98%))
|
||||
|
||||
#### PostCSS
|
||||
- **配置文件**: `/frontend/postcss.config.js`
|
||||
- **插件**: @tailwindcss/postcss (新版本原生支持)
|
||||
|
||||
#### 全局樣式
|
||||
- **入口**: `/frontend/src/index.css`
|
||||
- @tailwind 指令導入
|
||||
- CSS 變數定義
|
||||
- 亮色/暗色主題切換
|
||||
|
||||
---
|
||||
|
||||
## 項目目錄結構
|
||||
|
||||
```
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── assets/ # 靜態資源
|
||||
│ ├── components/ # React 組件
|
||||
│ │ ├── ui/ # UI 基礎組件庫
|
||||
│ │ │ ├── button.tsx # 按鈕組件 (多種變體)
|
||||
│ │ │ ├── card.tsx # 卡片容器
|
||||
│ │ │ ├── badge.tsx # 標籤組件
|
||||
│ │ │ ├── table.tsx # 表格組件
|
||||
│ │ │ ├── progress.tsx # 進度條
|
||||
│ │ │ └── toast.tsx # 提示通知
|
||||
│ │ ├── FileUpload.tsx # 文件上傳 (拖放支持)
|
||||
│ │ ├── ResultsTable.tsx # 結果表格
|
||||
│ │ ├── MarkdownPreview.tsx # Markdown 預覽
|
||||
│ │ └── Layout.tsx # 主佈局組件
|
||||
│ │
|
||||
│ ├── pages/ # 頁面組件
|
||||
│ │ ├── LoginPage.tsx # 登錄頁面
|
||||
│ │ ├── UploadPage.tsx # 文件上傳頁面
|
||||
│ │ ├── ProcessingPage.tsx # OCR 處理頁面
|
||||
│ │ ├── ResultsPage.tsx # 結果查看頁面
|
||||
│ │ ├── ExportPage.tsx # 導出配置頁面
|
||||
│ │ └── SettingsPage.tsx # 系統設置頁面
|
||||
│ │
|
||||
│ ├── store/ # 狀態管理 (Zustand)
|
||||
│ │ ├── authStore.ts # 認證狀態
|
||||
│ │ └── uploadStore.ts # 上傳狀態
|
||||
│ │
|
||||
│ ├── services/ # API 服務層
|
||||
│ │ └── api.ts # API 客戶端 (Axios)
|
||||
│ │
|
||||
│ ├── types/ # TypeScript 類型定義
|
||||
│ │ └── api.ts # API 接口類型
|
||||
│ │
|
||||
│ ├── lib/ # 工具函數
|
||||
│ │ └── utils.ts # cn() 函數 (Tailwind 類名合併)
|
||||
│ │
|
||||
│ ├── hooks/ # 自定義 React Hooks
|
||||
│ │ └── (待擴展)
|
||||
│ │
|
||||
│ ├── i18n/ # 國際化配置
|
||||
│ │ ├── index.ts # i18n 初始化
|
||||
│ │ └── locales/
|
||||
│ │ └── zh-TW.json # 繁體中文翻譯
|
||||
│ │
|
||||
│ ├── App.tsx # 應用根組件 (路由定義)
|
||||
│ ├── main.tsx # 應用入口
|
||||
│ ├── index.css # 全局樣式
|
||||
│ └── App.css # 應用樣式 (可棄用)
|
||||
│
|
||||
├── public/ # 公開靜態資源
|
||||
├── vite.config.ts # Vite 配置
|
||||
├── tailwind.config.js # Tailwind 配置
|
||||
├── postcss.config.js # PostCSS 配置
|
||||
├── tsconfig.json # TypeScript 配置
|
||||
├── tsconfig.app.json # 應用 TS 配置
|
||||
├── tsconfig.node.json # Node TS 配置
|
||||
├── eslint.config.js # ESLint 配置
|
||||
├── index.html # HTML 入口
|
||||
├── package.json # 依賴管理
|
||||
├── package-lock.json # 依賴鎖定
|
||||
└── .env # 環境變數
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心頁面和功能
|
||||
|
||||
### 頁面概述
|
||||
|
||||
| 頁面 | 路由 | 功能描述 | 行數 |
|
||||
|------|------|--------|------|
|
||||
| LoginPage | /login | 用戶認證 | 97 |
|
||||
| UploadPage | /upload | 文件上傳,支持拖放 | 140 |
|
||||
| ProcessingPage | /processing | 實時監控 OCR 處理進度 | 200 |
|
||||
| ResultsPage | /results | 查看和下載 OCR 結果 | 157 |
|
||||
| ExportPage | /export | 導出規則管理和結果導出 | 321 |
|
||||
| SettingsPage | /settings | 系統配置和用戶偏好 | 325 |
|
||||
|
||||
### 總 UI 頁面代碼: ~1,240 行
|
||||
|
||||
---
|
||||
|
||||
## 組件架構
|
||||
|
||||
### UI 基礎組件庫 (/src/components/ui/)
|
||||
|
||||
採用現代化的無頭 UI 組件設計,配合 Tailwind CSS:
|
||||
|
||||
1. **Button**
|
||||
- 變體: default, destructive, outline, secondary, ghost, link
|
||||
- 尺寸: default, sm, lg, icon
|
||||
- 使用 cn() 實現動態類名合併
|
||||
|
||||
2. **Card**
|
||||
- CardRoot, CardHeader, CardTitle, CardContent, CardFooter
|
||||
- 容器組件,使用 Tailwind 樣式
|
||||
|
||||
3. **Table**
|
||||
- 表格組件家族
|
||||
- Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell
|
||||
|
||||
4. **Badge**
|
||||
- 標籤/徽章組件
|
||||
- 用於狀態指示
|
||||
|
||||
5. **Progress**
|
||||
- 進度條組件
|
||||
- 百分比顯示
|
||||
|
||||
6. **Toast**
|
||||
- 通知系統
|
||||
- useToast() Hook
|
||||
- 支持多個變體
|
||||
|
||||
### 業務組件
|
||||
|
||||
1. **FileUpload**
|
||||
- react-dropzone 整合
|
||||
- 支持拖放和點擊選擇
|
||||
- 文件驗證 (類型、大小、數量)
|
||||
- 自定義 accept 配置
|
||||
|
||||
2. **ResultsTable**
|
||||
- 結果列表展示
|
||||
|
||||
3. **MarkdownPreview**
|
||||
- Markdown 內容預覽
|
||||
|
||||
4. **Layout**
|
||||
- 主體佈局
|
||||
- 導航欄
|
||||
- 頁頭
|
||||
- 頁腳
|
||||
|
||||
---
|
||||
|
||||
## 狀態管理方案
|
||||
|
||||
### Zustand (v5.0.8)
|
||||
|
||||
#### 1. authStore.ts
|
||||
```typescript
|
||||
interface AuthState {
|
||||
user: User | null
|
||||
isAuthenticated: boolean
|
||||
setUser: (user: User | null) => void
|
||||
logout: () => void
|
||||
}
|
||||
```
|
||||
- **持久化**: localStorage (auth-storage)
|
||||
- **用途**: 管理登錄用戶信息和認證狀態
|
||||
|
||||
#### 2. uploadStore.ts
|
||||
```typescript
|
||||
interface UploadState {
|
||||
batchId: number | null
|
||||
files: FileInfo[]
|
||||
uploadProgress: number
|
||||
setBatchId: (id: number) => void
|
||||
setFiles: (files: FileInfo[]) => void
|
||||
setUploadProgress: (progress: number) => void
|
||||
updateFileStatus: (fileId: number, status: string) => void
|
||||
clearUpload: () => void
|
||||
}
|
||||
```
|
||||
- **持久化**: localStorage (tool-ocr-upload-store)
|
||||
- 只持久化: batchId, files (不持久化進度)
|
||||
- **用途**: 管理當前上傳批次和文件信息
|
||||
|
||||
---
|
||||
|
||||
## API 服務層
|
||||
|
||||
### API 客戶端 (services/api.ts)
|
||||
|
||||
基於 Axios 的單例模式實現:
|
||||
|
||||
**配置**:
|
||||
- Base URL: http://localhost:12010 (可通過 VITE_API_BASE_URL 環境變數覆蓋)
|
||||
- API 版本: /api/v1
|
||||
- 超時時間: 30 秒
|
||||
- Content-Type: application/json
|
||||
|
||||
**功能模塊**:
|
||||
|
||||
1. **認證模塊**
|
||||
- login(username, password)
|
||||
- logout()
|
||||
- Token 管理 (localStorage)
|
||||
|
||||
2. **文件上傳模塊**
|
||||
- uploadFiles(files: File[])
|
||||
- FormData multipart 上傳
|
||||
|
||||
3. **OCR 處理模塊**
|
||||
- processOCR(batchId, lang, confidenceThreshold)
|
||||
- getTaskStatus(taskId)
|
||||
- getOCRResult(taskId)
|
||||
- getBatchStatus(batchId)
|
||||
|
||||
4. **導出模塊**
|
||||
- exportResults(batchId, format, options)
|
||||
- exportPDF(fileId, cssTemplate)
|
||||
- getExportRules()
|
||||
- createExportRule(rule)
|
||||
- updateExportRule(ruleId, rule)
|
||||
- deleteExportRule(ruleId)
|
||||
- getCSSTemplates()
|
||||
|
||||
5. **翻譯模塊** (計劃功能)
|
||||
- translateDocument() [501 Not Implemented]
|
||||
- getTranslationConfigs()
|
||||
- createTranslationConfig()
|
||||
|
||||
**攔截器**:
|
||||
- 請求攔截: 自動添加 Authorization Bearer token
|
||||
- 響應攔截: 401 時清除 token 並重定向到登錄頁
|
||||
|
||||
---
|
||||
|
||||
## 類型定義
|
||||
|
||||
### 主要類型 (types/api.ts)
|
||||
|
||||
#### 認證
|
||||
- LoginRequest, LoginResponse, User
|
||||
|
||||
#### 文件上傳
|
||||
- UploadResponse, FileInfo
|
||||
|
||||
#### OCR 處理
|
||||
- ProcessRequest, ProcessResponse, TaskStatus, BatchStatus, FileResult
|
||||
|
||||
#### OCR 結果
|
||||
- OCRResult, OCRJsonData, TextBlock, LayoutInfo
|
||||
|
||||
#### 導出
|
||||
- ExportRequest, ExportOptions, ExportRule, CSSTemplate
|
||||
|
||||
#### 翻譯 (Future)
|
||||
- TranslateRequest, TranslateResponse, TranslationConfig
|
||||
|
||||
#### 錯誤處理
|
||||
- ApiError, ApiResponse
|
||||
|
||||
---
|
||||
|
||||
## 國際化配置
|
||||
|
||||
### i18n (i18n/index.ts)
|
||||
|
||||
- **庫**: react-i18next v16.3.0
|
||||
- **默認語言**: 繁體中文 (zh-TW)
|
||||
- **翻譯文件**: /i18n/locales/zh-TW.json
|
||||
|
||||
### 翻譯內容結構
|
||||
```json
|
||||
{
|
||||
"app": { title, subtitle },
|
||||
"nav": { upload, processing, results, export, settings, logout },
|
||||
"auth": { login, username, password, loginButton, loginError, welcomeBack },
|
||||
"upload": { title, dragAndDrop, dropFilesHere, invalidFiles, ... },
|
||||
"processing": { title, status, progress, currentFile, ... },
|
||||
"results": { title, filename, status, confidence, ... },
|
||||
"export": { title, format, formats, options, rules, cssTemplates, ... },
|
||||
"settings": { ... },
|
||||
"errors": { ... },
|
||||
"common": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 路由結構
|
||||
|
||||
### React Router v7.9.5 配置 (App.tsx)
|
||||
|
||||
```
|
||||
/login → LoginPage (公開)
|
||||
/ → Layout (受保護)
|
||||
├── /upload → UploadPage
|
||||
├── /processing → ProcessingPage
|
||||
├── /results → ResultsPage
|
||||
├── /export → ExportPage
|
||||
└── /settings → SettingsPage
|
||||
* → 重定向到 /
|
||||
```
|
||||
|
||||
**保護機制**: ProtectedRoute 組件基於 authStore.isAuthenticated
|
||||
|
||||
---
|
||||
|
||||
## 樣式設計工具
|
||||
|
||||
### Tailwind 工具函數 (lib/utils.ts)
|
||||
|
||||
```typescript
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
```
|
||||
|
||||
**功能**:
|
||||
- clsx: 條件類名組合
|
||||
- tailwind-merge: 解決 Tailwind 類名衝突
|
||||
- 用於動態生成和合併 Tailwind 類
|
||||
|
||||
---
|
||||
|
||||
## 依賴概覽
|
||||
|
||||
### 主要依賴
|
||||
```json
|
||||
{
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-router-dom": "^7.9.5",
|
||||
"zustand": "^5.0.8",
|
||||
"@tanstack/react-query": "^5.90.7",
|
||||
"axios": "^1.13.2",
|
||||
"react-i18next": "^16.3.0",
|
||||
"i18next": "^25.6.2",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"tailwindcss": "^4.1.17",
|
||||
"clsx": "^2.1.1",
|
||||
"tailwind-merge": "^3.4.0"
|
||||
}
|
||||
```
|
||||
|
||||
### 開發依賴
|
||||
```json
|
||||
{
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "^7.2.2",
|
||||
"@vitejs/plugin-react": "^5.1.0",
|
||||
"eslint": "^9.39.1",
|
||||
"typescript-eslint": "^8.46.3",
|
||||
"@tailwindcss/postcss": "^4.1.17",
|
||||
"postcss": "^8.5.6",
|
||||
"autoprefixer": "^10.4.22"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 開發和構建
|
||||
|
||||
### 可用命令
|
||||
```bash
|
||||
npm run dev # 啟動開發服務器 (localhost:12011)
|
||||
npm run build # 生產構建 (TypeScript 編譯 + Vite 打包)
|
||||
npm run lint # ESLint 檢查
|
||||
npm run preview # 預覽構建結果
|
||||
```
|
||||
|
||||
### 開發服務器配置
|
||||
- **端口**: 12011
|
||||
- **代理**: /api → http://localhost:12010 (後端)
|
||||
|
||||
---
|
||||
|
||||
## 代碼質量
|
||||
|
||||
### ESLint 配置
|
||||
|
||||
- JavaScript ES2020 規範
|
||||
- TypeScript 類型檢查
|
||||
- React Hooks 規則 (recommended-latest)
|
||||
- React Refresh 支持
|
||||
- 瀏覽器全局變數支持
|
||||
|
||||
### TypeScript 配置
|
||||
|
||||
- 目標: ES2020
|
||||
- 模塊: ESNext
|
||||
- JSX: React-JSX (自動導入)
|
||||
- 嚴格模式啟用
|
||||
- 路徑別名: @ → ./src
|
||||
|
||||
---
|
||||
|
||||
## 文件統計
|
||||
|
||||
- **總文件數**: 28 個
|
||||
- **源碼總大小**: 152 KB
|
||||
- **代碼行數**: ~2,702 行 (TS/TSX/JSON)
|
||||
- **UI 組件**: 6 個基礎組件
|
||||
- **業務組件**: 4 個
|
||||
- **頁面**: 6 個
|
||||
- **工具函數**: 多個
|
||||
|
||||
---
|
||||
|
||||
## 樣式實現要點
|
||||
|
||||
1. **Tailwind CSS 優先**: 所有樣式都使用 Tailwind 工具類
|
||||
2. **CSS 變數系統**: 使用 HSL 色彩空間,支持主題切換
|
||||
3. **響應式設計**: 內置 Tailwind 響應式前綴支持
|
||||
4. **可訪問性**: 焦點環、禁用狀態等 UI 反饋
|
||||
5. **暗色模式**: 通過 .dark 類支持主題切換
|
||||
6. **組件化**: UI 組件開箱即用,無需額外 CSS
|
||||
|
||||
---
|
||||
|
||||
## 總結
|
||||
|
||||
Tool_OCR 前端項目採用現代化的技術棧:
|
||||
|
||||
- **框架**: React 19 + TypeScript
|
||||
- **構建**: Vite (快速開發和構建)
|
||||
- **樣式**: Tailwind CSS 4 (工具類優先)
|
||||
- **狀態**: Zustand (輕量級狀態管理)
|
||||
- **數據**: React Query (服務器狀態管理)
|
||||
- **路由**: React Router v7 (應用導航)
|
||||
- **國際化**: i18next (多語言支持)
|
||||
- **質量**: ESLint + TypeScript (靜態檢查)
|
||||
|
||||
**核心特點**:
|
||||
1. 類型安全的開發環境
|
||||
2. 響應式 UI 設計
|
||||
3. 模塊化組件架構
|
||||
4. 完整的狀態管理
|
||||
5. API 層分離
|
||||
6. 國際化就位
|
||||
7. 開發友好 (HMR, 快速刷新)
|
||||
|
||||
652
FRONTEND_CODE_EXAMPLES.md
Normal file
652
FRONTEND_CODE_EXAMPLES.md
Normal file
@@ -0,0 +1,652 @@
|
||||
# Tool_OCR 前端代碼示例和最佳實踐
|
||||
|
||||
## 1. Tailwind CSS 樣式使用示例
|
||||
|
||||
### 佈局組件 (Layout.tsx 提取)
|
||||
```typescript
|
||||
// 導航欄樣式示例
|
||||
<header className="border-b bg-card">
|
||||
<div className="container mx-auto px-4 py-4 flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold text-foreground">{t('app.title')}</h1>
|
||||
<button
|
||||
className="px-4 py-2 text-sm font-medium text-foreground hover:text-primary transition-colors"
|
||||
>
|
||||
{t('nav.logout')}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* 導航選項卡 */}
|
||||
<nav className="border-b bg-card">
|
||||
<div className="container mx-auto px-4">
|
||||
<ul className="flex space-x-1">
|
||||
{navLinks.map((link) => (
|
||||
<NavLink
|
||||
to={link.to}
|
||||
className={({ isActive }) =>
|
||||
`block px-4 py-3 text-sm font-medium transition-colors ${
|
||||
isActive
|
||||
? 'text-primary border-b-2 border-primary'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`
|
||||
}
|
||||
>
|
||||
{link.label}
|
||||
</NavLink>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
```
|
||||
|
||||
### 按鈕變體示例 (button.tsx)
|
||||
```typescript
|
||||
// 默認按鈕
|
||||
<Button>Save Changes</Button>
|
||||
|
||||
// 危險操作
|
||||
<Button variant="destructive">Delete</Button>
|
||||
|
||||
// 輪廓樣式
|
||||
<Button variant="outline">Cancel</Button>
|
||||
|
||||
// 幽靈按鈕
|
||||
<Button variant="ghost">Remove</Button>
|
||||
|
||||
// 鏈接樣式
|
||||
<Button variant="link">Learn More</Button>
|
||||
|
||||
// 不同尺寸
|
||||
<Button size="sm">Small</Button>
|
||||
<Button size="lg">Large</Button>
|
||||
<Button size="icon">+</Button>
|
||||
```
|
||||
|
||||
### 響應式卡片示例 (UploadPage.tsx 提取)
|
||||
```typescript
|
||||
<div className="max-w-4xl mx-auto space-y-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-lg">
|
||||
{t('upload.selectedFiles')} ({selectedFiles.length})
|
||||
</CardTitle>
|
||||
<Button variant="outline" size="sm" onClick={handleClearAll}>
|
||||
{t('upload.clearAll')}
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2">
|
||||
{selectedFiles.map((file, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center justify-between p-3 bg-muted rounded-md"
|
||||
>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-foreground truncate">
|
||||
{file.name}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{formatFileSize(file.size)}
|
||||
</p>
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" onClick={() => handleRemoveFile(index)}>
|
||||
{t('upload.removeFile')}
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 文件上傳拖放區域 (FileUpload.tsx 提取)
|
||||
```typescript
|
||||
<Card
|
||||
{...getRootProps()}
|
||||
className={cn(
|
||||
'border-2 border-dashed transition-colors cursor-pointer hover:border-primary/50',
|
||||
{
|
||||
'border-primary bg-primary/5': isDragActive && !isDragReject,
|
||||
'border-destructive bg-destructive/5': isDragReject,
|
||||
'opacity-50 cursor-not-allowed': disabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="p-12 text-center">
|
||||
<input {...getInputProps()} />
|
||||
|
||||
<div className="mb-4">
|
||||
<svg className="mx-auto h-12 w-12 text-muted-foreground" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
{isDragActive ? (
|
||||
<p className="text-lg font-medium text-primary">
|
||||
{isDragReject ? t('upload.invalidFiles') : t('upload.dropFilesHere')}
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<p className="text-lg font-medium text-foreground">
|
||||
{t('upload.dragAndDrop')}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('upload.supportedFormats')}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. React Query 使用示例
|
||||
|
||||
### 批次狀態查詢 (ResultsPage.tsx 提取)
|
||||
```typescript
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
|
||||
export default function ResultsPage() {
|
||||
const { batchId } = useUploadStore()
|
||||
const [selectedFileId, setSelectedFileId] = useState<number | null>(null)
|
||||
|
||||
// 獲取批次狀態
|
||||
const { data: batchStatus, isLoading } = useQuery({
|
||||
queryKey: ['batchStatus', batchId],
|
||||
queryFn: () => apiClient.getBatchStatus(batchId!),
|
||||
enabled: !!batchId, // 只在有 batchId 時查詢
|
||||
})
|
||||
|
||||
// 獲取 OCR 結果
|
||||
const { data: ocrResult, isLoading: isLoadingResult } = useQuery({
|
||||
queryKey: ['ocrResult', selectedFileId],
|
||||
queryFn: () => apiClient.getOCRResult(selectedFileId!.toString()),
|
||||
enabled: !!selectedFileId,
|
||||
})
|
||||
|
||||
if (!batchId) {
|
||||
return <div>Please upload files first</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isLoading ? (
|
||||
<p>Loading batch status...</p>
|
||||
) : (
|
||||
<ResultsTable data={batchStatus?.files} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Zustand 狀態管理示例
|
||||
|
||||
### 認證存儲 (authStore.ts)
|
||||
```typescript
|
||||
import { create } from 'zustand'
|
||||
import { persist } from 'zustand/middleware'
|
||||
import type { User } from '@/types/api'
|
||||
|
||||
interface AuthState {
|
||||
user: User | null
|
||||
isAuthenticated: boolean
|
||||
setUser: (user: User | null) => void
|
||||
logout: () => void
|
||||
}
|
||||
|
||||
export const useAuthStore = create<AuthState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
setUser: (user) =>
|
||||
set({
|
||||
user,
|
||||
isAuthenticated: user !== null,
|
||||
}),
|
||||
logout: () =>
|
||||
set({
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
}),
|
||||
}),
|
||||
{
|
||||
name: 'auth-storage', // localStorage 鍵名
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
// 使用示例
|
||||
function LoginPage() {
|
||||
const setUser = useAuthStore((state) => state.setUser)
|
||||
|
||||
const handleLogin = async (username: string, password: string) => {
|
||||
const response = await apiClient.login({ username, password })
|
||||
setUser({ id: 1, username })
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 上傳狀態存儲 (uploadStore.ts)
|
||||
```typescript
|
||||
export const useUploadStore = create<UploadState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
batchId: null,
|
||||
files: [],
|
||||
uploadProgress: 0,
|
||||
|
||||
setBatchId: (id) => {
|
||||
set({ batchId: id })
|
||||
},
|
||||
|
||||
setFiles: (files) => set({ files }),
|
||||
|
||||
setUploadProgress: (progress) => set({ uploadProgress: progress }),
|
||||
|
||||
updateFileStatus: (fileId, status) =>
|
||||
set((state) => ({
|
||||
files: state.files.map((file) =>
|
||||
file.id === fileId ? { ...file, status } : file
|
||||
),
|
||||
})),
|
||||
|
||||
clearUpload: () =>
|
||||
set({
|
||||
batchId: null,
|
||||
files: [],
|
||||
uploadProgress: 0,
|
||||
}),
|
||||
}),
|
||||
{
|
||||
name: 'tool-ocr-upload-store',
|
||||
// 只持久化 batchId 和 files,不持久化進度
|
||||
partialize: (state) => ({
|
||||
batchId: state.batchId,
|
||||
files: state.files,
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
// 使用示例
|
||||
function UploadPage() {
|
||||
const { setBatchId, setFiles } = useUploadStore()
|
||||
|
||||
const handleUploadSuccess = (data: UploadResponse) => {
|
||||
setBatchId(data.batch_id)
|
||||
setFiles(data.files)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. API 客戶端使用示例
|
||||
|
||||
### API 認證流程 (api.ts)
|
||||
```typescript
|
||||
// 登錄
|
||||
const response = await apiClient.login({
|
||||
username: 'admin',
|
||||
password: 'password'
|
||||
})
|
||||
// Token 自動保存到 localStorage
|
||||
|
||||
// 後續請求自動附帶 token
|
||||
const status = await apiClient.getBatchStatus(123)
|
||||
// 請求頭自動包含: Authorization: Bearer <token>
|
||||
|
||||
// 登出
|
||||
apiClient.logout()
|
||||
// Token 自動從 localStorage 清除
|
||||
```
|
||||
|
||||
### 文件上傳示例
|
||||
```typescript
|
||||
const handleUpload = async (files: File[]) => {
|
||||
try {
|
||||
const response = await apiClient.uploadFiles(files)
|
||||
// response: { batch_id: 1, files: [...] }
|
||||
setBatchId(response.batch_id)
|
||||
setFiles(response.files)
|
||||
} catch (error) {
|
||||
// 自動處理 401 錯誤並重定向到登錄頁
|
||||
showError(error.response?.data?.detail)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 導出功能示例
|
||||
```typescript
|
||||
// 導出 PDF
|
||||
const handleDownloadPDF = async (fileId: number) => {
|
||||
const blob = await apiClient.exportPDF(fileId)
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = `ocr-result-${fileId}.pdf`
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
// 導出規則管理
|
||||
const rules = await apiClient.getExportRules()
|
||||
const newRule = await apiClient.createExportRule({
|
||||
rule_name: 'My Rule',
|
||||
config_json: { /* ... */ }
|
||||
})
|
||||
await apiClient.updateExportRule(rule.id, { rule_name: 'Updated' })
|
||||
await apiClient.deleteExportRule(rule.id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 國際化使用示例
|
||||
|
||||
### 在組件中使用翻譯
|
||||
```typescript
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export default function UploadPage() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{t('upload.title')}</h1>
|
||||
<p>{t('upload.dragAndDrop')}</p>
|
||||
|
||||
{/* 帶插值的翻譯 */}
|
||||
<p>{t('upload.fileCount', { count: 5 })}</p>
|
||||
{/* 會渲染: "已選擇 5 個檔案" */}
|
||||
|
||||
<button>{t('upload.uploadButton')}</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### i18n 初始化 (i18n/index.ts)
|
||||
```typescript
|
||||
import i18n from 'i18next'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
import zhTW from './locales/zh-TW.json'
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
resources: {
|
||||
'zh-TW': {
|
||||
translation: zhTW,
|
||||
},
|
||||
},
|
||||
lng: 'zh-TW',
|
||||
fallbackLng: 'zh-TW',
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
})
|
||||
|
||||
export default i18n
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 路由和保護示例
|
||||
|
||||
### 受保護的路由 (App.tsx)
|
||||
```typescript
|
||||
function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return <Navigate to="/login" replace />
|
||||
}
|
||||
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Routes>
|
||||
{/* 公開路由 */}
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
|
||||
{/* 受保護的路由 */}
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<Layout />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
>
|
||||
<Route index element={<Navigate to="/upload" replace />} />
|
||||
<Route path="upload" element={<UploadPage />} />
|
||||
<Route path="processing" element={<ProcessingPage />} />
|
||||
<Route path="results" element={<ResultsPage />} />
|
||||
<Route path="export" element={<ExportPage />} />
|
||||
<Route path="settings" element={<SettingsPage />} />
|
||||
</Route>
|
||||
|
||||
{/* 全部匹配 */}
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 類型定義示例
|
||||
|
||||
### API 類型 (types/api.ts)
|
||||
```typescript
|
||||
// 認證
|
||||
export interface LoginRequest {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
access_token: string
|
||||
token_type: string
|
||||
}
|
||||
|
||||
// 文件上傳
|
||||
export interface UploadResponse {
|
||||
batch_id: number
|
||||
files: FileInfo[]
|
||||
}
|
||||
|
||||
export interface FileInfo {
|
||||
id: number
|
||||
filename: string
|
||||
file_size: number
|
||||
format: string
|
||||
status: 'pending' | 'processing' | 'completed' | 'failed'
|
||||
}
|
||||
|
||||
// OCR 結果
|
||||
export interface OCRResult {
|
||||
file_id: number
|
||||
filename: string
|
||||
status: string
|
||||
markdown_content: string
|
||||
json_data: OCRJsonData
|
||||
confidence: number
|
||||
processing_time: number
|
||||
}
|
||||
|
||||
export interface TextBlock {
|
||||
text: string
|
||||
confidence: number
|
||||
bbox: [number, number, number, number]
|
||||
position: number
|
||||
}
|
||||
|
||||
// 導出規則
|
||||
export interface ExportRule {
|
||||
id: number
|
||||
rule_name: string
|
||||
config_json: Record<string, any>
|
||||
css_template?: string
|
||||
created_at: string
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 最佳實踐
|
||||
|
||||
### 1. 組件結構
|
||||
```typescript
|
||||
// 導入順序
|
||||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
|
||||
// 內部導入
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { useAuthStore } from '@/store/authStore'
|
||||
import { apiClient } from '@/services/api'
|
||||
|
||||
// 組件定義
|
||||
export default function MyPage() {
|
||||
// Hooks
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const [state, setState] = useState(null)
|
||||
|
||||
// 查詢
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['key'],
|
||||
queryFn: () => apiClient.getData(),
|
||||
})
|
||||
|
||||
// 狀態更新
|
||||
const handleClick = () => {
|
||||
// ...
|
||||
}
|
||||
|
||||
// 渲染
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* JSX */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 錯誤處理
|
||||
```typescript
|
||||
const { toast } = useToast()
|
||||
|
||||
const handleAction = async () => {
|
||||
try {
|
||||
const result = await apiClient.doSomething()
|
||||
toast({
|
||||
title: t('success.title'),
|
||||
description: t('success.message'),
|
||||
variant: 'success',
|
||||
})
|
||||
} catch (error: any) {
|
||||
const errorMessage = error.response?.data?.detail || t('errors.generic')
|
||||
toast({
|
||||
title: t('error.title'),
|
||||
description: errorMessage,
|
||||
variant: 'destructive',
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 類名合併
|
||||
```typescript
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
// 條件類名
|
||||
const buttonClass = cn(
|
||||
'base-classes',
|
||||
{
|
||||
'conditional-classes': isActive,
|
||||
'other-classes': isDisabled,
|
||||
},
|
||||
customClassName
|
||||
)
|
||||
|
||||
// 動態樣式
|
||||
const cardClass = cn(
|
||||
'p-4 rounded-lg',
|
||||
variant === 'outlined' && 'border border-input',
|
||||
variant === 'elevated' && 'shadow-lg'
|
||||
)
|
||||
```
|
||||
|
||||
### 4. React Query 配置
|
||||
```typescript
|
||||
// 主入口 (main.tsx)
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false,
|
||||
staleTime: 1000 * 60 * 5, // 5 分鐘
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// 使用示例
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ['items', id],
|
||||
queryFn: () => apiClient.getItem(id),
|
||||
enabled: !!id, // 條件查詢
|
||||
staleTime: 1000 * 60 * 10, // 10 分鐘不新鮮
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 環境變數
|
||||
|
||||
### .env 文件
|
||||
```env
|
||||
VITE_API_BASE_URL=http://localhost:12010
|
||||
```
|
||||
|
||||
### 使用環境變數
|
||||
```typescript
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:12010'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 常見開發流程
|
||||
|
||||
### 創建新頁面
|
||||
1. 在 `/src/pages` 創建 `NewPage.tsx`
|
||||
2. 在 `App.tsx` 添加路由
|
||||
3. 使用 Zustand 管理狀態
|
||||
4. 使用 React Query 獲取數據
|
||||
5. 使用 Tailwind CSS 樣式
|
||||
6. 使用 i18next 添加文字
|
||||
|
||||
### 添加新 API 接口
|
||||
1. 在 `types/api.ts` 定義類型
|
||||
2. 在 `services/api.ts` 添加方法
|
||||
3. 在需要的地方使用 `apiClient.method()`
|
||||
4. 使用 React Query 或直接調用
|
||||
|
||||
### 修改樣式
|
||||
1. 優先使用 Tailwind CSS 工具類
|
||||
2. 使用 cn() 合併類名
|
||||
3. 修改 `tailwind.config.js` 自定義主題
|
||||
4. 在 `index.css` 添加全局樣式
|
||||
|
||||
397
FRONTEND_QUICK_REFERENCE.md
Normal file
397
FRONTEND_QUICK_REFERENCE.md
Normal file
@@ -0,0 +1,397 @@
|
||||
# Tool_OCR 前端快速參考指南
|
||||
|
||||
## 文件位置速查表
|
||||
|
||||
### 主要文件位置
|
||||
```
|
||||
/Users/egg/Projects/Tool_OCR/frontend/
|
||||
├── src/
|
||||
│ ├── App.tsx # 路由定義
|
||||
│ ├── main.tsx # 應用入口
|
||||
│ ├── index.css # 全局樣式
|
||||
│ ├── components/
|
||||
│ │ ├── ui/ # UI 組件庫
|
||||
│ │ ├── Layout.tsx # 主佈局
|
||||
│ │ ├── FileUpload.tsx # 文件上傳
|
||||
│ │ └── ...
|
||||
│ ├── pages/ # 頁面
|
||||
│ ├── store/ # 狀態管理
|
||||
│ ├── services/api.ts # API 客戶端
|
||||
│ ├── types/api.ts # 類型定義
|
||||
│ ├── i18n/ # 國際化
|
||||
│ └── lib/utils.ts # 工具函數
|
||||
├── vite.config.ts # Vite 配置
|
||||
├── tailwind.config.js # Tailwind 配置
|
||||
├── tsconfig.json # TypeScript 配置
|
||||
└── package.json # 依賴管理
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 技術棧速查
|
||||
|
||||
| 功能 | 技術 | 版本 |
|
||||
|-----|------|------|
|
||||
| UI 框架 | React | 19.2.0 |
|
||||
| 語言 | TypeScript | 5.9.3 |
|
||||
| 構建 | Vite | 7.2.2 |
|
||||
| 樣式 | Tailwind CSS | 4.1.17 |
|
||||
| 狀態管理 | Zustand | 5.0.8 |
|
||||
| 數據獲取 | React Query | 5.90.7 |
|
||||
| 路由 | React Router | 7.9.5 |
|
||||
| 國際化 | i18next | 25.6.2 |
|
||||
| HTTP | Axios | 1.13.2 |
|
||||
| 檔案上傳 | react-dropzone | 14.3.8 |
|
||||
|
||||
---
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
# 開發
|
||||
cd frontend
|
||||
npm run dev # 啟動開發服務器 (localhost:12011)
|
||||
npm run lint # 代碼檢查
|
||||
npm run build # 生產構建
|
||||
|
||||
# 類型檢查
|
||||
npx tsc --noEmit # 檢查 TypeScript 錯誤
|
||||
|
||||
# 格式化
|
||||
npm install -D prettier # (如果需要)
|
||||
npx prettier --write src/ # 格式化代碼
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 路由結構
|
||||
|
||||
```
|
||||
/login → LoginPage (公開)
|
||||
/upload → UploadPage (受保護)
|
||||
/processing → ProcessingPage (受保護)
|
||||
/results → ResultsPage (受保護)
|
||||
/export → ExportPage (受保護)
|
||||
/settings → SettingsPage (受保護)
|
||||
```
|
||||
|
||||
### 添加新路由
|
||||
```typescript
|
||||
// 在 App.tsx 中
|
||||
<Route path="new-page" element={<NewPage />} />
|
||||
|
||||
// 在導航中
|
||||
{ to: '/new-page', label: t('nav.newPage') }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常用代碼片段
|
||||
|
||||
### 使用 i18n 翻譯
|
||||
```typescript
|
||||
const { t } = useTranslation()
|
||||
<h1>{t('key.path')}</h1>
|
||||
```
|
||||
|
||||
### 使用 Zustand 狀態
|
||||
```typescript
|
||||
const { batchId, setBatchId } = useUploadStore()
|
||||
const { user, logout } = useAuthStore()
|
||||
```
|
||||
|
||||
### 調用 API
|
||||
```typescript
|
||||
const data = await apiClient.uploadFiles(files)
|
||||
const status = await apiClient.getBatchStatus(batchId)
|
||||
```
|
||||
|
||||
### React Query 查詢
|
||||
```typescript
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['key', id],
|
||||
queryFn: () => apiClient.getData(id),
|
||||
})
|
||||
```
|
||||
|
||||
### Tailwind 樣式
|
||||
```typescript
|
||||
className="flex items-center justify-between p-4 bg-card rounded-lg"
|
||||
className={cn('base', { 'conditional': isActive })}
|
||||
```
|
||||
|
||||
### 提示通知
|
||||
```typescript
|
||||
const { toast } = useToast()
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: 'Action completed',
|
||||
variant: 'success',
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 調試技巧
|
||||
|
||||
### 查看存儲在 localStorage 的狀態
|
||||
```javascript
|
||||
// 在瀏覽器控制台
|
||||
console.log(JSON.parse(localStorage.getItem('auth-storage')))
|
||||
console.log(JSON.parse(localStorage.getItem('tool-ocr-upload-store')))
|
||||
```
|
||||
|
||||
### 查看 React Query 緩存
|
||||
```javascript
|
||||
// 在瀏覽器控制台安裝 React Query DevTools
|
||||
// npm install @tanstack/react-query-devtools
|
||||
```
|
||||
|
||||
### TypeScript 錯誤
|
||||
```bash
|
||||
# 運行編譯器檢查類型
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 環境變數配置
|
||||
|
||||
### 開發環境 (.env)
|
||||
```env
|
||||
VITE_API_BASE_URL=http://localhost:12010
|
||||
```
|
||||
|
||||
### 使用環境變數
|
||||
```typescript
|
||||
const baseURL = import.meta.env.VITE_API_BASE_URL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常見問題
|
||||
|
||||
### Q: 如何添加新的頁面?
|
||||
A:
|
||||
1. 在 `/src/pages` 創建 `NewPage.tsx`
|
||||
2. 在 `App.tsx` 添加路由
|
||||
3. 在 Layout.tsx 導航中添加鏈接
|
||||
|
||||
### Q: 如何修改主題顏色?
|
||||
A:
|
||||
1. 編輯 `src/index.css` 中的 CSS 變數
|
||||
2. 或編輯 `tailwind.config.js`
|
||||
|
||||
### Q: 如何添加新的翻譯?
|
||||
A:
|
||||
1. 編輯 `src/i18n/locales/zh-TW.json`
|
||||
2. 在組件中使用 `t('key.path')`
|
||||
|
||||
### Q: 如何調試 API 請求?
|
||||
A:
|
||||
1. 打開瀏覽器開發者工具 (Network 標籤)
|
||||
2. 檢查 `/api/v1/` 的請求
|
||||
|
||||
### Q: 上傳文件怎樣無法工作?
|
||||
A:
|
||||
1. 檢查後端是否在運行 (localhost:12010)
|
||||
2. 檢查文件格式是否被支持
|
||||
3. 檢查文件大小是否超過 50MB
|
||||
|
||||
### Q: 認證登錄失敗?
|
||||
A:
|
||||
1. 檢查用戶名和密碼
|
||||
2. 檢查後端認證端點 `/api/v1/auth/login`
|
||||
3. 檢查 localStorage 中的 token
|
||||
|
||||
---
|
||||
|
||||
## 依賴更新
|
||||
|
||||
```bash
|
||||
# 檢查過期依賴
|
||||
npm outdated
|
||||
|
||||
# 更新依賴
|
||||
npm update
|
||||
|
||||
# 更新到最新版本
|
||||
npm install --save-latest package-name
|
||||
|
||||
# 清除 node_modules 並重新安裝
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能優化
|
||||
|
||||
### 代碼分割
|
||||
```typescript
|
||||
import { lazy, Suspense } from 'react'
|
||||
|
||||
const NewPage = lazy(() => import('./pages/NewPage'))
|
||||
|
||||
// 在 Routes 中
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<NewPage />
|
||||
</Suspense>
|
||||
```
|
||||
|
||||
### 優化 React Query
|
||||
```typescript
|
||||
const { data } = useQuery({
|
||||
queryKey: ['items'],
|
||||
queryFn: () => apiClient.getItems(),
|
||||
staleTime: 1000 * 60 * 5, // 5 分鐘內不重新獲取
|
||||
gcTime: 1000 * 60 * 10, // 10 分鐘後從緩存中移除
|
||||
})
|
||||
```
|
||||
|
||||
### 優化組件
|
||||
```typescript
|
||||
// 使用 React.memo 避免不必要的重新渲染
|
||||
export default React.memo(function MyComponent(props) {
|
||||
return <div>{props.data}</div>
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 測試 (可選)
|
||||
|
||||
```bash
|
||||
# 安裝測試工具
|
||||
npm install -D vitest @testing-library/react
|
||||
|
||||
# 運行測試
|
||||
npm test
|
||||
|
||||
# 生成覆蓋率報告
|
||||
npm test -- --coverage
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 構建和部署
|
||||
|
||||
```bash
|
||||
# 生產構建
|
||||
npm run build
|
||||
|
||||
# 預覽構建
|
||||
npm run preview
|
||||
|
||||
# 構建後的文件位置
|
||||
# dist/
|
||||
|
||||
# 部署
|
||||
# 將 dist/ 目錄上傳到服務器
|
||||
# 配置 Web 服務器支持 SPA (所有路由都指向 index.html)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 貢獻指南
|
||||
|
||||
### 代碼風格
|
||||
- 使用 TypeScript 進行類型安全
|
||||
- 遵循現有的命名約定
|
||||
- 優先使用 Tailwind CSS
|
||||
- 添加必要的類型定義
|
||||
|
||||
### 提交代碼
|
||||
```bash
|
||||
# 檢查代碼
|
||||
npm run lint
|
||||
|
||||
# 提交
|
||||
git add .
|
||||
git commit -m "description of changes"
|
||||
git push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 有用的資源
|
||||
|
||||
### 文檔
|
||||
- [React 文檔](https://react.dev)
|
||||
- [Tailwind CSS](https://tailwindcss.com)
|
||||
- [React Router](https://reactrouter.com)
|
||||
- [Zustand](https://github.com/pmndrs/zustand)
|
||||
- [React Query](https://tanstack.com/query)
|
||||
- [i18next](https://www.i18next.com)
|
||||
|
||||
### 工具
|
||||
- [Vite 文檔](https://vitejs.dev)
|
||||
- [TypeScript 文檔](https://www.typescriptlang.org)
|
||||
- [ESLint 文檔](https://eslint.org)
|
||||
|
||||
### IDE 擴展 (VS Code)
|
||||
- Tailwind CSS IntelliSense
|
||||
- Thunder Client (API 測試)
|
||||
- React Developer Tools
|
||||
- Redux DevTools (狀態檢查)
|
||||
|
||||
---
|
||||
|
||||
## 快速開始
|
||||
|
||||
```bash
|
||||
# 1. 進入前端目錄
|
||||
cd /Users/egg/Projects/Tool_OCR/frontend
|
||||
|
||||
# 2. 安裝依賴
|
||||
npm install
|
||||
|
||||
# 3. 啟動開發服務器
|
||||
npm run dev
|
||||
|
||||
# 4. 打開瀏覽器
|
||||
# 訪問 http://localhost:12011
|
||||
|
||||
# 5. 登錄
|
||||
# 使用後端提供的帳號密碼
|
||||
|
||||
# 6. 開始使用
|
||||
# 上傳檔案 → 處理 → 查看結果 → 匯出
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常用快捷鍵
|
||||
|
||||
### VS Code
|
||||
- `Ctrl+Shift+F` (或 `Cmd+Shift+F` 在 Mac) - 全文搜索
|
||||
- `Ctrl+K Ctrl+X` - 刪除行
|
||||
- `Alt+Up/Down` - 移動行
|
||||
- `Ctrl+/` - 註解行
|
||||
|
||||
### 瀏覽器開發者工具
|
||||
- `F12` 或 `Ctrl+Shift+I` - 打開開發者工具
|
||||
- `Ctrl+Shift+C` - 元素檢查器
|
||||
- `Ctrl+Shift+M` - 響應式設計模式
|
||||
|
||||
---
|
||||
|
||||
## 總結速記
|
||||
|
||||
**架構**:React 19 + TypeScript + Vite
|
||||
**樣式**:Tailwind CSS 4
|
||||
**狀態**:Zustand (輕量) + React Query (數據)
|
||||
**路由**:React Router v7
|
||||
**API**:Axios 單例客戶端
|
||||
**國際化**:i18next (繁體中文)
|
||||
**開發端口**:12011
|
||||
**後端 API**:localhost:12010
|
||||
|
||||
**核心文件**:
|
||||
- App.tsx (路由)
|
||||
- main.tsx (入口)
|
||||
- services/api.ts (API)
|
||||
- store/*.ts (狀態)
|
||||
- pages/ (頁面)
|
||||
- components/ (組件)
|
||||
|
||||
185
FRONTEND_README.md
Normal file
185
FRONTEND_README.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Tool_OCR 前端文檔索引
|
||||
|
||||
歡迎查閱 Tool_OCR 前端項目的完整文檔。本頁面將幫助您快速找到所需的信息。
|
||||
|
||||
## 文檔列表
|
||||
|
||||
### 1. FRONTEND_ANALYSIS.md
|
||||
**完整的技術棧和項目結構分析**
|
||||
|
||||
包含內容:
|
||||
- 項目概況和技術棧詳解
|
||||
- 完整的目錄結構說明
|
||||
- 核心頁面和功能概述
|
||||
- 組件架構設計
|
||||
- 狀態管理方案 (Zustand)
|
||||
- API 服務層設計
|
||||
- 類型定義系統
|
||||
- 國際化配置
|
||||
- 路由結構
|
||||
- 依賴概覽
|
||||
- 代碼質量檢查
|
||||
- 樣式實現要點
|
||||
|
||||
**適合閱讀場景:**
|
||||
- 全新成員了解項目整體架構
|
||||
- 深入理解技術選擇和設計決策
|
||||
- 查看完整的模塊組織結構
|
||||
- 了解樣式系統的設計方式
|
||||
|
||||
### 2. FRONTEND_CODE_EXAMPLES.md
|
||||
**實際代碼示例和最佳實踐**
|
||||
|
||||
包含內容:
|
||||
- Tailwind CSS 使用示例 (佈局、按鈕、卡片等)
|
||||
- React Query 數據獲取示例
|
||||
- Zustand 狀態管理示例
|
||||
- API 客戶端使用示例
|
||||
- 國際化集成示例
|
||||
- 路由和路由保護示例
|
||||
- TypeScript 類型定義示例
|
||||
- 最佳實踐指南 (組件結構、錯誤處理等)
|
||||
- 環境變數配置
|
||||
- 常見開發流程
|
||||
|
||||
**適合閱讀場景:**
|
||||
- 開發新功能時參考代碼模式
|
||||
- 尋找特定技術的使用示例
|
||||
- 學習項目內的最佳實踐
|
||||
- 複製粘貼代碼片段快速開發
|
||||
|
||||
### 3. FRONTEND_QUICK_REFERENCE.md
|
||||
**快速查閱和常見問題**
|
||||
|
||||
包含內容:
|
||||
- 文件位置速查表
|
||||
- 技術棧版本速查
|
||||
- 常用開發命令
|
||||
- 路由結構速查
|
||||
- 常用代碼片段
|
||||
- 調試技巧
|
||||
- 常見問題解答 (FAQ)
|
||||
- 依賴更新指南
|
||||
- 性能優化建議
|
||||
- 構建和部署指南
|
||||
- 有用資源鏈接
|
||||
|
||||
**適合閱讀場景:**
|
||||
- 快速查找文件位置
|
||||
- 尋找常用命令
|
||||
- 解決常見問題
|
||||
- 快速參考語法
|
||||
- 了解開發工作流
|
||||
|
||||
## 快速開始路線圖
|
||||
|
||||
### 如果您是新開發者:
|
||||
1. 先讀 **FRONTEND_QUICK_REFERENCE.md** 的「快速開始」部分
|
||||
2. 再讀 **FRONTEND_ANALYSIS.md** 了解整體架構
|
||||
3. 需要時查閱 **FRONTEND_CODE_EXAMPLES.md** 的相關示例
|
||||
|
||||
### 如果您需要開發新功能:
|
||||
1. 查閱 **FRONTEND_QUICK_REFERENCE.md** 的「常用代碼片段」
|
||||
2. 參考 **FRONTEND_CODE_EXAMPLES.md** 的相關示例
|
||||
3. 根據功能類型選擇合適的設計模式
|
||||
|
||||
### 如果您遇到問題:
|
||||
1. 查閱 **FRONTEND_QUICK_REFERENCE.md** 的「常見問題」
|
||||
2. 查閱 **FRONTEND_QUICK_REFERENCE.md** 的「調試技巧」
|
||||
3. 參考 **FRONTEND_CODE_EXAMPLES.md** 的「最佳實踐」
|
||||
|
||||
## 項目核心信息速記
|
||||
|
||||
### 技術棧
|
||||
- **UI 框架**: React 19.2.0 + TypeScript
|
||||
- **構建工具**: Vite 7.2.2
|
||||
- **樣式**: Tailwind CSS 4.1.17
|
||||
- **狀態管理**: Zustand 5.0.8 + React Query 5.90.7
|
||||
- **路由**: React Router v7.9.5
|
||||
- **HTTP**: Axios
|
||||
- **國際化**: i18next (繁體中文)
|
||||
|
||||
### 開發環境
|
||||
- **開發端口**: 12011
|
||||
- **後端 API**: localhost:12010
|
||||
- **命令**: `npm run dev` | `npm run build` | `npm run lint`
|
||||
|
||||
### 主要頁面
|
||||
- `/login` - 登錄頁面
|
||||
- `/upload` - 文件上傳
|
||||
- `/processing` - OCR 處理
|
||||
- `/results` - 結果查看
|
||||
- `/export` - 結果導出
|
||||
- `/settings` - 系統設置
|
||||
|
||||
### 核心目錄
|
||||
- `/components` - React 組件
|
||||
- `/pages` - 應用頁面
|
||||
- `/store` - 狀態管理 (Zustand)
|
||||
- `/services` - API 客戶端
|
||||
- `/types` - TypeScript 類型
|
||||
- `/i18n` - 國際化配置
|
||||
|
||||
## 文檔使用建議
|
||||
|
||||
### 打開文檔
|
||||
所有文檔都保存在項目根目錄:
|
||||
```
|
||||
/Users/egg/Projects/Tool_OCR/FRONTEND_*.md
|
||||
```
|
||||
|
||||
推薦使用 VS Code 或任何 Markdown 編輯器打開。
|
||||
|
||||
### 導航技巧
|
||||
- 使用 Markdown 編輯器的大綱功能快速導航
|
||||
- 使用 Ctrl+F (或 Cmd+F) 搜索關鍵詞
|
||||
- 點擊標題鏈接快速跳轉
|
||||
|
||||
### 保持同步
|
||||
文檔基於以下源代碼分析:
|
||||
- React 19.2.0
|
||||
- Vite 7.2.2
|
||||
- Tailwind CSS 4.1.17
|
||||
- 其他依賴均按 package.json 記錄
|
||||
|
||||
如果依賴升級或架構調整,請相應更新文檔。
|
||||
|
||||
## 常用快速鏈接
|
||||
|
||||
### 官方文檔
|
||||
- [React 官方文檔](https://react.dev)
|
||||
- [Tailwind CSS](https://tailwindcss.com)
|
||||
- [Vite 文檔](https://vitejs.dev)
|
||||
- [React Router](https://reactrouter.com)
|
||||
- [Zustand](https://github.com/pmndrs/zustand)
|
||||
- [React Query](https://tanstack.com/query)
|
||||
- [i18next](https://www.i18next.com)
|
||||
|
||||
### IDE 推薦擴展 (VS Code)
|
||||
- Tailwind CSS IntelliSense
|
||||
- ES7+ React/Redux/React-Native snippets
|
||||
- Thunder Client (API 測試)
|
||||
- TypeScript Vue Plugin
|
||||
- ESLint
|
||||
|
||||
## 文檔維護
|
||||
|
||||
這些文檔是對項目當前狀態的快照。當進行以下操作時應考慮更新:
|
||||
|
||||
- 升級主要依賴版本
|
||||
- 更改項目結構
|
||||
- 添加新的關鍵功能
|
||||
- 改變設計模式或最佳實踐
|
||||
- 優化或重構大型模塊
|
||||
|
||||
## 反饋和建議
|
||||
|
||||
如果發現文檔中有錯誤或遺漏,歡迎:
|
||||
1. 直接編輯文檔並提交 PR
|
||||
2. 創建 Issue 報告問題
|
||||
3. 聯繫專案維護者
|
||||
|
||||
---
|
||||
|
||||
最後更新: 2024年11月12日
|
||||
文檔覆蓋版本: React 19.2.0, Vite 7.2.2, Tailwind CSS 4.1.17
|
||||
Reference in New Issue
Block a user