feat: 人员库弹框显示人员图片

This commit is contained in:
2026-04-19 16:43:25 +08:00
parent e75425ac14
commit 69b57b1904
13 changed files with 63 additions and 1 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
data/events.db Normal file

Binary file not shown.

View File

@@ -620,6 +620,7 @@ class PersonManager:
'visit_count': p['visit_count'], 'visit_count': p['visit_count'],
'first_seen': p['first_seen'], # 已经精确到秒 'first_seen': p['first_seen'], # 已经精确到秒
'last_seen': p['last_seen'], # 已经精确到秒 'last_seen': p['last_seen'], # 已经精确到秒
'face_path': p.get('face_path', ''), # 人脸图片路径
} }
for p in self.persons.values() for p in self.persons.values()
] ]

Binary file not shown.

View File

@@ -195,6 +195,25 @@ async def rename_person(person_id: str, name: str):
raise HTTPException(status_code=404, detail="人员不存在") 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") @app.get("/api/stats/daily")
async def get_daily_stats(date: str = None): async def get_daily_stats(date: str = None):
"""获取每日统计数据""" """获取每日统计数据"""

View File

@@ -630,7 +630,15 @@ function loadPersonsList() {
var firstSeen = new Date(person.first_seen).toLocaleString(); var firstSeen = new Date(person.first_seen).toLocaleString();
var lastSeen = new Date(person.last_seen).toLocaleString(); var lastSeen = new Date(person.last_seen).toLocaleString();
item.innerHTML = '<div class="person-info">' + // 构建人员图片URL
var faceImgHtml = '<div class="person-face-placeholder">No Image</div>';
if (person.face_path) {
faceImgHtml = '<img class="person-face-img" src="/api/persons/' + person.person_id + '/face" alt="' + person.name + '" onerror="this.parentElement.innerHTML=\'<div class=&quot;person-face-placeholder&quot;>No Image</div>\'">';
}
item.innerHTML = '<div class="person-face">' + faceImgHtml + '</div>' +
'<div class="person-content">' +
'<div class="person-info">' +
'<span class="person-name">' + person.name + '</span>' + '<span class="person-name">' + person.name + '</span>' +
'<span class="person-id">' + person.person_id + '</span>' + '<span class="person-id">' + person.person_id + '</span>' +
'</div>' + '</div>' +
@@ -642,6 +650,7 @@ function loadPersonsList() {
'<div class="person-actions">' + '<div class="person-actions">' +
'<button onclick="renamePerson(\'' + person.person_id + '\')" class="btn-small">Rename</button>' + '<button onclick="renamePerson(\'' + person.person_id + '\')" class="btn-small">Rename</button>' +
'<button onclick="deletePerson(\'' + person.person_id + '\')" class="btn-small btn-danger">Delete</button>' + '<button onclick="deletePerson(\'' + person.person_id + '\')" class="btn-small btn-danger">Delete</button>' +
'</div>' +
'</div>'; '</div>';
listDiv.appendChild(item); listDiv.appendChild(item);

View File

@@ -751,6 +751,38 @@ button {
margin-bottom: 8px; margin-bottom: 8px;
border-radius: 8px; border-radius: 8px;
border-left: 4px solid #2196F3; 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 { .person-info {
@@ -771,6 +803,7 @@ button {
.person-stats { .person-stats {
display: flex; display: flex;
flex-wrap: wrap;
gap: 10px; gap: 10px;
color: #555; color: #555;
font-size: 12px; font-size: 12px;