Files
ESP32_COMM/tools/index.html
T
小龙虾 adb7dc65ca refactor(app): 删除过时的app.js备份文件
移除 _assistant_backups 目录中的 app.js.bak_2026-05-05_0451 备份文件,
该文件包含完整的JavaScript应用程序代码,包括全局状态管理、工具函数、
日志系统、连接管理、CAN帧发送、文件处理、固件上传等功能模块。
2026-05-07 23:50:04 +08:00

280 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESP32 OTA 中继系统 - 上位机</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<!-- 顶部标题栏 -->
<header class="header">
<div class="logo-section">
<div class="logo-icon">📡</div>
<div class="logo-text">
<h1>ESP32 OTA 中继系统</h1>
<p>GD32 固件空中升级管理平台</p>
</div>
</div>
<div class="status-badges">
<div class="badge disconnected" id="connectionBadge">
<span class="dot"></span>
<span id="connectionText">未连接</span>
</div>
<div class="badge" id="healthBadge">
<span class="dot"></span>
<span id="healthText">系统正常</span>
</div>
<div class="badge">
<span id="fwVersion">v--</span>
</div>
</div>
</header>
<!-- 主网格布局 -->
<div class="main-grid">
<!-- 左侧面板 -->
<div class="left-panel">
<!-- 连接配置 -->
<div class="card connection-card">
<div class="card-header">
<div class="card-title">
<div class="icon">🔗</div>
连接配置
</div>
</div>
<div class="form-group">
<label>ESP32 IP 地址</label>
<div class="input-group">
<input type="text" id="espIp" value="192.168.31.127" placeholder="输入 IP 地址">
<button class="btn btn-secondary" onclick="testConnection()">测试</button>
</div>
</div>
<div class="form-group">
<label>目标芯片</label>
<select id="targetChip" onchange="onTargetChipChange()" style="width:100%;background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;padding:10px 12px;color:var(--text-primary);font-size:13px;">
<option value="gd32">GD32(Flash 中继)</option>
<option value="esp32">ESP32(自身升级)</option>
</select>
</div>
<button class="btn btn-primary" style="width:100%; margin-top:8px;" onclick="restartEsp32()">
🔄 重启 ESP32
</button>
<a class="btn btn-secondary" href="ble_ota.html" style="width:100%; margin-top:8px; text-decoration:none; display:flex; align-items:center; justify-content:center;">
📶 打开 BLE 刷写页面
</a>
</div>
<!-- 文件上传 -->
<div class="card">
<div class="card-header">
<div class="card-title">
<div class="icon">📁</div>
固件文件
</div>
</div>
<div class="upload-zone" id="uploadZone" onclick="document.getElementById('fileInput').click()">
<div class="upload-icon">📤</div>
<h3>点击或拖拽文件到此处</h3>
<p>支持 .hex 和 .bin 格式</p>
</div>
<input type="file" id="fileInput" class="file-input" accept=".hex,.bin" onchange="handleFileSelect(event)">
<div class="file-info" id="fileInfo">
<div class="file-header">
<div class="file-name">
<span class="icon">📄</span>
<span id="fileName">-</span>
</div>
<button class="btn btn-secondary" onclick="clearFile()" style="padding:6px 12px;">✕</button>
</div>
<div class="file-meta">
<div class="meta-item"><span>类型:</span> <b id="fileType">-</b></div>
<div class="meta-item"><span>大小:</span> <b id="fileSize">-</b></div>
<div class="meta-item"><span>数据包:</span> <b id="fileChunks">-</b></div>
</div>
</div>
<div class="progress-section" id="progressSection">
<div class="progress-bar-container">
<div class="progress-bar" id="progressBar"></div>
</div>
<div class="progress-info">
<span id="progressText">准备上传...</span>
<span id="progressPercent">0%</span>
</div>
</div>
<div class="action-buttons">
<button class="btn btn-primary" id="uploadBtn" onclick="startUpload()" disabled>
⬆️ 上传固件
</button>
<button class="btn btn-success" id="relayBtn" onclick="triggerRelay()">
🚀 触发 OTA
</button>
</div>
<div id="esp32Actions" style="display:none; margin-top:12px;">
</div>
</div>
<!-- OTA 阶段指示 -->
<div class="card">
<div class="card-header">
<div class="card-title">
<div class="icon">📊</div>
OTA 流程阶段
</div>
</div>
<div class="stage-indicator">
<div class="stage" id="stage1">
<div class="stage-icon">1️⃣</div>
<div class="stage-label">握手认证</div>
</div>
<div class="stage" id="stage2">
<div class="stage-icon">2️⃣</div>
<div class="stage-label">传输准备</div>
</div>
<div class="stage" id="stage3">
<div class="stage-icon">3️⃣</div>
<div class="stage-label">数据转发</div>
</div>
<div class="stage" id="stage4">
<div class="stage-icon">4️⃣</div>
<div class="stage-label">完成跳转</div>
</div>
</div>
</div>
</div>
<!-- 右侧面板 -->
<div class="right-panel">
<!-- 实时状态 -->
<div class="card">
<div class="card-header">
<div class="card-title">
<div class="icon">📈</div>
实时状态
<span style="font-size:12px;color:var(--text-muted);font-weight:normal;margin-left:8px;">运行: <span id="statUptime">--</span></span>
</div>
</div>
<div class="status-grid">
<div class="status-item">
<div class="value" id="statTemperature">--</div>
<div class="label">芯片温度</div>
</div>
<div class="status-item">
<div class="value" id="statWifi">--</div>
<div class="label">WiFi 状态</div>
</div>
<div class="status-item">
<div class="value" id="statSignal">--</div>
<div class="label">信号强度</div>
</div>
<div class="status-item">
<div class="value" id="statHeap">0</div>
<div class="label">空闲堆</div>
</div>
<div class="status-item">
<div class="value" id="statMinHeap">0</div>
<div class="label">最低空闲堆</div>
</div>
<div class="status-item">
<div class="value" id="statProgress">0%</div>
<div class="label">上传进度</div>
</div>
<div class="status-item">
<div class="value" id="statRelayState">待机</div>
<div class="label">中继状态</div>
</div>
</div>
</div>
<!-- CAN 帧发送面板 -->
<div class="card">
<div class="card-header">
<div class="card-title">
<div class="icon">📨</div>
CAN 帧发送
</div>
</div>
<div style="display:grid; grid-template-columns:80px 1fr 80px 1fr; gap:12px; margin-bottom:16px;">
<div class="form-group" style="margin:0;">
<label style="font-size:11px;color:var(--text-muted);margin-bottom:6px;display:block;">通道</label>
<select id="canChannel" style="width:100%;background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;padding:10px 12px;color:var(--text-primary);font-size:13px;">
<option value="0">CH0</option>
<option value="1">CH1</option>
<option value="2">CH2</option>
</select>
</div>
<div class="form-group" style="margin:0;">
<label style="font-size:11px;color:var(--text-muted);margin-bottom:6px;display:block;">扩展帧 ID</label>
<input type="text" id="canId" value="0x18DA00F1" placeholder="例如: 0x18DA00F1" style="width:100%;background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;padding:10px 12px;color:var(--text-primary);font-size:13px;">
</div>
<div class="form-group" style="margin:0;">
<label style="font-size:11px;color:var(--text-muted);margin-bottom:6px;display:block;">DLC</label>
<input type="number" id="canDlc" value="8" min="1" max="64" style="width:100%;background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;padding:10px 12px;color:var(--text-primary);font-size:13px;">
</div>
<div class="form-group" style="margin:0;">
<label style="font-size:11px;color:var(--text-muted);margin-bottom:6px;display:block;">数据 (HEX)</label>
<input type="text" id="canData" value="0102030405060708" placeholder="01020304..." style="width:100%;background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;padding:10px 12px;color:var(--text-primary);font-size:13px;">
</div>
</div>
<div style="display:flex;gap:12px;">
<button class="btn btn-primary" style="flex:1;" onclick="sendCanFrame()">📤 发送</button>
<button class="btn btn-secondary" onclick="clearCanFields()">清空</button>
</div>
<div id="canResponse" style="margin-top:16px;background:var(--bg-secondary);border-radius:10px;padding:12px 16px;font-family:monospace;font-size:12px;display:none;">
<div style="color:var(--text-muted);margin-bottom:6px;">响应:</div>
<div id="canRespContent" style="color:var(--accent);"></div>
</div>
</div>
<!-- 调试日志 -->
<div class="card log-panel">
<div class="card-header">
<div class="card-title">
<div class="icon">🖥️</div>
调试日志
<span style="font-size:12px;color:var(--text-muted);font-weight:normal;margin-left:8px;">UDP:6666</span>
</div>
</div>
<div class="card-body">
<div class="log-header">
<span style="font-size:13px;color:var(--text-muted);">实时日志输出</span>
<div class="log-filters">
<button class="filter-btn active" onclick="toggleFilter('all', this)">全部</button>
<button class="filter-btn" onclick="toggleFilter('E', this)">错误</button>
<button class="filter-btn" onclick="toggleFilter('W', this)">警告</button>
<button class="filter-btn" onclick="toggleFilter('I', this)">信息</button>
</div>
</div>
<div class="log-content" id="logContent">
<div class="log-entry">
<span class="log-time">[--:--:--]</span>
<span class="log-level I">I</span>
<span class="log-tag">[系统]</span>
<span class="log-msg">等待上位机连接...</span>
</div>
</div>
<div class="log-footer">
<span class="log-stats"><span id="logCount">0</span> 条日志</span>
<button class="btn btn-secondary" onclick="clearLogs()" style="padding:6px 12px;font-size:12px;">清除日志</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Toast 提示 -->
<div class="toast" id="toast">
<span class="toast-icon" id="toastIcon">✓</span>
<span class="toast-message" id="toastMessage">操作成功</span>
</div>
<script src="app.js"></script>
</body>
</html>