<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>客户端控制面板</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body { padding: 20px; }
        .config { margin-bottom: 20px; }
        .actions { margin-bottom: 20px; }
        #commandResults {
            margin-top: 20px;
            border: 1px solid #ddd;
            padding: 10px;
            height: 400px;
            overflow-y: auto;
        }
        .result-item {
            margin-bottom: 10px;
            padding: 5px;
            border-bottom: 1px solid #eee;
        }
        .result-item pre {
            margin: 5px 0;
            white-space: pre-wrap;
            word-wrap: break-word;
        }
        .modal-dialog {
            display: flex;
            align-items: center;
            min-height: calc(100% - 1rem);
        }
        #manualCommand {
            height: 150px;
        }
        .group-tag {
            display: inline-block;
            margin-right: 5px;
            margin-bottom: 5px;
            padding: 5px 10px;
            background-color: #f0f0f0;
            border-radius: 3px;
            cursor: pointer;
        }
        .group-tag.active {
            background-color: #007bff;
            color: white;
        }
        #clientTable tbody tr {
            cursor: pointer;
        }
        #clientTable tbody tr.selected {
            background-color: #e0e0e0;
        }
        #selectionBox {
            position: absolute;
            border: 1px solid #007bff;
            background-color: rgba(0, 123, 255, 0.1);
            pointer-events: none;
        }
    </style>
</head>
<body>
    <div id="selectionBox" style="display: none;"></div>
    <div class="container">
        <div class="row mt-3 mb-3">
            <div class="col-md-12 text-end">
                <a href="/logout" class="btn btn-secondary">登出</a>
            </div>
        </div>
        <h1 class="mb-4">客户端控制面板</h1>
        <div class="config row">
            <div class="col-md-3">
                <label for="heartbeatTime" class="form-label">心跳时间(秒):</label>
                <input type="number" id="heartbeatTime" class="form-control" value="30">
            </div>
            <div class="col-md-3">
                <label for="reportTime" class="form-label">报告时间(秒):</label>
                <input type="number" id="reportTime" class="form-control" value="10">
            </div>
            <div class="col-md-3 mt-4">
                <button onclick="updateSettings()" class="btn btn-primary">更新设置</button>
            </div>
        </div>
        <div class="config row">
            <div class="col-md-3">
                <label for="configSelector" class="form-label">选择配置:</label>
                <select id="configSelector" class="form-select" onchange="loadSelectedConfig()">
                    <option value="">-- 选择配置 --</option>
                </select>
            </div>
            <div class="col-md-3">
                <label for="configName" class="form-label">配置名称:</label>
                <input type="text" id="configName" class="form-control" placeholder="输入新配置名称">
            </div>
            <div class="col-md-3 mt-4">
                <button onclick="saveConfig()" class="btn btn-success">保存配置</button>
                <button onclick="deleteConfig()" class="btn btn-danger">删除配置</button>
            </div>
        </div>
        <div class="config row">
            <div class="col-md-3">
                <label for="updateUrl" class="form-label">更新URL:</label>
                <input type="text" id="updateUrl" class="form-control" value="https://example.com/update">
            </div>
            <div class="col-md-3">
                <label for="programName" class="form-label">程序名:</label>
                <input type="text" id="programName" class="form-control" value="example-program">
            </div>
            <div class="col-md-3">
                <label for="programPath" class="form-label">程序路径:</label>
                <input type="text" id="programPath" class="form-control" value="/path/to/program">
            </div>
            <div class="col-md-3">
                <label for="commandParams" class="form-label">命令参数:</label>
                <input type="text" id="commandParams" class="form-control" value="--param value">
            </div>
        </div>
        <div class="group-selector row mb-3">
            <div class="col-md-12">
                <label class="form-label">选择分组:</label>
                <div id="groupTags"></div>
            </div>
            <div class="col-md-3 mt-2">
                <input type="text" id="newGroupName" class="form-control" placeholder="新分组名称">
            </div>
            <div class="col-md-3 mt-2">
                <button onclick="addNewGroup()" class="btn btn-secondary">添加新分组</button>
            </div>
        </div>
        <div class="actions">
            <button onclick="executeCommand('start')" class="btn btn-success">启动程序</button>
            <button onclick="executeCommand('stop')" class="btn btn-danger">停止程序</button>
            <button onclick="executeCommand('update')" class="btn btn-info">更新程序</button>
            <button onclick="executeCommand('restart')" class="btn btn-warning">重启程序</button>
            <button onclick="showManualCommandModal()" class="btn btn-secondary">手动命令</button>
            <button onclick="deleteSelectedClients()" class="btn btn-danger">删除选中客户端</button>
            <button onclick="changeBulkClientGroup()" class="btn btn-info">批量更改分组</button>
            <button onclick="executeCommand('force_update')" class="btn btn-warning">强制更新</button>
        </div>
        <div class="mt-3">
            <button onclick="selectAllClients()" class="btn btn-outline-primary">全选</button>
            <button onclick="deselectAllClients()" class="btn btn-outline-secondary">取消全选</button>
        </div>
        <table id="clientTable" class="table table-hover mt-3">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>客户端ID</th>
                    <th>IP地址</th>
                    <th>CPU使用率</th>
                    <th>命令状态</th>
                    <th>程序状态</th>
                    <th>分组</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody></tbody>
        </table>

        <div id="commandResults">
            <h3>命令执行结果</h3>
        </div>
    </div>

    <!-- 手动命令模态框 -->
    <div class="modal fade" id="manualCommandModal" tabindex="-1">
        <div class="modal-dialog modal-lg">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">输入手动命令</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <textarea id="manualCommand" class="form-control" placeholder="输入命令"></textarea>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
                    <button type="button" class="btn btn-primary" onclick="executeManualCommand()">执行</button>
                </div>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <script>
