刷新功能
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
|||||||
|
# Process
|
||||||
|
|
||||||
|
## 2026-04-19
|
||||||
|
|
||||||
|
### 今日战利品
|
||||||
|
- 修复了 163 邮箱顶部“刷新”按钮的识别问题。
|
||||||
|
- 根因不是插件不知道顶部按钮在哪里,而是它用 DOM 文本做识别时,代码只认“刷新”,但 163 实际渲染出来可能是“刷 新”。
|
||||||
|
- 给 `163` 邮箱补上了回归测试,确保以后再次遇到这种带空格的按钮文案时,脚本仍然会优先点击顶部工具栏刷新,而不是误退回到“收件箱”。
|
||||||
|
- 额外补了更直白的刷新分支日志,后续排查时可以直接看出本次命中的是“顶部刷新”“左侧收信”还是“收件箱兜底”。
|
||||||
|
|
||||||
|
### 知识地图
|
||||||
|
- 这一块属于“自动化脚本稳定性”里的 DOM 识别层。
|
||||||
|
- 老板可以把它理解成:脚本不是靠“看见页面上方那个位置”去点按钮,而是靠“读页面结构里的标签文字和属性”去认目标。
|
||||||
|
- 所以这里学到的核心不是某一个按钮怎么点,而是“页面自动化里,视觉上像同一个按钮,DOM 文本却可能有空格、换行、图标包裹等变体”,识别逻辑必须做抗噪处理。
|
||||||
|
- 这部分在大神知识体系里,位置属于:前端页面结构理解 -> 浏览器自动化 -> 选择器与文本匹配鲁棒性。
|
||||||
|
|
||||||
|
### 下一步导航
|
||||||
|
- 可以继续把 163 的删除兜底逻辑也做一次同类检查,因为顶部“删 除”从 DOM 结构看也可能存在同样的空格拆字风险。
|
||||||
|
- 如果老板接下来还想继续提稳收信成功率,可以再查一层:163 顶部“刷新”点击之后,页面到底有没有真正触发新的列表请求,以及请求完成信号该监听什么。
|
||||||
|
- 如果要把这一块做成长期稳定方案,下一步适合补“刷新动作观测点”日志,比如记录刷新前后列表首封邮件 ID、请求时间、是否出现 loading。
|
||||||
+13
-2
@@ -461,9 +461,18 @@ if (!isTopFrame) {
|
|||||||
async function refreshInbox() {
|
async function refreshInbox() {
|
||||||
const toolbarBtns = document.querySelectorAll('.nui-btn .nui-btn-text');
|
const toolbarBtns = document.querySelectorAll('.nui-btn .nui-btn-text');
|
||||||
for (const btn of toolbarBtns) {
|
for (const btn of toolbarBtns) {
|
||||||
if (normalizeText(btn.textContent || '') === '刷新') {
|
// 163 会把“刷 新”拆成带空格的文字,所以这里要先压平空白,
|
||||||
|
// 不然脚本明明看到了顶部刷新按钮,也会误以为没找到。
|
||||||
|
const compactLabel = String([
|
||||||
|
btn.textContent || '',
|
||||||
|
btn.getAttribute?.('aria-label') || '',
|
||||||
|
btn.getAttribute?.('title') || '',
|
||||||
|
].join('')).replace(/\s+/g, '');
|
||||||
|
|
||||||
|
if (compactLabel === '刷新') {
|
||||||
simulateClick(btn.closest('.nui-btn') || btn);
|
simulateClick(btn.closest('.nui-btn') || btn);
|
||||||
console.log(MAIL163_PREFIX, 'Clicked refresh button');
|
console.log(MAIL163_PREFIX, 'Clicked top toolbar refresh button');
|
||||||
|
log('[163 邮箱] 已命中顶部工具栏“刷新”按钮');
|
||||||
await sleep(800);
|
await sleep(800);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -474,6 +483,7 @@ if (!isTopFrame) {
|
|||||||
if (normalizeText(btn.textContent || '').includes('收信')) {
|
if (normalizeText(btn.textContent || '').includes('收信')) {
|
||||||
simulateClick(btn);
|
simulateClick(btn);
|
||||||
console.log(MAIL163_PREFIX, 'Clicked receive button');
|
console.log(MAIL163_PREFIX, 'Clicked receive button');
|
||||||
|
log('[163 邮箱] 未找到顶部刷新,改用左侧“收信”按钮');
|
||||||
await sleep(800);
|
await sleep(800);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -482,6 +492,7 @@ if (!isTopFrame) {
|
|||||||
const inboxLink = findInboxLink();
|
const inboxLink = findInboxLink();
|
||||||
if (inboxLink) {
|
if (inboxLink) {
|
||||||
simulateClick(inboxLink);
|
simulateClick(inboxLink);
|
||||||
|
log('[163 邮箱] 未找到顶部刷新或左侧“收信”,回退到点击“收件箱”');
|
||||||
await sleep(800);
|
await sleep(800);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,3 +146,64 @@ return {
|
|||||||
assert.equal(result.code, '480382');
|
assert.equal(result.code, '480382');
|
||||||
assert.deepEqual(api.getOpenedMailIds(), ['mail-1']);
|
assert.deepEqual(api.getOpenedMailIds(), ['mail-1']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('refreshInbox prefers the top toolbar refresh button even when 163 renders the label as 刷 新', async () => {
|
||||||
|
const bundle = [
|
||||||
|
extractFunction('normalizeText'),
|
||||||
|
extractFunction('refreshInbox'),
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const api = new Function(`
|
||||||
|
const MAIL163_PREFIX = '[MultiPage:mail-163]';
|
||||||
|
const clickOrder = [];
|
||||||
|
|
||||||
|
const refreshButton = {
|
||||||
|
tagName: 'DIV',
|
||||||
|
textContent: '刷 新',
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshLabel = {
|
||||||
|
textContent: '刷 新',
|
||||||
|
closest(selector) {
|
||||||
|
return selector === '.nui-btn' ? refreshButton : null;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const inboxLink = {
|
||||||
|
tagName: 'SPAN',
|
||||||
|
textContent: '收件箱',
|
||||||
|
};
|
||||||
|
|
||||||
|
const document = {
|
||||||
|
querySelectorAll(selector) {
|
||||||
|
if (selector === '.nui-btn .nui-btn-text') return [refreshLabel];
|
||||||
|
if (selector === '.ra0') return [];
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function simulateClick(node) {
|
||||||
|
clickOrder.push(node.textContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findInboxLink() {
|
||||||
|
return inboxLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sleep() {}
|
||||||
|
function log() {}
|
||||||
|
|
||||||
|
${bundle}
|
||||||
|
|
||||||
|
return {
|
||||||
|
refreshInbox,
|
||||||
|
getClickOrder() {
|
||||||
|
return clickOrder.slice();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
`)();
|
||||||
|
|
||||||
|
await api.refreshInbox();
|
||||||
|
|
||||||
|
assert.deepEqual(api.getClickOrder(), ['刷 新']);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user