feat: portrait cards, photo storage, edit button for face gallery
This commit is contained in:
parent
f3ae1ba6f4
commit
b52db0b434
@ -145,3 +145,14 @@ func (r *FaceGalleryRepo) DeletePerson(id int) error {
|
||||
_, err = db.Exec(`DELETE FROM face_persons WHERE id = ?`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *FaceGalleryRepo) RenamePerson(id int, name string) error {
|
||||
db, err := r.open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
now := time.Now().Format(time.RFC3339)
|
||||
_, err = db.Exec(`UPDATE face_persons SET name = ?, updated_at = ? WHERE id = ?`, name, now, id)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -670,6 +670,7 @@ func (u *UI) Routes() (chi.Router, error) {
|
||||
r.Post("/face-gallery/build", u.actionFaceGalleryBuild)
|
||||
r.Post("/face-gallery/add", u.actionFaceGalleryAdd)
|
||||
r.Post("/face-gallery/delete", u.actionFaceGalleryDelete)
|
||||
r.Post("/face-gallery/rename", u.actionFaceGalleryRename)
|
||||
r.Get("/monitor", u.pageMonitor)
|
||||
r.Get("/hls/*", u.proxyHLS)
|
||||
r.Get("/api/monitor/channels", u.apiMonitorChannels)
|
||||
@ -4080,7 +4081,9 @@ func (u *UI) actionFaceGalleryImport(w http.ResponseWriter, r *http.Request) {
|
||||
if len(parts) < 2 { continue }
|
||||
personName := strings.TrimSpace(parts[len(parts)-2])
|
||||
if personName != "" {
|
||||
repo.FindOrCreatePerson(personName)
|
||||
pid, _ := repo.FindOrCreatePerson(personName)
|
||||
// Store relative path for display
|
||||
repo.AddPhoto(pid, personName+"/"+filepath.Base(fp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4133,3 +4136,16 @@ func (u *UI) serveFacePhoto(w http.ResponseWriter, r *http.Request) {
|
||||
fullPath := filepath.Join("dataset", path)
|
||||
http.ServeFile(w, r, fullPath)
|
||||
}
|
||||
|
||||
func (u *UI) actionFaceGalleryRename(w http.ResponseWriter, r *http.Request) {
|
||||
id, _ := strconv.Atoi(r.FormValue("id"))
|
||||
name := strings.TrimSpace(r.FormValue("name"))
|
||||
if id <= 0 || name == "" {
|
||||
http.Error(w, "invalid", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(u.dbPath) != "" {
|
||||
storage.NewFaceGalleryRepo(u.dbPath).RenamePerson(id, name)
|
||||
}
|
||||
http.Redirect(w, r, "/ui/face-gallery", http.StatusFound)
|
||||
}
|
||||
|
||||
@ -50,13 +50,13 @@
|
||||
{{if .FaceGalleryPersons}}
|
||||
<div style="display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;margin-top:12px">
|
||||
{{range .FaceGalleryPersons}}
|
||||
<div class="card" style="padding:8px;position:relative">
|
||||
<div class="photo-viewer" data-photos='[{{range $i,$p := .Photos}}{{if $i}},{{end}}"/face-photo/{{$p}}"{{end}}]' style="width:100%;height:180px;background:var(--surface-soft);border-radius:var(--radius);overflow:hidden;position:relative">
|
||||
<div class="card" style="padding:8px">
|
||||
<div class="photo-viewer" data-photos='[{{range $i,$p := .Photos}}{{if $i}},{{end}}"/face-photo/{{$p}}"{{end}}]' style="width:100%;height:220px;background:var(--surface-soft);border-radius:var(--radius);overflow:hidden;position:relative">
|
||||
{{if .Photos}}
|
||||
<img src="/face-photo/{{index .Photos 0}}" style="width:100%;height:100%;object-fit:cover" id="img-{{.ID}}" />
|
||||
{{if gt .PhotoCount 1}}
|
||||
<button class="btn ghost" style="position:absolute;left:4px;top:50%;transform:translateY(-50%);padding:2px 6px;font-size:10px;opacity:0.8" onclick="prevPhoto({{.ID}})"><</button>
|
||||
<button class="btn ghost" style="position:absolute;right:4px;top:50%;transform:translateY(-50%);padding:2px 6px;font-size:10px;opacity:0.8" onclick="nextPhoto({{.ID}})">></button>
|
||||
<button class="btn ghost" style="position:absolute;left:2px;top:50%;transform:translateY(-50%);padding:2px 4px;font-size:10px;opacity:0.7" onclick="prevPhoto({{.ID}})">◀</button>
|
||||
<button class="btn ghost" style="position:absolute;right:2px;top:50%;transform:translateY(-50%);padding:2px 4px;font-size:10px;opacity:0.7" onclick="nextPhoto({{.ID}})">▶</button>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="muted" style="display:flex;align-items:center;justify-content:center;height:100%">无照片</div>
|
||||
@ -67,6 +67,7 @@
|
||||
<span style="font-weight:500;font-size:13px">{{.Name}}</span>
|
||||
<div class="muted small">{{.PhotoCount}} 张照片</div>
|
||||
</div>
|
||||
t <form method="post" action="/ui/face-gallery/rename" style="display:inline;margin-right:4px"><input type="hidden" name="id" value="{{.ID}}"><input type="text" name="name" value="{{.Name}}" style="width:60px;font-size:11px;padding:2px 4px"><button class="btn ghost" type="submit" style="font-size:10px;padding:2px 4px">改名</button></form>
|
||||
<form method="post" action="/ui/face-gallery/delete" style="display:inline" onsubmit="return confirm('确定删除 {{.Name}}?')">
|
||||
<input type="hidden" name="id" value="{{.ID}}">
|
||||
<button class="btn ghost" type="submit" style="color:var(--danger-soft-text);font-size:11px;padding:2px 6px">删除</button>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user