feat: 条件判断步骤(if_el 双分支:找到/未找到各执行独立步骤,可嵌套循环/条件;OCR识别选择器匹配图片文字,RapidOCR 跨平台实现含命中点击)+ 动作编辑器布局优化(操作库分组/加宽、任务弹窗加宽)

This commit is contained in:
2026-08-11 10:38:47 +08:00
parent 159649b6a0
commit 2f2d8cc184
5 changed files with 280 additions and 59 deletions
+60 -2
View File
@@ -75,6 +75,9 @@ STEP_TYPES = [
"params": {"loop_mode": "rounds", "max_iterations": 10, "loop_duration": 600, "children": []}},
{"type": "group", "label": "动作组", "icon": "📦",
"params": {"children": []}},
{"type": "if_el", "label": "条件判断", "icon": "❓",
"params": {"selector_type": "xpath", "selector_value": "", "timeout": 3,
"then": [], "else": []}},
]
@@ -177,8 +180,8 @@ class GenericStepsWorker(BaseWorker):
_log.error(f"[{self.serial}] 步骤 {label}({stype}) 异常: {e}")
else:
_log.warning(f"[{self.serial}] 未知步骤类型: {stype}")
# 容器步骤(loop/group)不计入操作计数,避免监控显示噪音
if stype not in ("loop", "group"):
# 容器步骤(loop/group/if_el)不计入操作计数,避免监控显示噪音
if stype not in ("loop", "group", "if_el"):
self._action_counts[label] = self._action_counts.get(label, 0) + 1
elapsed = self.elapsed()
# done=累计执行的操作次数(无固定总数),前端显示"已执行 N 次操作"
@@ -365,6 +368,61 @@ class GenericStepsWorker(BaseWorker):
_log.warning(f"[{self.serial}] 等待元素异常: {e}")
return False
def _exec_if_el(self, d, params, depth=0):
"""条件判断:找到元素 → 执行 then 分支;超时未找到 → 执行 else 分支。
selector_type=ocr 时用屏幕 OCR 匹配文字(图片/画布/WebView 里的文字也能找到),
命中且 ocr_click=True 时自动点击该文字中心。
分支里可放任意步骤(含循环/嵌套条件判断),实现"有弹窗就关掉、没弹窗就继续"这类逻辑。
返回 True=命中 / False=未命中 / None=缺选择器或 OCR 不可用。
"""
sel_type = params.get("selector_type", "xpath")
sel_val = params.get("selector_value", "")
timeout = float(params.get("timeout", 3))
if not sel_val:
_log.warning(f"[{self.serial}] if_el 缺少选择器,跳过")
return None
found = False
if sel_type == "ocr":
# OCR 模式:截图 → 识别文字 → 关键词匹配(UI 树里没有的文字也能找到)
try:
from core.ocr import available as _ocr_available, find_on_screen
ok, hint = _ocr_available()
if not ok:
_log.error(f"[{self.serial}] OCR 条件判断不可用: {hint}")
return None
img = d.screenshot()
if img is None:
_log.warning(f"[{self.serial}] OCR 截图失败")
else:
found, center, matched = find_on_screen(img, sel_val)
if found:
if params.get("ocr_click") and center:
d.click(*center)
_log.info(f"[{self.serial}] OCR 命中 '{matched}',已点击 ({center[0]},{center[1]})")
else:
_log.info(f"[{self.serial}] OCR 命中 '{matched}'")
else:
_log.info(f"[{self.serial}] OCR 未命中: '{sel_val}'")
except Exception as e:
_log.warning(f"[{self.serial}] OCR 条件判断异常: {e}")
found = False
else:
try:
if sel_type == "xpath":
found = d.xpath(sel_val).wait(timeout=timeout)
else:
found = d(**{sel_type: sel_val}).exists(timeout=timeout)
except Exception as e:
_log.warning(f"[{self.serial}] 条件判断异常: {e}")
found = False
self._track_selector_health(sel_val, found)
branch = params.get("then" if found else "else") or []
self.set_action(f"条件{'命中' if found else '未命中'} → 执行{'找到' if found else '未找到'}分支({len(branch)}步)")
_log.info(f"[{self.serial}] 条件判断 {'命中' if found else '未命中'}: {sel_val},执行{'then' if found else 'else'}分支 {len(branch)} 步")
self._exec_steps(d, branch, depth + 1)
return found
def _exec_swipe(self, d, params, depth=0):
direction = params.get("direction", "up")
dur_min = float(params.get("duration_min", 0.25))