7.3 KiB
7.3 KiB
People 頁面設計文件
1. 分層架構
| 層級 | 負責 |
|---|---|
| Rust (Backend) | API 代理、檔案系統存取、API key 管理 |
| Vue (Frontend) | UI 渲染、狀態管理、使用者交互 |
2. Core API 端點(已實測驗證)
詳見 CORE_API_REFERENCE.md。以下摘要 People 頁面使用的端點:
| 功能 | Method | 端點 | Response Key | 備註 |
|---|---|---|---|---|
| 列出人物 | GET | /api/v1/identities |
identities |
含 metadata.starred |
| 人物詳情 | GET | /api/v1/identity/{uuid} |
頂層 | 含 tmdb_profile 路徑 |
| 搜尋人物 | GET | /api/v1/identities/search?q= |
results |
欄位不同於 list |
| 大頭照 | — | 本地檔案讀取 | — | 非 Core API |
| 人臉列表 | GET | /api/v1/identity/{uuid}/faces |
data |
不是 faces |
| 追蹤列表 | GET | /api/v1/identity/{uuid}/traces |
traces |
|
| 改名 | PATCH | /api/v1/identity/{uuid} |
頂層 | Body: {"name": "..."} |
| 刪除 | DELETE | /api/v1/identity/{uuid} |
— | 204/404 |
| 建立 | POST | /api/v1/identity |
— | 需要 face_json_path |
| 合併 | POST | /api/v1/identity/{uuid}/mergeinto |
— | Body: {"into_uuid": "..."} |
| 綁定 | POST | /api/v1/identity/{uuid}/bind |
— | Body: {"face_id": "string", "file_uuid": "..."} |
| 解綁 | POST | /api/v1/identity/{uuid}/unbind |
— | Body: 同上 |
| 候選人臉 | GET | /api/v1/faces/candidates |
candidates |
|
| 縮圖 | GET | /api/v1/file/{uuid}/thumbnail |
Binary | |
| 影片 | GET | /api/v1/file/{uuid}/video |
Binary |
3. Rust Structs 定義
struct PersonInfo {
identity_uuid: String,
name: String,
starred: bool, // 從 metadata.starred 提取
}
struct FaceInfo {
id: i64,
file_uuid: String,
frame_number: i64,
timestamp_secs: f64,
face_id: Option<String>,
confidence: f64,
}
struct TraceInfo {
trace_id: i64,
file_uuid: String,
frame_count: i64,
first_sec: f64,
last_sec: f64,
avg_confidence: f64,
}
struct FaceCandidate {
id: i64,
face_id: Option<String>,
file_uuid: String,
frame_number: i64,
confidence: f64,
}
struct SearchIdentityResult {
identity_id: i64,
name: String,
source: String,
tmdb_id: Option<i64>,
file_uuid: Option<String>,
start_time: f64,
end_time: f64,
text_content: Option<String>,
}
4. Rust Commands 清單
4.1 現有(需修改)
| Command | 修改內容 |
|---|---|
get_people |
PersonInfo 加 starred: bool,解析 metadata.starred |
get_faces |
解析 data key 而非 faces,FaceInfo 加 timestamp_secs |
get_video_stream |
重構:寫入 temp file → 回傳 file:// URL |
4.2 現有(不變)
| Command | 行為 |
|---|---|
get_traces |
GET /identity/{uuid}/traces |
get_identity_profile |
讀本地 profile.jpg → base64 |
update_identity_name |
PATCH /identity/{uuid} |
delete_identity |
DELETE /identity/{uuid} |
get_thumbnail |
抓縮圖 → base64 |
4.3 新增
| Command | Core API | Body/Params | 回傳 |
|---|---|---|---|
search_identities |
GET /identities/search?q=... |
query: String, limit: usize |
Vec<SearchIdentityResult> |
merge_identities |
POST /identity/{uuid}/mergeinto |
uuid: String, into_uuid: String |
() |
bind_face |
POST /identity/{uuid}/bind |
uuid, face_id: String, file_uuid: String |
() |
unbind_face |
POST /identity/{uuid}/unbind |
uuid, face_id: String, file_uuid: String |
() |
get_face_candidates |
GET /faces/candidates |
page: usize, per_page: usize |
Vec<FaceCandidate> |
注意:create_identity 需要 face_json_path,不在 People 頁面直接使用。
5. Vue 前端元件樹
PeopleView.vue
├── Toolbar
│ ├── h1 "People"
│ ├── 搜尋框 → debounce 300ms → invoke('search_identities')
│ └── 建立按鈕(暫留,create 需要 face_json_path)
│
├── PeopleGrid
│ └── PersonCard × N (v-for filteredPeople)
│ ├── 大頭照 → invoke('get_identity_profile') cache
│ ├── 名稱
│ ├── ⭐ 星號 (starred)
│ └── @click → selectPerson()
│
├── DetailModal
│ ├── Header: 大頭照 + 可編輯名稱 + UUID
│ ├── FacesTab (橫向捲軸)
│ │ └── face × N → confidence %
│ ├── TracesTab (縱向列表)
│ │ └── trace × N → 時間 + 信心% → @click → VideoPlayer
│ └── ActionsTab
│ ├── Delete → confirm + invoke('delete_identity')
│ ├── Merge → 搜尋目標 + invoke('merge_identities')
│ ├── Bind Face → 從 Candidates 選取 → invoke('bind_face')
│ └── Unbind Face → invoke('unbind_face')
│
├── CandidatesModal
│ └── FaceCandidate × N → @click → bind_face()
│
└── VideoPlayer (共用元件)
└── <video :src="videoSrc"> (file:// URL from temp file)
6. CSS 設計系統
色彩
| 用途 | 色碼 |
|---|---|
| Primary | #2563eb |
| Secondary | #64748b |
| Success | #22c55e |
| Warning | #f59e0b |
| Error | #ef4444 |
| Card BG | #ffffff |
| Text Primary | #1e293b |
| Text Secondary | #64748b |
元件樣式
| 元件 | 樣式 |
|---|---|
| Grid | repeat(auto-fill, minmax(100px, 1fr)) gap 16px |
| Card | bg #fff, border 1px #e5e7eb, radius 12px, padding 16px, hover border #4f46e5 |
| Avatar | 56px 圓形, bg #eef2ff, color #4f46e5 |
| Modal | fixed inset:0, bg rgba(0,0,0,.6), centered, max-w 700px, max-h 80vh |
| VideoPlayer | fixed inset:0, bg rgba(0,0,0,.85), video max-w 80vw max-h 60vh, radius 16px |
7. 資料流
事件 Vue 呼叫 Rust Command
───────────────── ────────────── ────────────
頁面載入 invoke('get_people') get_people()
點擊人物 invoke('get_faces') get_faces()
invoke('get_traces') get_traces()
invoke('get_identity_profile') get_identity_profile()
搜尋輸入(debounce) invoke('search_identities') search_identities()
編輯名稱 invoke('update_identity_name') update_identity_name()
刪除人物 invoke('delete_identity') delete_identity()
合併人物 invoke('merge_identities') merge_identities()
綁定人臉 invoke('bind_face') bind_face()
解綁人臉 invoke('unbind_face') unbind_face()
載入候選人臉 invoke('get_face_candidates') get_face_candidates()
播放影片 invoke('get_video_stream') get_video_stream() → temp file
8. 實作順序
- Phase 1: 修改
PersonInfo+FaceInfostruct + 重構get_video_stream(temp file) - Phase 2: 新增
search_identities+get_face_candidates - Phase 3: 新增
merge_identities+bind_face+unbind_face - Phase 4: Vue PeopleView 完整 UI(grid + modal + tabs + actions + 搜尋 + 合併 + 綁定)
- Phase 5: 更新 AGENTS.md + 提交