Files
playwrite/test_playwright.py
Misaka_Company e3925c287f first commit
2026-01-13 10:51:01 +08:00

318 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import re
import time
from playwright.sync_api import Playwright, sync_playwright, expect, TimeoutError as PWTimeoutError
import pdb
def click_button_until_disappear(frame, button_name="保存提交", max_attempts=None, max_duration=None):
"""
持续点击按钮直到按钮消失。
包含业务逻辑:点击前检查【生产部门】是否为空,为空则自动回填数据。
"""
click_count = 0
start_time = time.time()
# 定义加载控件的定位器 (基于传入的 frame)
loading_locator = frame.locator("div").filter(has_text="加载中").nth(1)
print(f"开始监控'{button_name}'按钮 (含自动补全逻辑)...")
while True:
try:
# --- 1. 检查退出条件 ---
if max_attempts and click_count >= max_attempts:
print(f"已达到最大点击次数{max_attempts}次,停止操作")
break
if max_duration and (time.time() - start_time) >= max_duration:
elapsed = time.time() - start_time
print(f"已达到最大持续时间{max_duration}秒,停止操作")
break
# --- 2. 检查按钮状态 ---
button = frame.get_by_role("button", name=button_name)
# 如果按钮已经消失,任务完成
if button.count() == 0:
elapsed = time.time() - start_time
print(f"SUCCESS: '{button_name}'按钮已消失")
print(f"共点击{click_count}次,总耗时{elapsed:.1f}")
return {
"success": True,
"click_count": click_count,
"elapsed_time": elapsed
}
# ==========================================
# 插入业务逻辑:检查并回填数据
# ==========================================
try:
# 1. 获取生产部门输入框
dep_input = get_input_by_label(frame, "生产部门")
if dep_input:
current_val = dep_input.input_value()
# 检查是否为空 (strip去除空白字符)
if current_val != "压力表车间" and current_val != "CQ030101":
print(f" [补全] 检测到生产部门为空,开始自动填充...")
# A. 填入生产部门
dep_input.fill("压力表车间")
print(" [补全] -> 生产部门已填入: CQ030101")
# B. 填入订单类型 (仅在需要补全生产部门时才操作这个,根据你的需求逻辑)
type_input = get_input_by_label(frame, "订单类型")
if type_input:
type_input.fill("55C2-Cxx-02")
print(" [补全] -> 订单类型已填入: 55C2-Cxx-02")
else:
print(" [警告] 无法找到'订单类型'输入框")
# 稍微等待一下填入值的生效(可选)
time.sleep(0.5)
else:
print(f" [信息] 生产部门已有值: {current_val},跳过补全")
else:
print(" [警告] 无法找到'生产部门'输入框,跳过检查")
except Exception as logic_e:
print(f" [异常] 数据补全逻辑出错 (不影响继续点击): {logic_e}")
# ==========================================
# --- 3. 执行点击 ---
button.click()
click_count += 1
print(f"[{time.strftime('%H:%M:%S')}] 第{click_count}次点击'{button_name}'")
# --- 4. 智能等待加载逻辑 ---
# 4.1 缓冲等待点击后UI可能需要几秒钟才会弹出遮罩
# print(" - 等待遮罩层响应...")
time.sleep(2)
# 4.2 检测并等待遮罩消失
try:
# 尝试等待遮罩出现 (给它3秒的时间被检测到)
if loading_locator.is_visible(timeout=3000):
print(" - 检测到'加载中',正在等待数据载入...")
# 只要出现了,就无限等待它消失 (timeout=0)
loading_locator.wait_for(state="hidden", timeout=0)
print(" - 数据载入完毕")
else:
# 如果3秒内没检测到 visible可能是数据极少瞬间加载完了
pass
except Exception as e:
# 忽略定位超时等非致命错误
pass
# 稍微休息一下
time.sleep(0.5)
except Exception as e:
elapsed = time.time() - start_time
print(f"操作异常: {e}")
return {
"success": False,
"click_count": click_count,
"error": str(e)
}
def get_input_by_label(frame, label_text: str, label_locator=None):
"""
通过标签文本获取对应的输入框对象
参数:
frame: iframe对象
label_text: 标签文本(用于查找和日志输出)
label_locator: 可选已经定位好的标签locator。如果为None则函数内部查找
返回:
成功返回输入框的locator对象
失败返回None
"""
try:
# 如果没有传入label_locator则根据label_text查找
if label_locator is None:
label_locator = frame.locator("div").filter(has_text=re.compile(f"^{label_text}$")).first
print(f"找到{label_text}标签")
# 向上找到包含标签和输入框的共同父容器
parent_container = label_locator.locator("..") # 父元素
# 在父容器中查找输入框
input_box = parent_container.locator("input").first
# 如果父元素中没有,再向上一层
if input_box.count() == 0:
print(f"{label_text}: 在父元素中未找到,向上一层查找...")
grandparent = parent_container.locator("..") # 祖父元素
input_box = grandparent.locator("input").first
# 验证是否找到输入框
if input_box.count() > 0:
input_box.wait_for(state="visible", timeout=5000)
current_value = input_box.input_value()
print(f"{label_text}: {current_value}")
print(input_box.count())
return input_box
else:
print(f"{label_text}: 在父容器中找不到输入框")
return None
except Exception as e:
print(f"{label_text}失败: {e}")
return None
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
context = browser.new_context(ignore_https_errors=True)
page = context.new_page()
# 1. 登录主页面
try:
page.goto("https://68.11.34.30:8082/yonbip/resources/uap/rbac/login/main/index.html")
# 提取主 iframe
main_frame = page.locator("#forwardFrame").content_frame
main_frame.get_by_role("textbox", name="用户名").fill("BLDpengqiangqiang")
main_frame.get_by_role("textbox", name="密码").fill("Cqbld123456.")
main_frame.get_by_role("button", name="登录").click()
# 2. 处理强制登录弹窗(判断是否存在)
confirm_btn = main_frame.get_by_role("button", name="确定")
try:
# 短暂等待检测弹窗
if confirm_btn.is_visible(timeout=3000):
confirm_btn.click()
print("强制登录")
else:
print("正常登录")
except:
print("正常登录 (未检测到弹窗)")
# 3. 点击打开“补货安排”
main_frame.locator("i").first.click() # 假设是某个新建按钮
with page.expect_popup() as page1_info:
main_frame.get_by_title("补货安排", exact=True).click()
page1 = page1_info.value
# 4. 新页面:等待页面加载完成 + 提取嵌套 iframe
print("Page1 已打开,正在等待内层 iframe 加载...")
page1.wait_for_load_state("domcontentloaded")
# 提取外层 forwardFrame
outer_frame = page1.locator("#forwardFrame").content_frame
# 关键:等待内层 #mainiframe 出现并加载
inner_frame_locator = outer_frame.locator("#mainiframe")
inner_frame_locator.wait_for(state="visible", timeout=15000)
inner_frame = inner_frame_locator.content_frame
print("Page1 内层 iframe 已加载完成")
# 填写查询条件
textbox = inner_frame.get_by_role("textbox", name="期初标识≠")
textbox.fill("")
inner_frame.locator("#rc_select_0").fill("关闭")
textbox = inner_frame.get_by_role("textbox", name="排产号")
textbox.fill("R")
inner_frame.get_by_role("textbox", name="单据日期开始日期").fill("2025-12-28")
inner_frame.get_by_role("textbox", name="单据日期结束日期").fill("2025-12-31")
# inner_frame.get_by_text("今日").click()
# inner_frame.get_by_text("今日").click()
# 点击查询按钮
inner_frame.locator(".iconfont.icon-chaxun").click()
# 勾选所有合同
inner_frame.get_by_role("row", name="序号").get_by_label("").check()
# 获取订单合计数量
summary_locator = inner_frame.get_by_text(re.compile(r"合计:\s*\d+\s*行"))
try:
summary_text = summary_locator.inner_text(timeout=5000)
match = re.search(r"\d+", summary_text)
if match:
row_count = int(match.group())
print(f"当前总行数:{row_count}")
else:
print("未匹配到行数")
except:
print("未找到合计行")
# 进入 Page2
inner_frame.get_by_role("button").filter(has_text="补货安排").hover()
inner_frame.get_by_text("生产订单").click()
inner_frame.get_by_role("textbox", name="工厂").fill("10010705")
with page1.expect_popup(timeout=60000) as page2_info:
inner_frame.get_by_role("button", name="确定(Y)").click()
page2 = page2_info.value
# --- Page2 加载逻辑优化 ---
print("Page2 已打开,正在初始化...")
page2.wait_for_load_state("domcontentloaded")
outer_frame_p2 = page2.locator("#forwardFrame").content_frame
inner_frame_locator_p2 = outer_frame_p2.locator("#mainiframe")
inner_frame_locator_p2.wait_for(state="visible", timeout=15000)
inner_frame_p2 = inner_frame_locator_p2.content_frame
print("Page2 基础框架已就绪")
# 1. 按照建议等待3秒让加载遮罩有机会出现
print("等待 7 秒,检测加载遮罩是否出现...")
time.sleep(7)
# 2. 定义加载控件定位器
# 注意:这里直接基于 inner_frame_p2 构建,对应你的 path: ...locator("#mainiframe").content_frame.locator("div")...
loading_locator = inner_frame_p2.locator("div").filter(has_text="加载中").nth(1)
try:
# 尝试等待加载控件变为 visible。
# 给它 3秒 的检测时间,如果这 3秒 内出现了,说明页面正在加载数据。
# 如果没出现,触发 TimeoutError进入 except 块,说明数据量很少,可能瞬间加载完了。
loading_locator.wait_for(state="visible", timeout=3000)
print("检测到'加载中'控件,开始等待数据加载(时间不定)...")
# 3. 关键:等待控件消失 (state="hidden" 或 "detached")
# timeout=0 表示无限等待,防止数据量大加载时间过长导致报错
loading_locator.wait_for(state="hidden", timeout=0)
print(">>> 页面加载完毕!(加载控件已消失)")
except PWTimeoutError:
print(">>> 页面加载完毕!(未检测到'加载中'控件,可能已快速完成)")
except Exception as e:
print(f">>> 页面加载状态判断异常: {e},默认视为加载完毕")
# --- 这里可以继续 Page2 的后续操作 ---
input_box = get_input_by_label(inner_frame_p2, "生产部门")
input_box = get_input_by_label(inner_frame_p2, "订单类型")
input_box = get_input_by_label(inner_frame_p2, "数量")
print("\n>>> 开始执行连续点击操作 <<<")
# 这里的 inner_frame_p2 是你在 Page2 提取出来的内层 iframe
result = click_button_until_disappear(
frame=inner_frame_p2,
button_name="保存提交", # 修改为你实际要点击的按钮名字,比如 "下一页" 或 "加载更多"
max_attempts=500 # 防止死循环,设置一个最大上限
)
print("进入调试模式,你现在可以在终端输入 Python 代码来测试定位器...")
pdb.set_trace() # <--- 程序会在这里卡住,控制权交给终端
print("最终结果:", result)
input("操作完成,按回车关闭...")
finally:
context.close()
browser.close()
with sync_playwright() as playwright:
run(playwright)