From 82819ae9d16107e72106a82f4bbb02450d2089b2 Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Fri, 13 Feb 2026 13:06:17 +0800 Subject: [PATCH] refactor: improve button navigation logic with proper state detection - Add button-wrapper element locator for accessing action/navigation buttons - Create dedicated button objects (delete, next material, collapse) - Add _is_button_enabled() method using Playwright's is_enabled() - Replace index-based navigation with button state-driven loop - Add row number change detection to wait for data loading - Simplify navigation logic with explicit button click handling Co-Authored-By: Claude Sonnet 4.5 --- utils/离散备料计划维护数据清理.py | 101 +++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 16 deletions(-) diff --git a/utils/离散备料计划维护数据清理.py b/utils/离散备料计划维护数据清理.py index 1227d90..9e4bf8b 100644 --- a/utils/离散备料计划维护数据清理.py +++ b/utils/离散备料计划维护数据清理.py @@ -38,6 +38,24 @@ class DiscreteMaterialPlanCleaner: if self.verbose: print(*args, **kwargs) + def _is_button_enabled(self, button_locator): + """ + 判定按钮是否可用 + + Args: + button_locator: Playwright Locator 对象 + + Returns: + bool: True 表示按钮可用,False 表示按钮不可用(有 disabled 属性) + """ + try: + # 使用 Playwright 内置的 is_enabled() 方法 + # 这个方法会正确检查元素及其父元素的可启用状态 + return button_locator.is_enabled() + except Exception as e: + self._print(f"检查按钮状态时出错: {e}") + return False + def get_production_order_numbers(self, production_id_file): """ 读取总排号文件并查询数据库获取生产订单号 @@ -149,18 +167,17 @@ class DiscreteMaterialPlanCleaner: # 提取"详细信息"中的数字 detail_element = inner_frame.get_by_text(re.compile(r"^详细信息 \(\d+\)$")) - detail_text = detail_element.inner_text() # 使用正则表达式提取括号中的数字 - match = re.search(r"详细信息 \((\d+)\)", detail_text) + match = re.search(r"详细信息 \((\d+)\)", detail_element.inner_text()) if match: detail_count = int(match.group(1)) self._print(f"详细信息数量: {detail_count}") # 提取"备料状态"信息 - detail_element = inner_frame.get_by_text(re.compile(r"^备料状态:.+$")) - detail_text = detail_element.inner_text().replace("\n", "") + status_element = inner_frame.get_by_text(re.compile(r"^备料状态:.+$")) + status_text = status_element.inner_text().replace("\n", "") # 使用正则表达式提取括号中的数字 - match = re.search(r"^备料状态:(.+)$", detail_text) + match = re.search(r"^备料状态:(.+)$", status_text) if match: detail_status = match.group(1) self._print(f"备料状态: {detail_status}") @@ -180,11 +197,55 @@ class DiscreteMaterialPlanCleaner: child_form.wait_for(state="visible", timeout=5000) self._print(f"父容器 .card-table-side-box 已找到") + # 获取按钮容器对象 + button_wrapper = child_form.locator(".button-wrapper") + button_wrapper.wait_for(state="visible", timeout=5000) + self._print(f"按钮容器 .button-wrapper 已找到") + + # 创建按钮对象 + # 1. 删行按钮 + delete_row_button = button_wrapper.get_by_role("button", name="删行") + + # 2. 下一个物料按钮 (class="icon-jiantouyou") + next_material_button = button_wrapper.locator(".icon-jiantouyou") + + # 3. 侧拉收起按钮 (class="icon-celashouqi") + collapse_button = button_wrapper.locator(".icon-celashouqi") + + last_row_number = None # 用于记录上一个物料的行号,初始值为 None # page2.pause() - for id in range(detail_count): - id_lable_locator = child_form.get_by_text("序号 " + str(id + 1)) - id_lable_locator.wait_for(state="visible", timeout=10000) - self._print(f"处理 {id_lable_locator.inner_text()} ") + while self._is_button_enabled(next_material_button): + # 等待页面数据加载完成(带超时机制) + max_wait_time = 10 # 最大等待10秒 + start_time = time.time() + + while True: + row_number_locator = ( + child_form.locator("div") + .filter(has_text=re.compile(r"^行号$", re.MULTILINE)) + .locator("input") + .first + ) + row_number = row_number_locator.input_value() + + # 检查行号是否发生变化 + if row_number != last_row_number: + break + + # 检查是否超时 + if time.time() - start_time > max_wait_time: + self._print(f"⚠️ 等待数据加载超时,强制继续 (行号: {row_number})") + break + + # 等待后重新检查 + child_form.wait_for_timeout(500) + + self._print(f"当前行号: {row_number} (上一次: {last_row_number})") + + # ... 后续代码逻辑 ... + + # 处理完当前行后,更新 last_row_number + last_row_number = row_number # 获取材料编码(用于精确匹配) input_box = ( @@ -231,6 +292,13 @@ class DiscreteMaterialPlanCleaner: if should_delete: if not cumulative_pending_quantity : self._print(f"✅ 可以删除") + delete_row_button.click() + # 使用正则表达式提取括号中的数字 + match = re.search(r"详细信息 \((\d+)\)", detail_element.inner_text()) + if match: + detail_count = int(match.group(1)) + self._print(f"详细信息数量: {detail_count}") + break else: self._print(f"❌ 累计待发数量不为空,无法删除") self._print( @@ -240,14 +308,15 @@ class DiscreteMaterialPlanCleaner: else: self._print(f"保留:材料名称【{material_name}】材料编码【{material_code}】无需清理") - if id != detail_count - 1: - child_form.get_by_role("button").filter( - has_text=re.compile(r"^$") - ).nth(2).click() + # 检查"下一个物料"按钮是否可用 + if self._is_button_enabled(next_material_button): + self._print("按钮可用,可以点击") + next_material_button.click() else: - child_form.get_by_role("button").filter( - has_text=re.compile(r"^$") - ).nth(4).click() + self._print("按钮不可用,有 disabled 属性") + + collapse_button.click() + # page2.pause() elif detail_count == 0: