diff --git a/assets/css/custom.css b/assets/css/custom.css
index 65a1626..9e119eb 100644
--- a/assets/css/custom.css
+++ b/assets/css/custom.css
@@ -1,346 +1,295 @@
:root {
- --color-bg: #ffffff;
- --color-text: #1a1a1a;
- --color-primary: #2563EB; /* Vibrant Blue */
- --color-secondary: #000000;
- --color-accent: #A3E635; /* Lime Green */
- --color-surface: #f8f9fa;
- --font-heading: 'Space Grotesk', sans-serif;
- --font-body: 'Inter', sans-serif;
- --border-width: 2px;
- --shadow-hard: 5px 5px 0px #000;
- --shadow-hover: 8px 8px 0px #000;
- --radius-pill: 50rem;
- --radius-card: 1rem;
+ --primary-gradient: linear-gradient(135deg, #6366f1 0%, #a855f7 100%);
+ --accent-gradient: linear-gradient(135deg, #3b82f6 0%, #2dd4bf 100%);
+ --surface: #ffffff;
+ --background: #f8fafc;
+ --border: #e2e8f0;
+ --text-main: #0f172a;
+ --text-muted: #64748b;
+ --radius-lg: 16px;
+ --radius-md: 12px;
+ --radius-sm: 8px;
+ --shadow-subtle: 0 4px 6px -1px rgb(0 0 0 / 0.05), 0 2px 4px -2px rgb(0 0 0 / 0.05);
+ --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
+ --shadow-lg: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
}
body {
- font-family: var(--font-body);
- background-color: var(--color-bg);
- color: var(--color-text);
- overflow-x: hidden;
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
+ background-color: var(--background);
+ color: var(--text-main);
+ line-height: 1.6;
+ background-image:
+ radial-gradient(at 0% 0%, hsla(253,16%,7%,1) 0, transparent 50%),
+ radial-gradient(at 50% 0%, hsla(225,39%,30%,1) 0, transparent 50%),
+ radial-gradient(at 100% 0%, hsla(339,49%,30%,1) 0, transparent 50%);
+ background-attachment: fixed;
+ background-size: cover;
+ min-height: 100vh;
}
-h1, h2, h3, h4, h5, h6, .navbar-brand {
- font-family: var(--font-heading);
- letter-spacing: -0.03em;
+/* Glassmorphism background */
+body::before {
+ content: "";
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(248, 250, 252, 0.85);
+ backdrop-filter: blur(100px);
+ z-index: -1;
}
-/* Utilities */
-.text-primary { color: var(--color-primary) !important; }
-.bg-black { background-color: #000 !important; }
-.text-white { color: #fff !important; }
-.shadow-hard { box-shadow: var(--shadow-hard); }
-.border-2-black { border: var(--border-width) solid #000; }
-.py-section { padding-top: 5rem; padding-bottom: 5rem; }
-
-/* Navbar */
.navbar {
- background: rgba(255, 255, 255, 0.9);
- backdrop-filter: blur(10px);
- border-bottom: var(--border-width) solid transparent;
- transition: all 0.3s;
- padding-top: 1rem;
- padding-bottom: 1rem;
+ background: rgba(255, 255, 255, 0.7);
+ backdrop-filter: blur(10px);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.3);
+ padding: 1.25rem 0;
+ position: sticky;
+ top: 0;
+ z-index: 1000;
}
-.navbar.scrolled {
- border-bottom-color: #000;
- padding-top: 0.5rem;
- padding-bottom: 0.5rem;
+.navbar-brand {
+ font-weight: 800;
+ font-size: 1.5rem;
+ background: var(--primary-gradient);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
}
-.brand-text {
- font-size: 1.5rem;
- font-weight: 800;
+.card {
+ background: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.4);
+ border-radius: var(--radius-lg);
+ box-shadow: var(--shadow-subtle);
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
-.nav-link {
- font-weight: 500;
- color: var(--color-text);
- margin-left: 1rem;
- position: relative;
+.card:hover {
+ transform: translateY(-2px);
+ box-shadow: var(--shadow-md);
}
-.nav-link:hover, .nav-link.active {
- color: var(--color-primary);
-}
-
-/* Buttons */
.btn {
- font-weight: 700;
- font-family: var(--font-heading);
- padding: 0.8rem 2rem;
- border-radius: var(--radius-pill);
- border: var(--border-width) solid #000;
- transition: all 0.2s cubic-bezier(0.25, 1, 0.5, 1);
- box-shadow: var(--shadow-hard);
-}
-
-.btn:hover {
- transform: translate(-2px, -2px);
- box-shadow: var(--shadow-hover);
-}
-
-.btn:active {
- transform: translate(2px, 2px);
- box-shadow: 0 0 0 #000;
+ font-weight: 600;
+ border-radius: var(--radius-md);
+ padding: 0.75rem 1.5rem;
+ transition: all 0.2s ease;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.625rem;
+ border: none;
}
.btn-primary {
- background-color: var(--color-primary);
- border-color: #000;
- color: #fff;
+ background: var(--primary-gradient);
+ color: white;
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
}
.btn-primary:hover {
- background-color: #1d4ed8;
- border-color: #000;
- color: #fff;
+ transform: scale(1.02);
+ box-shadow: 0 6px 20px rgba(99, 102, 241, 0.4);
+ background: var(--primary-gradient);
+ filter: brightness(1.1);
}
-.btn-outline-dark {
- background-color: #fff;
- color: #000;
+.btn-light {
+ background: rgba(241, 245, 249, 0.8);
+ color: var(--text-main);
}
-.btn-cta {
- background-color: var(--color-accent);
- color: #000;
-}
-
-.btn-cta:hover {
- background-color: #8cc629;
- color: #000;
-}
-
-/* Hero Section */
-.hero-section {
- min-height: 100vh;
- padding-top: 80px;
-}
-
-.background-blob {
- position: absolute;
- border-radius: 50%;
- filter: blur(80px);
- opacity: 0.6;
- z-index: 1;
-}
-
-.blob-1 {
- top: -10%;
- right: -10%;
- width: 600px;
- height: 600px;
- background: radial-gradient(circle, var(--color-accent), transparent);
-}
-
-.blob-2 {
- bottom: 10%;
- left: -10%;
- width: 500px;
- height: 500px;
- background: radial-gradient(circle, var(--color-primary), transparent);
-}
-
-.highlight-text {
- background: linear-gradient(120deg, transparent 0%, transparent 40%, var(--color-accent) 40%, var(--color-accent) 100%);
- background-repeat: no-repeat;
- background-size: 100% 40%;
- background-position: 0 88%;
- padding: 0 5px;
-}
-
-.dot { color: var(--color-primary); }
-
-.badge-pill {
- display: inline-block;
- padding: 0.5rem 1rem;
- border: 2px solid #000;
- border-radius: 50px;
- font-weight: 700;
- background: #fff;
- box-shadow: 4px 4px 0 #000;
- font-family: var(--font-heading);
- font-size: 0.9rem;
-}
-
-/* Marquee */
-.marquee-container {
- overflow: hidden;
- white-space: nowrap;
- border-top: 2px solid #000;
- border-bottom: 2px solid #000;
-}
-
-.rotate-divider {
- transform: rotate(-2deg) scale(1.05);
- z-index: 10;
- position: relative;
- margin-top: -50px;
- margin-bottom: 30px;
-}
-
-.marquee-content {
- display: inline-block;
- animation: marquee 20s linear infinite;
- font-family: var(--font-heading);
- font-weight: 700;
- font-size: 1.5rem;
- letter-spacing: 2px;
-}
-
-@keyframes marquee {
- 0% { transform: translateX(0); }
- 100% { transform: translateX(-50%); }
-}
-
-/* Portfolio Cards */
-.project-card {
- border: 2px solid #000;
- border-radius: var(--radius-card);
- overflow: hidden;
- background: #fff;
- transition: transform 0.3s ease;
- box-shadow: var(--shadow-hard);
- height: 100%;
- display: flex;
- flex-direction: column;
-}
-
-.project-card:hover {
- transform: translateY(-10px);
- box-shadow: 8px 8px 0 #000;
-}
-
-.card-img-holder {
- height: 250px;
- display: flex;
- align-items: center;
- justify-content: center;
- border-bottom: 2px solid #000;
- position: relative;
- font-size: 4rem;
-}
-
-.placeholder-art {
- transition: transform 0.3s ease;
-}
-
-.project-card:hover .placeholder-art {
- transform: scale(1.2) rotate(10deg);
-}
-
-.bg-soft-blue { background-color: #e0f2fe; }
-.bg-soft-green { background-color: #dcfce7; }
-.bg-soft-purple { background-color: #f3e8ff; }
-.bg-soft-yellow { background-color: #fef9c3; }
-
-.category-tag {
- position: absolute;
- top: 15px;
- right: 15px;
- background: #000;
- color: #fff;
- padding: 5px 12px;
- border-radius: 20px;
- font-size: 0.75rem;
- font-weight: 700;
-}
-
-.card-body { padding: 1.5rem; }
-
-.link-arrow {
- text-decoration: none;
- color: #000;
- font-weight: 700;
- display: inline-flex;
- align-items: center;
- margin-top: auto;
-}
-
-.link-arrow i { transition: transform 0.2s; margin-left: 5px; }
-.link-arrow:hover i { transform: translateX(5px); }
-
-/* About */
-.about-image-stack {
- position: relative;
- height: 400px;
- width: 100%;
-}
-
-.stack-card {
- position: absolute;
- width: 80%;
- height: 100%;
- border-radius: var(--radius-card);
- border: 2px solid #000;
- box-shadow: var(--shadow-hard);
- left: 10%;
- transform: rotate(-3deg);
- background-size: cover;
-}
-
-/* Forms */
-.form-control {
- border: 2px solid #000;
- border-radius: 0.5rem;
- padding: 1rem;
- font-weight: 500;
- background: #f8f9fa;
+.form-control, .form-select {
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ padding: 0.75rem 1rem;
+ background: rgba(255, 255, 255, 0.8);
+ transition: all 0.2s ease;
}
.form-control:focus {
- box-shadow: 4px 4px 0 var(--color-primary);
- border-color: #000;
- background: #fff;
+ background: #fff;
+ border-color: #6366f1;
+ box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
}
-/* Animations */
-.animate-up {
- opacity: 0;
- transform: translateY(30px);
- animation: fadeUp 0.8s ease forwards;
+.dropzone {
+ border: 2px dashed #cbd5e1;
+ border-radius: var(--radius-md);
+ padding: 3.5rem 2rem;
+ background: rgba(248, 250, 252, 0.5);
+ cursor: pointer;
+ transition: all 0.3s ease;
}
-.delay-100 { animation-delay: 0.1s; }
-.delay-200 { animation-delay: 0.2s; }
-
-@keyframes fadeUp {
- to {
- opacity: 1;
- transform: translateY(0);
- }
+.dropzone:hover {
+ border-color: #6366f1;
+ background: rgba(99, 102, 241, 0.05);
+ transform: translateY(-2px);
}
-/* Social */
-.social-links a {
- transition: transform 0.2s;
- display: inline-block;
-}
-.social-links a:hover {
- transform: scale(1.2) rotate(10deg);
- color: var(--color-accent) !important;
+.dropzone i {
+ background: var(--primary-gradient);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ font-size: 3rem;
+ margin-bottom: 1.5rem;
}
-/* Responsive */
-@media (max-width: 991px) {
- .rotate-divider {
- transform: rotate(0);
- margin-top: 0;
- margin-bottom: 2rem;
- }
-
- .hero-section {
- padding-top: 120px;
- text-align: center;
- min-height: auto;
- padding-bottom: 100px;
- }
-
- .display-1 { font-size: 3.5rem; }
-
- .blob-1 { width: 300px; height: 300px; right: -20%; }
- .blob-2 { width: 300px; height: 300px; left: -20%; }
+.stats-row {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1.25rem;
+ margin-bottom: 2rem;
+}
+
+.stat-item {
+ background: white;
+ padding: 1.25rem;
+ border-radius: var(--radius-md);
+ border: 1px solid var(--border);
+ position: relative;
+ overflow: hidden;
+}
+
+.stat-item::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 4px;
+ height: 100%;
+ background: var(--primary-gradient);
+}
+
+.stat-label {
+ font-size: 0.75rem;
+ font-weight: 700;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin-bottom: 0.5rem;
+}
+
+.stat-value {
+ font-size: 1.875rem;
+ font-weight: 800;
+ color: var(--text-main);
+ line-height: 1;
+}
+
+.batch-card {
+ border: none;
+ background: white;
+ margin-bottom: 1.25rem;
+ position: relative;
+ overflow: hidden;
+}
+
+.batch-card::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 6px;
+ height: 100%;
+ background: var(--accent-gradient);
+}
+
+.badge-count {
+ background: rgba(59, 130, 246, 0.1);
+ color: #2563eb;
+ font-weight: 700;
+ padding: 0.35rem 0.75rem;
+ border-radius: 9999px;
+}
+
+.batch-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1rem;
+}
+
+.batch-title {
+ font-weight: 700;
+ color: var(--text-main);
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.dynamic-batch-input {
+ animation: fadeIn 0.3s ease-out;
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(10px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+.sticky-panel {
+ position: sticky;
+ top: 6rem;
+}
+
+footer {
+ background: transparent;
+ margin-top: 4rem;
+}
+
+.hero-section {
+ text-align: center;
+ padding: 4rem 0 2rem;
+}
+
+.hero-section h1 {
+ font-weight: 900;
+ font-size: 3rem;
+ letter-spacing: -0.05em;
+ margin-bottom: 1rem;
+ background: var(--primary-gradient);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.hero-section p {
+ color: var(--text-muted);
+ font-size: 1.25rem;
+ max-width: 600px;
+ margin: 0 auto;
+}
+
+/* Custom Scrollbar */
+::-webkit-scrollbar {
+ width: 10px;
+}
+::-webkit-scrollbar-track {
+ background: #f1f5f9;
+}
+::-webkit-scrollbar-thumb {
+ background: #cbd5e1;
+ border-radius: 5px;
+}
+::-webkit-scrollbar-thumb:hover {
+ background: #94a3b8;
+}
+
+/* Glass Detail Summary */
+details summary {
+ padding: 0.5rem 0;
+ color: var(--accent);
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+details summary:hover {
+ filter: brightness(0.8);
}
diff --git a/assets/js/main.js b/assets/js/main.js
index fdf2cfd..5fcc34f 100644
--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -1,73 +1,247 @@
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');
- // Smooth scrolling for navigation links
- document.querySelectorAll('a[href^="#"]').forEach(anchor => {
- anchor.addEventListener('click', function (e) {
- e.preventDefault();
- const targetId = this.getAttribute('href');
- if (targetId === '#') return;
-
- const targetElement = document.querySelector(targetId);
- if (targetElement) {
- // Close mobile menu if open
- const navbarToggler = document.querySelector('.navbar-toggler');
- const navbarCollapse = document.querySelector('.navbar-collapse');
- if (navbarCollapse.classList.contains('show')) {
- navbarToggler.click();
- }
+ let allNumbers = [];
- // Scroll with offset
- const offset = 80;
- const elementPosition = targetElement.getBoundingClientRect().top;
- const offsetPosition = elementPosition + window.pageYOffset - offset;
+ // Helper: Extract numbers from text (Mobile number regex)
+ const extractNumbers = (text) => {
+ const matches = text.match(/1[3-9]\d{9}/g) || [];
+ return matches;
+ };
- window.scrollTo({
- top: offsetPosition,
- behavior: "smooth"
- });
- }
- });
- });
+ // 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;
+ };
- // Navbar scroll effect
- const navbar = document.querySelector('.navbar');
- window.addEventListener('scroll', () => {
- if (window.scrollY > 50) {
- navbar.classList.add('scrolled', 'shadow-sm', 'bg-white');
- navbar.classList.remove('bg-transparent');
+ numberInput.addEventListener('input', updateStats);
+ autoDedupe.addEventListener('change', updateStats);
+
+ // Dynamic UI for split modes
+ const updateSplitUI = () => {
+ const mode = splitMode.value;
+ if (mode === 'count') {
+ unitText.textContent = '份';
+ dynamicCountsContainer.classList.remove('d-none');
+ generateDynamicInputs();
} else {
- navbar.classList.remove('scrolled', 'shadow-sm', 'bg-white');
- navbar.classList.add('bg-transparent');
+ unitText.textContent = '条';
+ dynamicCountsContainer.classList.add('d-none');
+ }
+ };
+
+ const generateDynamicInputs = () => {
+ const numBatches = parseInt(splitValue.value);
+ dynamicInputsList.innerHTML = '';
+
+ if (isNaN(numBatches) || numBatches <= 1) {
+ 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 dynamic-batch-input';
+ div.innerHTML = `
+ 第 ${i + 1} 份数量
+
+ `;
+ dynamicInputsList.appendChild(div);
+ }
+ };
+
+ splitMode.addEventListener('change', updateSplitUI);
+ splitValue.addEventListener('input', generateDynamicInputs);
+
+ // File handling
+ dropzone.addEventListener('click', () => fileInput.click());
+ fileInput.addEventListener('change', async (e) => {
+ const file = e.target.files[0];
+ if (!file) return;
+
+ if (file.name.endsWith('.txt') || file.name.endsWith('.csv')) {
+ const reader = new FileReader();
+ reader.onload = (event) => {
+ const content = event.target.result;
+ numberInput.value += (numberInput.value ? '\n' : '') + content;
+ updateStats();
+ fileInput.value = '';
+ };
+ reader.readAsText(file);
+ } else {
+ // Placeholder for Excel/Word/PDF if needed, but for now just inform
+ alert('当前支持直接读取 .txt 和 .csv 文件。Word/Excel/PDF 请直接复制其中的号码并粘贴到输入框。');
}
});
- // Intersection Observer for fade-up animations
- const observerOptions = {
- threshold: 0.1,
- rootMargin: "0px 0px -50px 0px"
- };
+ // Splitting logic
+ splitBtn.addEventListener('click', () => {
+ updateStats(); // Ensure we have latest data
+ if (allNumbers.length === 0) {
+ alert('请先导入或粘贴号码!');
+ return;
+ }
- const observer = new IntersectionObserver((entries) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- entry.target.classList.add('animate-up');
- entry.target.style.opacity = "1";
- observer.unobserve(entry.target); // Only animate once
+ 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 hasSpecificSizes = false;
+ let specificSizes = [];
+
+ sizeInputs.forEach(input => {
+ const s = parseInt(input.value);
+ if (!isNaN(s) && s > 0) {
+ hasSpecificSizes = true;
+ specificSizes.push(s);
+ } else {
+ specificSizes.push(null);
+ }
+ });
+
+ if (hasSpecificSizes) {
+ // Specific size splitting
+ for (let i = 0; i < val - 1; i++) {
+ const size = specificSizes[i];
+ if (size !== null && size > 0) {
+ batches.push(tempNumbers.splice(0, size));
+ } else {
+ // If one is empty but others are not, we need a strategy.
+ // Let's treat empty as 0 for now or average the rest?
+ // User said "if no number, system default split".
+ // But if some have numbers and others don't, it's ambiguous.
+ // We'll calculate the remaining needed batches.
+ const remainingCount = val - batches.length;
+ const avg = Math.ceil(tempNumbers.length / remainingCount);
+ batches.push(tempNumbers.splice(0, avg));
+ }
+ }
+ // Add the remainder to the last batch
+ if (tempNumbers.length > 0) {
+ batches.push(tempNumbers);
+ } else if (batches.length < val) {
+ // If no numbers left but we need more batches, add empty ones
+ while (batches.length < val) batches.push([]);
+ }
+ } else {
+ // Average splitting
+ const batchSize = Math.ceil(tempNumbers.length / val);
+ for (let i = 0; i < val; i++) {
+ if (tempNumbers.length > 0) {
+ batches.push(tempNumbers.splice(0, batchSize));
+ } else {
+ batches.push([]);
+ }
+ }
}
- });
- }, observerOptions);
+ } else {
+ // Split by fixed size per batch
+ while (tempNumbers.length > 0) {
+ batches.push(tempNumbers.splice(0, val));
+ }
+ }
- // Select elements to animate (add a class 'reveal' to them in HTML if not already handled by CSS animation)
- // For now, let's just make sure the hero animations run.
- // If we want scroll animations, we'd add opacity: 0 to elements in CSS and reveal them here.
- // Given the request, the CSS animation I added runs on load for Hero.
- // Let's make the project cards animate in.
-
- const projectCards = document.querySelectorAll('.project-card');
- projectCards.forEach((card, index) => {
- card.style.opacity = "0";
- card.style.animationDelay = `${index * 0.1}s`;
- observer.observe(card);
+ renderResults(batches);
});
+ const renderResults = (batches) => {
+ resultsContainer.innerHTML = '';
+ batchCountBadge.textContent = `${batches.length} 个批次`;
+ batchCountBadge.classList.remove('d-none');
+
+ batches.forEach((batch, index) => {
+ const card = document.createElement('div');
+ card.className = 'card batch-card animate-fadeIn';
+
+ const previewText = batch.slice(0, 30).join(', ');
+ const hasMore = batch.length > 30;
+
+ card.innerHTML = `
+
+
+
+
+ 查看部分预览
+
+ ${batch.length > 0 ? previewText + (hasMore ? ' ...' : '') : '此批次为空'}
+
+
+
+ `;
+
+ resultsContainer.appendChild(card);
+
+ // Download handler
+ card.querySelector('.download-btn').addEventListener('click', () => {
+ if (batch.length === 0) {
+ alert('此批次没有号码可下载。');
+ return;
+ }
+ const remark = card.querySelector('.remark-input').value.trim() || `批次_${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();
});
\ No newline at end of file
diff --git a/index.php b/index.php
index 7205f3d..90ec5f2 100644
--- a/index.php
+++ b/index.php
@@ -4,147 +4,183 @@ declare(strict_types=1);
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
-$phpVersion = PHP_VERSION;
-$now = date('Y-m-d H:i:s');
+$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '快速导入、自动去重、智能分批提取手机号码工具';
+$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
+$projectName = $_SERVER['PROJECT_NAME'] ?? '号码分批大师';
?>
-
+
-
-
- New Style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ = htmlspecialchars($projectName) ?> - 专业级号码分批与去重工具
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
Analyzing your requirements and generating your website…
-
- Loading…
-
-
= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.
-
This page will update automatically as the plan is implemented.
-
Runtime: PHP = htmlspecialchars($phpVersion) ?> — UTC = htmlspecialchars($now) ?>
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
拖放文件或点击上传
+
支持 TXT, CSV, EXCEL 格式内容提取
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2. 拆分配置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
3. 处理结果
+ 0 个批次
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file