diff --git a/internal/storage/face_gallery_repo.go b/internal/storage/face_gallery_repo.go index ef525cc..fb84d4b 100644 --- a/internal/storage/face_gallery_repo.go +++ b/internal/storage/face_gallery_repo.go @@ -7,10 +7,11 @@ import ( ) type PersonRecord struct { - ID int `json:"id"` - Name string `json:"name"` - PhotoCount int `json:"photo_count"` - CreatedAt string `json:"created_at"` + ID int `json:"id"` + Name string `json:"name"` + PhotoCount int `json:"photo_count"` + Photos []string `json:"photos,omitempty"` + CreatedAt string `json:"created_at"` } type FaceGalleryRepo struct { @@ -54,9 +55,7 @@ func (r *FaceGalleryRepo) ListPersons() ([]PersonRecord, error) { } defer db.Close() - rows, err := db.Query(`SELECT p.id, p.name, p.created_at, COUNT(ph.id) AS photo_count -FROM face_persons p LEFT JOIN face_photos ph ON ph.person_id = p.id -GROUP BY p.id ORDER BY p.name`) + rows, err := db.Query(`SELECT id, name, created_at FROM face_persons ORDER BY name`) if err != nil { return nil, err } @@ -65,11 +64,26 @@ GROUP BY p.id ORDER BY p.name`) var out []PersonRecord for rows.Next() { var p PersonRecord - if err := rows.Scan(&p.ID, &p.Name, &p.CreatedAt, &p.PhotoCount); err != nil { + if err := rows.Scan(&p.ID, &p.Name, &p.CreatedAt); err != nil { continue } out = append(out, p) } + // Load photos for each person + for i := range out { + photoRows, err := db.Query(`SELECT photo_path FROM face_photos WHERE person_id = ? ORDER BY id`, out[i].ID) + if err != nil { + continue + } + for photoRows.Next() { + var path string + if photoRows.Scan(&path) == nil { + out[i].Photos = append(out[i].Photos, path) + } + } + photoRows.Close() + out[i].PhotoCount = len(out[i].Photos) + } if out == nil { out = make([]PersonRecord, 0) } diff --git a/internal/web/ui.go b/internal/web/ui.go index d991e15..a22250a 100644 --- a/internal/web/ui.go +++ b/internal/web/ui.go @@ -665,6 +665,7 @@ func (u *UI) Routes() (chi.Router, error) { r.Get("/diagnostics", u.pageDiagnostics) r.Get("/alarms", u.pageAlarms) r.Get("/face-gallery", u.pageFaceGallery) + r.Get("/face-photo/*", u.serveFacePhoto) r.Post("/face-gallery/import", u.actionFaceGalleryImport) r.Post("/face-gallery/build", u.actionFaceGalleryBuild) r.Post("/face-gallery/add", u.actionFaceGalleryAdd) @@ -4122,3 +4123,13 @@ func personNameFromPath(path string) string { } return strings.TrimSpace(parts[len(parts)-2]) } + +func (u *UI) serveFacePhoto(w http.ResponseWriter, r *http.Request) { + path := chi.URLParam(r, "*") + if path == "" || strings.Contains(path, "..") { + http.Error(w, "invalid", http.StatusBadRequest) + return + } + fullPath := filepath.Join("dataset", path) + http.ServeFile(w, r, fullPath) +} diff --git a/internal/web/ui/templates/face_gallery.html b/internal/web/ui/templates/face_gallery.html index 6828a1b..29042ee 100644 --- a/internal/web/ui/templates/face_gallery.html +++ b/internal/web/ui/templates/face_gallery.html @@ -3,19 +3,19 @@

{{icon "profile"}}人脸库管理

-
批量导入或逐个添加人员照片,然后重新生成人脸库并下发到设备。
+
批量导入或新增人员照片,修改后需在资源管理页面同步到设备。
{{if .Message}} -
{{.Message}}
+
{{.Message}}
{{end}}

{{icon "overview"}}批量导入

-
选择按姓名分目录的照片根目录,系统自动识别子目录名作为人员姓名。
+
选择按姓名分目录的照片根目录。
@@ -25,10 +25,9 @@
-

{{icon "edit"}}新增人员

-
添加单个人员并上传照片。
+
添加单个人员。
@@ -45,37 +44,58 @@

{{icon "profile"}}人员列表

-
-
+ {{len .FaceGalleryPersons}} 人
-
- {{if .FaceGalleryPersons}} - - - - - - {{range .FaceGalleryPersons}} - - - - - + {{if .FaceGalleryPersons}} +
+ {{range .FaceGalleryPersons}} +
+
+ {{if .Photos}} + + {{if gt .PhotoCount 1}} + + {{end}} -
-
ID姓名操作
{{.ID}}{{.Name}} - - - - -
- {{else}} -
-
人脸库为空
-
请先批量导入或新增人员,然后点击"重新生成人脸库"。
+ {{else}} +
无照片
+ {{end}} +
+
+
+ {{.Name}} +
{{.PhotoCount}} 张照片
+
+
+ + +
+
{{end}}
+ {{else}} +
人脸库为空
+ {{end}}
+ + {{end}}