update FRONTEND documentation
This commit is contained in:
@@ -51,7 +51,9 @@
|
|||||||
"Bash(TOKEN=\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzIiwidXNlcm5hbWUiOiJhZG1pbiIsImV4cCI6MTc2Mjk1ODUzOX0.S1JjFxVVmifdkN5F_dORt5jTRdTFN9MKJ8UJKuYacA8\")",
|
"Bash(TOKEN=\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzIiwidXNlcm5hbWUiOiJhZG1pbiIsImV4cCI6MTc2Mjk1ODUzOX0.S1JjFxVVmifdkN5F_dORt5jTRdTFN9MKJ8UJKuYacA8\")",
|
||||||
"Bash(tree:*)",
|
"Bash(tree:*)",
|
||||||
"Bash(done)",
|
"Bash(done)",
|
||||||
"Bash(git add:*)"
|
"Bash(git add:*)",
|
||||||
|
"Bash(git commit:*)",
|
||||||
|
"Bash(git push)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"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