Kamixitong/app/web/templates/device/list.html
2025-11-11 21:39:12 +08:00

384 lines
13 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 "base.html" %}
{% block title %}设备管理 - 软件授权管理系统{% endblock %}
{% block page_title %}设备管理{% endblock %}
{% block content %}
<!-- 搜索和筛选 -->
<div class="card shadow mb-4">
<div class="card-body">
<form id="search-form" class="row g-3">
<div class="col-md-3">
<input type="text" class="form-control" id="search-keyword" placeholder="搜索设备信息...">
</div>
<div class="col-md-2">
<select class="form-select" id="search-status">
<option value="">全部状态</option>
<option value="0">离线</option>
<option value="1">在线</option>
<option value="2">禁用</option>
</select>
</div>
<div class="col-md-3">
<input type="text" class="form-control" id="search-product" placeholder="产品ID或名称...">
</div>
<div class="col-md-2">
<input type="text" class="form-control" id="search-license" placeholder="卡密...">
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-outline-primary">
<i class="fas fa-search me-2"></i>
搜索
</button>
</div>
</form>
</div>
</div>
<!-- 设备列表 -->
<div class="card shadow">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>设备ID</th>
<th>产品/卡密</th>
<th>机器码</th>
<th>IP地址</th>
<th>状态</th>
<th>激活时间</th>
<th>最后在线</th>
<th>操作</th>
</tr>
</thead>
<tbody id="device-list">
<tr>
<td colspan="8" class="text-center text-muted">
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
加载中...
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<nav aria-label="设备列表分页">
<ul class="pagination justify-content-center" id="pagination">
<!-- 分页将通过JavaScript动态生成 -->
</ul>
</nav>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
let currentPage = 1;
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
// 从URL参数中读取筛选条件
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('product_id')) {
document.getElementById('search-product').value = urlParams.get('product_id');
}
if (urlParams.has('software_version')) {
// 这里可以添加版本筛选但当前UI没有对应的输入框
}
if (urlParams.has('status')) {
document.getElementById('search-status').value = urlParams.get('status');
}
loadDevices();
initEventListeners();
});
// 初始化事件监听器
function initEventListeners() {
// 搜索表单
document.getElementById('search-form').addEventListener('submit', function(e) {
e.preventDefault();
currentPage = 1;
loadDevices();
});
}
// 加载设备列表
function loadDevices(page = 1) {
const params = new URLSearchParams({
page: page,
per_page: 10
});
// 添加搜索参数
const keyword = document.getElementById('search-keyword').value.trim();
const status = document.getElementById('search-status').value;
const product = document.getElementById('search-product').value.trim();
const license = document.getElementById('search-license').value.trim();
// 从URL参数中读取额外的筛选条件
const urlParams = new URLSearchParams(window.location.search);
const productId = urlParams.get('product_id');
const softwareVersion = urlParams.get('software_version');
if (keyword) params.append('keyword', keyword);
if (status) params.append('status', status);
if (product) params.append('product', product);
if (license) params.append('license', license);
if (productId) params.append('product_id', productId);
if (softwareVersion) params.append('software_version', softwareVersion);
apiRequest(`/api/v1/devices?${params}`)
.then(data => {
if (data.success) {
renderDeviceList(data.data.devices);
renderPagination(data.data.pagination);
} else {
showNotification(data.message || '加载设备列表失败', 'error');
}
})
.catch(error => {
console.error('Failed to load devices:', error);
showNotification('加载设备列表失败', 'error');
});
}
// 渲染设备列表
function renderDeviceList(devices) {
const tbody = document.getElementById('device-list');
if (devices.length === 0) {
tbody.innerHTML = '<tr><td colspan="8" class="text-center text-muted">暂无数据</td></tr>';
return;
}
tbody.innerHTML = devices.map(device => `
<tr>
<td>
<code>${String(device.device_id).substring(0, 8)}...</code>
<br><small class="text-muted">${device.device_id}</small>
</td>
<td>
${device.product_name || '-'}
<br><small class="text-muted">${device.license_key ? device.license_key.substring(0, 16) + '...' : '-'}</small>
</td>
<td>
<code>${device.machine_code || '-'}</code>
</td>
<td>
-
</td>
<td>
<span class="badge ${getDeviceStatusClass(device.status)}">
${getDeviceStatusText(device.status)}
</span>
</td>
<td>
<small>${device.activate_time ? formatDate(device.activate_time) : '-'}</small>
</td>
<td>
<small>${device.last_verify_time ? formatDate(device.last_verify_time) : '-'}</small>
</td>
<td>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-primary btn-detail"
data-device-id="${device.device_id}"
title="详情">
<i class="fas fa-eye"></i>
</button>
${device.status === 1 ? `
<button type="button" class="btn btn-outline-warning btn-disable"
data-device-id="${device.device_id}"
title="禁用">
<i class="fas fa-ban"></i>
</button>
` : device.status === 2 ? `
<button type="button" class="btn btn-outline-success btn-enable"
data-device-id="${device.device_id}"
title="启用">
<i class="fas fa-check"></i>
</button>
` : ''}
<button type="button" class="btn btn-outline-danger btn-delete"
data-device-id="${device.device_id}"
title="删除">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
`).join('');
// 绑定事件
document.querySelectorAll('.btn-detail').forEach(btn => {
btn.addEventListener('click', function() {
const deviceId = this.dataset.deviceId;
showDeviceDetail(deviceId);
});
});
document.querySelectorAll('.btn-disable').forEach(btn => {
btn.addEventListener('click', function() {
const deviceId = this.dataset.deviceId;
updateDeviceStatus(deviceId, 2); // 禁用
});
});
document.querySelectorAll('.btn-enable').forEach(btn => {
btn.addEventListener('click', function() {
const deviceId = this.dataset.deviceId;
updateDeviceStatus(deviceId, 1); // 启用
});
});
document.querySelectorAll('.btn-delete').forEach(btn => {
btn.addEventListener('click', function() {
const deviceId = this.dataset.deviceId;
deleteDevice(deviceId);
});
});
}
// 获取设备状态文本
function getDeviceStatusText(status) {
const statusMap = {
0: '离线',
1: '在线',
2: '禁用'
};
return statusMap[status] || '未知';
}
// 获取设备状态样式类
function getDeviceStatusClass(status) {
const classMap = {
0: 'bg-secondary',
1: 'bg-success',
2: 'bg-danger'
};
return classMap[status] || 'bg-secondary';
}
// 渲染分页
function renderPagination(pagination) {
const paginationEl = document.getElementById('pagination');
if (pagination.pages <= 1) {
paginationEl.innerHTML = '';
return;
}
let html = '';
// 上一页
if (pagination.has_prev) {
html += `<li class="page-item">
<a class="page-link" href="#" data-page="${pagination.page - 1}">上一页</a>
</li>`;
}
// 页码
const startPage = Math.max(1, pagination.page - 2);
const endPage = Math.min(pagination.pages, pagination.page + 2);
if (startPage > 1) {
html += `<li class="page-item"><a class="page-link" href="#" data-page="1">1</a></li>`;
if (startPage > 2) {
html += `<li class="page-item disabled"><span class="page-link">...</span></li>`;
}
}
for (let i = startPage; i <= endPage; i++) {
html += `<li class="page-item ${i === pagination.page ? 'active' : ''}">
<a class="page-link" href="#" data-page="${i}">${i}</a>
</li>`;
}
if (endPage < pagination.pages) {
if (endPage < pagination.pages - 1) {
html += `<li class="page-item disabled"><span class="page-link">...</span></li>`;
}
html += `<li class="page-item"><a class="page-link" href="#" data-page="${pagination.pages}">${pagination.pages}</a></li>`;
}
// 下一页
if (pagination.has_next) {
html += `<li class="page-item">
<a class="page-link" href="#" data-page="${pagination.page + 1}">下一页</a>
</li>`;
}
paginationEl.innerHTML = html;
// 绑定分页点击事件
paginationEl.querySelectorAll('.page-link').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const page = parseInt(this.dataset.page);
if (page && page !== currentPage) {
currentPage = page;
loadDevices(page);
}
});
});
}
// 显示设备详情
function showDeviceDetail(deviceId) {
// 这里可以实现详情弹窗或跳转到详情页
showNotification('功能开发中...', 'info');
}
// 更新设备状态
function updateDeviceStatus(deviceId, status) {
const action = status === 1 ? '启用' : '禁用';
if (!confirm(`确定要${action}设备 ${deviceId} 吗?`)) {
return;
}
apiRequest(`/api/v1/devices/${deviceId}/status`, {
method: 'PUT',
body: JSON.stringify({ status: status })
})
.then(data => {
if (data.success) {
showNotification(`${action}成功`, 'success');
loadDevices(currentPage);
} else {
showNotification(data.message || `${action}失败`, 'error');
}
})
.catch(error => {
console.error('Failed to update device status:', error);
showNotification(`${action}失败`, 'error');
});
}
// 删除设备
function deleteDevice(deviceId) {
if (!confirm(`确定要删除设备 ${deviceId} 吗?`)) {
return;
}
apiRequest(`/api/v1/devices/${deviceId}`, {
method: 'DELETE'
})
.then(data => {
if (data.success) {
showNotification('删除成功', 'success');
loadDevices(currentPage);
} else {
showNotification(data.message || '删除失败', 'error');
}
})
.catch(error => {
console.error('Failed to delete device:', error);
showNotification('删除失败', 'error');
});
}
</script>
{% endblock %}