| | |
| | | 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> |