feat(mmp): 领取人ID/模块版本检测,并修正状态条误报红

展示 receiverId 与 activityPoolId;检测 Hook 是否最新;按真实时间排序;有心跳与定时拉取时不再因久无新数据爆红。
This commit is contained in:
mars
2026-08-05 15:09:51 +08:00
parent a4aed01b29
commit 293979122d
24 changed files with 1641 additions and 186 deletions

View File

@@ -41,6 +41,14 @@
padding: 7px 12px; border-radius: 6px; cursor: pointer; font-size: 13px;
}
.btn.primary { background: #ea580c; border-color: #ea580c; color: #fff; font-weight: 600; }
.conn-bar {
background: #1f2937; color: #e5e7eb; padding: 8px 16px; font-size: 12px;
line-height: 1.45; flex-shrink: 0; border-bottom: 1px solid #111827;
}
.conn-bar.ok { background: #064e3b; color: #d1fae5; }
.conn-bar.bad { background: #7f1d1d; color: #fee2e2; }
.conn-bar.warn { background: #78350f; color: #fef3c7; }
.uid { display: block; font-size: 11px; color: var(--muted); font-weight: 500; margin-top: 2px; }
.toolbar {
background: var(--panel); border-bottom: 1px solid var(--line);
padding: 10px 16px; display: grid; gap: 8px; flex-shrink: 0;
@@ -184,6 +192,7 @@
<button class="btn" onclick="loadData()">刷新</button>
<button class="btn primary" onclick="clearAll()">清空</button>
</header>
<div class="conn-bar warn" id="connBar">模块检测中…</div>
<div class="toolbar">
<div class="row">
@@ -223,6 +232,7 @@
<input type="number" id="cfgPagePoll" min="500" max="30000" step="100" />
</label>
<label class="check"><input type="checkbox" id="cfgOpenHistory" /> 无模板时自动打开历史页</label>
<label class="check"><input type="checkbox" id="cfgBounceHistory" /> 检测不到连接时:自动进历史页再返回(重建连接)</label>
</div>
<div class="settings-actions">
<button type="button" class="chip" onclick="applyPreset('fast')">极速</button>
@@ -267,7 +277,7 @@
</div>
<div class="section">领取排行</div>
<table>
<thead><tr><th class="rank">#</th><th>昵称</th><th>金额</th><th>时间</th></tr></thead>
<thead><tr><th class="rank">#</th><th>昵称 / ID</th><th>金额</th><th>时间</th></tr></thead>
<tbody id="board"></tbody>
</table>
</div>
@@ -277,26 +287,28 @@
<script>
let packets = [], activeId = null, pagePollTimer = null;
let statusFilter = 'all', timeFilter = 'all', keyword = '';
let settings = { historyCooldownSec: 8, detailCooldownSec: 8, dedupSec: 3, detailGapMs: 80, pagePollMs: 1000, openHistoryIfNoTemplate: true };
let settings = { historyCooldownSec: 8, detailCooldownSec: 8, dedupSec: 3, detailGapMs: 80, pagePollMs: 1000, openHistoryIfNoTemplate: true, autoBounceHistoryOnDisconnect: true };
function toggleSettings(){ document.getElementById('settingsPanel').classList.toggle('open'); }
function fillSettingsForm(){
cfgHistory.value = settings.historyCooldownSec; cfgDetail.value = settings.detailCooldownSec;
cfgDedup.value = settings.dedupSec; cfgGap.value = settings.detailGapMs;
cfgPagePoll.value = settings.pagePollMs; cfgOpenHistory.checked = !!settings.openHistoryIfNoTemplate;
cfgBounceHistory.checked = settings.autoBounceHistoryOnDisconnect !== false;
cfgHint.textContent = '刷新: 历史' + settings.historyCooldownSec + 's / 详情' + settings.detailCooldownSec + 's';
}
function readSettingsForm(){
return {
historyCooldownSec: Number(cfgHistory.value), detailCooldownSec: Number(cfgDetail.value),
dedupSec: Number(cfgDedup.value), detailGapMs: Number(cfgGap.value),
pagePollMs: Number(cfgPagePoll.value), openHistoryIfNoTemplate: cfgOpenHistory.checked
pagePollMs: Number(cfgPagePoll.value), openHistoryIfNoTemplate: cfgOpenHistory.checked,
autoBounceHistoryOnDisconnect: cfgBounceHistory.checked
};
}
function applyPreset(name){
if (name==='fast') settings={historyCooldownSec:5,detailCooldownSec:5,dedupSec:2,detailGapMs:50,pagePollMs:800,openHistoryIfNoTemplate:true};
else if (name==='slow') settings={historyCooldownSec:30,detailCooldownSec:45,dedupSec:15,detailGapMs:200,pagePollMs:3000,openHistoryIfNoTemplate:true};
else settings={historyCooldownSec:8,detailCooldownSec:8,dedupSec:3,detailGapMs:80,pagePollMs:1000,openHistoryIfNoTemplate:true};
if (name==='fast') settings={historyCooldownSec:5,detailCooldownSec:5,dedupSec:2,detailGapMs:50,pagePollMs:800,openHistoryIfNoTemplate:true,autoBounceHistoryOnDisconnect:true};
else if (name==='slow') settings={historyCooldownSec:30,detailCooldownSec:45,dedupSec:15,detailGapMs:200,pagePollMs:3000,openHistoryIfNoTemplate:true,autoBounceHistoryOnDisconnect:true};
else settings={historyCooldownSec:8,detailCooldownSec:8,dedupSec:3,detailGapMs:80,pagePollMs:1000,openHistoryIfNoTemplate:true,autoBounceHistoryOnDisconnect:true};
fillSettingsForm();
}
async function loadSettings(){ settings = await (await fetch('/api/mmp/settings')).json(); fillSettingsForm(); restartPagePoll(); }
@@ -348,7 +360,7 @@
if(statusFilter==='done'&&!p.finished) return false;
if(!inTimeRange(p)) return false;
if(!keyword) return true;
const blob=[p.packetId,p.sender,p.title,p.group,p.total,p.bestNick,p.worstNick,...(p.leaderboard||[]).map(r=>r.nickname)].join(' ').toLowerCase();
const blob=[p.packetId,p.sender,p.title,p.group,p.total,p.bestNick,p.worstNick,...(p.leaderboard||[]).map(r=>[r.nickname,r.userId,r.poolId].join(' '))].join(' ').toLowerCase();
return blob.indexOf(keyword)>=0;
});
}
@@ -362,11 +374,28 @@
}
async function loadData(){
packets = await (await fetch('/api/mmp')).json();
const [pktRes, stRes] = await Promise.all([
fetch('/api/mmp'),
fetch('/api/mmp/status')
]);
packets = await pktRes.json();
updated.textContent = '更新 ' + new Date().toLocaleTimeString();
try {
const st = await stRes.json();
renderConnBar(st);
} catch (e) {
renderConnBar({ text: '模块状态不可用', ok: false });
}
renderList();
}
function renderConnBar(st){
const el = document.getElementById('connBar');
if (!el) return;
el.textContent = (st && st.text) ? st.text : '模块状态未知';
el.className = 'conn-bar ' + (st && st.ok ? 'ok' : (st && st.liveAt ? 'bad' : 'warn'));
}
function renderList(){
const view=filteredPackets();
pktCount.textContent = view.length + '/' + packets.length + ' 个';
@@ -442,9 +471,12 @@
let nick=esc(row.nickname);
if(isBest) nick += '<span class="tag-inline best">最高</span>';
if(isWorst) nick += '<span class="tag-inline worst">最低</span>';
let nickHtml=`<div>${nick}</div>`;
if(row.userId) nickHtml += `<span class="uid">用户ID ${esc(row.userId)}</span>`;
if(row.poolId) nickHtml += `<span class="uid">领取ID ${esc(row.poolId)}</span>`;
tr.innerHTML=`
<td class="rank">${row.rank}</td>
<td class="nick">${nick}</td>
<td class="nick">${nickHtml}</td>
<td class="amt">${esc(row.amountText || money(row.amount))}</td>
<td>${esc(row.claimTime || '-')}</td>`;
board.appendChild(tr);