document.addEventListener('DOMContentLoaded', () => { const numberInput = document.getElementById('numberInput'); const fileInput = document.getElementById('fileInput'); const dropzone = document.getElementById('dropzone'); const splitMode = document.getElementById('splitMode'); const splitValue = document.getElementById('splitValue'); const splitBtn = document.getElementById('splitBtn'); const autoDedupe = document.getElementById('autoDedupe'); const resultsContainer = document.getElementById('resultsContainer'); const totalCountEl = document.getElementById('totalCount'); const uniqueCountEl = document.getElementById('uniqueCount'); const dynamicCountsContainer = document.getElementById('dynamicCountsContainer'); const dynamicInputsList = document.getElementById('dynamicInputsList'); const unitText = document.getElementById('unitText'); const batchCountBadge = document.getElementById('batchCountBadge'); let allNumbers = []; // Helper: Extract numbers from text (Flexible digit extraction) const extractNumbers = (text) => { // First try to match standard 11-digit mobile numbers let matches = text.match(/1[3-9]\d{9}/g) || []; // If no mobile numbers found, try matching any sequence of 5-15 digits (IDs, account numbers, etc.) if (matches.length === 0) { matches = text.match(/\d{5,15}/g) || []; } return matches; }; // Update stats and allNumbers array const updateStats = () => { const text = numberInput.value; const extracted = extractNumbers(text); const unique = [...new Set(extracted)]; totalCountEl.textContent = extracted.length.toLocaleString(); uniqueCountEl.textContent = unique.length.toLocaleString(); allNumbers = autoDedupe.checked ? unique : extracted; }; numberInput.addEventListener('input', updateStats); autoDedupe.addEventListener('change', updateStats); // Dynamic UI for split modes const updateSplitUI = () => { const mode = splitMode.value; if (mode === 'count') { unitText.textContent = '份'; generateDynamicInputs(); } else { unitText.textContent = '条'; dynamicCountsContainer.classList.add('d-none'); } }; const generateDynamicInputs = () => { const numBatches = parseInt(splitValue.value); dynamicInputsList.innerHTML = ''; if (isNaN(numBatches) || numBatches <= 1 || splitMode.value !== 'count') { dynamicCountsContainer.classList.add('d-none'); return; } dynamicCountsContainer.classList.remove('d-none'); // Create N-1 inputs because the last one is always the remainder for (let i = 0; i < numBatches - 1; i++) { const div = document.createElement('div'); div.className = 'input-group input-group-sm mb-2'; div.innerHTML = ` 第 ${i + 1} 份数量 `; dynamicInputsList.appendChild(div); } }; splitMode.addEventListener('change', updateSplitUI); splitValue.addEventListener('input', generateDynamicInputs); // File handling const handleFile = (file) => { if (!file) return; const reader = new FileReader(); reader.onload = (event) => { const content = event.target.result; numberInput.value += (numberInput.value ? '\n' : '') + content; updateStats(); fileInput.value = ''; }; reader.readAsText(file); }; dropzone.addEventListener('click', () => fileInput.click()); fileInput.addEventListener('change', (e) => handleFile(e.target.files[0])); // Drag and Drop ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropzone.addEventListener(eventName, (e) => { e.preventDefault(); e.stopPropagation(); }, false); }); ['dragenter', 'dragover'].forEach(eventName => { dropzone.addEventListener(eventName, () => dropzone.classList.add('drag-active'), false); }); ['dragleave', 'drop'].forEach(eventName => { dropzone.addEventListener(eventName, () => dropzone.classList.remove('drag-active'), false); }); dropzone.addEventListener('drop', (e) => { const dt = e.dataTransfer; const file = dt.files[0]; handleFile(file); }, false); // Splitting logic splitBtn.addEventListener('click', () => { updateStats(); if (allNumbers.length === 0) { alert('请先导入或粘贴号码!'); return; } const mode = splitMode.value; const val = parseInt(splitValue.value); if (isNaN(val) || val <= 0) { alert('请输入有效的分批数值!'); return; } let batches = []; let tempNumbers = [...allNumbers]; if (mode === 'count') { const sizeInputs = document.querySelectorAll('.batch-size-input'); let hasAnySpecificSize = false; let specificSizes = []; sizeInputs.forEach(input => { const s = parseInt(input.value); if (!isNaN(s) && s > 0) { hasAnySpecificSize = true; specificSizes.push(s); } else { specificSizes.push(null); } }); if (hasAnySpecificSize) { // If some sizes are specified, follow them. for (let i = 0; i < val - 1; i++) { const size = specificSizes[i]; if (size !== null && size > 0) { batches.push(tempNumbers.splice(0, size)); } else { // For empty inputs when others are filled, we calculate an average of the remaining const remainingSlots = val - batches.length; const avg = Math.ceil(tempNumbers.length / remainingSlots); batches.push(tempNumbers.splice(0, avg)); } } // Last batch always gets the remainder batches.push(tempNumbers); } else { // Default even split if no inputs are filled const batchSize = Math.ceil(tempNumbers.length / val); for (let i = 0; i < val; i++) { if (i === val - 1) { batches.push(tempNumbers); // Last one takes all remainder } else { batches.push(tempNumbers.splice(0, batchSize)); } } } } else { // Split by fixed size per batch (mode === 'size') while (tempNumbers.length > 0) { batches.push(tempNumbers.splice(0, val)); } } renderResults(batches); }); const renderResults = (batches) => { resultsContainer.innerHTML = ''; // Filter out empty batches if they occurred due to split logic const finalBatches = batches.filter(b => b.length > 0); batchCountBadge.textContent = `${finalBatches.length} 个批次`; batchCountBadge.classList.remove('d-none'); finalBatches.forEach((batch, index) => { const card = document.createElement('div'); card.className = 'card batch-card animate-fadeIn'; const previewText = batch.slice(0, 15).join(', '); const hasMore = batch.length > 15; card.innerHTML = `