106 lines
7.0 KiB
HTML
106 lines
7.0 KiB
HTML
{{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()">×</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)">◀</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)">▶</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}}]'>✎</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()">✕</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+')">×</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;
|
||
fetch('/ui/face-gallery/delete-photo',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'id='+id}).then(function(){window.location.reload();});
|
||
}
|
||
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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
||
</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}}
|