ui: redesign WiFi list with signal bars and cleaner layout

- Replace <select> with scrollable list of clickable items
- Show signal strength as 4-bar icon instead of percentage text
- Remove security info from parentheses, show lock icon only
- Smaller font size for compact mobile display

Made-with: Cursor
This commit is contained in:
stswangzhiping
2026-03-16 13:11:23 +08:00
parent c6f55c8c15
commit 17d0711f67
2 changed files with 58 additions and 27 deletions

View File

@@ -1,5 +1,6 @@
fix: pre-scan WiFi before entering AP mode
ui: redesign WiFi list with signal bars and cleaner layout
wlan0 cannot scan while in AP mode (hardware limitation).
Now scan nearby networks before starting hotspot and cache
the results for the captive portal page.
- Replace <select> with scrollable list of clickable items
- Show signal strength as 4-bar icon instead of percentage text
- Remove security info from parentheses, show lock icon only
- Smaller font size for compact mobile display

View File

@@ -162,9 +162,19 @@ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;b
.device-id{text-align:center;background:#f8f9fa;border-radius:8px;padding:8px;margin-bottom:20px;font-size:14px;color:#555}
.device-id strong{color:#1a1a2e;font-size:16px}
label{display:block;font-size:14px;font-weight:500;margin-bottom:6px;color:#555}
select,input{width:100%;padding:12px;border:1.5px solid #ddd;border-radius:8px;font-size:15px;outline:none;transition:border-color .2s}
select:focus,input:focus{border-color:#4a6cf7}
input{width:100%;padding:12px;border:1.5px solid #ddd;border-radius:8px;font-size:15px;outline:none;transition:border-color .2s}
input:focus{border-color:#4a6cf7}
.field{margin-bottom:16px}
.wifi-list{max-height:220px;overflow-y:auto;border:1.5px solid #ddd;border-radius:8px;margin-bottom:16px}
.wifi-item{display:flex;align-items:center;padding:10px 12px;border-bottom:1px solid #f0f0f0;cursor:pointer;font-size:13px;transition:background .15s}
.wifi-item:last-child{border-bottom:none}
.wifi-item:hover,.wifi-item.active{background:#e8f0fe}
.wifi-item.active{font-weight:600;color:#1a1a2e}
.wifi-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wifi-signal{display:flex;align-items:flex-end;gap:1.5px;height:14px;margin-left:8px;flex-shrink:0}
.wifi-signal i{display:block;width:3px;background:#ccc;border-radius:1px}
.wifi-signal i.on{background:#4a6cf7}
.wifi-lock{margin-left:6px;font-size:11px;color:#888;flex-shrink:0}
.btn{width:100%;padding:14px;background:linear-gradient(135deg,#4a6cf7,#3b5de7);color:#fff;border:none;border-radius:8px;font-size:16px;font-weight:600;cursor:pointer;transition:opacity .2s}
.btn:hover{opacity:.9}
.btn:disabled{opacity:.5;cursor:not-allowed}
@@ -188,11 +198,9 @@ select:focus,input:focus{border-color:#4a6cf7}
</div>
<div class="device-id">设备 ID: <strong>${this._clawId}</strong></div>
<button class="btn btn-scan" onclick="doScan()">🔍 扫描 WiFi</button>
<div class="field">
<label for="ssid">WiFi 网络</label>
<select id="ssid"><option value="">-- 点击上方扫描 --</option></select>
<label>WiFi 网络</label>
<div class="wifi-list" id="wifiList"><div style="padding:12px;text-align:center;color:#999;font-size:13px">加载中...</div></div>
</div>
<div class="manual">
@@ -200,6 +208,7 @@ select:focus,input:focus{border-color:#4a6cf7}
<label for="manualToggle">手动输入 SSID</label>
<input type="text" id="manualSsid" placeholder="输入 WiFi 名称" style="display:none;margin-top:8px">
</div>
<input type="hidden" id="selectedSsid" value="">
<div class="field" style="margin-top:16px">
<label for="password">密码</label>
@@ -214,35 +223,56 @@ select:focus,input:focus{border-color:#4a6cf7}
function $(id){return document.getElementById(id)}
function setStatus(msg,type){var s=$('status');s.textContent=msg;s.className='status '+type}
function signalBars(pct){
var bars=[4,7,10,14];
var on=pct>=80?4:pct>=60?3:pct>=40?2:1;
return bars.map(function(h,i){
return '<i style="height:'+h+'px" class="'+(i<on?'on':'')+'"></i>';
}).join('');
}
function selectWifi(ssid){
$('selectedSsid').value=ssid;
var items=document.querySelectorAll('.wifi-item');
items.forEach(function(el){el.classList.toggle('active',el.dataset.ssid===ssid)});
}
async function doScan(){
$('connectBtn').disabled=true;
setStatus('正在扫描...','info');
var list=$('wifiList');
list.innerHTML='<div style="padding:12px;text-align:center;color:#999;font-size:13px">加载中...</div>';
try{
var r=await fetch('/api/scan');
var d=await r.json();
var sel=$('ssid');
sel.innerHTML='<option value="">-- 请选择 --</option>';
(d.wifi||[]).forEach(function(w){
var o=document.createElement('option');
o.value=w.ssid;
o.textContent=w.ssid+' ('+w.signal+'% '+w.security+')';
sel.appendChild(o);
var arr=d.wifi||[];
if(arr.length===0){
list.innerHTML='<div style="padding:12px;text-align:center;color:#999;font-size:13px">未发现网络,请手动输入</div>';
return;
}
list.innerHTML='';
arr.forEach(function(w){
var div=document.createElement('div');
div.className='wifi-item';
div.dataset.ssid=w.ssid;
div.onclick=function(){selectWifi(w.ssid)};
var lock=w.security&&w.security!=='Open'?'🔒':'';
div.innerHTML='<span class="wifi-name">'+w.ssid+'</span>'
+'<span class="wifi-signal">'+signalBars(w.signal)+'</span>'
+'<span class="wifi-lock">'+lock+'</span>';
list.appendChild(div);
});
var msg='发现 '+d.wifi.length+' 个网络';
if(d.wifi.length===0) msg='未发现网络,请手动输入 SSID';
setStatus(msg,'ok');
}catch(e){setStatus('扫描失败: '+e.message,'err')}
$('connectBtn').disabled=false;
}catch(e){
list.innerHTML='<div style="padding:12px;text-align:center;color:#c62828;font-size:13px">加载失败</div>';
}
}
function toggleManual(){
var on=$('manualToggle').checked;
$('manualSsid').style.display=on?'block':'none';
$('ssid').style.display=on?'none':'block';
$('wifiList').style.display=on?'none':'block';
}
async function doConnect(){
var ssid=$('manualToggle').checked?$('manualSsid').value:$('ssid').value;
var ssid=$('manualToggle').checked?$('manualSsid').value:$('selectedSsid').value;
var pw=$('password').value;
if(!ssid){setStatus('请选择或输入 WiFi','err');return}
$('connectBtn').disabled=true;
@@ -251,7 +281,7 @@ async function doConnect(){
var r=await fetch('/api/connect',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({ssid:ssid,password:pw})});
var d=await r.json();
if(d.success){
setStatus('✓ 设备正在连接 WiFi热点将关闭。如连接失败热点会自动恢复,请重新连接配网。','ok');
setStatus('✓ 设备正在连接 WiFi热点将关闭。如连接失败热点会自动恢复。','ok');
}else{
setStatus('失败: '+(d.error||'未知错误'),'err');
$('connectBtn').disabled=false;