实现设备管理功能
This commit is contained in:
@@ -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.
@@ -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>
|
||||||
|
|
||||||
<!-- 设备列表 -->
|
<!-- 设备列表 -->
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user