From f4641c3e2dfe07cf88d29dd31953b15e6bcb86c6 Mon Sep 17 00:00:00 2001 From: butubb <1422726308@qq.com> Date: Mon, 12 Oct 2026 13:54:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=BC=96=E8=BE=91=E5=99=A8):=20=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E5=99=A8=E5=80=BC=E6=97=81=E8=BE=B9=E5=8A=A0=E3=80=8C?= =?UTF-8?q?{=E5=8F=98=E9=87=8F}=E3=80=8D=E6=8C=89=E9=92=AE=20+=20=E3=80=8C?= =?UTF-8?q?=E7=82=B9=E5=87=BB=E5=85=83=E7=B4=A0=E3=80=8D=E8=A1=A5=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户反馈:"我看你这个点击元素也没有说点击变量啊" —— 上一版只在输入框 placeholder 里提了一句,等于没说:placeholder 只在空值时才显形,也没个能点的东西。 · 选择器值右边加 **「{变量}」按钮**(在「抓取元素/测试」旁边):点一下弹出 `{本次要发的名字}`(本轮要发的那个号)/ `{本机账号名字}`(读到的本机账号), 选中即插到光标处;通用编辑器和「发布计划」页的就地编辑**两处都有** · **「点击元素」参数面板加说明块**:讲清"每轮都不一样的东西怎么点"—— 类型选 textContains、值填 `{本次要发的名字}`;变量为空会变空串 + 记警告, **不会拿 `{…}` 字面量去乱点** · doc/TASK_DEV §3.3 补一句"编辑器里点按钮就行,不用记写法" --- doc/TASK_DEV.md | 3 +++ static/admin/editor.js | 55 +++++++++++++++++++++++++++++++++++++++-- static/admin/release.js | 6 ++++- 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/doc/TASK_DEV.md b/doc/TASK_DEV.md index 587e214..2a5b5a6 100644 --- a/doc/TASK_DEV.md +++ b/doc/TASK_DEV.md @@ -234,6 +234,9 @@ from .generic import task # 触发 @register_task(当前唯一任务类型) 例:账号列表里点自己要发的那个号 —— 选择器类型 `textContains`、值 `{本次要发的名字}`。 +**编辑器里不用记写法**:选择器值右边的 **`{变量}`** 按钮点一下就能插(两个变量各带一句说明); +「点击元素」的参数面板里也有专门的说明块。 + **规矩(都是刻意的)**: - **没写花括号的步骤一个字不改**,老任务零影响。 diff --git a/static/admin/editor.js b/static/admin/editor.js index 9674ce1..0ab8eab 100644 --- a/static/admin/editor.js +++ b/static/admin/editor.js @@ -81,6 +81,8 @@ function _selRowHtml(p, extra, withOcr, withDedup, keys){ var toggle=withOcr?' onchange="_stepEditor._condTypeChanged(this)"':''; var pick=_can('devices')?'':''; var test=_can('devices')?'':''; + // 变量按钮:选择器值里可以直接写 {本次要发的名字} —— 点它弹出来选,省得记写法 + var varbtn=''; var isScreen=p[kType]==='screen', isFg=p[kType]==='foreground'; var valLabel=isScreen?'屏幕状态':(isFg?'前台包名':'选择器值'); var valHtml; @@ -90,7 +92,7 @@ function _selRowHtml(p, extra, withOcr, withDedup, keys){ ''; }else{ valHtml=''+(isFg?'':pick+test); + '" placeholder="'+(isFg?'如 com.ss.android.ugc.aweme':'如 点赞;可用 {本次要发的名字} / {本机账号名字}')+'">'+(isFg?'':pick+test+varbtn); } var head='
'+ '
'; @@ -379,7 +381,13 @@ var _stepEditor={ h+=_humanizeRowHtml(p,'
'); h+='
想要"照我划的那一下原样重放",请用操作库里的《录制手势》步骤——那个是纯轨迹回放,不套这里的参数
'; }else if(step.type==='click'){ - h+=_selRowHtml(p,'
'); + h+=_selRowHtml(p,'
' + +'
要用"每轮都不一样的东西"点元素?选择器值里可以直接写变量,' + +'点值输入框右边的 {变量} 按钮插入:{本次要发的名字}(循环轮到 / 要发的那条计划的名字)、' + +'{本机账号名字}(「读取文字」/「读取本机账号名字」读到的)。' + +'例:在账号列表里点自己要发的那个号 —— 类型选 textContains(文本包含)、值填 {本次要发的名字}。' + +'
变量那一刻还是空的(还没轮到 / 没读到)→ 值会变成空串并记一条警告,' + +'这一步就是"找不到元素",不会拿 {…} 字面量去乱点。
'); }else if(step.type==='long_click'){ h+=_selRowHtml(p,'
'+ '
'); @@ -918,6 +926,49 @@ var _stepEditor={ setTimeout(function(){self._renderCanvas();},0); }, // 条件判断:候选值来源切到"平台给"(本次要发的名字 / 发布计划)时,把"比对的值"藏起来 + // 在选择器值里插入变量占位符:点「{变量}」→ 弹出两个变量 → 选中即插到光标处 + insertVar:function(btn){ + var old=document.getElementById('_varMenu'); + if(old){old.remove();} + var box=btn.closest('.sel-picker')||btn.parentNode; + var input=box.querySelector('input[data-key],textarea[data-key]')||box.querySelector('input,textarea'); + if(!input){return;} + var items=[['{本次要发的名字}','本轮要发的那个号(循环块给的)'], + ['{本机账号名字}','读到的本机账号(读取文字/读取本机账号名字)']]; + var menu=document.createElement('div'); + menu.id='_varMenu'; + menu.style.cssText='position:fixed;z-index:9999;background:#fff;border:1px solid #d0d5dd;' + +'border-radius:6px;box-shadow:0 6px 16px rgba(0,0,0,.16);padding:4px;min-width:260px;font-size:12px'; + menu.innerHTML=items.map(function(o){ + return '
' + +''+o[0]+'' + +'
'+o[1]+'
';}).join(''); + menu.querySelectorAll('._varItem').forEach(function(el){ + el.onmouseenter=function(){el.style.background='#f2f4f7';}; + el.onmouseleave=function(){el.style.background='';}; + el.onclick=function(e){ + e.stopPropagation(); + var t=input.value||''; + var a=input.selectionStart==null?t.length:input.selectionStart; + var b=input.selectionEnd==null?t.length:input.selectionEnd; + input.value=t.slice(0,a)+el.dataset.v+t.slice(b); + try{input.setSelectionRange(a+el.dataset.v.length,a+el.dataset.v.length);}catch(_){} + input.dispatchEvent(new Event('input',{bubbles:true})); + input.dispatchEvent(new Event('change',{bubbles:true})); + input.focus(); + menu.remove(); + }; + }); + document.body.appendChild(menu); + var r=btn.getBoundingClientRect(); + menu.style.left=Math.max(8,Math.min(r.left,window.innerWidth-280))+'px'; + menu.style.top=(r.bottom+4)+'px'; + setTimeout(function(){ + document.addEventListener('click',function h(){ + var m=document.getElementById('_varMenu'); if(m){m.remove();} + document.removeEventListener('click',h);}); + },0); + }, _cmpSourceChanged:function(sel){ // 同 _condTypeChanged:先把 DOM 的值同步回 _steps,否则重渲染会吞掉刚改的东西 this._syncFromDom(); diff --git a/static/admin/release.js b/static/admin/release.js index 918b8d5..f11ad1c 100644 --- a/static/admin/release.js +++ b/static/admin/release.js @@ -968,10 +968,14 @@ function _rpField(type, p, f, path) { var pick = (kind === 'selval' && typeof _can === 'function' && _can('devices')) ? '' : ''; + // 「{变量}」:选择器值里写 {本次要发的名字}(本轮要发的那个号)—— 点它插进去,不用记写法 + var varbtn = (kind === 'selval' && typeof _stepEditor !== 'undefined' && _stepEditor.insertVar) + ? '' + : ''; var ph = (kind === 'selval') ? ' placeholder="如 点赞;可用 {本次要发的名字}"' : ''; return '
' + head + '
' + pick + '
'; + + esc(v == null ? '' : v) + '">' + pick + varbtn + ''; } // 抓取元素回填后:把选择器类型下拉也切过去(通用编辑器那套要看 .sc-body,这里结构不同)