refactor: adb远程终端与Tailscale管理从维护页移至工具Tab(维护仅保留STF服务),设备列表共享缓存+复用esc
This commit is contained in:
+27
-18
@@ -289,16 +289,15 @@ function showTab(name){
|
||||
if(name==='monitor'){loadMonitor();_monitorTimer=setInterval(loadMonitor,5000);}
|
||||
if(name==='tasks'){showSubTab('tasks',_activeSubs.tasks);loadTasks();loadCustomActions();}
|
||||
if(name==='apps'){showSubTab('apps',_activeSubs.apps);loadApks();}
|
||||
if(name==='maintenance'){showSubTab('maintenance',_activeSubs.maintenance);loadAdbDevices();loadTailscaleDevices();}
|
||||
if(name==='tools'){showSubTab('tools',_activeSubs.tools);loadToolsDevices();}
|
||||
if(name==='tools'){showSubTab('tools',_activeSubs.tools);loadToolsDevices();loadAdbDevices();loadTailscaleDevices();}
|
||||
if(name==='groups')loadGroups();
|
||||
if(name==='logs'){loadLogs();if(document.getElementById('log-auto').checked)_logTimer=setInterval(loadLogs,3000);}
|
||||
if(name==='users')loadUsers();
|
||||
}
|
||||
|
||||
// ================== 页内子分栏(任务/应用/维护/工具 通用) ==================
|
||||
// ================== 页内子分栏(任务/应用/工具 通用) ==================
|
||||
// 每个带子分栏的 Tab 记住上次选中的子分栏,切走再切回来保持原位
|
||||
let _activeSubs = {tasks: 'plan', apps: 'apks', maintenance: 'stf', tools: 'clipboard'};
|
||||
let _activeSubs = {tasks: 'plan', apps: 'apks', tools: 'clipboard'};
|
||||
|
||||
function showSubTab(tabId, name){
|
||||
_activeSubs[tabId] = name;
|
||||
@@ -311,15 +310,25 @@ function showSubTab(tabId, name){
|
||||
let _clipDevices = []; // [{serial, state}],来源 /api/adb/devices(本地 adb + STF 池)
|
||||
let _clipSelected = new Set();
|
||||
|
||||
function _escHtml(s){
|
||||
return String(s==null?'':s).replace(/[&<>"']/g, c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||||
// 本地 adb 设备列表共享缓存(剪贴板/终端两个面板同源,10 秒内复用避免重复请求;force 强制刷新)
|
||||
let _adbDevicesCache = null; // {ts, data}
|
||||
|
||||
async function _fetchAdbDevices(force){
|
||||
const now = Date.now();
|
||||
if(!force && _adbDevicesCache && now - _adbDevicesCache.ts < 10000){
|
||||
return _adbDevicesCache.data;
|
||||
}
|
||||
const r = await apiGet('/api/adb/devices');
|
||||
if(!r || !r.ok) return null;
|
||||
_adbDevicesCache = {ts: now, data: r.devices || []};
|
||||
return _adbDevicesCache.data;
|
||||
}
|
||||
|
||||
async function loadToolsDevices(){
|
||||
const r = await apiGet('/api/adb/devices');
|
||||
async function loadToolsDevices(force){
|
||||
const list = await _fetchAdbDevices(force);
|
||||
const status = document.getElementById('clip-status');
|
||||
if(!r||!r.ok){status.textContent='加载失败';showToast('加载设备失败: '+(r&&r.error||''),'error');return;}
|
||||
_clipDevices = r.devices||[];
|
||||
if(list === null){status.textContent='加载失败';showToast('加载设备失败','error');return;}
|
||||
_clipDevices = list;
|
||||
const wrap = document.getElementById('clip-devices');
|
||||
if(!_clipDevices.length){
|
||||
wrap.innerHTML='<div class="empty" style="padding:20px">无可用设备(本地 adb 或 STF 池均无)</div>';
|
||||
@@ -329,9 +338,9 @@ async function loadToolsDevices(){
|
||||
wrap.innerHTML = _clipDevices.map(d=>{
|
||||
const st = d.state==='stf' ? 'STF池' : (d.state==='device' ? '已连接' : d.state);
|
||||
const stCls = (d.state==='device'||d.state==='stf') ? 'label-success' : 'label-warning';
|
||||
return '<label data-serial="'+_escHtml(d.serial)+'">'
|
||||
+'<input type="checkbox" value="'+_escHtml(d.serial)+'"'+( _clipSelected.has(d.serial)?' checked':'')+' onchange="toggleClipDevice(this)">'
|
||||
+'<span style="font-family:monospace;flex:1;overflow:hidden;text-overflow:ellipsis">'+_escHtml(d.serial)+'</span>'
|
||||
return '<label data-serial="'+esc(d.serial)+'">'
|
||||
+'<input type="checkbox" value="'+esc(d.serial)+'"'+( _clipSelected.has(d.serial)?' checked':'')+' onchange="toggleClipDevice(this)">'
|
||||
+'<span style="font-family:monospace;flex:1;overflow:hidden;text-overflow:ellipsis">'+esc(d.serial)+'</span>'
|
||||
+'<span class="label '+stCls+'">'+st+'</span></label>';
|
||||
}).join('');
|
||||
status.textContent = '共 '+_clipDevices.length+' 台设备';
|
||||
@@ -352,7 +361,7 @@ async function injectClipboard(){
|
||||
const r = await apiPost('/api/tools/clipboard/set', {serials:[..._clipSelected], text});
|
||||
if(!r||!r.ok){showToast('注入失败: '+(r&&r.error||''),'error');return;}
|
||||
const rows = Object.entries(r.results).map(([s,v])=>
|
||||
'<tr><td style="font-family:monospace">'+_escHtml(s)+'</td><td>'+(v.ok?'<span class="label label-success">成功</span>':'<span class="label label-danger">失败</span> '+_escHtml(v.msg))+'</td></tr>').join('');
|
||||
'<tr><td style="font-family:monospace">'+esc(s)+'</td><td>'+(v.ok?'<span class="label label-success">成功</span>':'<span class="label label-danger">失败</span> '+esc(v.msg))+'</td></tr>').join('');
|
||||
document.getElementById('clip-result').innerHTML = rows;
|
||||
document.getElementById('clip-result-wrap').style.display = 'block';
|
||||
showToast('注入完成:成功 '+r.ok_count+' 台,失败 '+r.fail_count+' 台', r.fail_count?'error':'success');
|
||||
@@ -2266,15 +2275,15 @@ function _attachDevice(cmd){
|
||||
if(_NO_DEVICE_CMDS.indexOf(first)>=0)return cmd;
|
||||
return '-s '+serial+' '+cmd;
|
||||
}
|
||||
async function loadAdbDevices(){
|
||||
async function loadAdbDevices(force){
|
||||
const sel=document.getElementById('adb-device');
|
||||
if(!sel)return;
|
||||
const r=await apiGet('/api/adb/devices');
|
||||
if(!r||!r.ok)return;
|
||||
const list=await _fetchAdbDevices(force);
|
||||
if(list===null)return;
|
||||
const cur=sel.value;
|
||||
const stateTxt={device:'在线',offline:'离线',stf:'STF池(未连接)'};
|
||||
const opts=['<option value="">(不指定设备)</option>'].concat(
|
||||
(r.devices||[]).map(d=>
|
||||
list.map(d=>
|
||||
'<option value="'+esc(d.serial)+'">'+esc(d.serial)+' ('+(stateTxt[d.state]||d.state)+')</option>'
|
||||
)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user