diff --git a/__pycache__/analyzer.cpython-310.pyc b/__pycache__/analyzer.cpython-310.pyc new file mode 100644 index 0000000..ab2c875 Binary files /dev/null and b/__pycache__/analyzer.cpython-310.pyc differ diff --git a/__pycache__/camera.cpython-310.pyc b/__pycache__/camera.cpython-310.pyc new file mode 100644 index 0000000..8c09e59 Binary files /dev/null and b/__pycache__/camera.cpython-310.pyc differ diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000..51a72ee Binary files /dev/null and b/__pycache__/config.cpython-310.pyc differ diff --git a/__pycache__/database.cpython-310.pyc b/__pycache__/database.cpython-310.pyc new file mode 100644 index 0000000..649be16 Binary files /dev/null and b/__pycache__/database.cpython-310.pyc differ diff --git a/__pycache__/local_analyzer.cpython-310.pyc b/__pycache__/local_analyzer.cpython-310.pyc new file mode 100644 index 0000000..d59a517 Binary files /dev/null and b/__pycache__/local_analyzer.cpython-310.pyc differ diff --git a/__pycache__/person_manager.cpython-310.pyc b/__pycache__/person_manager.cpython-310.pyc new file mode 100644 index 0000000..bf0bd8c Binary files /dev/null and b/__pycache__/person_manager.cpython-310.pyc differ diff --git a/__pycache__/scheduler.cpython-310.pyc b/__pycache__/scheduler.cpython-310.pyc new file mode 100644 index 0000000..47ed64a Binary files /dev/null and b/__pycache__/scheduler.cpython-310.pyc differ diff --git a/data/events.db b/data/events.db new file mode 100644 index 0000000..958fcd2 Binary files /dev/null and b/data/events.db differ diff --git a/person_manager.py b/person_manager.py index a278037..5e54896 100644 --- a/person_manager.py +++ b/person_manager.py @@ -620,6 +620,7 @@ class PersonManager: 'visit_count': p['visit_count'], 'first_seen': p['first_seen'], # 已经精确到秒 'last_seen': p['last_seen'], # 已经精确到秒 + 'face_path': p.get('face_path', ''), # 人脸图片路径 } for p in self.persons.values() ] diff --git a/web/__pycache__/app.cpython-310.pyc b/web/__pycache__/app.cpython-310.pyc new file mode 100644 index 0000000..998687e Binary files /dev/null and b/web/__pycache__/app.cpython-310.pyc differ diff --git a/web/app.py b/web/app.py index d4c3550..7f2114c 100644 --- a/web/app.py +++ b/web/app.py @@ -195,6 +195,25 @@ async def rename_person(person_id: str, name: str): raise HTTPException(status_code=404, detail="人员不存在") +@app.get("/api/persons/{person_id}/face") +async def get_person_face(person_id: str): + """获取人员人脸图片""" + from person_manager import person_manager + from pathlib import Path + + if person_id in person_manager.persons: + face_path = person_manager.persons[person_id].get('face_path', '') + if face_path and Path(face_path).exists(): + return FileResponse(face_path) + + # 检查 faces_dir 中的默认图片 + face_file = person_manager.faces_dir / f"{person_id}.jpg" + if face_file.exists(): + return FileResponse(str(face_file)) + + raise HTTPException(status_code=404, detail="人员图片不存在") + + @app.get("/api/stats/daily") async def get_daily_stats(date: str = None): """获取每日统计数据""" diff --git a/web/static/app.js b/web/static/app.js index 4b5d54a..5d0826c 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -630,7 +630,15 @@ function loadPersonsList() { var firstSeen = new Date(person.first_seen).toLocaleString(); var lastSeen = new Date(person.last_seen).toLocaleString(); - item.innerHTML = '
' + + // 构建人员图片URL + var faceImgHtml = '
No Image
'; + if (person.face_path) { + faceImgHtml = '' + person.name + ''; + } + + item.innerHTML = '
' + faceImgHtml + '
' + + '
' + + '
' + '' + person.name + '' + '' + person.person_id + '' + '
' + @@ -642,6 +650,7 @@ function loadPersonsList() { '
' + '' + '' + + '
' + '
'; listDiv.appendChild(item); diff --git a/web/static/style.css b/web/static/style.css index c171ce9..f4b2cac 100644 --- a/web/static/style.css +++ b/web/static/style.css @@ -751,6 +751,38 @@ button { margin-bottom: 8px; border-radius: 8px; border-left: 4px solid #2196F3; + display: flex; + gap: 12px; + align-items: flex-start; +} + +.person-face { + width: 80px; + height: 80px; + border-radius: 8px; + overflow: hidden; + background: #e0e0e0; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.person-face-img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.person-face-placeholder { + color: #888; + font-size: 12px; + text-align: center; +} + +.person-content { + flex: 1; + min-width: 0; } .person-info { @@ -771,6 +803,7 @@ button { .person-stats { display: flex; + flex-wrap: wrap; gap: 10px; color: #555; font-size: 12px;