5D3420315FA54219AB2427B0C674AD66auto
|
|
@ -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": "",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
|
@ -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)": "账期支付",
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||