feat: 抓取元素选择器精度优化 + 设备列表补全 + 刷新同步
This commit is contained in:
+36
-9
@@ -1397,7 +1397,7 @@ var _stepEditor={
|
||||
'<div class="el-picker-main">'+
|
||||
'<div class="el-shot-wrap">'+
|
||||
'<div class="el-shot-toolbar">'+
|
||||
'<button class="btn" onclick="_stepEditor._refreshShot()">刷新截图</button>'+
|
||||
'<button class="btn" onclick="_stepEditor._refreshShot()" title="重新拉取截图和元素树(界面变化后刷新用)">刷新截图+元素</button>'+
|
||||
'<span class="el-shot-info" id="el-shot-info">加载中...</span>'+
|
||||
'</div>'+
|
||||
'<div class="el-shot-container" id="el-shot-container">'+
|
||||
@@ -1412,21 +1412,35 @@ var _stepEditor={
|
||||
document.getElementById('el-picker-footer').innerHTML=
|
||||
'<button class="btn" onclick="_stepEditor._showDevicePicker()">← 返回选设备</button>'+
|
||||
'<button class="btn" onclick="closeElPicker()">取消</button>';
|
||||
// 加载截图和元素树
|
||||
// 加载截图和元素树(_refreshShot 内部同时刷新两者)
|
||||
self._refreshShot();
|
||||
fetch('/api/uiauto/elements?serial='+encodeURIComponent(serial))
|
||||
},
|
||||
// 拉取元素树(进入页面和刷新时共用;带时间戳防缓存)
|
||||
_loadElData:function(){
|
||||
var serial=this._elSerial;
|
||||
if(!serial)return;
|
||||
var listEl=document.getElementById('el-list');
|
||||
if(listEl)listEl.innerHTML='<div class="se-empty">加载元素树...</div>';
|
||||
var self=this;
|
||||
fetch('/api/uiauto/elements?serial='+encodeURIComponent(serial)+'&_='+Date.now())
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(data){
|
||||
var le=document.getElementById('el-list');
|
||||
if(!le)return;
|
||||
if(!data.ok){
|
||||
document.getElementById('el-list').innerHTML='<div class="se-empty">'+esc(data.error||'抓取失败')+'</div>';
|
||||
le.innerHTML='<div class="se-empty">'+esc(data.error||'抓取失败')+'</div>';
|
||||
return;
|
||||
}
|
||||
self._elData=data.elements||[];
|
||||
self._renderEl(self._elData);
|
||||
// 刷新后保持搜索过滤状态
|
||||
var search=document.getElementById('el-search');
|
||||
if(search&&search.value)self._filterEl(search.value);
|
||||
self._renderOverlays();
|
||||
})
|
||||
.catch(function(e){
|
||||
document.getElementById('el-list').innerHTML='<div class="se-empty">请求失败: '+esc(e)+'</div>';
|
||||
var le=document.getElementById('el-list');
|
||||
if(le)le.innerHTML='<div class="se-empty">请求失败: '+esc(e)+'</div>';
|
||||
});
|
||||
},
|
||||
// 刷新截图
|
||||
@@ -1454,6 +1468,8 @@ var _stepEditor={
|
||||
if(infoEl)infoEl.textContent='失败';
|
||||
};
|
||||
img.src='/api/uiauto/screenshot?serial='+encodeURIComponent(serial)+'&_='+Date.now();
|
||||
// 同时重新拉取元素树——只刷截图会导致"截图是新的、元素框是旧的"错位
|
||||
this._loadElData();
|
||||
},
|
||||
// 在截图上渲染元素边界框(可点击)
|
||||
_renderOverlays:function(){
|
||||
@@ -1531,6 +1547,12 @@ var _stepEditor={
|
||||
item.classList.add('active');
|
||||
item.scrollIntoView({block:'nearest',behavior:'smooth'});
|
||||
}
|
||||
// 信息栏显示将填入的选择器,确认是否精确命中目标
|
||||
var infoEl=document.getElementById('el-shot-info');
|
||||
if(infoEl&&this._elData&&this._elData[i]){
|
||||
var sg=this._elData[i].suggested||{};
|
||||
infoEl.textContent='已选中: '+(sg.type||'')+' '+(sg.value||'');
|
||||
}
|
||||
},
|
||||
_renderEl:function(list){
|
||||
var self=this;
|
||||
@@ -1541,12 +1563,14 @@ var _stepEditor={
|
||||
if(el.text)attrs.push('<span>text:'+esc(el.text)+'</span>');
|
||||
if(el.description)attrs.push('<span>desc:'+esc(el.description)+'</span>');
|
||||
var indent=' '.repeat(Math.min(el.depth||0,6));
|
||||
return '<div class="el-picker-item" data-idx="'+idx+'" onclick="_stepEditor._pickEl('+idx+')">'+
|
||||
var sg=el.suggested||{};
|
||||
return '<div class="el-picker-item" data-idx="'+idx+'" title="点击填入: '+esc(sg.value||'')+'" onclick="_stepEditor._pickEl('+idx+')">'+
|
||||
'<span class="ep-depth">'+(el.depth||0)+'</span>'+
|
||||
'<div class="ep-info"><div class="ep-name">'+indent+esc(el.name||el.class||'')+'</div>'+
|
||||
'<div class="ep-attrs">'+attrs.join('')+'</div></div>'+
|
||||
'<span class="ep-sel">'+esc(el.suggested.type)+'</span>'+
|
||||
(el.suggested&&el.suggested.broad?' <span class="label label-warning" title="无唯一属性,选择器可能误匹配其他元素,建议选带文字/id 的元素">⚠宽泛</span>':'')+
|
||||
'<span class="ep-sel">'+esc(sg.type)+'</span>'+
|
||||
(sg.indexed?' <span class="label label-info" title="界面中同属性元素共 '+sg.total+' 个,按从上到下这是第 '+sg.occ+' 个,选择器已带序号精确定位">#'+sg.occ+'/'+sg.total+'</span>':'')+
|
||||
(sg.broad?' <span class="label label-warning" title="无唯一属性,选择器可能误匹配其他元素,建议选带文字/id 的元素">⚠宽泛</span>':'')+
|
||||
'</div>';
|
||||
}).join('');
|
||||
document.getElementById('el-list').innerHTML=html||'<div class="se-empty">无元素</div>';
|
||||
@@ -1834,17 +1858,20 @@ async function loadUsers(){
|
||||
filter:(u,kw)=>(u.username||'').toLowerCase().includes(kw),
|
||||
render:renderUserRow,
|
||||
tbody:'tb-users',pager:'pager-users',pageSize:10,
|
||||
empty:'<tr><td colspan="4" class="empty">暂无用户</td></tr>'
|
||||
empty:'<tr><td colspan="5" class="empty">暂无用户</td></tr>'
|
||||
});
|
||||
applyListPager('users');
|
||||
initListSearch('users','search-users');
|
||||
}
|
||||
function renderUserRow(u){
|
||||
const admin=u.is_admin?'<span class="label label-primary">管理员</span>':'<span class="label label-default">普通</span>';
|
||||
const perms=(u.perms||[]).map(p=>'<span class="label label-info">'+(PERM_LABELS[p]||p)+'</span>').join(' ')
|
||||
||'<span class="text-muted">-</span>';
|
||||
return '<tr>'+
|
||||
'<td>'+u.id+'</td>'+
|
||||
'<td><strong>'+esc(u.username)+'</strong></td>'+
|
||||
'<td>'+admin+'</td>'+
|
||||
'<td>'+perms+'</td>'+
|
||||
'<td>'+
|
||||
'<button class="btn btn-xs" onclick="openUserModal('+u.id+')">编辑</button>'+
|
||||
(u.username==='admin'?'':'<button class="btn btn-danger btn-xs" onclick="deleteUser('+u.id+')">删除</button>')+
|
||||
|
||||
Reference in New Issue
Block a user