const ACCESS_TOKEN = '131417';

const socket = io('http://ore.uqdm.com:5003', {
    transports: ['websocket', 'polling'],
    upgrade: true,
    auth: {
        token: ACCESS_TOKEN
    }
});

let clients = {};
let groups = [];
let manualCommandModal;
let selectedClients = new Set();
let currentGroup = 'all';
let isMouseDown = false;
let startX, startY;
const selectionBox = document.getElementById('selectionBox');

socket.on('connect', () => {
    console.log('Connected to server');
    fetchClients();
    loadConfig();
});

socket.on('command_result', (data) => {
    console.log('Received real-time command result:', data);
    displayCommandResult(data);
});

function fetchClients() {
    console.log('Fetching clients...');
    fetch('/api/clients', {
        headers: {
            'Authorization': `Bearer ${ACCESS_TOKEN}`
        }
    })
    .then(response => {
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        return response.json();
    })
    .then(data => {
        console.log('Received client data:', data);
        if (data.clients && Object.keys(data.clients).length > 0) {
            clients = data.clients;
            groups = data.groups;
            updateClientTable();
            updateGroupTags();
        } else {
            console.warn('No clients data received');
            document.querySelector('#clientTable tbody').innerHTML = '<tr><td colspan="8">No clients found</td></tr>';
        }
    })
    .catch(error => {
        console.error('Error fetching clients:', error);
        document.querySelector('#clientTable tbody').innerHTML = '<tr><td colspan="8">Error loading clients</td></tr>';
    });
}

function updateClientTable() {
    console.log('Updating client table');
    console.log('Current group:', currentGroup);
    console.log('Clients:', clients);

    const tbody = document.querySelector('#clientTable tbody');
    tbody.innerHTML = '';
    let clientCount = 0;

    for (const [clientId, data] of Object.entries(clients)) {
        if (currentGroup === 'all' || data.group === currentGroup) {
            clientCount++;
            const row = tbody.insertRow();
            row.dataset.clientId = clientId;
            row.className = selectedClients.has(clientId) ? 'selected' : '';
            
            // 添加序号列
            row.insertCell(0).textContent = clientCount;

            row.insertCell(1).textContent = clientId;
            row.insertCell(2).textContent = data.system_info?.ip_address || 'N/A';
            row.insertCell(3).textContent = data.system_info?.cpu || 'N/A';
            
            const commandStatusCell = row.insertCell(4);
            if (data.command_status && typeof data.command_status === 'object') {
                commandStatusCell.textContent = data.command_status.status || 'N/A';
                commandStatusCell.title = data.command_status.output || data.command_status.error || '';
            } else {
                commandStatusCell.textContent = 'N/A';
            }
            
            row.insertCell(5).textContent = data.system_info?.program_status || 'N/A';
            row.insertCell(6).textContent = data.group || 'default';
            row.insertCell(7).innerHTML = `<button onclick="changeClientGroup('${clientId}')" class="btn btn-sm btn-outline-primary">更改分组</button>`;
        }
    }

    console.log(`Updated table with ${clientCount} clients`);
}

