问题说明
微信小店商品数据页采用 micro-app + ShadowRoot + iframe 结构,日期控件位于 iframe 内,导致影刀普通捕获出现内联框架、元素偏移,智能日期指令无法稳定使用。
改用【执行JS脚本】直接操作 DOM。通过 micro-app → shadowRoot → iframe.contentDocument 进入日期页面,先点击“时间范围”打开日期面板,再根据传入的开始、结束日期自动选择年份、月份和日期。
参数格式:2026-08-01|2026-08-18
流程:打开时间范围 → 选择开始日期 → 选择结束日期 → 完成
打开日期框代码:function (element, input) {
var micro = document.querySelector(
'micro-app[name="statistics"]'
);
if (!micro) {
return "失败:找不到 micro-app";
}
var iframe = micro.shadowRoot.querySelector(
".iframe-container iframe"
);
if (!iframe) {
return "失败:找不到 iframe";
}
var doc =
iframe.contentDocument ||
iframe.contentWindow.document;
function getText(el) {
return String(
el.innerText ||
el.textContent ||
""
).replace(/\s+/g, "");
}
function isVisible(el) {
if (!el) {
return false;
}
var rect =
el.getBoundingClientRect();
return (
rect.width > 0 &&
rect.height > 0
);
}
/*
* 优先找截图里这种 target 容器
*/
var targets =
doc.querySelectorAll(
".target"
);
for (var i = 0; i < targets.length; i++) {
var text =
getText(targets[i]);
if (
text.indexOf("时间范围") >= 0 &&
isVisible(targets[i])
) {
targets[i].click();
return (
"成功点击时间范围:" +
text
);
}
}
/*
* 如果 class 变化,则按文本兜底
*/
var all =
doc.querySelectorAll(
"button,div,span"
);
var best = null;
for (var j = 0; j < all.length; j++) {
if (!isVisible(all[j])) {
continue;
}
var t =
getText(all[j]);
if (
t.indexOf("时间范围") === 0 &&
t.length < 80
) {
/*
* 选择最小的那个元素,
* 防止找到整个筛选栏父容器
*/
if (
!best ||
getText(best).length >
t.length
) {
best =
all[j];
}
}
}
if (!best) {
return "失败:找不到时间范围选择框";
}
/*
* 看看父级有没有 target
*/
var node = best;
for (var k = 0; k < 4 && node; k++) {
if (
String(
node.className || ""
).indexOf("target") >= 0
) {
node.click();
return (
"成功点击时间范围:" +
getText(node)
);
}
node =
node.parentElement;
}
/*
* 找不到特殊父级就直接点击文字元素
*/
best.click();
return (
"成功点击时间范围:" +
getText(best)
);
}选择日期范围代码:function (element, input) {
/* =========================================================
参数格式:
2026-01-05|2026-01-20
========================================================= */
var text = String(input || "").trim();
var parts = text.split("|");
if (parts.length !== 2) {
return "失败:参数格式应为 开始日期|结束日期,例如 2026-01-05|2026-01-20";
}
function parseDate(text) {
var m = String(text).trim().match(
/(\d{4})\D+(\d{1,2})\D+(\d{1,2})/
);
if (!m) {
return null;
}
return {
year: parseInt(m[1], 10),
month: parseInt(m[2], 10),
day: parseInt(m[3], 10)
};
}
var startDate = parseDate(parts[0]);
var endDate = parseDate(parts[1]);
if (!startDate || !endDate) {
return "失败:开始日期或结束日期格式错误";
}
/* =========================================================
日期合法性
========================================================= */
function toDate(d) {
return new Date(
d.year,
d.month - 1,
d.day
);
}
var startObj = toDate(startDate);
var endObj = toDate(endDate);
if (startObj > endObj) {
return "失败:开始日期不能晚于结束日期";
}
var diffDays =
Math.floor(
(endObj - startObj) /
86400000
) + 1;
if (diffDays > 31) {
return "失败:时间范围超过31天,当前微信小店最多支持连续31天";
}
/* =========================================================
获取 iframe
========================================================= */
var micro = document.querySelector(
'micro-app[name="statistics"]'
);
if (!micro) {
return "失败:找不到 micro-app";
}
var shadow = micro.shadowRoot;
if (!shadow) {
return "失败:拿不到 shadowRoot";
}
var iframe = shadow.querySelector(
".iframe-container iframe"
);
if (!iframe) {
return "失败:找不到 iframe";
}
var doc;
try {
doc =
iframe.contentDocument ||
iframe.contentWindow.document;
} catch (e) {
return "失败:iframe无法访问:" + e.message;
}
if (!doc) {
return "失败:iframe document为空";
}
/* =========================================================
通用工具
========================================================= */
function getText(el) {
if (!el) {
return "";
}
return String(
el.innerText ||
el.textContent ||
""
).trim();
}
function isVisible(el) {
if (!el) {
return false;
}
var style =
doc.defaultView.getComputedStyle(el);
if (
style.display === "none" ||
style.visibility === "hidden"
) {
return false;
}
var rect =
el.getBoundingClientRect();
return (
rect.width > 0 &&
rect.height > 0
);
}
function realClick(el) {
if (!el) {
return;
}
try {
el.dispatchEvent(
new MouseEvent(
"mousedown",
{
bubbles: true,
cancelable: true,
view: doc.defaultView
}
)
);
el.dispatchEvent(
new MouseEvent(
"mouseup",
{
bubbles: true,
cancelable: true,
view: doc.defaultView
}
)
);
} catch (e) {}
el.click();
}
function waitFor(finder, timeout) {
timeout = timeout || 4000;
return new Promise(
function (resolve, reject) {
var start = Date.now();
function check() {
var result = null;
try {
result = finder();
} catch (e) {}
if (result) {
resolve(result);
return;
}
if (
Date.now() - start >
timeout
) {
reject(
new Error(
"等待页面变化超时"
)
);
return;
}
setTimeout(
check,
80
);
}
check();
}
);
}
/* =========================================================
找指定年月对应的日历面板
========================================================= */
function findMonthPanel(target) {
var panels =
doc.querySelectorAll(
".weui-desktop-picker__panel"
);
for (
var i = 0;
i < panels.length;
i++
) {
if (!isVisible(panels[i])) {
continue;
}
var labels =
panels[i].querySelectorAll(
".weui-desktop-picker__panel__label"
);
var year = null;
var month = null;
for (
var j = 0;
j < labels.length;
j++
) {
var t =
getText(labels[j]);
var ym =
t.match(/^(\d{4})年$/);
if (ym) {
year =
parseInt(
ym[1],
10
);
}
var mm =
t.match(/^(\d{1,2})月$/);
if (mm) {
month =
parseInt(
mm[1],
10
);
}
}
if (
year === target.year &&
month === target.month
) {
return panels[i];
}
}
return null;
}
/* =========================================================
找当前日期模式中的年份按钮
========================================================= */
function findYearLabel() {
var labels =
doc.querySelectorAll(
".weui-desktop-picker__panel__label"
);
for (
var i = 0;
i < labels.length;
i++
) {
if (
/^\d{4}年$/.test(
getText(labels[i])
)
&&
isVisible(labels[i])
) {
return labels[i];
}
}
return null;
}
/* =========================================================
年份选择界面找年份
========================================================= */
function findYearOption(year) {
var elements =
doc.querySelectorAll(
"a,td,button"
);
for (
var i = 0;
i < elements.length;
i++
) {
if (
getText(elements[i]) ===
String(year)
&&
isVisible(elements[i])
) {
/*
* 优先点击 a
*/
if (
elements[i].tagName
.toLowerCase() ===
"a"
) {
return elements[i];
}
var a =
elements[i]
.querySelector &&
elements[i]
.querySelector("a");
if (a) {
return a;
}
return elements[i];
}
}
return null;
}
/* =========================================================
月份选择界面找月份
========================================================= */
function findMonthOption(month) {
var monthText =
month + "月";
var elements =
doc.querySelectorAll(
"a,td,button"
);
for (
var i = 0;
i < elements.length;
i++
) {
if (
getText(elements[i]) ===
monthText
&&
isVisible(elements[i])
) {
if (
elements[i].tagName
.toLowerCase() ===
"a"
) {
return elements[i];
}
var a =
elements[i]
.querySelector &&
elements[i]
.querySelector("a");
if (a) {
return a;
}
return elements[i];
}
}
return null;
}
/* =========================================================
切换到指定年月
如果当前左右两个月已经有目标月份,则不切。
========================================================= */
function gotoMonth(target) {
var current =
findMonthPanel(target);
if (current) {
return Promise.resolve(
current
);
}
var yearLabel =
findYearLabel();
if (!yearLabel) {
return Promise.reject(
new Error(
"找不到年份按钮"
)
);
}
/*
* 点击年份标题
*/
realClick(yearLabel);
/*
* 等待目标年份出现
*/
return waitFor(
function () {
return findYearOption(
target.year
);
},
4000
)
.then(
function (
yearOption
) {
/*
* 点击年份后,
* 微信小店自动进入月份选择
*/
realClick(
yearOption
);
return waitFor(
function () {
return findMonthOption(
target.month
);
},
4000
);
}
)
.then(
function (
monthOption
) {
/*
* 点击月份后,
* 自动回日期模式
*/
realClick(
monthOption
);
return waitFor(
function () {
return findMonthPanel(
target
);
},
4000
);
}
);
}
/* =========================================================
在指定年月里面寻找日期
========================================================= */
function findDay(target) {
var panel =
findMonthPanel(target);
if (!panel) {
return null;
}
/*
* 先找 a。
* 微信小店日期一般位于 td/a。
*/
var links =
panel.querySelectorAll(
"a"
);
for (
var i = 0;
i < links.length;
i++
) {
if (
getText(links[i]) !==
String(target.day)
) {
continue;
}
if (!isVisible(links[i])) {
continue;
}
var node =
links[i];
var disabled =
false;
/*
* 检查自身和父级,
* 排除禁用日期。
*/
for (
var p = 0;
p < 4 && node;
p++
) {
var cls =
String(
node.className || ""
).toLowerCase();
if (
cls.indexOf(
"disabled"
) >= 0
||
cls.indexOf(
"disable"
) >= 0
) {
disabled =
true;
break;
}
node =
node.parentElement;
}
if (!disabled) {
return links[i];
}
}
/*
* 兜底
*/
var items =
panel.querySelectorAll(
"td,button,span,div"
);
for (
var j = 0;
j < items.length;
j++
) {
if (
getText(items[j]) ===
String(target.day)
&&
items[j].children.length === 0
&&
isVisible(items[j])
) {
return items[j];
}
}
return null;
}
/* =========================================================
选择一个日期
========================================================= */
function selectOne(target) {
return gotoMonth(
target
)
.then(
function () {
return waitFor(
function () {
return findDay(
target
);
},
4000
);
}
)
.then(
function (
dayElement
) {
realClick(
dayElement
);
/*
* 给微信小店一点时间,
* 更新范围选择状态。
*/
return new Promise(
function (
resolve
) {
setTimeout(
function () {
resolve(
true
);
},
300
);
}
);
}
);
}
/* =========================================================
正式流程:
开始日期 → 结束日期
========================================================= */
return new Promise(
function (resolve) {
selectOne(
startDate
)
.then(
function () {
/*
* 第一天下去以后,
* 微信小店进入结束日期选择状态
*/
return selectOne(
endDate
);
}
)
.then(
function () {
function pad2(n) {
return String(n)
.padStart(
2,
"0"
);
}
var startText =
startDate.year +
"-" +
pad2(
startDate.month
) +
"-" +
pad2(
startDate.day
);
var endText =
endDate.year +
"-" +
pad2(
endDate.month
) +
"-" +
pad2(
endDate.day
);
resolve(
"成功选择时间范围:" +
startText +
" ~ " +
endText
);
}
)
.catch(
function (e) {
resolve(
"失败:" +
e.message
);
}
);
}
);
}