实现设备管理功能
This commit is contained in:
@@ -108,10 +108,15 @@ func HandleMetricsPost(c *gin.Context) {
|
||||
agentName = device.Name
|
||||
}
|
||||
|
||||
// 更新设备状态为active
|
||||
if err := deviceStorage.UpdateDeviceStatus(deviceID, "active"); err != nil {
|
||||
// 只记录警告,不影响指标处理
|
||||
log.Printf("Warning: Failed to update device status: %v", err)
|
||||
// 确保设备状态为active
|
||||
if device.Status != "active" {
|
||||
// 更新设备状态为active
|
||||
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名称
|
||||
|
||||
Binary file not shown.
@@ -340,11 +340,29 @@
|
||||
<!-- 设备管理 -->
|
||||
<div id="devicesContent" class="hidden">
|
||||
<div class="bg-white rounded-xl shadow-md p-6">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<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>
|
||||
<div class="mb-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<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>
|
||||
</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>
|
||||
|
||||
<!-- 设备列表 -->
|
||||
|
||||
@@ -328,6 +328,9 @@ function createServerCard(device) {
|
||||
return card;
|
||||
}
|
||||
|
||||
// 存储原始设备列表,用于搜索和筛选
|
||||
let originalDeviceList = [];
|
||||
|
||||
// 设备管理
|
||||
async function loadDeviceManagementList() {
|
||||
try {
|
||||
@@ -342,7 +345,9 @@ async function loadDeviceManagementList() {
|
||||
// 如果设备列表为空,使用模拟数据作为回退
|
||||
if (devices.length === 0) {
|
||||
console.warn('No devices available, using mock data');
|
||||
renderDeviceManagementList(getMockDevices());
|
||||
const mockDevices = getMockDevices();
|
||||
originalDeviceList = mockDevices;
|
||||
renderDeviceManagementList(mockDevices);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -356,14 +361,63 @@ async function loadDeviceManagementList() {
|
||||
token: device.token || 'N/A'
|
||||
}));
|
||||
|
||||
renderDeviceManagementList(processedDevices);
|
||||
// 存储原始设备列表
|
||||
originalDeviceList = processedDevices;
|
||||
|
||||
// 应用当前筛选条件
|
||||
applyDeviceFilters();
|
||||
} catch (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() {
|
||||
return [
|
||||
@@ -385,25 +439,81 @@ function copyToClipboard(text) {
|
||||
}
|
||||
|
||||
// 显示临时提示消息
|
||||
function showToast(message) {
|
||||
function showToast(message, type = 'success') {
|
||||
// 检查是否已存在toast元素
|
||||
let toast = document.getElementById('toast');
|
||||
if (!toast) {
|
||||
toast = document.createElement('div');
|
||||
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);
|
||||
}
|
||||
|
||||
// 设置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.style.opacity = '1';
|
||||
|
||||
// 2秒后隐藏
|
||||
// 2秒后自动隐藏
|
||||
setTimeout(() => {
|
||||
toast.style.opacity = '0';
|
||||
}, 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) {
|
||||
// 这里可以实现编辑设备的逻辑,比如打开模态框等
|
||||
@@ -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">
|
||||
<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>
|
||||
</td>
|
||||
`;
|
||||
@@ -1563,13 +1675,23 @@ function closeModal() {
|
||||
|
||||
// 编辑设备
|
||||
function editDevice(deviceId) {
|
||||
// 查找对应的设备数据
|
||||
const deviceData = state.deviceList.find(device => device.id === deviceId);
|
||||
if (deviceData) {
|
||||
openModal(true, deviceData);
|
||||
} else {
|
||||
showToast('未找到设备数据', 'error');
|
||||
}
|
||||
// 直接从API获取设备详情,确保数据最新
|
||||
fetch(`${API_BASE_URL}/devices/${deviceId}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch device details');
|
||||
}
|
||||
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');
|
||||
if (customTimeQuery) {
|
||||
|
||||
Reference in New Issue
Block a user