document.addEventListener('DOMContentLoaded', () => { // Elements const clockEl = document.getElementById('real-time-clock'); const outputEl = document.getElementById('reportOutput'); const btnCopy = document.getElementById('btnCopy'); const btnBatchParse = document.getElementById('btnBatchParse'); const btnClearBatch = document.getElementById('btnClearBatch'); const batchInput = document.getElementById('batchInput'); const toastMessage = document.getElementById('toastMessage'); const toastEl = document.getElementById('liveToast'); const toast = new bootstrap.Toast(toastEl); // Initialize Real-time Clock const updateClock = () => { const now = new Date(); const options = { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }; const formatted = now.toLocaleString('zh-CN', options).replace(/\//g, '-'); if (clockEl) clockEl.textContent = formatted; }; setInterval(updateClock, 1000); updateClock(); // Show Toast Helper const showToast = (msg, isError = false) => { toastMessage.textContent = msg; const iconContainer = toastMessage.previousElementSibling; if (iconContainer) { const icon = isError ? 'fa-circle-xmark text-danger' : 'fa-circle-check text-success'; iconContainer.className = `fa-solid ${icon} me-2`; } toast.show(); }; // Helper to format the final report const formatReport = (stats) => { return `总WS数量: ${stats.totalWS} WS今日封号: ${stats.wsBanned} 总永封WS: ${stats.wsPermBanned} 总XHS数量: ${stats.totalXHS} 总SMS数量: ${stats.totalSMS} XHS: ${stats.xhs} 回复: ${stats.xhs_reply} WS: ${stats.ws} 回复: ${stats.ws_reply} SMS: ${stats.sms} 回复: ${stats.sms_reply} 总招呼量: ${stats.totalGreeting} 总回复: ${stats.totalReply} 再聊: ${stats.rechat} 引流: ${stats.traffic} 语音: ${stats.voice}`; }; // Improved Extraction Logic const parseBatchData = () => { const text = batchInput.value.trim(); if (!text) { showToast('请先粘贴报表内容!', true); return; } const totals = { totalWS: 0, wsBanned: 0, wsPermBanned: 0, totalXHS: 0, totalSMS: 0, xhs: 0, xhs_reply: 0, ws: 0, ws_reply: 0, sms: 0, sms_reply: 0, totalGreeting: 0, totalReply: 0, rechat: 0, traffic: 0, voice: 0 }; const extractVal = (patterns, content) => { if (!Array.isArray(patterns)) patterns = [patterns]; for (const p of patterns) { const match = content.match(p); if (match) { const cleanVal = match[1].replace(/,/g, ''); return parseInt(cleanVal, 10) || 0; } } return 0; }; const extractPlatformStats = (platformLabel, content) => { // Priority: specifically look for "Platform招呼量" // Second: look for "Platform" if not part of "总...数量" or "...封号" const regex = new RegExp('(总)?' + platformLabel + '(数量|今日封号|招呼量)?[^0-9]*([0-9,]+)', 'gi'); let bestMatch = null; let m; while ((m = regex.exec(content)) !== null) { const isTotal = !!m[1]; const suffix = m[2] || ''; const isMeta = suffix === '数量' || suffix === '今日封号'; // We want the one that is NOT "总WS数量" and NOT "WS今日封号" // Ideally it's "WS招呼量" or just "WS" if (!isTotal && !isMeta) { bestMatch = m; if (suffix === '招呼量') break; // Best possible match found } } if (!bestMatch) return { count: 0, reply: 0 }; const count = parseInt(bestMatch[3].replace(/,/g, ''), 10) || 0; const afterMatch = content.substring(bestMatch.index + bestMatch[0].length); // Look for "回复" after this count, but stop at next major label const stopLabel = /(XHS|WS|SMS|总招呼量|总回复|再聊|引流|语音|总WS数量|WS今日封号|总永封WS|总XHS数量|总SMS数量)/i.exec(afterMatch); const searchRange = afterMatch.substring(0, stopLabel ? stopLabel.index : 100); const rMatch = searchRange.match(/回复[^0-9]*([0-9,]+)/); return { count: count, reply: rMatch ? (parseInt(rMatch[1].replace(/,/g, ''), 10) || 0) : 0 }; }; // Split by "总WS数量" const reportChunks = text.split(/(?=总WS数量)/); reportChunks.forEach((chunk) => { if (!chunk.trim()) return; // Global Metrics totals.totalWS += extractVal([/总WS数量[::\s]*([0-9,]+)/, /WS数量[::\s]*([0-9,]+)/], chunk); totals.wsBanned += extractVal([/WS今日封号[::\s]*([0-9,]+)/, /今日封号[::\s]*([0-9,]+)/], chunk); totals.wsPermBanned += extractVal([/总永封WS[::\s]*([0-9,]+)/, /永封WS[::\s]*([0-9,]+)/], chunk); totals.totalXHS += extractVal([/总XHS数量[::\s]*([0-9,]+)/, /XHS数量[::\s]*([0-9,]+)/], chunk); totals.totalSMS += extractVal([/总SMS数量[::\s]*([0-9,]+)/, /SMS数量[::\s]*([0-9,]+)/], chunk); // Platform Specifics const xData = extractPlatformStats('XHS', chunk); totals.xhs += xData.count; totals.xhs_reply += xData.reply; const wData = extractPlatformStats('WS', chunk); totals.ws += wData.count; totals.ws_reply += wData.reply; const sData = extractPlatformStats('SMS', chunk); totals.sms += sData.count; totals.sms_reply += sData.reply; // Bottom Metrics totals.rechat += extractVal(/再聊[::\s]*([0-9,]+)/, chunk); totals.traffic += extractVal(/引流[::\s]*([0-9,]+)/, chunk); totals.voice += extractVal(/语音[::\s]*([0-9,]+)/, chunk); }); // Calculate Totals by Summing individual platform stats (More accurate than relying on member input) totals.totalGreeting = totals.xhs + totals.ws + totals.sms; totals.totalReply = totals.xhs_reply + totals.ws_reply + totals.sms_reply; outputEl.textContent = formatReport(totals); btnCopy.disabled = false; showToast('报表汇总成功!'); }; // Events if (btnBatchParse) { btnBatchParse.addEventListener('click', () => { showToast('解析中...'); setTimeout(parseBatchData, 400); }); } if (btnClearBatch) { btnClearBatch.addEventListener('click', () => { batchInput.value = ''; outputEl.textContent = '数据结果将在此处生成...'; btnCopy.disabled = true; showToast('输入区域已清空'); }); } if (btnCopy) { btnCopy.addEventListener('click', () => { const text = outputEl.textContent; if (text && !text.includes('在此处生成')) { navigator.clipboard.writeText(text).then(() => { showToast('已复制成功!'); }).catch(err => { showToast('复制失败', true); }); } else { showToast('无可复制内容', true); } }); } });