function updateGroupTags() {
    const groupTags = document.getElementById('groupTags');
    groupTags.innerHTML = '';
    const allTag = document.createElement('span');
    allTag.className = `group-tag ${currentGroup === 'all' ? 'active' : ''}`;
    allTag.textContent = '全部';
    allTag.onclick = () => selectGroup('all');
    groupTags.appendChild(allTag);
    
    groups.forEach(group => {
        const tag = document.createElement('span');
        tag.className = `group-tag ${currentGroup === group ? 'active' : ''}`;
        tag.textContent = group;
        tag.onclick = () => selectGroup(group);
        if (group !== 'default' && group !== '离线') {
            const deleteBtn = document.createElement('button');
            deleteBtn.textContent = 'x';
            deleteBtn.className = 'btn btn-sm btn-danger ml-1';
            deleteBtn.onclick = (e) => {
                e.stopPropagation();
                deleteGroup(group);
            };
            tag.appendChild(deleteBtn);
        }
        groupTags.appendChild(tag);
    });
}

function selectGroup(group) {
    currentGroup = group;
    updateGroupTags();
    updateClientTable();
}

function toggleClientSelection(clientId, row, addToSelection = true) {
    if (addToSelection) {
        if (selectedClients.has(clientId)) {
            selectedClients.delete(clientId);
            row.classList.remove('selected');
        } else {
            selectedClients.add(clientId);
            row.classList.add('selected');
        }
    } else {
        if (selectedClients.has(clientId)) {
            selectedClients.delete(clientId);
            row.classList.remove('selected');
        }
    }
}

function selectAllClients() {
    const rows = document.querySelectorAll('#clientTable tbody tr');
    rows.forEach(row => {
        const clientId = row.dataset.clientId;
        selectedClients.add(clientId);
        row.classList.add('selected');
    });
}

function deselectAllClients() {
    const rows = document.querySelectorAll('#clientTable tbody tr');
    rows.forEach(row => {
        const clientId = row.dataset.clientId;
        selectedClients.delete(clientId);
        row.classList.remove('selected');
    });
}

function getSelectedClients() {
    return Array.from(selectedClients);
}

function updateClientStatus(clientId, data) {
    console.log('Updating client status:', clientId, data);

    if (clients[clientId]) {
        clients[clientId].command_status = {
            status: data.status || 'N/A',
            program_status: data.program_status || 'N/A'
        };
        if (data.output) {
            clients[clientId].command_status.output = data.output;
        }
        if (data.error) {
            clients[clientId].command_status.error = data.error;
        }
    }
}

function executeCommand(action) {
    const selectedClients = getSelectedClients();
    if (selectedClients.length === 0) {
        alert('请选择至少一个客户端');
        return;
    }
    let command = action;
    let params = {
        updateUrl: document.getElementById('updateUrl').value,
        programName: document.getElementById('programName').value,
        programPath: document.getElementById('programPath').value,
        commandParams: document.getElementById('commandParams').value
    };
    
    if (action === 'manual') {
        params.manualCommand = document.getElementById('manualCommand').value;
    }

    // 清空之前的结果
    const resultsDiv = document.getElementById('commandResults');
    while (resultsDiv.children.length > 1) { // 保留标题 <h3>
        resultsDiv.removeChild(resultsDiv.lastChild);
    }

    fetch('/api/execute_command', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${ACCESS_TOKEN}`
        },
        body: JSON.stringify({
            client_ids: selectedClients,
            command: command,
            params: params
        }),
    })
    .then(response => response.json())
    .then(data => {
        console.log('Command sent:', data);
        alert(`命令已发送到 ${data.affected_clients} 个客户端。请等待结果...`);
        selectedClients.forEach(clientId => {
            if (clients[clientId]) {
                updateClientStatus(clientId, { status: '执行中' });
            }
        });
        updateClientTable();
    })
    .catch(error => {
        console.error('Error sending command:', error);
        alert('发送命令时出错');
    });
}

function updateSettings() {
    const heartbeatTime = document.getElementById('heartbeatTime').value;
    const reportTime = document.getElementById('reportTime').value;
    fetch('/api/update_settings', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${ACCESS_TOKEN}`
        },
        body: JSON.stringify({ heartbeatTime, reportTime }),
    })
    .then(response => response.json())
    .then(data => {
        console.log('Settings updated:', data);
        alert('设置已更新');
    });
}

