feat: 条件判断步骤(if_el 双分支:找到/未找到各执行独立步骤,可嵌套循环/条件;OCR识别选择器匹配图片文字,RapidOCR 跨平台实现含命中点击)+ 动作编辑器布局优化(操作库分组/加宽、任务弹窗加宽)
This commit is contained in:
+60
-2
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user