Flatlogic Bot 5bfcb4a7af 拆分
2026-02-07 07:59:20 +00:00

254 lines
10 KiB
JavaScript

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 = `
<span class="input-group-text bg-white border-2 fw-bold" style="width: 100px;">第 ${i + 1} 份数量</span>
<input type="number" class="form-control border-2 batch-size-input" placeholder="留空则自动分配" min="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 = `
<div class="card-body p-4">
<div class="batch-header">
<div class="batch-title">
<span class="badge bg-primary" style="background: var(--primary-gradient) !important;">第 ${index + 1} 份</span>
<span class="badge-count">${batch.length.toLocaleString()} 条</span>
</div>
<button class="btn btn-primary btn-sm px-3 download-btn">
<i data-lucide="download" style="width: 14px;"></i> 下载
</button>
</div>
<div class="mb-3">
<input type="text" class="form-control form-control-sm bg-light border-0 remark-input" placeholder="备注名称(即文件名)">
</div>
<div class="small text-muted font-monospace bg-light p-2 rounded" style="font-size: 0.75rem; word-break: break-all;">
预览: ${batch.length > 0 ? previewText + (hasMore ? ' ...' : '') : '无号码'}
</div>
</div>
`;
resultsContainer.appendChild(card);
// Download handler
card.querySelector('.download-btn').addEventListener('click', () => {
const remark = card.querySelector('.remark-input').value.trim() ||
`batch_${index + 1}_${batch.length}`;
const blob = new Blob([batch.join('\n')], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${remark}.txt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
});
if (window.lucide) {
lucide.createIcons();
}
resultsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
};
// Initial UI Setup
updateSplitUI();
});