feat: 应用列表按包名合并 + 剪贴板/安装/版本管理显示设备名
两件事都是"列表看不清楚"的问题:
1) **同一个应用传多个版本会并排显示多行**(v1.2 / v1.3 各占一行),看着像重复。
现在按 **package_name 合并成一个应用一行**,显示最新版:
- `apk_manager.list_latest()`:版本按 version_code 排序取最新,其余版本放进
`versions` 字段;解析不出包名的 APK 各自独立成行(不硬合并)
- 管理页版本列显示 `1.3(另有 1 个旧版本)`,多一个「清理旧版本」按钮
- 「删除」语义改为**删掉这个应用的所有版本**(行=应用),另给
`?scope=older` 只清理旧版本;老的单条删除行为保留(不带 scope)
- **设备端商店也走同一个列表** —— 手机上不会再看到同一个 App 的 v1.2/v1.3 两行
2) **设备只显示地址**(`192.168.20.100:5555`),同型号多台时分不清是哪一台。
三处补齐设备名(平台给设备起的名字,如 A08),统一「名称 · 地址」:
- 剪贴板注入的设备勾选列表(`/api/adb/devices` 经 `_merged_device_list` 带 name)
- 应用管理的安装弹窗(`/api/apks/install/devices` 带 name)
- 应用版本管理表格(`/api/tools/appver` 结果里带 name)
- 顺带:安装进度表里的「设备」列原来显示**型号**(而且是 STF 时代残留的空 dict),
改为显示设备名
没名字的设备(不在池里)自动回退只显示地址,不显示空占位
验证:上传同包名 v1.1/v1.2/v1.3 三个版本 → 列表只出现一行(v1.3,标注共 4 个版本)、
设备端 bootstrap 也只剩 2 个应用;`scope=older` 只留最新、`scope=package` 整包删除;
三个面板在浏览器里渲染无 JS 报错,devText 有名字/没名字两种形态都正确。
This commit is contained in:
+26
-8
@@ -29,15 +29,22 @@ async function loadApks(){
|
||||
await fillAppsDeviceList();
|
||||
}
|
||||
function renderApkRow(a){
|
||||
// 一行 = 一个应用(列表已按包名合并,显示最新版);旧版本给个数提示 + 清理入口
|
||||
const n=a.version_count||1;
|
||||
const verCell=(a.version_name?esc(a.version_name):'-')+
|
||||
(n>1?' <span style="font-size:11px;color:#94a3b8">(另有 '+(n-1)+' 个旧版本)</span>':'');
|
||||
const clean=n>1?'<button class="btn btn-xs" onclick="cleanOldVersions(\''+a.id+'\',\''+esc(a.display_name||'')+'\','+(n-1)+')" title="只保留最新版,删掉旧版本">清理旧版本</button> ':'';
|
||||
const title=n>1?('共 '+n+' 个版本:'+ (a.versions||[]).map(v=>v.version_name||'?').join(' / ')):'';
|
||||
return '<tr>'+
|
||||
'<td><strong>'+esc(a.display_name||'(未命名)')+'</strong></td>'+
|
||||
'<td style="font-family:monospace;font-size:12px;color:#64748b">'+esc(a.package_name||'-')+'</td>'+
|
||||
'<td>'+(a.version_name?esc(a.version_name):'-')+'</td>'+
|
||||
'<td title="'+esc(title)+'">'+verCell+'</td>'+
|
||||
'<td>'+fmtSize(a.size)+'</td>'+
|
||||
'<td style="font-size:12px;color:#94a3b8">'+esc(a.upload_time)+'</td>'+
|
||||
'<td>'+
|
||||
'<button class="btn btn-primary btn-xs" onclick="openInstallModal(\''+a.id+'\')">安装</button> '+
|
||||
'<button class="btn btn-danger btn-xs" onclick="deleteApk(\''+a.id+'\')">删除</button>'+
|
||||
clean+
|
||||
'<button class="btn btn-danger btn-xs" onclick="deleteApk(\''+a.id+'\','+n+')">删除</button>'+
|
||||
'</td>'+
|
||||
'</tr>';
|
||||
}
|
||||
@@ -50,7 +57,7 @@ async function fillAppsDeviceList(){
|
||||
const cur=sel.value;
|
||||
const devices=(sr.devices||[]).filter(d=>d.present);
|
||||
sel.innerHTML='<option value="">选择设备...</option>'+devices.map(d=>
|
||||
'<option value="'+esc(d.serial)+'">'+esc((d.serial||'').replace(/:5555$/,'')+' ('+(d.device_name||d.model||'未知设备')+')')+'</option>'
|
||||
'<option value="'+esc(d.serial)+'">'+esc(devText(d.device_name||d.name, d.serial))+'</option>'
|
||||
).join('');
|
||||
if(cur)sel.value=cur;
|
||||
}
|
||||
@@ -140,9 +147,20 @@ async function uploadApk(input){
|
||||
input.value='';
|
||||
}
|
||||
|
||||
async function deleteApk(id){
|
||||
if(!confirm('确认删除此APK文件?'))return;
|
||||
const r=await apiDelete('/api/apks/'+id);
|
||||
async function deleteApk(id, versionCount){
|
||||
const n=versionCount||1;
|
||||
const msg = n>1
|
||||
? ('确认删除这个应用?\n\n它一共有 '+n+' 个版本,会**全部**删掉(想只留最新版请用「清理旧版本」)。')
|
||||
: '确认删除此APK文件?';
|
||||
if(!confirm(msg))return;
|
||||
// 一行 = 一个应用 → 删除即删掉该包的所有版本
|
||||
const r=await apiDelete('/api/apks/'+id+'?scope=package');
|
||||
handleResult(r,loadApks);
|
||||
}
|
||||
|
||||
async function cleanOldVersions(id, name, oldCount){
|
||||
if(!confirm('清理「'+name+'」的 '+oldCount+' 个旧版本?\n\n只保留最新版,旧版本文件会被删除。'))return;
|
||||
const r=await apiDelete('/api/apks/'+id+'?scope=older');
|
||||
handleResult(r,loadApks);
|
||||
}
|
||||
|
||||
@@ -168,8 +186,8 @@ async function openInstallModal(apkId){
|
||||
online.map(d=>{
|
||||
const isUsb=d.source==='usb';
|
||||
const st='<span class="label label-'+(isUsb?'primary':'default')+'" style="margin-left:6px">'+(isUsb?'本地USB':(d.source==='pool'?'设备池':'adb'))+'</span>';
|
||||
// serial 唯一,显示 IP + 型号便于区分同型号设备
|
||||
const label=(d.serial||'').replace(/:5555$/,'')+' · '+(d.model||(isUsb?'USB 有线设备':'设备'));
|
||||
// 一律「名称 · 地址」——同型号多台时靠名称区分;没有名称的设备(不在池里)只显示地址
|
||||
const label=devText(d.name, d.serial);
|
||||
return '<label><input type="checkbox" value="'+esc(d.serial)+'" checked>'+esc(label)+st+'</label>';
|
||||
}).join('')+
|
||||
'</div>';
|
||||
|
||||
@@ -35,7 +35,7 @@ async function loadToolsDevices(force){
|
||||
const stCls = d.state==='device' ? 'label-success' : 'label-warning';
|
||||
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 style="font-family:monospace;flex:1;overflow:hidden;text-overflow:ellipsis">'+esc(devText(d.name,d.serial))+'</span>'
|
||||
+'<span class="label '+stCls+'">'+st+'</span></label>';
|
||||
}).join('');
|
||||
status.textContent = '共 '+_clipDevices.length+' 台设备';
|
||||
@@ -252,12 +252,14 @@ async function queryAppVers(){
|
||||
}
|
||||
}
|
||||
function renderAppVerRow(x){
|
||||
if(x.error)return '<tr><td style="font-family:monospace">'+esc(x.serial)+'</td>'+
|
||||
// 设备列显示「名称 · 地址」(名称来自设备池;没名字就只显示地址)
|
||||
const dev='<td style="font-family:monospace">'+esc(devText(x.name,x.serial))+'</td>';
|
||||
if(x.error)return '<tr>'+dev+
|
||||
'<td><span class="label label-warning">查询失败</span></td><td colspan="2">'+esc(x.error)+'</td></tr>';
|
||||
if(x.installed)return '<tr><td style="font-family:monospace">'+esc(x.serial)+'</td>'+
|
||||
if(x.installed)return '<tr>'+dev+
|
||||
'<td><span class="label label-success">已安装</span></td>'+
|
||||
'<td>'+esc(x.version_name||'')+'</td><td style="font-family:monospace">'+esc(x.version_code||'')+'</td></tr>';
|
||||
return '<tr><td style="font-family:monospace">'+esc(x.serial)+'</td>'+
|
||||
return '<tr>'+dev+
|
||||
'<td><span class="label label-default">未安装</span></td><td>-</td><td>-</td></tr>';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user