127 lines
6.7 KiB
HTML
127 lines
6.7 KiB
HTML
{{define "face_gallery"}}
|
||
<div class="card">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2 class="title-with-icon">{{icon "profile"}}<span>人脸库管理</span></h2>
|
||
<div class="form-hint">批量导入或新增人员照片,修改后需在资源管理页面同步到设备。</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{if .Message}}
|
||
<div class="card"><div class="muted small" style="margin:0">{{.Message}}</div></div>
|
||
{{end}}
|
||
|
||
<div class="detail-grid">
|
||
<div class="card">
|
||
<h3 class="title-with-icon">{{icon "overview"}}<span>批量导入</span></h3>
|
||
<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>
|
||
</div>
|
||
<div class="actions" style="margin-top:12px">
|
||
<button class="btn" type="submit">批量导入</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<div class="card">
|
||
<h3 class="title-with-icon">{{icon "edit"}}<span>新增人员</span></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" placeholder="例如:张三" required /></label>
|
||
<label><span>照片</span><input type="file" name="photo" accept="image/*" required /></label>
|
||
</div>
|
||
<div class="actions" style="margin-top:12px">
|
||
<button class="btn" type="submit">新增</button>
|
||
</div>
|
||
</form>
|
||
</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>
|
||
{{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">
|
||
<div class="photo-viewer" data-photos='[{{range $i,$p := .Photos}}{{if $i}},{{end}}"/ui/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="/ui/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: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>
|
||
{{end}}
|
||
</div>
|
||
<div style="margin-top:6px;display:flex;justify-content:space-between;align-items:center">
|
||
<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}}'/ui/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>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
{{else}}
|
||
<div class="empty-state"><div class="empty-title">人脸库为空</div></div>
|
||
{{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) {
|
||
var photos = JSON.parse(el.dataset.photos);
|
||
var id = el.querySelector('img') ? el.querySelector('img').id.replace('img-','') : '';
|
||
if (id) photoData[id] = {photos: photos, idx: 0};
|
||
});
|
||
function prevPhoto(id) {
|
||
var d = photoData[id]; if (!d || d.photos.length<2) return;
|
||
d.idx = (d.idx - 1 + d.photos.length) % d.photos.length;
|
||
document.getElementById('img-'+id).src = d.photos[d.idx];
|
||
}
|
||
function nextPhoto(id) {
|
||
var d = photoData[id]; if (!d || d.photos.length<2) return;
|
||
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}}
|