fix: 元素选择器序号语义 + 抓取弹窗直接点击测试
- uiauto_helper: 同属性多实例的选择器由 `//*[@id="x"][k]` 改为 `(//*[@id="x"])[k]`—— 前者在 XPath 里是"父节点内排第 k",多实例时 [2..n] 全部失配(实测抖音底部 4 个同 id tab: 仅 [1] 可用),任务里表现为"未找到元素"但界面上元素明明存在。 - tasks/generic/task.py: 新增 _norm_legacy_xpath,执行前把**历史遗留**的 `//*[@attr=…][k]` 窄范围纠正为带括号形式(只改前缀,结构路径 …/FrameLayout[2] 的 兄弟序号保持不动)——已存任务无需重抓即可恢复。 - editor.js: 抓取弹窗每条元素新增「▶ 点一下」(按 bounds 中心真点一次,/api/screen/tap snap=1 吸附)与「✓ 测选择器」(用将填入的选择器跑 /api/steps/test 验证命中), 点击后自动刷新截图;底部加用法提示。 - doc/TASK_DEV.md:写明 xpath 序号必须整体加括号 + 旧形态自动纠正 + 优先文字/唯一 id。
This commit is contained in:
+43
-1
@@ -845,7 +845,10 @@ var _stepEditor={
|
||||
'</div>';
|
||||
document.getElementById('el-picker-footer').innerHTML=
|
||||
'<button class="btn" onclick="_stepEditor._showDevicePicker()">← 返回选设备</button>'+
|
||||
'<button class="btn" onclick="closeElPicker()">取消</button>';
|
||||
'<button class="btn" onclick="closeElPicker()">取消</button>'+
|
||||
'<span class="text-muted" style="margin-left:10px;font-size:11.5px">'
|
||||
+'提示:先「▶ 点一下」在设备上验证位置,再点元素回填选择器;'
|
||||
+'「✓ 测选择器」会用将填入的选择器真跑一次点击</span>';
|
||||
// 加载截图和元素树(_refreshShot 内部同时刷新两者)
|
||||
self._refreshShot();
|
||||
},
|
||||
@@ -999,6 +1002,14 @@ var _stepEditor={
|
||||
var indent=' '.repeat(Math.min(el.depth||0,6));
|
||||
var sg=el.suggested||{};
|
||||
if(sg.invalid)attrs.push('<span style="color:#dc2626">⚠ '+esc(sg.reason||'无可用属性')+'</span>');
|
||||
var acts='<div class="ep-acts" style="margin-left:auto;display:flex;gap:4px;flex:none">'
|
||||
+'<button class="btn btn-xs" title="直接在设备上点这个元素(按元素中心坐标点击,服务端自动吸附到可点元素)" '
|
||||
+'onclick="event.stopPropagation();_stepEditor._testTap('+idx+')">▶ 点一下</button>'
|
||||
+(!sg.invalid&&sg.value
|
||||
? '<button class="btn btn-xs" title="用将填入的选择器在设备上真跑一次 click,验证是否命中" '
|
||||
+'onclick="event.stopPropagation();_stepEditor._testSelector('+idx+')">✓ 测选择器</button>'
|
||||
: '')
|
||||
+'</div>';
|
||||
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>'+
|
||||
@@ -1006,10 +1017,41 @@ var _stepEditor={
|
||||
'<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>':'')+
|
||||
acts+
|
||||
'</div>';
|
||||
}).join('');
|
||||
document.getElementById('el-list').innerHTML=html||'<div class="se-empty">无元素</div>';
|
||||
},
|
||||
// 抓取时"直接点一下":按元素 bounds 中心在设备上点击(服务端 snap 吸附)
|
||||
_testTap:function(idx){
|
||||
var el=this._elData&&this._elData[idx]; var serial=this._elSerial;
|
||||
if(!el||!serial)return;
|
||||
var m=(el.bounds||'').match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
|
||||
if(!m){ showToast('该元素没有 bounds,无法坐标点击','error'); return; }
|
||||
var x=Math.round((+m[1]+ +m[3])/2), y=Math.round((+m[2]+ +m[4])/2);
|
||||
var self=this;
|
||||
apiPost('/api/screen/tap', {serial:serial, x:x, y:y, snap:1}).then(function(r){
|
||||
if(!r||!r.ok){ showToast((r&&r.error)||'点击失败','error'); return; }
|
||||
showToast('已点击'+(r.label?('「'+r.label+'」'):'')+' ('+r.x+','+r.y+')'+(r.snapped?' [已吸附]':''),'success');
|
||||
setTimeout(function(){ if(self._elSerial===serial)self._refreshShot(); }, 800);
|
||||
});
|
||||
},
|
||||
// 抓取时验证"将填入的选择器":走 /api/steps/test 真跑一次 click
|
||||
_testSelector:function(idx){
|
||||
var el=this._elData&&this._elData[idx]; var serial=this._elSerial;
|
||||
if(!el||!serial)return;
|
||||
var sg=el.suggested||{};
|
||||
if(!sg.value){ showToast('该元素没有可用选择器','error'); return; }
|
||||
var self=this;
|
||||
var step={type:'click', label:'测试点击',
|
||||
params:{selector_type:sg.type||'xpath', selector_value:sg.value, wait_timeout:2}};
|
||||
apiPost('/api/steps/test', {serial:serial, step:step}).then(function(r){
|
||||
if(!r||!r.ok){ showToast((r&&r.error)||'测试失败','error'); return; }
|
||||
var ok=(r.result==='命中');
|
||||
showToast('选择器测试: '+(r.result||'')+(r.msg?(' — '+r.msg):''), ok?'success':'error');
|
||||
setTimeout(function(){ if(self._elSerial===serial)self._refreshShot(); }, 800);
|
||||
});
|
||||
},
|
||||
_filterEl:function(kw){
|
||||
if(!kw){this._renderEl(this._elData);this._renderOverlays();return;}
|
||||
kw=kw.toLowerCase();
|
||||
|
||||
Reference in New Issue
Block a user