safesight-control/internal/web/ui/templates/face_gallery.html

109 lines
7.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{{define "face_gallery"}}
<div id="modal" class="modal-overlay" style="display:none">
<div class="card" style="max-width:500px;width:90%;max-height:80vh;overflow-y:auto;position:relative">
<span class="btn-icon ghost" style="position:absolute;top:8px;right:8px;font-size:14px" title="关闭" onclick="closeModal()">&times;</span>
<div id="modal-body"></div>
</div>
</div>
<div class="card">
<div class="section-title">
<div>
<h3 class="title-with-icon">{{icon "profile"}}<span>人员列表</span></h3>
<span class="muted small">{{len .FaceGalleryPersons}} 人</span>
</div>
<div class="actions compact">
<button class="btn secondary" type="button" onclick="showImport()">批量导入</button>
<button class="btn" type="button" onclick="showAdd()">新增人员</button>
</div>
</div>
{{if .FaceGalleryPersons}}
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:16px;margin-top:12px">
{{range .FaceGalleryPersons}}
<div class="card" style="padding:8px;max-width:200px">
<div class="photo-viewer" data-photos='[{{range $i,$p := .Photos}}{{if $i}},{{end}}"/ui/face-photo/{{$p}}"{{end}}]' style="width:100%;aspect-ratio:3/4;background:var(--surface-soft);border-radius:var(--radius);overflow:hidden;position:relative">
{{if .Photos}}
<img src="/ui/face-photo/{{index .Photos 0}}" style="width:100%;height:100%;object-fit:cover" id="img-{{.ID}}" />
{{if gt .PhotoCount 1}}
<span class="btn-icon ghost nav-btn" id="prev-{{.ID}}" style="position:absolute;left:2px;top:50%;transform:translateY(-50%);font-size:10px;opacity:0.7;display:none" onclick="navPhoto({{.ID}},-1)">&#9664;</span>
<span class="btn-icon ghost nav-btn" id="next-{{.ID}}" style="position:absolute;right:2px;top:50%;transform:translateY(-50%);font-size:10px;opacity:0.7" onclick="navPhoto({{.ID}},1)">&#9654;</span>
{{end}}
{{else}}
<div class="muted" style="display:flex;align-items:center;justify-content:center;height:100%">无照片</div>
{{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> <span class="muted small">{{.PhotoCount}}张</span></div>
<div class="actions compact" style="gap:2px">
<span class="btn-icon ghost edit-btn" title="编辑" data-id="{{.ID}}" data-name="{{.Name}}" data-photos='[{{$person := .}}{{range $i,$p := .Photos}}{{if $i}},{{end}}{"p":"/ui/face-photo/{{$p}}","id":{{index $person.PhotoIDs $i}}}{{end}}]'>&#9998;</span>
<form method="post" action="/ui/face-gallery/delete" style="display:inline" onsubmit="return confirm('确定删除 {{.Name}}')">
<input type="hidden" name="id" value="{{.ID}}">
<span class="btn-icon ghost" style="color:var(--danger-soft-text)" title="删除" onclick="this.closest('form').requestSubmit()">&#10005;</span>
</form>
</div>
</div>
</div>
{{end}}
</div>
{{else}}
<div class="empty-state"><div class="empty-title">人脸库为空</div></div>
{{end}}
</div>
<script>
var photoData={};
document.querySelectorAll('.photo-viewer').forEach(function(el){
var p=JSON.parse(el.dataset.photos);
var id=el.querySelector('img')?el.querySelector('img').id.replace('img-',''):'';
if(id)photoData[id]={photos:p,idx:0};
});
function navPhoto(id,dir){
var d=photoData[id];if(!d||d.photos.length<2)return;
d.idx=(d.idx+dir+d.photos.length)%d.photos.length;
document.getElementById('img-'+id).src=d.photos[d.idx];
document.getElementById('prev-'+id).style.display=d.idx===0?'none':'';
document.getElementById('next-'+id).style.display=d.idx===d.photos.length-1?'none':'';
}
function showImport(){
document.getElementById('modal-body').innerHTML='<h3>批量导入</h3><div class="form-hint" style="margin-bottom:12px">选择按姓名分目录的照片根目录</div><form method="post" action="/ui/face-gallery/import" enctype="multipart/form-data"><label class="full" style="margin-bottom:12px;display:block"><span>选择照片目录</span><input type="file" name="photos" webkitdirectory multiple required /></label><div class="actions"><button class="btn" type="submit">导入</button></div></form>';
document.getElementById('modal').style.display='flex';
}
function showAdd(){
document.getElementById('modal-body').innerHTML='<h3>新增人员</h3><form method="post" action="/ui/face-gallery/add" enctype="multipart/form-data"><div class="field-grid"><label><span>姓名</span><input type="text" name="name" required /></label><label><span>照片</span><input type="file" name="photo" accept="image/*" required /></label></div><div class="actions"><button class="btn" type="submit">新增</button></div></form>';
document.getElementById('modal').style.display='flex';
}
function showEdit(id,name,photos){
var h='<h3>编辑人员</h3>';
h+='<form method="post" action="/ui/face-gallery/rename" style="margin-bottom:12px;display:flex;gap:8px;align-items:flex-end">';
h+='<input type="hidden" name="id" value="'+id+'"><label style="flex:1"><span class="small muted">姓名</span><input type="text" name="name" value="'+esc(name)+'" required /></label>';
h+='<button class="btn" type="submit">保存</button></form>';
h+='<div style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px">';
photos.forEach(function(p){
h+='<div style="position:relative;width:80px;height:100px"><img src="'+p.p+'" style="width:100%;height:100%;object-fit:cover;border-radius:4px">';
h+='<span class="btn-icon ghost" style="position:absolute;top:2px;right:2px;font-size:10px;color:red;background:rgba(0,0,0,0.5)" onclick="delPhoto('+p.id+')">&times;</span>';
h+='</div>';
});
h+='</div>';
h+='<form method="post" action="/ui/face-gallery/add-photo" enctype="multipart/form-data" style="display:flex;gap:8px;align-items:flex-end">';
h+='<input type="hidden" name="id" value="'+id+'"><label style="flex:1"><span class="small muted">添加照片</span><input type="file" name="photo" accept="image/*" /></label>';
h+='<button class="btn ghost" type="submit">上传</button></form>';
document.getElementById('modal-body').innerHTML=h;
document.getElementById('modal').style.display='flex';
}
function delPhoto(id){
if(!confirm('确认删除这张照片?'))return;
var f=document.createElement('form');
f.method='post';f.action='/ui/face-gallery/delete-photo';
var i=document.createElement('input');i.type='hidden';i.name='id';i.value=id;f.appendChild(i);
document.body.appendChild(f);f.submit();
}
function closeModal(){document.getElementById('modal').style.display='none';}
document.addEventListener('click',function(e){var b=e.target.closest('.edit-btn');if(b){showEdit(b.dataset.id,b.dataset.name,JSON.parse(b.dataset.photos));}});
function esc(s){return(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
</script>
<style>
.modal-overlay{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}
</style>
{{end}}