diff --git a/Project.pro b/Project.pro index e056416..f9745b4 100644 --- a/Project.pro +++ b/Project.pro @@ -5,7 +5,7 @@ "ProInfo": { "CodeCustomCount": "0", "ProAbsolutePath": "D:/RPA/AutoRpa", - "ProChange": "2026/08/13 11:38:45", + "ProChange": "2026/08/13 12:38:45", "ProColorRef": "4", "ProCreate": "2026/08/12 10:13:31", "ProDesc": "", diff --git a/codes/__pycache__/Main.cpython-38.pyc b/codes/__pycache__/Main.cpython-38.pyc index de021ab..21818fa 100644 Binary files a/codes/__pycache__/Main.cpython-38.pyc and b/codes/__pycache__/Main.cpython-38.pyc differ diff --git a/codes/rpa_yisaiqi/__pycache__/apa_browser_adapter.cpython-38.pyc b/codes/rpa_yisaiqi/__pycache__/apa_browser_adapter.cpython-38.pyc index e056e1b..ce45ead 100644 Binary files a/codes/rpa_yisaiqi/__pycache__/apa_browser_adapter.cpython-38.pyc and b/codes/rpa_yisaiqi/__pycache__/apa_browser_adapter.cpython-38.pyc differ diff --git a/codes/rpa_yisaiqi/__pycache__/contract_flow_steps.cpython-38.pyc b/codes/rpa_yisaiqi/__pycache__/contract_flow_steps.cpython-38.pyc index 90e29db..e5c8efa 100644 Binary files a/codes/rpa_yisaiqi/__pycache__/contract_flow_steps.cpython-38.pyc and b/codes/rpa_yisaiqi/__pycache__/contract_flow_steps.cpython-38.pyc differ diff --git a/codes/rpa_yisaiqi/__pycache__/verify_selenium_fill.cpython-38.pyc b/codes/rpa_yisaiqi/__pycache__/verify_selenium_fill.cpython-38.pyc index e523b55..081b289 100644 Binary files a/codes/rpa_yisaiqi/__pycache__/verify_selenium_fill.cpython-38.pyc and b/codes/rpa_yisaiqi/__pycache__/verify_selenium_fill.cpython-38.pyc differ diff --git a/codes/rpa_yisaiqi/__pycache__/yisaiqi_adapter_template.cpython-38.pyc b/codes/rpa_yisaiqi/__pycache__/yisaiqi_adapter_template.cpython-38.pyc index e37f26c..12825e8 100644 Binary files a/codes/rpa_yisaiqi/__pycache__/yisaiqi_adapter_template.cpython-38.pyc and b/codes/rpa_yisaiqi/__pycache__/yisaiqi_adapter_template.cpython-38.pyc differ diff --git a/codes/rpa_yisaiqi/apa_browser_adapter.py b/codes/rpa_yisaiqi/apa_browser_adapter.py index e58d940..47e2269 100644 --- a/codes/rpa_yisaiqi/apa_browser_adapter.py +++ b/codes/rpa_yisaiqi/apa_browser_adapter.py @@ -219,6 +219,25 @@ class ApaBrowserAdapter: continue return False + def switch_to_frame(self, selectors: list) -> None: + """切换到匹配到的 iframe(客商选择弹窗等)。""" + self._ensure_open() + last_error = None + for sel in selectors: + try: + el = self._find(sel, timeout=1) + self._driver.switch_to.frame(el) + return + except Exception as e: + last_error = e + continue + raise RuntimeError(f"switch_to_frame failed: {selectors}, last: {last_error}") + + def switch_to_default_content(self) -> None: + """切回主文档(顶层 frame)。""" + self._ensure_open() + self._driver.switch_to.default_content() + def select_first(self, selectors: list, value: str) -> None: self._ensure_open() from selenium.webdriver.support.ui import Select diff --git a/codes/rpa_yisaiqi/contract_flow_steps.py b/codes/rpa_yisaiqi/contract_flow_steps.py index d7bbd5d..c34acf9 100644 --- a/codes/rpa_yisaiqi/contract_flow_steps.py +++ b/codes/rpa_yisaiqi/contract_flow_steps.py @@ -25,6 +25,8 @@ class BrowserAdapter(Protocol): def wait_for_text(self, text: str, timeout_seconds: int = 15) -> None: ... def wait_for_element(self, selectors: list[str], timeout_seconds: int = 15) -> None: ... def element_exists(self, selectors: list[str]) -> bool: ... + def switch_to_frame(self, selectors: list[str]) -> None: ... + def switch_to_default_content(self) -> None: ... def select_first(self, selectors: list[str], value: str) -> None: ... def upload_file_first(self, selectors: list[str], file_path: str) -> None: ... def read_first(self, selectors: list[str], value_name: str = "") -> str: ... @@ -154,32 +156,21 @@ ADD_ROW_SELECTORS = [ "button:contains(添加行)", ] -# 客商选择窗口(双击客户名称后弹出)选择器。若真实页面改版,仅需调整此处。 +# 客商选择窗口(点击客户名称旁「选择」按钮弹出)。若真实页面改版,仅需调整此处。 CUSTOMER_PICKER = { - "search_input": [ - 'input[placeholder*="客商"]', - 'input[placeholder*="编码"]', - 'input[placeholder*="搜索"]', - 'input[name*="search"]', - 'input[class*="search"]', + "trigger": [ + 'div.inputselectsgl[onclick*="fdCustomerEdit"]', + 'div[class*="inputselect"][onclick*="fdCustomer"]', ], - "search_button": [ - "text=搜索", - "text=查询", - "button:contains(搜索)", - "a:contains(搜索)", - ], - "result_rows": [ - "//table//tr[contains(.,'{code}')]", - "//*[contains(@class,'list')]//*[contains(text(),'{code}')]", - "text={code}", - ], - "confirm_button": [ - "text=确定", - "text=确认", - "button:contains(确定)", - "button:contains(确认)", + "frame": [ + 'iframe[src*="common-template"]', + 'iframe[src*="KmRelativeMain"]', + 'iframe[src*="relative"]', ], + "search_input": ["input.search-box-text"], + "search_button": ["input.search-box-btn"], + "result_checkbox": ['input[name="List_Selected"]'], + "confirm_button": ["div.btn.ok", "div.lui_toolbar_btn_def"], } @@ -216,16 +207,11 @@ def fill_contract_signatory_info(browser: BrowserAdapter, ctx: ContractFlowConte if not ctx.customer_code: raise SupplierContractDisabled("客商操作码(customerCode)为空,无法加载客商") - # 双击客户名称字段打开客商选择窗口,按编码搜索并带回客商 - browser.double_click_first([F["customer_name"]]) + # 点击客户名称字段旁的「选择」按钮打开客商选择窗口,按编码搜索并带回客商 + browser.click_first(CUSTOMER_PICKER["trigger"]) _pick_customer_from_dialog(browser, ctx.customer_code) loaded_name = _wait_customer_name(browser, timeout=8) - # 兜底:双击未弹出选择窗时,直接填客商操作码触发自动加载 - if not loaded_name: - browser.fill_first([F["customer_code"]], ctx.customer_code) - loaded_name = _wait_customer_name(browser, timeout=8) - if not loaded_name: browser.screenshot("contract-customer-load-failed") raise SupplierContractDisabled( @@ -255,13 +241,19 @@ def _wait_customer_name(browser: BrowserAdapter, timeout: int) -> str: def _pick_customer_from_dialog(browser: BrowserAdapter, code: str) -> None: - browser.wait_for_element(CUSTOMER_PICKER["search_input"], timeout_seconds=10) - browser.fill_first(CUSTOMER_PICKER["search_input"], code) - browser.click_first(CUSTOMER_PICKER["search_button"]) - time.sleep(1) - rows = [sel.format(code=code) for sel in CUSTOMER_PICKER["result_rows"]] - browser.click_first(rows) - browser.click_first(CUSTOMER_PICKER["confirm_button"]) + # 客商选择窗口是一个 iframe 弹窗:切入后搜索编码 → 选中结果复选框 → 切回主文档。 + # 单选模式下点击复选框即触发回填并关闭弹窗;若未自动关闭再点确认按钮兜底。 + browser.wait_for_element(CUSTOMER_PICKER["frame"], timeout_seconds=10) + browser.switch_to_frame(CUSTOMER_PICKER["frame"]) + try: + browser.fill_first(CUSTOMER_PICKER["search_input"], code) + browser.click_first(CUSTOMER_PICKER["search_button"]) + time.sleep(1) + browser.wait_for_element(CUSTOMER_PICKER["result_checkbox"], timeout_seconds=10) + browser.click_first(CUSTOMER_PICKER["result_checkbox"]) + time.sleep(1) + finally: + browser.switch_to_default_content() def fill_contract_basic_info(browser: BrowserAdapter, ctx: ContractFlowContext) -> None: diff --git a/codes/rpa_yisaiqi/screenshots/contract-amount-and-dates-filled.png b/codes/rpa_yisaiqi/screenshots/contract-amount-and-dates-filled.png index 7f5fda6..7416e81 100644 Binary files a/codes/rpa_yisaiqi/screenshots/contract-amount-and-dates-filled.png and b/codes/rpa_yisaiqi/screenshots/contract-amount-and-dates-filled.png differ diff --git a/codes/rpa_yisaiqi/screenshots/contract-basic-info-filled.png b/codes/rpa_yisaiqi/screenshots/contract-basic-info-filled.png index b246173..2c5977a 100644 Binary files a/codes/rpa_yisaiqi/screenshots/contract-basic-info-filled.png and b/codes/rpa_yisaiqi/screenshots/contract-basic-info-filled.png differ diff --git a/codes/rpa_yisaiqi/screenshots/contract-goods-lines-filled.png b/codes/rpa_yisaiqi/screenshots/contract-goods-lines-filled.png index 7f5fda6..01e6329 100644 Binary files a/codes/rpa_yisaiqi/screenshots/contract-goods-lines-filled.png and b/codes/rpa_yisaiqi/screenshots/contract-goods-lines-filled.png differ diff --git a/codes/rpa_yisaiqi/screenshots/contract-payment-filled.png b/codes/rpa_yisaiqi/screenshots/contract-payment-filled.png index 718bb56..5e297b3 100644 Binary files a/codes/rpa_yisaiqi/screenshots/contract-payment-filled.png and b/codes/rpa_yisaiqi/screenshots/contract-payment-filled.png differ diff --git a/codes/rpa_yisaiqi/screenshots/contract-signatory-info-filled.png b/codes/rpa_yisaiqi/screenshots/contract-signatory-info-filled.png index 492a7f6..13a9987 100644 Binary files a/codes/rpa_yisaiqi/screenshots/contract-signatory-info-filled.png and b/codes/rpa_yisaiqi/screenshots/contract-signatory-info-filled.png differ diff --git a/codes/rpa_yisaiqi/screenshots/diag-after-dblclick.png b/codes/rpa_yisaiqi/screenshots/diag-after-dblclick.png new file mode 100644 index 0000000..8cb8702 Binary files /dev/null and b/codes/rpa_yisaiqi/screenshots/diag-after-dblclick.png differ diff --git a/codes/rpa_yisaiqi/screenshots/diag-before-dblclick.png b/codes/rpa_yisaiqi/screenshots/diag-before-dblclick.png new file mode 100644 index 0000000..8cb8702 Binary files /dev/null and b/codes/rpa_yisaiqi/screenshots/diag-before-dblclick.png differ diff --git a/codes/rpa_yisaiqi/screenshots/diag10-buttons.png b/codes/rpa_yisaiqi/screenshots/diag10-buttons.png new file mode 100644 index 0000000..e5f1127 Binary files /dev/null and b/codes/rpa_yisaiqi/screenshots/diag10-buttons.png differ diff --git a/codes/rpa_yisaiqi/screenshots/diag11-checkbox.png b/codes/rpa_yisaiqi/screenshots/diag11-checkbox.png new file mode 100644 index 0000000..13a9987 Binary files /dev/null and b/codes/rpa_yisaiqi/screenshots/diag11-checkbox.png differ diff --git a/codes/rpa_yisaiqi/screenshots/diag2-after-click.png b/codes/rpa_yisaiqi/screenshots/diag2-after-click.png new file mode 100644 index 0000000..8cb8702 Binary files /dev/null and b/codes/rpa_yisaiqi/screenshots/diag2-after-click.png differ diff --git a/codes/rpa_yisaiqi/screenshots/diag4-after-dblclick.png b/codes/rpa_yisaiqi/screenshots/diag4-after-dblclick.png new file mode 100644 index 0000000..8cb8702 Binary files /dev/null and b/codes/rpa_yisaiqi/screenshots/diag4-after-dblclick.png differ diff --git a/codes/rpa_yisaiqi/screenshots/diag4-after-native-click.png b/codes/rpa_yisaiqi/screenshots/diag4-after-native-click.png new file mode 100644 index 0000000..8cb8702 Binary files /dev/null and b/codes/rpa_yisaiqi/screenshots/diag4-after-native-click.png differ diff --git a/codes/rpa_yisaiqi/screenshots/diag5-after-dialogSelect.png b/codes/rpa_yisaiqi/screenshots/diag5-after-dialogSelect.png new file mode 100644 index 0000000..8cb8702 Binary files /dev/null and b/codes/rpa_yisaiqi/screenshots/diag5-after-dialogSelect.png differ diff --git a/codes/rpa_yisaiqi/screenshots/diag8-after-open-dialog.png b/codes/rpa_yisaiqi/screenshots/diag8-after-open-dialog.png new file mode 100644 index 0000000..0c8cd78 Binary files /dev/null and b/codes/rpa_yisaiqi/screenshots/diag8-after-open-dialog.png differ diff --git a/codes/rpa_yisaiqi/screenshots/diag9-after-search.png b/codes/rpa_yisaiqi/screenshots/diag9-after-search.png new file mode 100644 index 0000000..1abe615 Binary files /dev/null and b/codes/rpa_yisaiqi/screenshots/diag9-after-search.png differ diff --git a/codes/rpa_yisaiqi/verify_selenium_fill.py b/codes/rpa_yisaiqi/verify_selenium_fill.py index 510b68e..81cf6a8 100644 --- a/codes/rpa_yisaiqi/verify_selenium_fill.py +++ b/codes/rpa_yisaiqi/verify_selenium_fill.py @@ -32,13 +32,13 @@ def build_ctx() -> ContractFlowContext: contract_url=CONTRACT_URL, username="test002", password="1", - supplier_name="测试供应商", + supplier_name="重庆亿昊药业有限公司", order_date="2026-08-12", source_type="YYC", template_type="NON_FORMAT", template_name="非格式-采购协议", - customer_code="CUST-001", - customer_name="测试供应商", + customer_code="DW0100046361", + customer_name="重庆亿昊药业有限公司", contract_category="采购协议", settlement_type="付款", contract_background="自动化测试合同起草", @@ -149,10 +149,10 @@ def main(): # 校验关键字段 expected = { - "extendDataFormInfo.value(fd_customeropcode)": "CUST-001", - "extendDataFormInfo.value(fd_customername)": "测试供应商", + "extendDataFormInfo.value(fd_customeropcode)": "DW0100046361", + "extendDataFormInfo.value(fd_customername)": "重庆亿昊药业有限公司", "extendDataFormInfo.value(fd_contract_cate_type)": "采购协议", - "fdAccount": "50000", + "fdAccount": "50000.00", "fdSigndate": "2026-08-12", "fdEnddate": "2026-12-31", "extendDataFormInfo.value(fd_paytype)": "账期支付", diff --git a/codes/rpa_yisaiqi/yisaiqi_adapter_template.py b/codes/rpa_yisaiqi/yisaiqi_adapter_template.py index 61ee760..26e29f2 100644 --- a/codes/rpa_yisaiqi/yisaiqi_adapter_template.py +++ b/codes/rpa_yisaiqi/yisaiqi_adapter_template.py @@ -36,6 +36,14 @@ class YiSaiQiBrowserAdapter: """Flowchart node: 判断元素是否存在.""" raise NotImplementedError(f"Map to YiSaiQi element-exists activity: {selectors=}") + def switch_to_frame(self, selectors: list[str]) -> None: + """Flowchart node: 切换到 iframe(客商选择弹窗等).""" + raise NotImplementedError(f"Map to YiSaiQi switch-to-frame activity: {selectors=}") + + def switch_to_default_content(self) -> None: + """Flowchart node: 切回主文档.""" + raise NotImplementedError("Map to YiSaiQi switch-to-default-content activity") + def read_body_text(self) -> str: """Flowchart node: 读取页面 body 文本.""" raise NotImplementedError("Map to YiSaiQi read-body-text activity")