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). - Replace <select> with scrollable list of clickable items
Now scan nearby networks before starting hotspot and cache - Show signal strength as 4-bar icon instead of percentage text
the results for the captive portal page. - 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{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} .device-id strong{color:#1a1a2e;font-size:16px}
label{display:block;font-size:14px;font-weight:500;margin-bottom:6px;color:#555} 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} 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:focus{border-color:#4a6cf7}
.field{margin-bottom:16px} .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{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:hover{opacity:.9}
.btn:disabled{opacity:.5;cursor:not-allowed} .btn:disabled{opacity:.5;cursor:not-allowed}
@@ -188,11 +198,9 @@ select:focus,input:focus{border-color:#4a6cf7}
</div> </div>
<div class="device-id">设备 ID: <strong>${this._clawId}</strong></div> <div class="device-id">设备 ID: <strong>${this._clawId}</strong></div>
<button class="btn btn-scan" onclick="doScan()">🔍 扫描 WiFi</button>
<div class="field"> <div class="field">
<label for="ssid">WiFi 网络</label> <label>WiFi 网络</label>
<select id="ssid"><option value="">-- 点击上方扫描 --</option></select> <div class="wifi-list" id="wifiList"><div style="padding:12px;text-align:center;color:#999;font-size:13px">加载中...</div></div>
</div> </div>
<div class="manual"> <div class="manual">
@@ -200,6 +208,7 @@ select:focus,input:focus{border-color:#4a6cf7}
<label for="manualToggle">手动输入 SSID</label> <label for="manualToggle">手动输入 SSID</label>
<input type="text" id="manualSsid" placeholder="输入 WiFi 名称" style="display:none;margin-top:8px"> <input type="text" id="manualSsid" placeholder="输入 WiFi 名称" style="display:none;margin-top:8px">
</div> </div>
<input type="hidden" id="selectedSsid" value="">
<div class="field" style="margin-top:16px"> <div class="field" style="margin-top:16px">
<label for="password">密码</label> <label for="password">密码</label>
@@ -214,35 +223,56 @@ select:focus,input:focus{border-color:#4a6cf7}
function $(id){return document.getElementById(id)} function $(id){return document.getElementById(id)}
function setStatus(msg,type){var s=$('status');s.textContent=msg;s.className='status '+type} 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(){ async function doScan(){
$('connectBtn').disabled=true; var list=$('wifiList');
setStatus('正在扫描...','info'); list.innerHTML='<div style="padding:12px;text-align:center;color:#999;font-size:13px">加载中...</div>';
try{ try{
var r=await fetch('/api/scan'); var r=await fetch('/api/scan');
var d=await r.json(); var d=await r.json();
var sel=$('ssid'); var arr=d.wifi||[];
sel.innerHTML='<option value="">-- 请选择 --</option>'; if(arr.length===0){
(d.wifi||[]).forEach(function(w){ list.innerHTML='<div style="padding:12px;text-align:center;color:#999;font-size:13px">未发现网络,请手动输入</div>';
var o=document.createElement('option'); return;
o.value=w.ssid; }
o.textContent=w.ssid+' ('+w.signal+'% '+w.security+')'; list.innerHTML='';
sel.appendChild(o); 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+' 个网络'; }catch(e){
if(d.wifi.length===0) msg='未发现网络,请手动输入 SSID'; list.innerHTML='<div style="padding:12px;text-align:center;color:#c62828;font-size:13px">加载失败</div>';
setStatus(msg,'ok'); }
}catch(e){setStatus('扫描失败: '+e.message,'err')}
$('connectBtn').disabled=false;
} }
function toggleManual(){ function toggleManual(){
var on=$('manualToggle').checked; var on=$('manualToggle').checked;
$('manualSsid').style.display=on?'block':'none'; $('manualSsid').style.display=on?'block':'none';
$('ssid').style.display=on?'none':'block'; $('wifiList').style.display=on?'none':'block';
} }
async function doConnect(){ async function doConnect(){
var ssid=$('manualToggle').checked?$('manualSsid').value:$('ssid').value; var ssid=$('manualToggle').checked?$('manualSsid').value:$('selectedSsid').value;
var pw=$('password').value; var pw=$('password').value;
if(!ssid){setStatus('请选择或输入 WiFi','err');return} if(!ssid){setStatus('请选择或输入 WiFi','err');return}
$('connectBtn').disabled=true; $('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 r=await fetch('/api/connect',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({ssid:ssid,password:pw})});
var d=await r.json(); var d=await r.json();
if(d.success){ if(d.success){
setStatus('✓ 设备正在连接 WiFi热点将关闭。如连接失败热点会自动恢复,请重新连接配网。','ok'); setStatus('✓ 设备正在连接 WiFi热点将关闭。如连接失败热点会自动恢复。','ok');
}else{ }else{
setStatus('失败: '+(d.error||'未知错误'),'err'); setStatus('失败: '+(d.error||'未知错误'),'err');
$('connectBtn').disabled=false; $('connectBtn').disabled=false;