function addNewGroup() {
    const newGroup = document.getElementById('newGroupName').value;
    if (newGroup && !groups.includes(newGroup)) {
        fetch('/api/add_group', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${ACCESS_TOKEN}`
            },
            body: JSON.stringify({ group: newGroup }),
        })
        .then(response => response.json())
        .then(data => {
            console.log('Group added:', data);
            alert(`新分组 "${newGroup}" 已添加`);
            fetchClients();
        });
    } else {
        alert("分组名称无效或已存在");
    }
}

function deleteGroup(group) {
    if (group === 'default' || group === '离线') {
        alert('无法删除"默认"或"离线"分组');
        return;
    }
    if (confirm(`确定要删除分组 "${group}" 吗？`)) {
        fetch('/api/delete_group', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${ACCESS_TOKEN}`
            },
            body: JSON.stringify({ group: group }),
        })
        .then(response => response.json())
        .then(data => {
            console.log('Group deleted:', data);
            alert(`分组 "${group}" 已删除`);
            fetchClients();
        });
    }
}

function changeClientGroup(clientId) {
    const newGroup = prompt(`请为客户端 ${clientId} 输入新的分组名称：`);
    if (newGroup) {
        fetch('/api/update_client_group', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${ACCESS_TOKEN}`
            },
            body: JSON.stringify({ client_ids: [clientId], group: newGroup }),
        })
        .then(response => response.json())
        .then(data => {
            console.log('Client group updated:', data);
            if (data.status === "Client group updated") {
                alert(`客户端 ${clientId} 已更新到分组 "${newGroup}"`);
                fetchClients();
            } else {
                alert('更新分组失败');
            }
        });
    }
}

function displayCommandResult(data) {
    console.log('Displaying command result:', data);

    const resultsDiv = document.getElementById('commandResults');
    const resultItem = document.createElement('div');
    resultItem.className = 'result-item';
    
    // 创建一个 Date 对象并格式化为北京时间
    const timestamp = new Date(data.timestamp);
    const localTime = timestamp.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });

    resultItem.innerHTML = `
        <strong>客户端 ID:</strong> ${data.client_id}<br>
        <strong>命令:</strong> ${data.command || 'N/A'}<br>
        <strong>状态:</strong> ${data.status || 'N/A'}<br>
        <strong>输出:</strong> <pre>${data.output || '无输出'}</pre>
        <strong>错误:</strong> <pre>${data.error || '无错误'}</pre>
        <strong>程序状态:</strong> ${data.program_status || 'N/A'}<br>
        <strong>时间:</strong> ${localTime}
    `;
    
    // 添加新结果到底部
    resultsDiv.appendChild(resultItem);

    // 保持最多显示10条结果
    while (resultsDiv.children.length > 11) { // 11 = 标题 + 10条结果
        resultsDiv.removeChild(resultsDiv.children[1]);
    }

    // 滚动到底部
    resultsDiv.scrollTop = resultsDiv.scrollHeight;

    updateClientStatus(data.client_id, {
        status: data.status,
        program_status: data.program_status
    });

    updateClientTable();
}

function showManualCommandModal() {
    if (!manualCommandModal) {
        manualCommandModal = new bootstrap.Modal(document.getElementById('manualCommandModal'));
    }
    manualCommandModal.show();
}

function executeManualCommand() {
    const command = document.getElementById('manualCommand').value;
    if (command) {
        executeCommand('manual');
        manualCommandModal.hide();
    } else {
        alert('请输入命令');
    }
}

function saveConfig() {
    const configName = document.getElementById('configName').value;
    if (!configName) {
        alert('请输入配置名称');
        return;
    }
    const config = {
        updateUrl: document.getElementById('updateUrl').value,
        programName: document.getElementById('programName').value,
        programPath: document.getElementById('programPath').value,
        commandParams: document.getElementById('commandParams').value
    };
    fetch('/api/save_config', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${ACCESS_TOKEN}`
        },
        body: JSON.stringify({ name: configName, config: config }),
    })
    .then(response => response.json())
    .then(data => {
        if (data.status === 'success') {
            alert('配置已保存');
            loadConfig();
        } else {
            alert('保存配置失败: ' + data.message);
        }
    })
    .catch(error => {
        console.error('Error:', error);
        alert('保存配置时发生错误');
    });
}

function loadConfig() {
    fetch('/api/get_configs', {
        headers: {
            'Authorization': `Bearer ${ACCESS_TOKEN}`
        }
    })
    .then(response => response.json())
    .then(configs => {
        updateConfigSelector(configs);
    })
    .catch(error => {
        console.error('Error:', error);
        alert('加载配置时发生错误');
    });
}

