实现设备管理功能

This commit is contained in:
Alex Yang
2025-12-03 00:46:46 +08:00
parent a7d794405c
commit c8cdd09455
4 changed files with 185 additions and 22 deletions

View File

@@ -108,10 +108,15 @@ func HandleMetricsPost(c *gin.Context) {
agentName = device.Name agentName = device.Name
} }
// 更新设备状态为active // 确保设备状态为active
if err := deviceStorage.UpdateDeviceStatus(deviceID, "active"); err != nil { if device.Status != "active" {
// 只记录警告,不影响指标处理 // 更新设备状态为active
log.Printf("Warning: Failed to update device status: %v", err) if err := deviceStorage.UpdateDeviceStatus(deviceID, "active"); err != nil {
// 只记录警告,不影响指标处理
log.Printf("Warning: Failed to update device status: %v", err)
} else {
log.Printf("Device %s activated successfully", deviceID)
}
} }
// 创建基础标签包含Agent名称 // 创建基础标签包含Agent名称

Binary file not shown.

View File

@@ -340,11 +340,29 @@
<!-- 设备管理 --> <!-- 设备管理 -->
<div id="devicesContent" class="hidden"> <div id="devicesContent" class="hidden">
<div class="bg-white rounded-xl shadow-md p-6"> <div class="bg-white rounded-xl shadow-md p-6">
<div class="flex justify-between items-center mb-6"> <div class="mb-6">
<h2 class="text-xl font-bold text-gray-900">设备管理</h2> <div class="flex justify-between items-center mb-4">
<button id="addDeviceBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-all duration-200"> <h2 class="text-xl font-bold text-gray-900">设备管理</h2>
添加设备 <button id="addDeviceBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-all duration-200">
</button> 添加设备
</button>
</div>
<div class="flex gap-4">
<div class="flex-1">
<input type="text" id="deviceSearch" placeholder="搜索设备名称、ID或IP..." class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<div>
<select id="deviceStatusFilter" class="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="all">所有状态</option>
<option value="active">活跃</option>
<option value="inactive">非活跃</option>
<option value="offline">离线</option>
</select>
</div>
<button id="clearFilterBtn" class="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors">
清除筛选
</button>
</div>
</div> </div>
<!-- 设备列表 --> <!-- 设备列表 -->

View File

