feat: edit modal with photo management for face gallery
This commit is contained in:
parent
b52db0b434
commit
96e94a1519
@ -671,6 +671,7 @@ func (u *UI) Routes() (chi.Router, error) {
|
||||
r.Post("/face-gallery/add", u.actionFaceGalleryAdd)
|
||||
r.Post("/face-gallery/delete", u.actionFaceGalleryDelete)
|
||||
r.Post("/face-gallery/rename", u.actionFaceGalleryRename)
|
||||
r.Post("/face-gallery/add-photo", u.actionFaceGalleryAddPhoto)
|
||||
r.Get("/monitor", u.pageMonitor)
|
||||
r.Get("/hls/*", u.proxyHLS)
|
||||
r.Get("/api/monitor/channels", u.apiMonitorChannels)
|
||||
@ -4149,3 +4150,28 @@ func (u *UI) actionFaceGalleryRename(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
http.Redirect(w, r, "/ui/face-gallery", http.StatusFound)
|
||||
}
|
||||
|
||||
func (u *UI) actionFaceGalleryAddPhoto(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseMultipartForm(10 << 20); err != nil { http.Error(w, "form error", http.StatusBadRequest); return }
|
||||
id, _ := strconv.Atoi(r.FormValue("id"))
|
||||
if id <= 0 { http.Error(w, "invalid id", http.StatusBadRequest); return }
|
||||
file, hdr, err := r.FormFile("photo")
|
||||
if err != nil { http.Error(w, "photo required", http.StatusBadRequest); return }
|
||||
defer file.Close()
|
||||
// Get person name for directory
|
||||
if strings.TrimSpace(u.dbPath) == "" { http.Redirect(w, r, "/ui/face-gallery", http.StatusFound); return }
|
||||
repo := storage.NewFaceGalleryRepo(u.dbPath)
|
||||
persons, _ := repo.ListPersons()
|
||||
var personName string
|
||||
for _, p := range persons { if p.ID == id { personName = p.Name; break } }
|
||||
if personName == "" { http.Error(w, "person not found", http.StatusNotFound); return }
|
||||
personDir := filepath.Join("dataset", personName)
|
||||
os.MkdirAll(personDir, 0o755)
|
||||
dst := filepath.Join(personDir, hdr.Filename)
|
||||
f, err := os.Create(dst)
|
||||
if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError); return }
|
||||
defer f.Close()
|
||||
io.Copy(f, file)
|
||||
repo.AddPhoto(id, personName+"/"+hdr.Filename)
|
||||
http.Redirect(w, r, "/ui/face-gallery", http.StatusFound)
|
||||
}
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
<div class="detail-grid">
|
||||
<div class="card">
|
||||
<h3 class="title-with-icon">{{icon "overview"}}<span>批量导入</span></h3>
|
||||
<div class="form-hint" style="margin-bottom:12px">选择按姓名分目录的照片根目录。</div>
|
||||
<form method="post" action="/ui/face-gallery/import" enctype="multipart/form-data">
|
||||
<div class="field-grid">
|
||||
<label class="full"><span>选择照片目录</span><input type="file" name="photos" webkitdirectory multiple required /></label>
|
||||
@ -27,7 +26,6 @@
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3 class="title-with-icon">{{icon "edit"}}<span>新增人员</span></h3>
|
||||
<div class="form-hint" style="margin-bottom:12px">添加单个人员。</div>
|
||||
<form method="post" action="/ui/face-gallery/add" enctype="multipart/form-data">
|
||||
<div class="field-grid">
|
||||
<label><span>姓名</span><input type="text" name="name" placeholder="例如:张三" required /></label>
|
||||
@ -63,15 +61,14 @@
|
||||
{{end}}
|
||||
</div>
|
||||
<div style="margin-top:6px;display:flex;justify-content:space-between;align-items:center">
|
||||
<div>
|
||||
<span style="font-weight:500;font-size:13px">{{.Name}}</span>
|
||||
<div class="muted small">{{.PhotoCount}} 张照片</div>
|
||||
<span style="font-weight:500;font-size:13px">{{.Name}}</span>
|
||||
<div class="actions compact">
|
||||
<button class="btn ghost" type="button" style="font-size:11px;padding:2px 6px" onclick="editPerson({{.ID}},'{{.Name}}',[{{range $i,$p := .Photos}}{{if $i}},{{end}}'/face-photo/{{$p}}'{{end}}])">编辑</button>
|
||||
<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>
|
||||
</form>
|
||||
</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>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@ -81,6 +78,24 @@ t <form method="post" action="/ui/face-gallery/rename" style="display:inline;mar
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div id="edit-modal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);z-index:1000;align-items:center;justify-content:center">
|
||||
<div class="card" style="max-width:500px;width:90%;max-height:80vh;overflow-y:auto">
|
||||
<h3 style="margin-bottom:12px">编辑人员</h3>
|
||||
<form method="post" action="/ui/face-gallery/rename" style="margin-bottom:12px">
|
||||
<input type="hidden" name="id" id="edit-id">
|
||||
<div class="field-grid"><label class="full"><span>姓名</span><input type="text" name="name" id="edit-name" required /></label></div>
|
||||
<div class="actions" style="margin-top:8px"><button class="btn" type="submit">保存姓名</button></div>
|
||||
</form>
|
||||
<div id="edit-photos" style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px"></div>
|
||||
<form method="post" action="/ui/face-gallery/add-photo" enctype="multipart/form-data">
|
||||
<input type="hidden" name="id" id="add-photo-id">
|
||||
<div class="field-grid"><label><span>添加照片</span><input type="file" name="photo" accept="image/*" /></label></div>
|
||||
<div class="actions" style="margin-top:8px"><button class="btn ghost" type="submit">上传</button></div>
|
||||
</form>
|
||||
<div class="actions" style="margin-top:12px"><button class="btn ghost" type="button" onclick="closeEdit()">关闭</button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var photoData = {};
|
||||
document.querySelectorAll('.photo-viewer').forEach(function(el) {
|
||||
@ -98,5 +113,14 @@ function nextPhoto(id) {
|
||||
d.idx = (d.idx + 1) % d.photos.length;
|
||||
document.getElementById('img-'+id).src = d.photos[d.idx];
|
||||
}
|
||||
function editPerson(id, name, photos) {
|
||||
document.getElementById('edit-id').value = id;
|
||||
document.getElementById('edit-name').value = name;
|
||||
document.getElementById('add-photo-id').value = id;
|
||||
var ph = document.getElementById('edit-photos');
|
||||
ph.innerHTML = photos.map(function(p,i) { return '<div style="position:relative;width:80px;height:100px"><img src="'+p+'" style="width:100%;height:100%;object-fit:cover;border-radius:4px">' + '<button class="btn ghost" style="position:absolute;top:2px;right:2px;font-size:10px;padding:1px 3px;color:red;background:rgba(0,0,0,0.5)" onclick="return confirm(\'删除这张照片?\')">x</button></div>'; }).join('');
|
||||
document.getElementById('edit-modal').style.display = 'flex';
|
||||
}
|
||||
function closeEdit() { document.getElementById('edit-modal').style.display = 'none'; }
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user