commit e3925c287f04ad95e7ad1dd3ccf34a34996d92a5 Author: Misaka_Company Date: Tue Jan 13 10:51:01 2026 +0800 first commit diff --git a/main.py b/main.py new file mode 100644 index 0000000..e69de29 diff --git a/materialDelete.py b/materialDelete.py new file mode 100644 index 0000000..e69de29 diff --git a/record.py b/record.py new file mode 100644 index 0000000..1d8a9e7 --- /dev/null +++ b/record.py @@ -0,0 +1,246 @@ +import re +import time +from playwright.sync_api import Playwright, sync_playwright, expect, TimeoutError as PWTimeoutError +import time + +def click_button_until_disappear(frame, button_name="保存提交", interval=5, max_attempts=None, max_duration=None): + """ + 持续点击按钮直到按钮消失 + + 参数: + frame: iframe对象 + button_name: 按钮名称 + interval: 点击间隔时间(秒) + max_attempts: 最大点击次数,None表示不限制 + max_duration: 最大持续时间(秒),None表示不限制 + + 返回: + dict: 包含点击次数、耗时等信息 + """ + click_count = 0 + start_time = time.time() + + print(f"开始监控'{button_name}'按钮...") + + while True: + try: + # 检查最大点击次数 + 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}秒(实际{elapsed:.1f}秒),停止操作") + break + + button = frame.get_by_role("button", name=button_name) + + # 检查按钮是否存在 + if button.count() == 0: + elapsed = time.time() - start_time + print(f"'{button_name}'按钮已消失") + print(f"共点击{click_count}次,耗时{elapsed:.1f}秒") + return { + "success": True, + "click_count": click_count, + "elapsed_time": elapsed + } + + # 点击按钮 + button.click() + click_count += 1 + print(f"[{time.strftime('%H:%M:%S')}] 第{click_count}次点击'{button_name}'") + + # 等待指定时间 + time.sleep(interval) + + except Exception as e: + elapsed = time.time() - start_time + print(f"点击过程中出现异常: {e}") + return { + "success": False, + "click_count": click_count, + "elapsed_time": elapsed, + "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. 登录主页面 + 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="确定") + if confirm_btn.count() > 0: + confirm_btn.click() + print("强制登录") + else: + 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("新页面已打开,正在等待内层 iframe 加载...") + page1.wait_for_load_state("domcontentloaded") # 等待 DOM 加载 + # 或者更稳:page1.wait_for_load_state("networkidle") # 网络空闲 + + # 提取外层 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) # 最多等15秒 + inner_frame = inner_frame_locator.content_frame + + # 现在内层 iframe 一定就绪了 + print("内层 iframe 已加载完成") + + textbox = inner_frame.get_by_role("textbox", name="期初标识≠") + textbox.fill("") # 直接 fill,不需要 press CapsLock + inner_frame.locator("#rc_select_0").fill("关闭") + # 输入排产号 + textbox = inner_frame.get_by_role("textbox", name="排产号") + textbox.fill("R") # 直接 fill,不需要 press CapsLock + + # 输入日期 + inner_frame.get_by_role("textbox", name="单据日期结束日期").click() + 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*行")) + + # 获取元素的完整文本 + summary_text = summary_locator.inner_text() # 例如 "合计: 135 行" + + # 用正则提取数字 + match = re.search(r"\d+", summary_text) + if match: + row_count = int(match.group()) + print(f"当前总行数:{row_count}") + else: + print("未匹配到行数") + row_count = 0 + + + 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 + + + # 新页面:等待页面加载完成 + 提取嵌套 iframe + print("新页面已打开,正在等待内层 iframe 加载...") + page2.wait_for_load_state("domcontentloaded") # 等待 DOM 加载 + + # 提取外层 forwardFrame + outer_frame = page2.locator("#forwardFrame").content_frame + # 关键:等待内层 #mainiframe 出现并加载 + inner_frame_locator = outer_frame.locator("#mainiframe") + inner_frame_locator.wait_for(state="visible", timeout=15000) # 最多等15秒 + inner_frame = inner_frame_locator.content_frame + print("内层 iframe 已加载完成") + # label_div = inner_frame.locator("div").filter(has_text=re.compile(r"^生产部门$")).first + # input_box = label_div.locator("..").locator(".wui-input-close > .wui-input") + time.sleep(25) # 等待页面完全加载 + + + # pro_dep_input = get_input_by_label(inner_frame, "生产部门") + # if pro_dep_input: + # print(pro_dep_input.input_value()) + # bill_type_input = get_input_by_label(inner_frame, "订单类型") + # if bill_type_input: + # print(bill_type_input.input_value()) + # pro_SN_input = get_input_by_label(inner_frame, "排产号") + # if pro_SN_input: + # print(pro_SN_input.input_value()) + + + + #result = click_button_until_disappear(inner_frame, "保存提交", interval=5) + #print(result) + + + + + input("操作完成,按回车关闭...") + + context.close() + browser.close() + +with sync_playwright() as playwright: + run(playwright) \ No newline at end of file diff --git a/test_playwright.py b/test_playwright.py new file mode 100644 index 0000000..48ca9e7 --- /dev/null +++ b/test_playwright.py @@ -0,0 +1,318 @@ +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) \ No newline at end of file