function updateConfigSelector(configs) {
    const selector = document.getElementById('configSelector');
    selector.innerHTML = '<option value="">-- 选择配置 --</option>';
    for (const [name, _] of Object.entries(configs)) {
        const option = document.createElement('option');
        option.value = name;
        option.textContent = name;
        selector.appendChild(option);
    }
}

function loadSelectedConfig() {
    const configName = document.getElementById('configSelector').value;
    if (!configName) {
        // 如果没有选择配置，清空所有字段
        clearConfigFields();
        return;
    }
    fetch('/api/get_configs', {
        headers: {
            'Authorization': `Bearer ${ACCESS_TOKEN}`
        }
    })
    .then(response => {
        if (!response.ok) {
            throw new Error('Network response was not ok');
        }
        return response.json();
    })
    .then(configs => {
        const config = configs[configName];
        if (config) {
            document.getElementById('updateUrl').value = config.updateUrl || '';
            document.getElementById('programName').value = config.programName || '';
            document.getElementById('programPath').value = config.programPath || '';
            document.getElementById('commandParams').value = config.commandParams || '';
            document.getElementById('configName').value = configName;
        } else {
            throw new Error('Selected configuration not found');
        }
    })
    .catch(error => {
        console.error('Error loading configuration:', error);
        alert('加载配置时发生错误: ' + error.message);
        clearConfigFields();
    });
}

function clearConfigFields() {
    document.getElementById('updateUrl').value = '';
    document.getElementById('programName').value = '';
    document.getElementById('programPath').value = '';
    document.getElementById('commandParams').value = '';
    document.getElementById('configName').value = '';
}

function deleteConfig() {
    const configName = document.getElementById('configSelector').value;
    if (!configName) {
        alert('请选择要删除的配置');
        return;
    }
    if (confirm(`确定要删除配置 "${configName}" 吗？`)) {
        fetch('/api/delete_config', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${ACCESS_TOKEN}`
            },
            body: JSON.stringify({ name: configName }),
        })
        .then(response => response.json())
        .then(data => {
            if (data.status === 'success') {
                alert('配置已删除');
                loadConfig();
                // 清空输入框
                clearConfigFields();
            } else {
                alert('删除配置失败: ' + data.message);
            }
        })
        .catch(error => {
            console.error('Error:', error);
            alert('删除配置时发生错误');
        });
    }
}

function deleteSelectedClients() {
    const selectedClients = getSelectedClients();
    if (selectedClients.length === 0) {
        alert('请选择至少一个客户端');
        return;
    }
    if (confirm(`确定要删除选中的 ${selectedClients.length} 个客户端吗？`)) {
        fetch('/api/delete_clients', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${ACCESS_TOKEN}`
            },
            body: JSON.stringify({ client_ids: selectedClients }),
        })
        .then(response => response.json())
        .then(data => {
            if (data.status === 'success') {
                alert(data.message);
                selectedClients.forEach(clientId => {
                    delete clients[clientId];
                });
                selectedClients.clear();
                updateClientTable();
            } else {
                alert('删除客户端失败: ' + data.message);
            }
        })
        .catch(error => {
            console.error('Error:', error);
            alert('删除客户端时发生错误');
        });
    }
}

