189 lines
7.6 KiB
JavaScript
189 lines
7.6 KiB
JavaScript
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);
|
||
}
|
||
});
|
||
}
|
||
}); |