|
|
@@ -185,6 +185,17 @@ async function loadClients() {
|
|
|
statusCell.innerHTML = isActive ? '<span class="status-active">● 在线</span>' : '<span class="status-expired">● 已过期</span>';
|
|
|
row.appendChild(statusCell);
|
|
|
|
|
|
+ // Action buttons
|
|
|
+ const actionCell = document.createElement('td');
|
|
|
+ if (isActive) {
|
|
|
+ const evictBtn = document.createElement('button');
|
|
|
+ evictBtn.textContent = '剔除';
|
|
|
+ evictBtn.style.cssText = 'background-color: #f39c12; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; margin-right: 5px;';
|
|
|
+ evictBtn.onclick = () => evictClient(lease.MAC, lease.IP);
|
|
|
+ actionCell.appendChild(evictBtn);
|
|
|
+ }
|
|
|
+ row.appendChild(actionCell);
|
|
|
+
|
|
|
tbody.appendChild(row);
|
|
|
});
|
|
|
|
|
|
@@ -502,6 +513,28 @@ async function deleteBinding(id) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+async function evictClient(mac, ip) {
|
|
|
+ if (!confirm(`确定要剔除主机 ${mac} (${ip}) 吗?\n\n主机将被迫释放该 IP 并重新获取。`)) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const response = await fetch(`/api/dhcp/leases/evict`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: { 'X-Session-ID': sessionId, 'Content-Type': 'application/json' },
|
|
|
+ body: JSON.stringify({ mac, ip })
|
|
|
+ });
|
|
|
+
|
|
|
+ if (response.ok) {
|
|
|
+ alert('主机已剔除,将重新获取 IP');
|
|
|
+ loadClients();
|
|
|
+ } else {
|
|
|
+ const data = await response.json();
|
|
|
+ alert('剔除失败:' + (data.error || '未知错误'));
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ alert('剔除失败:' + error.message);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// Load DNS Config
|
|
|
async function loadDNSConfig() {
|
|
|
try {
|