function changeBulkClientGroup() {
    const selectedClients = getSelectedClients();
    if (selectedClients.length === 0) {
        alert('请选择至少一个客户端');
        return;
    }
    const newGroup = prompt(`请为选中的 ${selectedClients.length} 个客户端输入新的分组名称：`);
    if (newGroup) {
        fetch('/api/update_client_group', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${ACCESS_TOKEN}`
            },
            body: JSON.stringify({ client_ids: selectedClients, group: newGroup }),
        })
        .then(response => response.json())
        .then(data => {
            console.log('Client group updated:', data);
            if (data.status === "Client group updated") {
                alert(`${selectedClients.length} 个客户端已更新到分组 "${newGroup}"`);
                fetchClients();
            } else {
                alert('更新分组失败');
            }
        });
    }
}

function fetchCommandResults() {
    console.log('Fetching command results...');
    fetch('/api/command_results', {
        headers: {
            'Authorization': `Bearer ${ACCESS_TOKEN}`
        }
    })
    .then(response => {
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        return response.json();
    })
    .then(results => {
        console.log('Received command results:', results);
        // 清空现有结果
        const resultsDiv = document.getElementById('commandResults');
        while (resultsDiv.children.length > 1) { // 保留标题 <h3>
            resultsDiv.removeChild(resultsDiv.lastChild);
        }
        // 按时间正序排列结果
        results.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
        // 显示最新的结果(最多10条)
        results.slice(-10).forEach(result => {
            const resultItem = document.createElement('div');
            resultItem.className = 'result-item';
            // 创建一个 Date 对象并格式化为北京时间
            const timestamp = new Date(result.timestamp);
            const localTime = timestamp.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
            resultItem.innerHTML = `
                <strong>客户端 ID:</strong> ${result.client_id}<br>
                <strong>命令:</strong> ${result.command || 'N/A'}<br>
                <strong>状态:</strong> ${result.status || 'N/A'}<br>
                <strong>输出:</strong> <pre>${result.output || '无输出'}</pre>
                <strong>错误:</strong> <pre>${result.error || '无错误'}</pre>
                <strong>程序状态:</strong> ${result.program_status || 'N/A'}<br>
                <strong>时间:</strong> ${localTime}
            `;
            resultsDiv.appendChild(resultItem);
        });
        // 滚动到底部
        resultsDiv.scrollTop = resultsDiv.scrollHeight;
    })
    .catch(error => {
        console.error('Error fetching command results:', error);
    });
}

function handleMouseDown(e) {
    if (e.target.closest('#clientTable')) {
        isMouseDown = true;
        startX = e.clientX;
        startY = e.clientY;
        selectionBox.style.left = startX + 'px';
        selectionBox.style.top = startY + 'px';
    }
}

function handleMouseMove(e) {
    if (!isMouseDown) return;

    const currentX = e.clientX;
    const currentY = e.clientY;

    const boxLeft = Math.min(startX, currentX);
    const boxTop = Math.min(startY, currentY);
    const boxWidth = Math.abs(currentX - startX);
    const boxHeight = Math.abs(currentY - startY);

    selectionBox.style.display = 'block';
    selectionBox.style.left = boxLeft + 'px';
    selectionBox.style.top = boxTop + 'px';
    selectionBox.style.width = boxWidth + 'px';
    selectionBox.style.height = boxHeight + 'px';

    const tableRect = document.getElementById('clientTable').getBoundingClientRect();
    const rows = document.querySelectorAll('#clientTable tbody tr');

    rows.forEach(row => {
        const rowRect = row.getBoundingClientRect();
        const isIntersecting = !(rowRect.right < boxLeft || 
                                 rowRect.left > boxLeft + boxWidth || 
                                 rowRect.bottom < boxTop || 
                                 rowRect.top > boxTop + boxHeight);

        if (isIntersecting) {
            const clientId = row.dataset.clientId;
            toggleClientSelection(clientId, row, true);
        } else if (!e.shiftKey && !e.ctrlKey && !e.metaKey) {
            const clientId = row.dataset.clientId;
            toggleClientSelection(clientId, row, false);
        }
    });
}

function handleMouseUp() {
    isMouseDown = false;
    selectionBox.style.display = 'none';
    selectionBox.style.width = '0';
    selectionBox.style.height = '0';
}

function handleRowClick(e) {
    const row = e.target.closest('tr');
    if (!row) return;

    const clientId = row.dataset.clientId;

    if (e.shiftKey) {
        const rows = Array.from(document.querySelectorAll('#clientTable tbody tr'));
        const lastSelectedIndex = rows.findIndex(r => r.classList.contains('selected'));
        const currentIndex = rows.indexOf(row);

        const start = Math.min(lastSelectedIndex, currentIndex);
        const end = Math.max(lastSelectedIndex, currentIndex);

        for (let i = start; i <= end; i++) {
            const r = rows[i];
            toggleClientSelection(r.dataset.clientId, r, true);
        }
    } else if (e.ctrlKey || e.metaKey) {
        toggleClientSelection(clientId, row, true);
    } else {
        selectedClients.clear();
        document.querySelectorAll('#clientTable tbody tr').forEach(r => r.classList.remove('selected'));
        toggleClientSelection(clientId, row, true);
    }
}

function init() {
    console.log('Initializing...');
    fetchClients();
    loadConfig();
    fetchCommandResults();
    setInterval(fetchClients, 10000);
    setInterval(fetchCommandResults, 5000);

    document.addEventListener('mousedown', handleMouseDown);
    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);
    document.getElementById('clientTable').addEventListener('click', handleRowClick);
}

window.addEventListener('load', init);
    </script>
</body>
</html>