@@ -328,6 +328,9 @@ function createServerCard(device) {
return card; return card;
} }
// 存储原始设备列表,用于搜索和筛选
let originalDeviceList = [];
// 设备管理 // 设备管理
async function loadDeviceManagementList() { async function loadDeviceManagementList() {
try { try {
@@ -342,7 +345,9 @@ async function loadDeviceManagementList() {
// 如果设备列表为空,使用模拟数据作为回退 // 如果设备列表为空,使用模拟数据作为回退
if (devices.length === 0) { if (devices.length === 0) {
console.warn('No devices available, using mock data'); console.warn('No devices available, using mock data');
renderDeviceManagementList(getMockDevices()); const mockDevices = getMockDevices();
originalDeviceList = mockDevices;
renderDeviceManagementList(mockDevices);
return; return;
} }
@@ -356,14 +361,63 @@ async function loadDeviceManagementList() {
token: device.token || 'N/A' token: device.token || 'N/A'
})); }));
renderDeviceManagementList(processedDevices); // 存储原始设备列表
originalDeviceList = processedDevices;
// 应用当前筛选条件
applyDeviceFilters();
} catch (error) { } catch (error) {
console.error('加载设备管理列表失败:', error); console.error('加载设备管理列表失败:', error);
// 使用模拟数据作为回退 // 使用模拟数据作为回退
renderDeviceManagementList(getMockDevices()); const mockDevices = getMockDevices();
originalDeviceList = mockDevices;
renderDeviceManagementList(mockDevices);
} }
} }
// 应用设备筛选条件
function applyDeviceFilters() {
const searchTerm = document.getElementById('deviceSearch').value.toLowerCase();
const statusFilter = document.getElementById('deviceStatusFilter').value;
// 复制原始列表
let filteredDevices = [...originalDeviceList];
// 应用搜索筛选
if (searchTerm) {
filteredDevices = filteredDevices.filter(device =>
device.name.toLowerCase().includes(searchTerm) ||
device.id.toLowerCase().includes(searchTerm) ||
device.ip.toLowerCase().includes(searchTerm)
);
}
// 应用状态筛选
if (statusFilter && statusFilter !== 'all') {
filteredDevices = filteredDevices.filter(device => device.status === statusFilter);
}
// 渲染筛选后的设备列表
renderDeviceManagementList(filteredDevices);
// 显示或隐藏无数据提示
const noDataMessage = document.getElementById('noDataMessage');
if (noDataMessage) {
if (filteredDevices.length === 0) {
noDataMessage.classList.remove('hidden');
} else {
noDataMessage.classList.add('hidden');
}
}
}
// 清除筛选条件
function clearDeviceFilters() {
document.getElementById('deviceSearch').value = '';
document.getElementById('deviceStatusFilter').value = 'all';
applyDeviceFilters();
}
// 获取模拟设备数据 // 获取模拟设备数据
function getMockDevices() { function getMockDevices() {
return [ return [
@@ -385,25 +439,81 @@ function copyToClipboard(text) {
} }
// 显示临时提示消息 // 显示临时提示消息
function showToast(message) { function showToast(message, type = 'success') {
// 检查是否已存在toast元素 // 检查是否已存在toast元素
let toast = document.getElementById('toast'); let toast = document.getElementById('toast');
if (!toast) { if (!toast) {
toast = document.createElement('div'); toast = document.createElement('div');
toast.id = 'toast'; toast.id = 'toast';
toast.className = 'fixed bottom-4 right-4 bg-gray-800 text-white px-4 py-2 rounded opacity-0 transition-opacity duration-300 z-50';
document.body.appendChild(toast); document.body.appendChild(toast);
} }
// 设置toast样式根据类型
let bgColor = 'bg-gray-800';
switch(type) {
case 'success':
bgColor = 'bg-green-800';
break;
case 'error':
bgColor = 'bg-red-800';
break;
case 'warning':
bgColor = 'bg-yellow-800';
break;
}
toast.className = `${bgColor} text-white px-4 py-2 rounded opacity-0 transition-opacity duration-300 z-50 fixed bottom-4 right-4`;
toast.textContent = message; toast.textContent = message;
toast.style.opacity = '1'; toast.style.opacity = '1';
// 2秒后隐藏 // 2秒后自动隐藏
setTimeout(() => { setTimeout(() => {
toast.style.opacity = '0'; toast.style.opacity = '0';
}, 2000); }, 2000);
} }
// 切换设备状态(激活/停用)
async function toggleDeviceStatus(deviceId, newStatus) {
try {
// 先获取设备详情
const deviceResponse = await fetch(`${API_BASE_URL}/devices/${deviceId}`);
if (!deviceResponse.ok) {
throw new Error('Failed to fetch device details');
}
const deviceData = await deviceResponse.json();
const device = deviceData.device;
// 更新设备状态
const updateResponse = await fetch(`${API_BASE_URL}/devices/${deviceId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
status: newStatus,
name: device.name,
ip: device.ip
})
});
if (!updateResponse.ok) {
throw new Error(`HTTP error! status: ${updateResponse.status}`);
}
// 重新加载设备列表
await loadDeviceManagementList();
// 显示成功提示
const statusText = newStatus === 'active' ? '激活' : '停用';
showToast(`设备已成功${statusText}`, 'success');
} catch (error) {
console.error('Failed to toggle device status:', error);
const statusText = newStatus === 'active' ? '激活' : '停用';
showToast(`设备${statusText}失败`, 'error');
}
}
// 编辑设备 // 编辑设备
function editDevice(deviceId) { function editDevice(deviceId) {
// 这里可以实现编辑设备的逻辑,比如打开模态框等 // 这里可以实现编辑设备的逻辑,比如打开模态框等
@@ -474,6 +584,8 @@ function renderDeviceManagementList(devices) {
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${device.created_at}</td> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${device.created_at}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<button class="text-blue-600 hover:text-blue-800 mr-3" onclick="editDevice('${device.id}')">编辑</button> <button class="text-blue-600 hover:text-blue-800 mr-3" onclick="editDevice('${device.id}')">编辑</button>
<button class="text-green-600 hover:text-green-800 mr-3 ${device.status === 'active' ? 'hidden' : ''}" onclick="toggleDeviceStatus('${device.id}', 'active')">激活</button>
<button class="text-yellow-600 hover:text-yellow-800 mr-3 ${device.status !== 'active' ? 'hidden' : ''}" onclick="toggleDeviceStatus('${device.id}', 'inactive')">停用</button>
<button class="text-red-600 hover:text-red-800" onclick="deleteDevice('${device.id}')">删除</button> <button class="text-red-600 hover:text-red-800" onclick="deleteDevice('${device.id}')">删除</button>
</td> </td>
`; `;
@@ -1563,13 +1675,23 @@ function closeModal() {
// 编辑设备 // 编辑设备
function editDevice(deviceId) { function editDevice(deviceId) {
// 查找对应的设备数据 // 直接从API获取设备详情确保数据最新
const deviceData = state.deviceList.find(device => device.id === deviceId); fetch(`${API_BASE_URL}/devices/${deviceId}`)
if (deviceData) { .then(response => {
openModal(true, deviceData); if (!response.ok) {
} else { throw new Error('Failed to fetch device details');
showToast('未找到设备数据', 'error'); }
} return response.json();
})
.then(data => {
// 提取设备数据API返回的是 {"device": {...}} 格式)
const deviceData = data.device;
openModal(true, deviceData);
})
.catch(error => {
console.error('Failed to get device details:', error);
showToast('获取设备详情失败', 'error');
});
} }
// 处理设备表单提交 // 处理设备表单提交
@@ -1686,6 +1808,24 @@ function bindEvents() {
}); });
} }
// 设备搜索事件
const deviceSearch = document.getElementById('deviceSearch');
if (deviceSearch) {
deviceSearch.addEventListener('input', applyDeviceFilters);
}
// 设备状态筛选事件
const deviceStatusFilter = document.getElementById('deviceStatusFilter');
if (deviceStatusFilter) {
deviceStatusFilter.addEventListener('change', applyDeviceFilters);
}
// 清除筛选按钮事件
const clearFilterBtn = document.getElementById('clearFilterBtn');
if (clearFilterBtn) {
clearFilterBtn.addEventListener('click', clearDeviceFilters);
}
// 自定义时间查询事件 // 自定义时间查询事件
const customTimeQuery = document.getElementById('customTimeQuery'); const customTimeQuery = document.getElementById('customTimeQuery');
if (customTimeQuery) { if (customTimeQuery) {