Files
ESP32_GDEY042T81_server/templates/admin/device_detail.html
jeremygan2021 a2682dc040 first commit
2025-11-16 17:21:25 +08:00

277 lines
12 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.
{% extends "admin/base.html" %}
{% block title %}设备详情 - {{ device.name }} - 墨水屏管理系统{% endblock %}
{% block content %}
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2"><i class="fas fa-tv me-2"></i>设备详情 - {{ device.name }}</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<a href="/admin/devices" class="btn btn-secondary me-2">
<i class="fas fa-arrow-left me-1"></i> 返回设备列表
</a>
<a href="/admin/devices/{{ device.device_id }}/contents/add" class="btn btn-primary">
<i class="fas fa-plus me-1"></i> 添加内容
</a>
</div>
</div>
<div class="row">
<!-- 设备信息 -->
<div class="col-lg-6 mb-4">
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<h6 class="m-0 font-weight-bold text-primary"><i class="fas fa-info-circle me-2"></i>设备信息</h6>
</div>
<div class="card-body">
<table class="table table-borderless table-hover">
<tr>
<th width="30%"><i class="fas fa-barcode me-2"></i>设备ID</th>
<td><code>{{ device.device_id }}</code></td>
</tr>
<tr>
<th><i class="fas fa-tag me-2"></i>设备名称</th>
<td>{{ device.name }}</td>
</tr>
<tr>
<th><i class="fas fa-map-marker-alt me-2"></i>应用场景</th>
<td>{{ device.scene }}</td>
</tr>
<tr>
<th><i class="fas fa-toggle-on me-2"></i>状态</th>
<td>
{% if device.is_active %}
{% if device.is_online %}
<span class="badge bg-success"><i class="fas fa-wifi me-1"></i>在线</span>
{% else %}
<span class="badge bg-warning"><i class="fas fa-wifi-slash me-1"></i>离线</span>
{% endif %}
{% else %}
<span class="badge bg-secondary"><i class="fas fa-ban me-1"></i>禁用</span>
{% endif %}
</td>
</tr>
<tr>
<th><i class="fas fa-clock me-2"></i>最后上线</th>
<td>{{ device.last_online.strftime('%Y-%m-%d %H:%M:%S') if device.last_online else '从未' }}</td>
</tr>
<tr>
<th><i class="fas fa-calendar-plus me-2"></i>创建时间</th>
<td>{{ device.created_at.strftime('%Y-%m-%d %H:%M:%S') }}</td>
</tr>
</table>
</div>
</div>
</div>
<!-- 设备操作 -->
<div class="col-lg-6 mb-4">
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<h6 class="m-0 font-weight-bold text-info"><i class="fas fa-cogs me-2"></i>设备操作</h6>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="/admin/devices/{{ device.device_id }}/contents/add" class="btn btn-primary">
<i class="fas fa-plus me-2"></i> 添加内容
</a>
<a href="/admin/upload?device_id={{ device.device_id }}" class="btn btn-success">
<i class="fas fa-upload me-2"></i> 上传图片
</a>
<button class="btn btn-warning" onclick="refreshDevice('{{ device.device_id }}')">
<i class="fas fa-sync me-2"></i> 刷新设备状态
</button>
<button class="btn btn-info" onclick="rebootDevice('{{ device.device_id }}')">
<i class="fas fa-power-off me-2"></i> 重启设备
</button>
</div>
<div class="mt-4">
<h6 class="mb-3"><i class="fas fa-chart-line me-2"></i>设备统计</h6>
<div class="list-group list-group-flush">
<div class="list-group-item px-0 d-flex justify-content-between align-items-center">
<span><i class="fas fa-file-alt me-2"></i>内容总数</span>
<span class="badge bg-primary rounded-pill">{{ contents|length }}</span>
</div>
<div class="list-group-item px-0 d-flex justify-content-between align-items-center">
<span><i class="fas fa-check-circle me-2"></i>活跃内容</span>
<span class="badge bg-success rounded-pill">{{ contents|selectattr('is_active')|list|length }}</span>
</div>
<div class="list-group-item px-0 d-flex justify-content-between align-items-center">
<span><i class="fas fa-image me-2"></i>图片内容</span>
<span class="badge bg-info rounded-pill">{{ contents|selectattr('image_path')|list|length }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 内容列表 -->
<div class="card shadow-sm">
<div class="card-header bg-white py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary"><i class="fas fa-file-alt me-2"></i>内容列表</h6>
<div>
<button class="btn btn-sm btn-outline-secondary me-2" onclick="toggleContentList()">
<i class="fas fa-compress-alt me-1"></i> 折叠/展开
</button>
<a href="/admin/devices/{{ device.device_id }}/contents/add" class="btn btn-sm btn-primary">
<i class="fas fa-plus me-1"></i> 添加内容
</a>
</div>
</div>
<div class="card-body" id="contentList">
{% if contents %}
<div class="table-responsive">
<table class="table table-hover">
<thead class="table-light">
<tr>
<th><i class="fas fa-code-branch me-1"></i>版本</th>
<th><i class="fas fa-heading me-1"></i>标题</th>
<th><i class="fas fa-file-image me-1"></i>类型</th>
<th><i class="fas fa-toggle-on me-1"></i>状态</th>
<th><i class="fas fa-calendar me-1"></i>创建时间</th>
<th><i class="fas fa-cogs me-1"></i>操作</th>
</tr>
</thead>
<tbody>
{% for content in contents %}
<tr>
<td><span class="badge bg-info">v{{ content.version }}</span></td>
<td><a href="/admin/devices/{{ device.device_id }}/contents/{{ content.version }}" class="text-decoration-none">{{ content.title }}</a></td>
<td>
{% if content.image_path %}
<span class="badge bg-info"><i class="fas fa-image me-1"></i>图片</span>
{% else %}
<span class="badge bg-secondary"><i class="fas fa-font me-1"></i>文本</span>
{% endif %}
</td>
<td>
{% if content.is_active %}
<span class="badge bg-success"><i class="fas fa-check-circle me-1"></i>活跃</span>
{% else %}
<span class="badge bg-secondary"><i class="fas fa-pause-circle me-1"></i>禁用</span>
{% endif %}
</td>
<td>{{ content.created_at.strftime('%Y-%m-%d %H:%M') }}</td>
<td>
<div class="btn-group btn-group-sm" role="group">
<a href="/admin/devices/{{ device.device_id }}/contents/{{ content.version }}" class="btn btn-outline-info" title="查看">
<i class="fas fa-eye"></i>
</a>
<button class="btn btn-outline-success" onclick="pushContent('{{ device.device_id }}', {{ content.version }})" title="推送">
<i class="fas fa-paper-plane"></i>
</button>
<a href="/admin/upload?device_id={{ device.device_id }}&version={{ content.version }}" class="btn btn-outline-primary" title="上传图片">
<i class="fas fa-image"></i>
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-5">
<i class="fas fa-folder-open fa-3x text-muted mb-3"></i>
<h5>暂无内容</h5>
<p class="text-muted">此设备还没有任何内容。</p>
<a href="/admin/devices/{{ device.device_id }}/contents/add" class="btn btn-primary">
<i class="fas fa-plus me-1"></i> 添加第一个内容
</a>
</div>
{% endif %}
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
function refreshDevice(deviceId) {
showToast('正在刷新设备状态...', 'info');
fetch(`/api/devices/${deviceId}/status`, {
method: 'GET'
})
.then(response => response.json())
.then(data => {
if (data.online) {
showToast('设备在线', 'success');
} else {
showToast('设备离线', 'warning');
}
location.reload();
})
.catch(error => {
console.error('Error:', error);
showToast('获取设备状态失败', 'error');
});
}
function rebootDevice(deviceId) {
if (confirm('确定要重启设备吗?设备可能需要几分钟才能重新上线。')) {
showToast('正在发送重启命令...', 'info');
fetch(`/api/devices/${deviceId}/reboot`, {
method: 'POST'
})
.then(response => response.json())
.then(data => {
showToast('重启命令已发送', 'success');
})
.catch(error => {
console.error('Error:', error);
showToast('发送重启命令失败', 'error');
});
}
}
function pushContent(deviceId, version) {
if (confirm('确定要推送此内容到设备吗?')) {
showToast('正在推送内容...', 'info');
fetch(`/api/devices/${deviceId}/update`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
version: version
})
})
.then(response => response.json())
.then(data => {
showToast('内容推送成功', 'success');
})
.catch(error => {
console.error('Error:', error);
showToast('内容推送失败', 'error');
});
}
}
function toggleContentList() {
const contentList = document.getElementById('contentList');
contentList.classList.toggle('d-none');
}
function showToast(message, type) {
// 创建一个简单的toast通知而不是使用alert
const toast = document.createElement('div');
toast.className = `alert alert-${type === 'success' ? 'success' : type === 'warning' ? 'warning' : type === 'info' ? 'info' : 'danger'} position-fixed top-0 end-0 m-3`;
toast.style.zIndex = '1050';
toast.innerHTML = `<i class="fas fa-${type === 'success' ? 'check-circle' : type === 'warning' ? 'exclamation-triangle' : type === 'info' ? 'info-circle' : 'exclamation-circle'} me-2"></i>${message}`;
document.body.appendChild(toast);
// 3秒后自动移除
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transition = 'opacity 0.5s';
setTimeout(() => {
document.body.removeChild(toast);
}, 500);
}, 3000);
}
</script>
{% endblock %}