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 <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-02-13 13:06:17 +08:00
parent 3b491d7b36
commit 82819ae9d1

View File

@@ -38,6 +38,24 @@ class DiscreteMaterialPlanCleaner:
if self.verbose: if self.verbose:
print(*args, **kwargs) 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): 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_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: if match:
detail_count = int(match.group(1)) detail_count = int(match.group(1))
self._print(f"详细信息数量: {detail_count}") self._print(f"详细信息数量: {detail_count}")
# 提取"备料状态"信息 # 提取"备料状态"信息
detail_element = inner_frame.get_by_text(re.compile(r"^备料状态:.+$")) status_element = inner_frame.get_by_text(re.compile(r"^备料状态:.+$"))
detail_text = detail_element.inner_text().replace("\n", "") status_text = status_element.inner_text().replace("\n", "")
# 使用正则表达式提取括号中的数字 # 使用正则表达式提取括号中的数字
match = re.search(r"^备料状态:(.+)$", detail_text) match = re.search(r"^备料状态:(.+)$", status_text)
if match: if match:
detail_status = match.group(1) detail_status = match.group(1)
self._print(f"备料状态: {detail_status}") self._print(f"备料状态: {detail_status}")
@@ -180,11 +197,55 @@ class DiscreteMaterialPlanCleaner:
child_form.wait_for(state="visible", timeout=5000) child_form.wait_for(state="visible", timeout=5000)
self._print(f"父容器 .card-table-side-box 已找到") 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() # page2.pause()
for id in range(detail_count): while self._is_button_enabled(next_material_button):
id_lable_locator = child_form.get_by_text("序号 " + str(id + 1)) # 等待页面数据加载完成(带超时机制)
id_lable_locator.wait_for(state="visible", timeout=10000) max_wait_time = 10 # 最大等待10秒
self._print(f"处理 {id_lable_locator.inner_text()} ") 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 = ( input_box = (
@@ -231,6 +292,13 @@ class DiscreteMaterialPlanCleaner:
if should_delete: if should_delete:
if not cumulative_pending_quantity : if not cumulative_pending_quantity :
self._print(f"✅ 可以删除") 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: else:
self._print(f"❌ 累计待发数量不为空,无法删除") self._print(f"❌ 累计待发数量不为空,无法删除")
self._print( self._print(
@@ -240,14 +308,15 @@ class DiscreteMaterialPlanCleaner:
else: else:
self._print(f"保留:材料名称【{material_name}】材料编码【{material_code}】无需清理") self._print(f"保留:材料名称【{material_name}】材料编码【{material_code}】无需清理")
if id != detail_count - 1: # 检查"下一个物料"按钮是否可用
child_form.get_by_role("button").filter( if self._is_button_enabled(next_material_button):
has_text=re.compile(r"^$") self._print("按钮可用,可以点击")
).nth(2).click() next_material_button.click()
else: else:
child_form.get_by_role("button").filter( self._print("按钮不可用,有 disabled 属性")
has_text=re.compile(r"^$")
).nth(4).click() collapse_button.click()
# page2.pause() # page2.pause()
elif detail_count == 0: elif detail_count == 0: