276 lines
13 KiB
HTML
276 lines
13 KiB
HTML
|
|
{% extends "base.html" %}
|
||
|
|
{% block title %}自动签名系统 - 数据库管理{% endblock %}
|
||
|
|
{% block content %}
|
||
|
|
<div class="main-content">
|
||
|
|
<div class="two-col">
|
||
|
|
<div class="col-left" style="max-width:360px;flex-shrink:0;">
|
||
|
|
<div class="card-section">
|
||
|
|
<h6><i class="bi bi-search"></i> 搜索</h6>
|
||
|
|
<div class="input-group input-group-sm">
|
||
|
|
<input class="form-control" type="text" id="searchKeyword" placeholder="人员ID或姓名">
|
||
|
|
<button class="btn btn-primary" id="btnSearch"><i class="bi bi-search"></i></button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card-section">
|
||
|
|
<h6><i class="bi bi-plus-circle"></i> 新增记录</h6>
|
||
|
|
<div class="mb-2">
|
||
|
|
<label class="form-label">人员ID</label>
|
||
|
|
<input class="form-control form-control-sm" type="text" id="addUserId" placeholder="仅字母和数字">
|
||
|
|
</div>
|
||
|
|
<div class="mb-2">
|
||
|
|
<label class="form-label">人员姓名</label>
|
||
|
|
<input class="form-control form-control-sm" type="text" id="addUserName" placeholder="中文或英文">
|
||
|
|
</div>
|
||
|
|
<div class="mb-2">
|
||
|
|
<label class="form-label">签章图片</label>
|
||
|
|
<input class="form-control form-control-sm" type="file" id="addSignImage" accept="image/png,image/jpeg">
|
||
|
|
</div>
|
||
|
|
<div class="mb-2">
|
||
|
|
<div class="form-check">
|
||
|
|
<input class="form-check-input" type="checkbox" id="addIsSignature">
|
||
|
|
<label class="form-check-label" for="addIsSignature" style="font-size:12px;">手写签字(预览时执行黑白二值化)</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<button class="btn btn-primary btn-sm w-100" id="btnAddRecord"><i class="bi bi-plus"></i> 新增</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card-section">
|
||
|
|
<h6><i class="bi bi-upload"></i> 批量导入</h6>
|
||
|
|
<div class="mb-2">
|
||
|
|
<input class="form-control form-control-sm" type="file" id="batchFiles" multiple accept="image/png,image/jpeg">
|
||
|
|
<small class="text-muted">文件名自动作为人员姓名</small>
|
||
|
|
</div>
|
||
|
|
<div class="mb-2">
|
||
|
|
<div class="form-check">
|
||
|
|
<input class="form-check-input" type="checkbox" id="batchAutoId">
|
||
|
|
<label class="form-check-label" for="batchAutoId" style="font-size:12px;">自动生成ID</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="mb-2">
|
||
|
|
<input class="form-control form-control-sm" type="text" id="batchIdPrefix" placeholder="ID前缀(默认USER)" value="USER">
|
||
|
|
</div>
|
||
|
|
<div class="mb-2">
|
||
|
|
<div class="form-check">
|
||
|
|
<input class="form-check-input" type="checkbox" id="batchIsSignature">
|
||
|
|
<label class="form-check-label" for="batchIsSignature" style="font-size:12px;">手写签字</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<button class="btn btn-success btn-sm w-100" id="btnBatchImport"><i class="bi bi-upload"></i> 批量导入</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card-section" id="editSection" style="display:none;">
|
||
|
|
<h6><i class="bi bi-pencil"></i> 编辑记录</h6>
|
||
|
|
<div class="mb-2">
|
||
|
|
<label class="form-label">人员ID</label>
|
||
|
|
<input class="form-control form-control-sm" type="text" id="editUserId" readonly>
|
||
|
|
</div>
|
||
|
|
<div class="mb-2">
|
||
|
|
<label class="form-label">人员姓名</label>
|
||
|
|
<input class="form-control form-control-sm" type="text" id="editUserName">
|
||
|
|
</div>
|
||
|
|
<div class="mb-2">
|
||
|
|
<label class="form-label">签章图片(留空不修改)</label>
|
||
|
|
<input class="form-control form-control-sm" type="file" id="editSignImage" accept="image/png,image/jpeg">
|
||
|
|
</div>
|
||
|
|
<div class="mb-2">
|
||
|
|
<div class="form-check">
|
||
|
|
<input class="form-check-input" type="checkbox" id="editIsSignature">
|
||
|
|
<label class="form-check-label" for="editIsSignature" style="font-size:12px;">手写签字</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="d-grid gap-2">
|
||
|
|
<button class="btn btn-primary btn-sm" id="btnUpdateRecord"><i class="bi bi-check"></i> 保存</button>
|
||
|
|
<button class="btn btn-outline-secondary btn-sm" id="btnCancelEdit"><i class="bi bi-x"></i> 取消</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card-section" id="previewSection" style="display:none;">
|
||
|
|
<h6><i class="bi bi-eye"></i> 图片预览</h6>
|
||
|
|
<div class="preview-container">
|
||
|
|
<div>
|
||
|
|
<div class="preview-box"><img id="previewOriginal" src="" alt="原图"></div>
|
||
|
|
<div class="label">原图</div>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<div class="preview-box"><img id="previewProcessed" src="" alt="效果图"></div>
|
||
|
|
<div class="label" id="previewLabel">抠图效果</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="col-right">
|
||
|
|
<div class="card-section">
|
||
|
|
<h6><i class="bi bi-table"></i> 签章数据</h6>
|
||
|
|
<div id="dataTableArea"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
{% endblock %}
|
||
|
|
|
||
|
|
{% block extra_js %}
|
||
|
|
<script>
|
||
|
|
let currentPage = 1;
|
||
|
|
const pageSize = 20;
|
||
|
|
|
||
|
|
function loadRecords(page) {
|
||
|
|
currentPage = page || 1;
|
||
|
|
const keyword = document.getElementById('searchKeyword').value;
|
||
|
|
fetch(`/api/db/list?page=${currentPage}&page_size=${pageSize}&keyword=${encodeURIComponent(keyword)}`)
|
||
|
|
.then(r => r.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.success) {
|
||
|
|
renderTable(data.data);
|
||
|
|
} else {
|
||
|
|
alert('加载列表失败: ' + data.message);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(err => alert('请求失败: ' + err.message));
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderTable(pageData) {
|
||
|
|
const area = document.getElementById('dataTableArea');
|
||
|
|
const totalPages = Math.ceil(pageData.total / pageData.page_size);
|
||
|
|
let html = `
|
||
|
|
<table class="table table-hover table-sm">
|
||
|
|
<thead>
|
||
|
|
<tr><th>人员ID</th><th>人员姓名</th><th>图片属性</th><th>创建时间</th><th>操作</th></tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
`;
|
||
|
|
pageData.data.forEach(row => {
|
||
|
|
const sigLabel = row.is_signature ? '<span class="badge bg-info">手写签字</span>' : '<span class="badge bg-secondary">普通签章</span>';
|
||
|
|
html += `
|
||
|
|
<tr>
|
||
|
|
<td>${escapeHtml(row.user_id)}</td>
|
||
|
|
<td>${escapeHtml(row.user_name)}</td>
|
||
|
|
<td>${sigLabel}</td>
|
||
|
|
<td>${row.create_time || '-'}</td>
|
||
|
|
<td>
|
||
|
|
<button class="btn btn-outline-primary btn-sm" onclick="previewRecord('${escapeHtml(row.user_id)}')"><i class="bi bi-eye"></i></button>
|
||
|
|
<button class="btn btn-outline-warning btn-sm" onclick="editRecord('${escapeHtml(row.user_id)}')"><i class="bi bi-pencil"></i></button>
|
||
|
|
<button class="btn btn-outline-danger btn-sm" onclick="deleteRecord('${escapeHtml(row.user_id)}')"><i class="bi bi-trash"></i></button>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
`;
|
||
|
|
});
|
||
|
|
html += '</tbody></table>';
|
||
|
|
|
||
|
|
html += `<div class="pagination-area">`;
|
||
|
|
if (currentPage > 1) html += `<button class="btn btn-sm btn-outline-primary" onclick="loadRecords(${currentPage - 1})">上一页</button>`;
|
||
|
|
html += `<span style="padding:4px 12px;font-size:13px;">第 ${currentPage}/${totalPages} 页 (共${pageData.total}条)</span>`;
|
||
|
|
if (currentPage < totalPages) html += `<button class="btn btn-sm btn-outline-primary" onclick="loadRecords(${currentPage + 1})">下一页</button>`;
|
||
|
|
html += `</div>`;
|
||
|
|
|
||
|
|
area.innerHTML = html;
|
||
|
|
}
|
||
|
|
|
||
|
|
function previewRecord(userId) {
|
||
|
|
document.getElementById('previewSection').style.display = '';
|
||
|
|
document.getElementById('previewOriginal').src = `/api/db/image/${encodeURIComponent(userId)}?t=${Date.now()}`;
|
||
|
|
document.getElementById('previewProcessed').src = `/api/db/preview/${encodeURIComponent(userId)}?t=${Date.now()}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
function editRecord(userId) {
|
||
|
|
fetch(`/api/db/get/${encodeURIComponent(userId)}`)
|
||
|
|
.then(r => r.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.success) {
|
||
|
|
const row = data.data;
|
||
|
|
document.getElementById('editUserId').value = row.user_id;
|
||
|
|
document.getElementById('editUserName').value = row.user_name;
|
||
|
|
document.getElementById('editIsSignature').checked = row.is_signature;
|
||
|
|
document.getElementById('editSection').style.display = '';
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function deleteRecord(userId) {
|
||
|
|
if (!confirm(`确定删除人员 ${userId} 的记录?`)) return;
|
||
|
|
fetch(`/api/db/delete/${encodeURIComponent(userId)}`, { method: 'DELETE' })
|
||
|
|
.then(r => r.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.success) {
|
||
|
|
loadRecords(currentPage);
|
||
|
|
} else {
|
||
|
|
alert('删除失败: ' + data.message);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
document.getElementById('btnSearch').addEventListener('click', () => loadRecords(1));
|
||
|
|
document.getElementById('searchKeyword').addEventListener('keypress', e => { if (e.key === 'Enter') loadRecords(1); });
|
||
|
|
|
||
|
|
document.getElementById('btnAddRecord').addEventListener('click', function() {
|
||
|
|
const formData = new FormData();
|
||
|
|
formData.append('user_id', document.getElementById('addUserId').value);
|
||
|
|
formData.append('user_name', document.getElementById('addUserName').value);
|
||
|
|
formData.append('sign_image', document.getElementById('addSignImage').files[0]);
|
||
|
|
formData.append('is_signature', document.getElementById('addIsSignature').checked ? '1' : '0');
|
||
|
|
|
||
|
|
fetch('/api/db/add', { method: 'POST', body: formData })
|
||
|
|
.then(r => r.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.success) {
|
||
|
|
document.getElementById('addUserId').value = '';
|
||
|
|
document.getElementById('addUserName').value = '';
|
||
|
|
document.getElementById('addSignImage').value = '';
|
||
|
|
document.getElementById('addIsSignature').checked = false;
|
||
|
|
loadRecords(1);
|
||
|
|
} else {
|
||
|
|
alert('新增失败: ' + data.message);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(err => alert('请求失败: ' + err.message));
|
||
|
|
});
|
||
|
|
|
||
|
|
document.getElementById('btnUpdateRecord').addEventListener('click', function() {
|
||
|
|
const userId = document.getElementById('editUserId').value;
|
||
|
|
const formData = new FormData();
|
||
|
|
formData.append('user_name', document.getElementById('editUserName').value);
|
||
|
|
formData.append('is_signature', document.getElementById('editIsSignature').checked ? '1' : '0');
|
||
|
|
const imgFile = document.getElementById('editSignImage').files[0];
|
||
|
|
if (imgFile) formData.append('sign_image', imgFile);
|
||
|
|
|
||
|
|
fetch(`/api/db/update/${encodeURIComponent(userId)}`, { method: 'PUT', body: formData })
|
||
|
|
.then(r => r.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.success) {
|
||
|
|
document.getElementById('editSection').style.display = 'none';
|
||
|
|
loadRecords(currentPage);
|
||
|
|
} else {
|
||
|
|
alert('更新失败: ' + data.message);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
document.getElementById('btnCancelEdit').addEventListener('click', function() {
|
||
|
|
document.getElementById('editSection').style.display = 'none';
|
||
|
|
});
|
||
|
|
|
||
|
|
document.getElementById('btnBatchImport').addEventListener('click', function() {
|
||
|
|
const formData = new FormData();
|
||
|
|
const files = document.getElementById('batchFiles').files;
|
||
|
|
for (let f of files) formData.append('files', f);
|
||
|
|
formData.append('auto_id', document.getElementById('batchAutoId').checked ? '1' : '0');
|
||
|
|
formData.append('id_prefix', document.getElementById('batchIdPrefix').value || 'USER');
|
||
|
|
formData.append('is_signature', document.getElementById('batchIsSignature').checked ? '1' : '0');
|
||
|
|
|
||
|
|
fetch('/api/db/batch_import', { method: 'POST', body: formData })
|
||
|
|
.then(r => r.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.success) {
|
||
|
|
const d = data.data;
|
||
|
|
alert(`批量导入完成: 成功${d.success}条, 失败${d.fail}条`);
|
||
|
|
loadRecords(1);
|
||
|
|
} else {
|
||
|
|
alert('批量导入失败: ' + data.message);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
loadRecords(1);
|
||
|
|
</script>
|
||
|
|
{% endblock %}
|