Files
vd_test_fixture/edc-web/app/templates/device_logs.html

147 lines
5.4 KiB
HTML

{% extends "base.html" %}
{% block title %}设备日志 - EDC 工装管理系统{% endblock %}
{% block content %}
<h2>设备事件日志</h2>
<div class="search-bar">
<label>设备序列号:<input type="text" id="search-serial" placeholder="筛选设备..."></label>
<label>事件类型:
<select id="search-event">
<option value="">全部</option>
<option value="login">登录</option>
<option value="online">在线</option>
<option value="offline">离线</option>
<option value="poor">通信不良</option>
<option value="tcp_connect">TCP连接</option>
<option value="tcp_disconnect">TCP断开</option>
</select>
</label>
<button onclick="searchLogs(1)" class="btn-search">查询</button>
{% if current_user.role == 'admin' %}
<button onclick="confirmDeleteLogs()" class="btn-delete">🗑 删除</button>
{% endif %}
</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>设备序列号</th>
<th>设备IP</th>
<th>事件类型</th>
<th>事件内容</th>
<th>时间</th>
</tr>
</thead>
<tbody id="log-tbody"></tbody>
</table>
<div class="pagination" id="pagination"></div>
{% endblock %}
{% block scripts %}
<script>
let currentPage = 1, totalPages = 1;
async function searchLogs(page = 1) {
currentPage = page;
const serial = document.getElementById("search-serial").value;
const event_type = document.getElementById("search-event").value;
const params = new URLSearchParams({page, per_page: 30});
if (serial) params.set("serial", serial);
if (event_type) params.set("event_type", event_type);
const resp = await fetch(`/api/device-logs?${params}`);
const data = await resp.json();
renderTable(data.records);
totalPages = data.pages;
renderPagination();
}
function renderTable(records) {
const tbody = document.getElementById("log-tbody");
if (!records.length) {
tbody.innerHTML = '<tr><td colspan="6" style="text-align:center;color:#999;">暂无记录</td></tr>';
return;
}
tbody.innerHTML = records.map(r => {
let typeStyle = '';
if (r.event_type === 'online' || r.event_type === 'login') typeStyle = 'color:#27ae60;font-weight:bold;';
else if (r.event_type === 'offline') typeStyle = 'color:#e74c3c;font-weight:bold;';
else if (r.event_type === 'poor') typeStyle = 'color:#f39c12;font-weight:bold;';
else if (r.event_type === 'tcp_disconnect') typeStyle = 'color:#e74c3c;';
else if (r.event_type === 'tcp_connect') typeStyle = 'color:#3498db;';
return `
<tr>
<td>${r.id}</td>
<td>${escHtml(r.device_serial || '-')}</td>
<td>${r.device_ip || '-'}</td>
<td style="${typeStyle}">${eventLabel(r.event_type)}</td>
<td style="max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${escHtml(r.event_content || '')}">${r.event_content || '-'}</td>
<td>${fmtTime(r.create_time)}</td>
</tr>`;
}).join("");
}
function eventLabel(t) {
const m = {login: '登录', online: '在线', offline: '离线', poor: '通信不良',
tcp_connect: 'TCP连接', tcp_disconnect: 'TCP断开'};
return m[t] || t;
}
function fmtTime(v) {
if (!v) return '-';
const d = new Date(v);
if (isNaN(d.getTime())) return String(v).substring(0, 19);
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, '0');
const d2 = String(d.getDate()).padStart(2, '0');
const h = String(d.getHours()).padStart(2, '0');
const min = String(d.getMinutes()).padStart(2, '0');
const s = String(d.getSeconds()).padStart(2, '0');
return `${y}-${m}-${d2} ${h}:${min}:${s}`;
}
function renderPagination() {
const div = document.getElementById("pagination");
let html = `<button onclick="searchLogs(${currentPage-1})" ${currentPage<=1?'disabled':''}>上一页</button>`;
for (let i = 1; i <= totalPages; i++) {
html += `<button onclick="searchLogs(${i})" class="${i===currentPage?'active':''}">${i}</button>`;
}
html += `<button onclick="searchLogs(${currentPage+1})" ${currentPage>=totalPages?'disabled':''}>下一页</button>`;
div.innerHTML = html;
}
function escHtml(s) {
return String(s || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
async function confirmDeleteLogs() {
const serial = document.getElementById("search-serial").value;
const event_type = document.getElementById("search-event").value;
if (!serial && !event_type) {
alert("请至少输入设备序列号或选择事件类型作为删除条件");
return;
}
const msg = `确认删除设备日志?\n条件: serial=${serial || '(无)'} type=${event_type || '(无)'}\n此操作不可撤销!`;
if (!confirm(msg)) return;
const resp = await fetch("/api/device-logs/delete", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({serial, event_type}),
});
const data = await resp.json();
if (data.ok) {
alert(`已删除 ${data.deleted} 条记录`);
searchLogs(1);
} else {
alert("删除失败: " + (data.error || "未知错误"));
}
}
searchLogs(1);
</script>
{% endblock %}