1
zhang
3 天以前 bc56530307e0e92b94a1abb5d38368f04b92e990
zy-acs-hex/src/main/webapp/views/index.html
@@ -10,156 +10,650 @@
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: Arial, sans-serif;
            background-color: #f0f2f5;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: #f5f7fa;
            color: #333;
        }
        .header {
            background-color: #1890ff;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 15px 20px;
            padding: 15px 30px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }
        .header h1 {
            font-size: 22px;
            font-weight: 600;
        }
        .header button {
            background-color: rgba(255, 255, 255, 0.2);
            color: white;
            border: 1px solid rgba(255, 255, 255, 0.3);
            padding: 8px 20px;
            border-radius: 20px;
            cursor: pointer;
            font-size: 14px;
            transition: all 0.3s ease;
        }
        .header button:hover {
            background-color: rgba(255, 255, 255, 0.3);
        }
        .container {
            padding: 30px;
        }
        .filter-section {
            background-color: white;
            padding: 20px;
            border-radius: 12px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
            margin-bottom: 20px;
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            animation: fadeIn 0.5s ease-in-out;
        }
        @keyframes fadeIn {
            from {
                opacity: 0;
                transform: translateY(20px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }
        .filter-group {
            display: flex;
            flex-direction: column;
        }
        .filter-group label {
            margin-bottom: 8px;
            font-weight: 500;
            color: #666;
            font-size: 14px;
        }
        .filter-group select,
        .filter-group input {
            padding: 10px 12px;
            border: 2px solid #e0e0e0;
            border-radius: 8px;
            font-size: 14px;
            transition: border-color 0.3s ease;
        }
        .filter-group select:focus,
        .filter-group input:focus {
            outline: none;
            border-color: #667eea;
            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
        }
        .filter-actions {
            grid-column: 1 / -1;
            display: flex;
            gap: 10px;
            margin-top: 10px;
        }
        .btn {
            padding: 10px 20px;
            border: none;
            border-radius: 8px;
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
        }
        .btn-secondary {
            background-color: #f0f0f0;
            color: #333;
        }
        .btn-secondary:hover {
            background-color: #e0e0e0;
        }
        .log-section {
            background-color: white;
            border-radius: 12px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
            overflow: hidden;
        }
        .log-header {
            padding: 20px;
            border-bottom: 1px solid #f0f0f0;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .header h1 {
            font-size: 20px;
        .log-header h2 {
            font-size: 18px;
            font-weight: 600;
            color: #333;
        }
        .header button {
            background-color: transparent;
            color: white;
            border: 1px solid white;
            padding: 5px 15px;
            border-radius: 4px;
            cursor: pointer;
        .log-content {
            padding: 0;
        }
        .header button:hover {
            background-color: rgba(255,255,255,0.1);
        }
        .container {
            padding: 20px;
        }
        .refresh-btn {
            background-color: #1890ff;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
            margin-bottom: 20px;
        }
        .refresh-btn:hover {
            background-color: #40a9ff;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            background-color: white;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        th, td {
            padding: 12px;
            padding: 15px 20px;
            text-align: left;
            border-bottom: 1px solid #ddd;
            border-bottom: 1px solid #f0f0f0;
        }
        th {
            background-color: #f5f5f5;
            font-weight: bold;
            background-color: #f9f9f9;
            font-weight: 600;
            color: #666;
            font-size: 14px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }
        tr:hover {
            background-color: #f5f5f5;
            background-color: #f9f9f9;
        }
        td {
            font-size: 14px;
            color: #555;
        }
        .loading {
            text-align: center;
            padding: 20px;
            color: #666;
            padding: 60px;
            color: #999;
            font-size: 16px;
        }
        .error {
            text-align: center;
            padding: 20px;
            color: red;
            padding: 60px;
            color: #ff4d4f;
            font-size: 16px;
        }
        .empty {
            text-align: center;
            padding: 60px;
            color: #999;
            font-size: 16px;
        }
        .status-badge {
            display: inline-block;
            padding: 4px 12px;
            border-radius: 12px;
            font-size: 12px;
            font-weight: 500;
        }
        .status-up {
            background-color: #e6f7ee;
            color: #52c41a;
        }
        .status-down {
            background-color: #fff2e8;
            color: #fa8c16;
        }
        @media (max-width: 768px) {
            .container {
                padding: 15px;
            }
            .filter-section {
                grid-template-columns: 1fr;
            }
            .filter-actions {
                flex-direction: column;
            }
            .btn {
                width: 100%;
            }
            table {
                display: block;
                overflow-x: auto;
            }
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>机器人上下行日志</h1>
        <button id="logoutBtn">登出</button>
<div class="header">
    <h1>机器人上下行日志</h1>
    <button id="logoutBtn">登出</button>
</div>
<div class="container">
    <div class="filter-section">
        <div class="filter-group">
            <label for="deviceId">小车编号</label>
            <input type="text" id="deviceId" placeholder="请输入小车编号">
        </div>
        <div class="filter-group">
            <label for="messageType">消息类型</label>
            <select id="messageType">
                <option value="">全部</option>
                <!-- 从后台接口获取 -->
            </select>
        </div>
        <div class="filter-group">
            <label for="tag">标签</label>
            <select id="tag">
                <option value="">全部</option>
                <!-- 从后台接口获取 -->
            </select>
        </div>
        <div class="filter-group">
            <label for="startTime">开始时间</label>
            <input type="datetime-local" id="startTime" step="1">
        </div>
        <div class="filter-group">
            <label for="endTime">结束时间</label>
            <input type="datetime-local" id="endTime" step="1">
        </div>
        <div class="filter-actions">
            <button class="btn btn-primary" id="searchBtn">查询</button>
            <button class="btn btn-secondary" id="resetBtn">重置</button>
            <button class="btn btn-secondary" id="refreshBtn">刷新</button>
        </div>
    </div>
    <div class="container">
        <button class="refresh-btn" id="refreshBtn">刷新数据</button>
        <div id="loading" class="loading">加载中...</div>
        <div id="error" class="error" style="display: none;"></div>
        <table id="logTable" style="display: none;">
            <thead>
                <tr>
                    <th>时间</th>
                    <th>设备ID</th>
                    <th>消息类型</th>
                    <th>消息内容</th>
                </tr>
            </thead>
            <tbody id="logTableBody">
            </tbody>
        </table>
    <div class="log-section">
        <div class="log-header">
            <h2>日志记录</h2>
            <span id="recordCount">共 0 条记录</span>
        </div>
        <div class="log-content">
            <div id="loading" class="loading">加载中...</div>
            <div id="error" class="error" style="display: none;"></div>
            <div id="empty" class="empty" style="display: none;">暂无符合条件的日志记录</div>
            <table id="logTable" style="display: none;">
                <thead>
                        <tr>
                            <th>时间</th>
                            <th>设备ID</th>
                            <th>消息类型</th>
                            <th>标签</th>
                            <th>消息内容</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                <tbody id="logTableBody">
                </tbody>
            </table>
        </div>
    </div>
    <script>
        // 检查登录状态
        function checkLogin() {
            if (!localStorage.getItem('loggedIn')) {
                window.location.href = '/login';
            }
        }
        // 登出功能
        document.getElementById('logoutBtn').addEventListener('click', function() {
            localStorage.removeItem('loggedIn');
</div>
<script>
    // 检查登录状态
    function checkLogin() {
        if (!localStorage.getItem('loggedIn')) {
            window.location.href = '/login';
        });
        // 加载日志数据
        function loadLogData() {
            document.getElementById('loading').style.display = 'block';
            document.getElementById('error').style.display = 'none';
            document.getElementById('logTable').style.display = 'none';
            fetch('/deviceLog/query')
                .then(response => response.json())
                .then(data => {
                    document.getElementById('loading').style.display = 'none';
                    if (data && data.length > 0) {
        }
    }
    // 登出功能
    document.getElementById('logoutBtn').addEventListener('click', function () {
        localStorage.removeItem('loggedIn');
        window.location.href = '/login';
    });
    // 重置筛选条件
    document.getElementById('resetBtn').addEventListener('click', function () {
        document.getElementById('deviceId').value = '';
        document.getElementById('messageType').value = '';
        document.getElementById('tag').value = '';
        document.getElementById('startTime').value = '';
        document.getElementById('endTime').value = '';
        loadLogData();
    });
    // 查询按钮点击事件
    document.getElementById('searchBtn').addEventListener('click', loadLogData);
    // 刷新按钮点击事件
    document.getElementById('refreshBtn').addEventListener('click', loadLogData);
    // 加载日志数据
    function loadLogData() {
        document.getElementById('loading').style.display = 'block';
        document.getElementById('error').style.display = 'none';
        document.getElementById('empty').style.display = 'none';
        document.getElementById('logTable').style.display = 'none';
        // 获取筛选条件
        const deviceId = document.getElementById('deviceId').value;
        const messageType = document.getElementById('messageType').value;
        const tag = document.getElementById('tag').value;
        const startTime = document.getElementById('startTime').value;
        const endTime = document.getElementById('endTime').value;
        // 构建查询参数
        const params = new URLSearchParams();
        if (deviceId) params.append('deviceId', deviceId);
        if (messageType) params.append('type', messageType);
        if (tag) params.append('event', tag);
        if (startTime) {
            // 使用原始的日期格式
            params.append('startTime', startTime+"Z");
        }
        if (endTime) {
            // 使用原始的日期格式
            params.append('endTime', endTime+"Z");
        }
        // 调用查询接口
        fetch(`/deviceLog/query?${params.toString()}`)
            .then(response => response.json())
            .then(data => {
                document.getElementById('loading').style.display = 'none';
                // 检查接口返回格式
                if (data && data.code === 200 && data.data) {
                    // 应用筛选条件(如果后台没有处理筛选)
                    let filteredData = data.data;
                    if (deviceId) {
                        // 模糊匹配设备ID
                        filteredData = filteredData.filter(item => {
                            return item.deviceId && item.deviceId.includes(deviceId);
                        });
                    }
                    if (messageType) {
                        // 直接使用messageType进行匹配
                        filteredData = filteredData.filter(item => {
                            return item.type && item.type === messageType;
                        });
                    }
                    if (tag) {
                        // 使用event字段作为标签进行筛选
                        filteredData = filteredData.filter(item => {
                            return item.event && item.event === tag;
                        });
                    }
                    if (startTime) {
                        const start = new Date(startTime).getTime();
                        filteredData = filteredData.filter(item => {
                            const itemTime = new Date(item.timestamp).getTime();
                            return itemTime >= start;
                        });
                    }
                    if (endTime) {
                        const end = new Date(endTime).getTime();
                        filteredData = filteredData.filter(item => {
                            const itemTime = new Date(item.timestamp).getTime();
                            return itemTime <= end;
                        });
                    }
                    // 更新记录数
                    document.getElementById('recordCount').textContent = `共 ${filteredData.length} 条记录`;
                    if (filteredData && filteredData.length > 0) {
                        document.getElementById('logTable').style.display = 'table';
                        const tbody = document.getElementById('logTableBody');
                        tbody.innerHTML = '';
                        data.forEach(item => {
                            const row = document.createElement('tr');
                            row.innerHTML = `
                                <td>${item.time || '-'}</td>
                                <td>${item.deviceId || '-'}</td>
                                <td>${item.messageType || '-'}</td>
                                <td>${item.messageContent || '-'}</td>
                            `;
                            tbody.appendChild(row);
                        });
                        filteredData.forEach(item => {
                                const row = document.createElement('tr');
                                const statusClass = item.type === 'up' ? 'status-up' : 'status-down';
                                const statusText = item.type === 'up' ? '上行' : '下行';
                                const sourceHexStr = item.sourceHexStr || '';
                                // 格式化时间戳
                                let formattedTime = '-';
                                if (item.timestamp) {
                                    try {
                                        const date = new Date(item.timestamp/1000/1000);
                                        formattedTime = date.toLocaleString('zh-CN', {
                                            year: 'numeric',
                                            month: '2-digit',
                                            day: '2-digit',
                                            hour: '2-digit',
                                            minute: '2-digit',
                                            second: '2-digit'
                                        });
                                    } catch (e) {
                                        formattedTime = item.timestamp;
                                    }
                                }
                                row.innerHTML = `
                                    <td>${formattedTime}</td>
                                    <td>${item.deviceId || '-'}</td>
                                    <td><span class="status-badge ${statusClass}">${statusText}</span></td>
                                    <td>${item.event || '-'}</td>
                                    <td>${sourceHexStr}</td>
                                    <td>
                                        <button class="btn btn-secondary parse-btn" data-hex="${sourceHexStr}">解析</button>
                                    </td>
                                `;
                                tbody.appendChild(row);
                            });
                            // 为解析按钮添加点击事件
                            document.querySelectorAll('.parse-btn').forEach(btn => {
                                btn.addEventListener('click', function() {
                                    const hexData = this.getAttribute('data-hex');
                                    console.log('解析按钮点击,hexData:', hexData);
                                    if (hexData) {
                                        parseHexData(hexData);
                                    } else {
                                        alert('没有可解析的消息内容');
                                    }
                                });
                            });
                    } else {
                        document.getElementById('error').textContent = '暂无日志数据';
                        document.getElementById('error').style.display = 'block';
                        document.getElementById('empty').style.display = 'block';
                    }
                })
                .catch(error => {
                    document.getElementById('loading').style.display = 'none';
                    document.getElementById('error').textContent = '加载数据失败: ' + error.message;
                } else {
                    document.getElementById('error').textContent = '加载数据失败: ' + (data.message || '未知错误');
                    document.getElementById('error').style.display = 'block';
                });
                }
            })
            .catch(error => {
                document.getElementById('loading').style.display = 'none';
                document.getElementById('error').textContent = '加载数据失败: ' + error.message;
                document.getElementById('error').style.display = 'block';
            });
    }
    // 从后台接口获取消息类型和标签
    function loadFilterOptions() {
        // 获取消息类型
        fetch('/deviceLog/queryType')
            .then(response => response.json())
            .then(data => {
                if (data && data.code === 200 && data.data) {
                    const messageTypeSelect = document.getElementById('messageType');
                    messageTypeSelect.innerHTML = '<option value="">全部</option>';
                    data.data.forEach(type => {
                        const option = document.createElement('option');
                        option.value = type.label;
                        option.textContent = type.value;
                        messageTypeSelect.appendChild(option);
                    });
                    // 消息类型变化时,重新加载标签
                    messageTypeSelect.addEventListener('change', function () {
                        loadTags(this.value);
                    });
                    // 初始加载标签
                    loadTags('');
                }
            })
            .catch(error => {
                console.error('加载消息类型失败:', error);
            });
    }
    // 加载标签
    function loadTags(directionType) {
        let url = '/deviceLog/queryEvent';
        if (directionType) {
            url += '?directionType=' + directionType;
        }
        fetch(url)
            .then(response => response.json())
            .then(data => {
                if (data && data.code === 200 && data.data) {
                    const tagSelect = document.getElementById('tag');
                    tagSelect.innerHTML = '<option value="">全部</option>';
                    data.data.forEach(tag => {
                        const option = document.createElement('option');
                        option.value = tag.value;
                        option.textContent = tag.label;
                        tagSelect.appendChild(option);
                    });
                }
            })
            .catch(error => {
                console.error('加载标签失败:', error);
            });
    }
    // 解析十六进制数据
    function parseHexData(hexData) {
        console.log('开始解析,hexData:', hexData);
        // 先关闭之前的加载状态和结果弹窗
        const oldLoading = document.getElementById('parseLoading');
        if (oldLoading) {
            oldLoading.remove();
        }
        
        // 刷新按钮点击事件
        document.getElementById('refreshBtn').addEventListener('click', loadLogData);
        const oldResult = document.querySelector('[id^="resultDiv"]');
        if (oldResult) {
            oldResult.remove();
        }
        // 显示加载状态
        const loadingDiv = document.createElement('div');
        loadingDiv.className = 'loading';
        loadingDiv.textContent = '解析中...';
        loadingDiv.style.position = 'fixed';
        loadingDiv.style.top = '50%';
        loadingDiv.style.left = '50%';
        loadingDiv.style.transform = 'translate(-50%, -50%)';
        loadingDiv.style.backgroundColor = 'rgba(255, 255, 255, 0.9)';
        loadingDiv.style.padding = '20px';
        loadingDiv.style.borderRadius = '8px';
        loadingDiv.style.boxShadow = '0 2px 10px rgba(0,0,0,0.1)';
        loadingDiv.id = 'parseLoading';
        document.body.appendChild(loadingDiv);
        // 构建请求URL
const url = `/proxy/decode?hexData=${encodeURIComponent(hexData)}`;
console.log('请求URL:', url);
        // 调用解析接口
        fetch(url, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            mode: 'cors' // 允许跨域请求
        })
        .then(response => {
            console.log('响应状态:', response.status);
            console.log('响应头:', response.headers);
            if (!response.ok) {
                throw new Error(`解析失败,状态码: ${response.status}`);
            }
            return response.json();
        })
        .then(data => {
            console.log('解析结果:', data);
            // 移除加载状态
            document.getElementById('parseLoading').remove();
            // 显示解析结果
            const resultDiv = document.createElement('div');
            resultDiv.id = 'resultDiv_' + Date.now(); // 添加唯一ID
            resultDiv.style.position = 'fixed';
            resultDiv.style.top = '50%';
            resultDiv.style.left = '50%';
            resultDiv.style.transform = 'translate(-50%, -50%)';
            resultDiv.style.backgroundColor = 'white';
            resultDiv.style.padding = '20px';
            resultDiv.style.borderRadius = '8px';
            resultDiv.style.boxShadow = '0 2px 20px rgba(0,0,0,0.2)';
            resultDiv.style.maxWidth = '80%';
            resultDiv.style.maxHeight = '80%';
            resultDiv.style.overflow = 'auto';
            resultDiv.style.zIndex = '1000';
            // 构建结果HTML
            let resultHTML = '<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">';
            resultHTML += '<h3 style="margin: 0;">解析结果</h3>';
            resultHTML += '<button id="closeResult" style="padding: 5px 10px; background-color: #f0f0f0; color: #333; border: none; border-radius: 4px; cursor: pointer;">×</button>';
            resultHTML += '</div>';
            resultHTML += '<pre style="background-color: #f5f5f5; padding: 10px; border-radius: 4px; font-family: monospace; margin: 0;">';
            resultHTML += JSON.stringify(data, null, 2);
            resultHTML += '</pre>';
            resultDiv.innerHTML = resultHTML;
            document.body.appendChild(resultDiv);
            // 关闭按钮点击事件
            document.getElementById('closeResult').addEventListener('click', function() {
                resultDiv.remove();
            });
        })
        .catch(error => {
            console.error('解析错误:', error);
            // 移除加载状态
            document.getElementById('parseLoading').remove();
            // 显示详细的错误信息
            let errorMessage = '解析失败: ' + error.message;
            if (error.message.includes('Failed to fetch')) {
                errorMessage += '\n可能的原因:\n1. 解析服务未启动\n2. 跨域问题\n3. 网络连接问题';
            }
            alert(errorMessage);
        });
    }
        
        // 页面加载时检查登录状态并加载数据
        checkLogin();
        loadFilterOptions();
        loadLogData();
    </script>
</script>
</body>
</html>