38730-vm/assets/js/main.js
Flatlogic Bot f33d58af44 报表2
2026-02-24 14:51:35 +00:00

189 lines
7.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
});
}
});