diff --git a/inbound_verify/sites/yunda.py b/inbound_verify/sites/yunda.py index c742069..c6e1a89 100644 --- a/inbound_verify/sites/yunda.py +++ b/inbound_verify/sites/yunda.py @@ -77,6 +77,35 @@ def _remove_if_exists(path): pass +def _resolve_export_frame(ws_frame): + """定位韵达数据导出面板内嵌的 iframe。 + + 韵达改版后导出面板换用 Element UI(全选/向右转移/导出按钮)。实到流程的面板 + iframe 名为 myFrame(已验证);应到流程历史上为 target1。这里短超时轮流探测, + 返回首个出现「全选」按钮的 frame;都未命中则 dump 面板内所有 iframe 名便于排查。 + """ + for name in ("myFrame", "target1"): + frame = ws_frame.frame_locator(f'iframe[name="{name}"]') + try: + frame.locator("button.el-button", has_text="全选").first.wait_for( + state="visible", timeout=5000 + ) + print(f" [导出iframe] 命中 iframe[name={name}]") + return frame + except Exception: + continue + try: + names = ws_frame.locator("iframe").evaluate_all( + "els => els.map(e => e.name || '(无name)')" + ) + print( + f" [导出iframe] myFrame/target1 均未命中全选;面板 iframe 名: {names}" + ) + except Exception as e: + print(f" [导出iframe] dump 失败: {e}") + return ws_frame.frame_locator('iframe[name="myFrame"]') + + def yunda_login(page): """韵达自动登录:未登录则填充表单并提交,已登录则跳过。""" print(">> 正在检查韵达登录状态...") @@ -274,8 +303,11 @@ def yunda_expected_download_impl(page): ws_frame.locator("#docSum").wait_for(state="visible", timeout=15000) page.wait_for_timeout(500) - # 导出弹窗双层重试:外层重新打开面板,内层重新提交。 - # 区分字段漏选(补点全选)与字段列表消失(重新打开面板)。 + # 导出弹窗重试:外层重新打开面板(最多 3 次)。 + # 韵达站点已将导出面板从 jQuery(.allRight/#submitbutton) 改版为 Element UI + # (与实到流程同一导出组件,iframe=myFrame): + # 全选(button“全选”) → 向右转移(i.el-icon-d-arrow-right) → 导出(i.el-icon-download) + # → 正在导出中(.el-loading-mask) → 成功提示(.el-message-box 导出任务建立成功) → 确定 task_success = False for major_attempt in range(3): print(f" >> 正在打开数据导出面板 (尝试 {major_attempt + 1}/3)...") @@ -285,74 +317,61 @@ def yunda_expected_download_impl(page): state="visible", timeout=15000 ) - export_frame = ws_frame.frame_locator('iframe[name="target1"]') + export_frame = _resolve_export_frame(ws_frame) try: - # 校验字段列表是否加载完成(以“交接单号”为标志) - export_frame.get_by_text("交接单号").first.wait_for( - state="visible", timeout=3000 - ) + # 校验 Element UI 字段选择区是否加载完成(以「全选」按钮就绪为标志) + export_frame.locator( + "button.el-button", has_text="全选" + ).first.wait_for(state="visible", timeout=8000) except Exception: - print(" ⚠️ 字段列表未加载,关闭面板后重试...") + print(" ⚠️ 导出面板字段区未加载,关闭面板后重试...") ws_frame.locator(".layui-layer-close1").click() page.wait_for_timeout(1000) continue - export_frame.locator(".allRight").click() + print(" -> 全选字段并向右转移...") + export_frame.locator("button.el-button", has_text="全选").first.click() + page.wait_for_timeout(400) + export_frame.locator( + "button.el-button:has(i.el-icon-d-arrow-right)" + ).first.click() page.wait_for_timeout(500) - inner_success = False - needs_reopen = False - print(" -> 正在提交导出任务...") - for attempt in range(4): - export_frame.locator("#submitbutton", has_text="导出数据").click() - confirm_link = export_frame.get_by_role("link", name="确定") - try: - confirm_link.wait_for(state="visible", timeout=6000) - if export_frame.get_by_text("导出任务建立成功").is_visible(): - print(" ✅ 导出任务已建立成功。") - confirm_link.click() - inner_success = True - break - elif export_frame.get_by_text( - "请选择格式相应的导出字段" - ).is_visible(): - confirm_link.click() - page.wait_for_timeout(500) + export_frame.locator( + "button.el-button:has(i.el-icon-download)" + ).first.click() - # 区分:字段漏选 还是 字段列表消失 - if export_frame.get_by_text("交接单号").first.is_visible(): - print( - " ⚠️ 检测到未选择字段(字段列表仍在),重新点击全选..." - ) - export_frame.locator(".allRight").click() - page.wait_for_timeout(500) - else: - print( - " ⚠️ 字段列表异常消失,重新打开导出面板..." - ) - needs_reopen = True - break # 跳出内层循环,重新打开面板 - else: - confirm_link.click() - page.wait_for_timeout(1000) - except Exception: - page.wait_for_timeout(1000) + # 等待「正在导出中」遮罩出现并消失 + loading = export_frame.locator(".el-loading-mask").first + try: + loading.wait_for(state="visible", timeout=5000) + loading.wait_for(state="hidden", timeout=60000) + except Exception: + pass + + # 等待结果提示并判定 + inner_success = False + try: + msg_box = export_frame.locator(".el-message-box.my-alert").first + msg_box.wait_for(state="visible", timeout=30000) + if msg_box.get_by_text("导出任务建立成功").is_visible(): + print(" ✅ 导出任务已建立成功。") + inner_success = True + else: + print(" ⚠️ 导出结果提示非成功状态,将重试。") + msg_box.locator("button.el-button", has_text="确定").first.click() + page.wait_for_timeout(500) + except Exception: + print(" ⚠️ 未检测到导出结果提示,将重试。") + + ws_frame.locator(".layui-layer-close1").click() + page.wait_for_timeout(500) if inner_success: task_success = True - ws_frame.locator(".layui-layer-close1").click() - page.wait_for_timeout(500) - break # 跳出外层循环,继续后续步骤 - elif needs_reopen: - ws_frame.locator(".layui-layer-close1").click() - page.wait_for_timeout(1000) - continue # 重新打开面板 - else: - ws_frame.locator(".layui-layer-close1").click() - page.wait_for_timeout(1000) - continue + break if not task_success: raise RuntimeError("多次重试后仍未能建立应到数据离线任务。") @@ -497,8 +516,10 @@ def yunda_actual_download_impl(page): ).click() return - # 导出弹窗双层重试:外层重新打开面板,内层重新提交。 - # 区分字段漏选(补点全选)与字段列表消失(重新打开面板)。 + # 导出弹窗重试:外层重新打开面板(最多 3 次)。 + # 韵达站点已将导出面板从 jQuery(.allRight/#submitbutton) 改版为 Element UI: + # 全选(button“全选”) → 向右转移(i.el-icon-d-arrow-right) → 导出(i.el-icon-download) + # → 正在导出中(.el-loading-mask) → 成功提示(.el-message-box 导出任务建立成功) → 确定 print(">> 正在发起导出...") task_success = False @@ -512,69 +533,58 @@ def yunda_actual_download_impl(page): export_frame = ws_frame.frame_locator('iframe[name="myFrame"]') try: - # 校验字段列表是否加载完成(以“扫描类型”为标志) - export_frame.get_by_text("扫描类型").first.wait_for( - state="visible", timeout=3000 - ) + # 校验 Element UI 字段选择区是否加载完成(以「全选」按钮就绪为标志) + export_frame.locator( + "button.el-button", has_text="全选" + ).first.wait_for(state="visible", timeout=8000) except Exception: - print(" ⚠️ 字段列表未加载,关闭面板后重试...") + print(" ⚠️ 导出面板字段区未加载,关闭面板后重试...") ws_frame.locator(".layui-layer-close1").click() page.wait_for_timeout(1000) continue - export_frame.locator(".allRight").click() + print(" -> 全选字段并向右转移...") + export_frame.locator("button.el-button", has_text="全选").first.click() + page.wait_for_timeout(400) + export_frame.locator( + "button.el-button:has(i.el-icon-d-arrow-right)" + ).first.click() page.wait_for_timeout(500) - inner_success = False - needs_reopen = False - print(" -> 正在提交导出任务...") - for attempt in range(4): - export_frame.locator("#submitbutton", has_text="导出数据").click() - confirm_link = export_frame.get_by_role("link", name="确定") - try: - confirm_link.wait_for(state="visible", timeout=6000) - if export_frame.get_by_text("导出任务建立成功").is_visible(): - print(" ✅ 导出任务已建立成功。") - confirm_link.click() - inner_success = True - break - elif export_frame.get_by_text( - "请选择格式相应的导出字段" - ).is_visible(): - confirm_link.click() - page.wait_for_timeout(500) + export_frame.locator( + "button.el-button:has(i.el-icon-download)" + ).first.click() - # 区分:字段漏选 还是 字段列表消失 - if export_frame.get_by_text("扫描类型").first.is_visible(): - print( - " ⚠️ 检测到未选择字段(字段列表仍在),重新点击全选..." - ) - export_frame.locator(".allRight").click() - page.wait_for_timeout(500) - else: - print(" ⚠️ 字段列表异常消失,重新打开导出面板...") - needs_reopen = True - break - else: - confirm_link.click() - page.wait_for_timeout(1000) - except Exception: - page.wait_for_timeout(1000) + # 等待「正在导出中」遮罩出现并消失 + loading = export_frame.locator(".el-loading-mask").first + try: + loading.wait_for(state="visible", timeout=5000) + loading.wait_for(state="hidden", timeout=60000) + except Exception: + pass + + # 等待结果提示并判定 + inner_success = False + try: + msg_box = export_frame.locator(".el-message-box.my-alert").first + msg_box.wait_for(state="visible", timeout=30000) + if msg_box.get_by_text("导出任务建立成功").is_visible(): + print(" ✅ 导出任务已建立成功。") + inner_success = True + else: + print(" ⚠️ 导出结果提示非成功状态,将重试。") + msg_box.locator("button.el-button", has_text="确定").first.click() + page.wait_for_timeout(500) + except Exception: + print(" ⚠️ 未检测到导出结果提示,将重试。") + + ws_frame.locator(".layui-layer-close1").click() + page.wait_for_timeout(500) if inner_success: task_success = True - ws_frame.locator(".layui-layer-close1").click() - page.wait_for_timeout(500) break - elif needs_reopen: - ws_frame.locator(".layui-layer-close1").click() - page.wait_for_timeout(1000) - continue - else: - ws_frame.locator(".layui-layer-close1").click() - page.wait_for_timeout(1000) - continue if not task_success: raise RuntimeError("多次重试后仍未能建立实到数据离线任务。")