From 047036f46b67dcb9dd3e1604c59335bb836f4735 Mon Sep 17 00:00:00 2001 From: Misaka Date: Wed, 29 Jul 2026 20:26:45 +0800 Subject: [PATCH] feat(yunda): cross-month calendar navigation for date-specific download MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 韵达两个流程的日期组件不同,分别适配跨月翻月: - 应到(.layui-laydate 新版):读 .laydate-set-ym 当前年月,点 .laydate-prev-m/.laydate-next-m 翻月,格子 td[lay-ymd='YYYY-M-D']。 - 实到(#laydate_box 旧版):读 #laydate_y/#laydate_m 输入框值,点 #laydate_MM 内 .laydate_chprev/.laydate_chnext 翻月,格子 td[y][m][d]。 两处设日期改走 _yunda_pick_laydate_new / _yunda_pick_laydate_old,同月(offset 当月) 行为不变,仅跨月时翻月。年*12+月 比较天然支持跨年。 实到 #startDate/#endDate 保持普通 click(force=True 会在导航后 laydate 绑定完成前 抢先点击导致面板打不开);新增 #laydate_box:visible 就绪等待。 Co-Authored-By: Claude --- inbound_verify/sites/yunda.py | 82 ++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 12 deletions(-) diff --git a/inbound_verify/sites/yunda.py b/inbound_verify/sites/yunda.py index d544253..73c18b5 100644 --- a/inbound_verify/sites/yunda.py +++ b/inbound_verify/sites/yunda.py @@ -77,6 +77,64 @@ def _remove_if_exists(path): pass +def _yunda_pick_laydate_new(ws_frame, page, date_ymd): + """应到(新版 laydate .layui-laydate):在已打开的面板上选中指定日期(含跨月翻月)。 + + 目标格子 td[lay-ymd='YYYY-M-D'](非补零)不在当前月视窗时,读 .laydate-set-ym 的 + 当前年月(形如「2026年7月」),按差值点 .laydate-prev-m / .laydate-next-m 翻到目标月, + 再点格子;同月则直接点。调用前提:#startTime/#endTime 已点开,.layui-laydate:visible 就绪。 + """ + cal = ws_frame.locator(".layui-laydate:visible").first + cell = cal.locator(f"td[lay-ymd='{date_ymd}']").first + if not cell.is_visible(): + ty, tm = (int(x) for x in date_ymd.split("-")[:2]) + print(f" ℹ️ 目标日期 {date_ymd} 不在当前月视窗,正在翻月导航 ...") + for _ in range(24): + nums = re.findall(r"\d+", cal.locator(".laydate-set-ym").first.inner_text()) + if len(nums) >= 2: + cur_y, cur_m = int(nums[0]), int(nums[1]) + if cur_y == ty and cur_m == tm: + break + cur = cur_y * 12 + (cur_m - 1) + btn = ( + ".laydate-prev-m" + if (ty * 12 + (tm - 1)) < cur + else ".laydate-next-m" + ) + cal.locator(btn).first.click() + page.wait_for_timeout(300) + cell = cal.locator(f"td[lay-ymd='{date_ymd}']").first + cell.click() + + +def _yunda_pick_laydate_old(ws_frame, page, date_ymd): + """实到(旧版 laydate #laydate_box):在已打开的面板上选中指定日期(含跨月翻月)。 + + 目标格子 td[y][m][d](非补零)不在当前月视窗时,读 #laydate_y/#laydate_m 输入框值 + (形如「2026年」「07月」)得当前年月,按差值点 #laydate_MM 内 .laydate_chprev / + .laydate_chnext 翻到目标月,再点格子;同月则直接点。调用前提:#startDate/#endDate + 已点开(force=True),#laydate_box:visible 就绪。 + """ + box = ws_frame.locator("#laydate_box:visible").first + ty, tm, td = (int(x) for x in date_ymd.split("-")[:3]) + cell = box.locator(f"td[y='{ty}'][m='{tm}'][d='{td}']").first + if not cell.is_visible(): + print(f" ℹ️ 目标日期 {date_ymd} 不在当前月视窗,正在翻月导航 ...") + for _ in range(24): + yv = box.locator("#laydate_y").first.evaluate("e=>e.value") + mv = box.locator("#laydate_m").first.evaluate("e=>e.value") + cur_y = int(re.search(r"\d+", yv).group()) + cur_m = int(re.search(r"\d+", mv).group()) + if cur_y == ty and cur_m == tm: + break + cur = cur_y * 12 + (cur_m - 1) + btn = ".laydate_chprev" if (ty * 12 + (tm - 1)) < cur else ".laydate_chnext" + box.locator(f"#laydate_MM {btn}").first.click() + page.wait_for_timeout(300) + cell = box.locator(f"td[y='{ty}'][m='{tm}'][d='{td}']").first + cell.click() + + def _resolve_export_frame(ws_frame): """定位韵达数据导出面板内嵌的 iframe。 @@ -228,7 +286,7 @@ def yunda_expected_download_impl(page, force=False, date=None): calendar1 = ws_frame.locator(".layui-laydate:visible").first calendar1.wait_for(state="visible", timeout=5000) - calendar1.locator(f"td[lay-ymd='{start_date_ymd}']").click() + _yunda_pick_laydate_new(ws_frame, page, start_date_ymd) calendar1.locator(".laydate-btns-confirm").click() page.wait_for_timeout(400) @@ -238,7 +296,7 @@ def yunda_expected_download_impl(page, force=False, date=None): calendar2 = ws_frame.locator(".layui-laydate:visible").first calendar2.wait_for(state="visible", timeout=5000) - calendar2.locator(f"td[lay-ymd='{today_ymd}']").click() + _yunda_pick_laydate_new(ws_frame, page, today_ymd) calendar2.locator(".laydate-btns-confirm").click() page.wait_for_timeout(500) @@ -472,8 +530,8 @@ def yunda_actual_download_impl(page, date=None): target = datetime.strptime(date, "%Y-%m-%d") else: target = today - timedelta(days=offset) - start_date = target # 单日范围:起止同日 - today = target # 让下方"截止时间"选择器也指向 target + # 旧版 laydate 的日期格子 td[y][m][d] 用非补零整数值;单日范围起止同日 + target_ymd = f"{target.year}-{target.month}-{target.day}" src = f"指定 {date}" if date else f"偏移 {offset},0=今天" print( f">> 设置实到查询日期: [{target.year}-{target.month}-{target.day}]({src})" @@ -482,19 +540,19 @@ def yunda_actual_download_impl(page, date=None): print(" >> 正在设定起始时间...") ws_frame.locator("#startDate").click() page.wait_for_timeout(400) - box1 = ws_frame.locator("#laydate_box:visible").first - box1.locator( - f"td[y='{start_date.year}'][m='{start_date.month}'][d='{start_date.day}']" - ).click() + ws_frame.locator("#laydate_box:visible").first.wait_for( + state="visible", timeout=5000 + ) + _yunda_pick_laydate_old(ws_frame, page, target_ymd) page.wait_for_timeout(400) print(" >> 正在设定截止时间...") ws_frame.locator("#endDate").click() page.wait_for_timeout(400) - box2 = ws_frame.locator("#laydate_box:visible").first - box2.locator( - f"td[y='{today.year}'][m='{today.month}'][d='{today.day}']" - ).click() + ws_frame.locator("#laydate_box:visible").first.wait_for( + state="visible", timeout=5000 + ) + _yunda_pick_laydate_old(ws_frame, page, target_ymd) page.wait_for_timeout(500) print(" >> 正在变更扫描类型为【到件】...")