""" 页面导航服务实现 实现页面导航、iframe 操作等具体逻辑 """ import re import time from playwright.sync_api import TimeoutError as PlaywrightTimeoutError from interfaces.i_page_navigator import IPageNavigator from config.ui_constants import UIConstants class PageNavigator(IPageNavigator): """页面导航实现""" def __init__(self, config: UIConstants, verbose: bool = True): """ 初始化页面导航器 Args: config: UI 配置 verbose: 是否打印详细日志 """ self.config = config self.verbose = verbose def _print(self, *args, **kwargs): """打印日志(如果 verbose=True)""" if self.verbose: print(*args, **kwargs) def navigate_to_main_page(self, page) -> tuple: """ 导航到主页面 iframe 实际返回 (page, main_frame, inner_frame) 的元组 """ # 点击打开"功能菜单" main_frame = page.locator(self.config.selectors.FORWARD_FRAME).content_frame main_frame.locator("i").first.click() # 点击打开"离散生产订单维护" with page.expect_popup() as page1_info: main_frame.get_by_title("离散生产订单维护", exact=True).first.click() page1 = page1_info.value # 获取 nested iframe main_frame = page1.locator(self.config.selectors.FORWARD_FRAME).content_frame inner_frame_locator = main_frame.locator(self.config.selectors.MAIN_IFRAME) inner_frame_locator.wait_for( state="visible", timeout=self.config.timeouts.IFRAME_VISIBLE ) inner_frame = inner_frame_locator.content_frame return page1, main_frame, inner_frame def navigate_to_order_page(self, main_frame, page): """导航到订单页面""" # 这个方法在当前实现中与 navigate_to_main_page 类似 # 返回新的页面对象和内部 iframe return self.navigate_to_main_page(page) def navigate_to_material_plan_page(self, page): """ 导航到备料计划页面 Args: page: 订单页面对象 (page1) Returns: (page2, inner_frame) - 新页面和内部 iframe """ # 获取主 iframe main_frame = page.locator(self.config.selectors.FORWARD_FRAME).content_frame inner_frame = main_frame.locator(self.config.selectors.MAIN_IFRAME).content_frame # 点击"更多" inner_frame.locator(self.config.selectors.HOT_KEY_HEAD).get_by_text( self.config.selectors.MORE_BUTTON ).click() # 点击"备料计划"并等待新页面 with page.expect_popup() as page2_info: inner_frame.get_by_text(self.config.selectors.MATERIAL_PLAN).click() page2 = page2_info.value # 获取 nested iframe main_frame = page2.locator(self.config.selectors.FORWARD_FRAME).content_frame inner_frame_locator = main_frame.locator(self.config.selectors.MAIN_IFRAME) inner_frame_locator.wait_for( state="visible", timeout=self.config.timeouts.IFRAME_VISIBLE ) inner_frame = inner_frame_locator.content_frame # 等待备料计划页面加载完成 self._wait_for_plan_page_loaded(inner_frame) return page2, inner_frame def setup_query_interface(self, frame): """设置查询界面""" # 点击图标按钮打开查询界面 frame.locator(self.config.selectors.SEARCH_ICON).click() frame.get_by_text(self.config.selectors.QUERY_BY_ORDER).click() frame.get_by_role("tab", name=self.config.selectors.TAB_ALL).click() # 填充并验证显示数量,如果失败则重试 max_retries = self.config.retry.DISPLAY_COUNT_MAX_RETRIES expected_value = self.config.retry.EXPECTED_DISPLAY_COUNT for attempt in range(max_retries): frame.locator(self.config.selectors.DISPLAY_COUNT_INPUT).fill(expected_value) frame.locator(self.config.selectors.DISPLAY_COUNT_INPUT).press("Enter") # 检查填充是否成功 actual_value = frame.locator(self.config.selectors.DISPLAY_COUNT_INPUT).input_value() if actual_value == expected_value: self._print(f"文本框填充成功: {expected_value}") break else: self._print( f"第 {attempt + 1} 次填充失败,实际值: {actual_value},重试..." ) if attempt == max_retries - 1: self._print( f"警告: {max_retries} 次尝试后仍未成功填充,继续执行..." ) def wait_for_page_loaded(self, frame): """等待页面加载完成""" loading_locator = frame.locator("div").filter(has_text="加载中").nth(1) try: loading_locator.wait_for( state="visible", timeout=self.config.timeouts.LOADING_VISIBLE_WAIT ) loading_locator.wait_for( state="hidden", timeout=self.config.timeouts.LOADING_HIDDEN_WAIT ) except PlaywrightTimeoutError: # 加载很快完成,或者没有出现加载提示 pass return True def _wait_for_plan_page_loaded(self, inner_frame): """等待备料计划页面加载完成(内部方法)""" self._print("等待备料计划页面加载完成...") plan_code_locator = inner_frame.get_by_text( re.compile(r"^离散备料计划维护:") ) plan_code_locator.wait_for( state="visible", timeout=self.config.timeouts.PLAN_CODE_VISIBLE ) # 循环检查编码是否已加载 max_wait = self.config.timeouts.PLAN_CODE_MAX_WAIT wait_interval = self.config.timeouts.PLAN_CHECK_INTERVAL waited = 0 plan_code = None while waited < max_wait: plan_text = plan_code_locator.inner_text() match = re.search(r"离散备料计划维护:(.+)", plan_text) if match and match.group(1).strip(): plan_code = match.group(1).strip() break time.sleep(wait_interval) waited += wait_interval if plan_code: self._print(f"备料计划页面加载完成,编码: {plan_code}") else: self._print(f"警告: 备料计划页面加载超时")