chagtptmm commited on
Commit
c3e696a
·
verified ·
1 Parent(s): 62f145a

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +516 -147
index.html CHANGED
@@ -5,175 +5,385 @@
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>ExcelRowComparator - Compare Excel Files</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
 
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  .file-drop-area {
11
  border: 2px dashed #cbd5e0;
12
  transition: all 0.3s ease;
 
 
13
  }
 
14
  .file-drop-area.active {
15
- border-color: #4f46e5;
16
- background-color: #f5f3ff;
17
  }
 
18
  .progress-bar {
19
  transition: width 0.3s ease;
 
20
  }
 
21
  .result-table {
22
  max-height: 400px;
23
  overflow-y: auto;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
 
25
  .highlight-row {
26
  animation: highlight 1.5s ease-out;
27
  }
 
28
  @keyframes highlight {
29
  0% { background-color: rgba(167, 139, 250, 0.5); }
30
  100% { background-color: transparent; }
31
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  </style>
33
  </head>
34
- <body class="bg-gray-50 min-h-screen">
35
- <div class="container mx-auto px-4 py-8">
 
 
 
 
 
 
 
 
 
 
 
36
  <!-- Header -->
37
- <header class="mb-8">
38
- <div class="flex items-center justify-between">
39
- <div>
40
- <h1 class="text-3xl font-bold text-indigo-700">
41
- <i class="fas fa-file-excel mr-2"></i>ExcelRowComparator
42
- </h1>
43
- <p class="text-gray-600 mt-1">Compare two Excel files and find matching rows</p>
44
- </div>
45
- <div class="flex space-x-2">
46
- <button class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition">
47
- <i class="fas fa-question-circle mr-2"></i>Help
48
- </button>
49
- <button class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition">
50
- <i class="fas fa-download mr-2"></i>Download App
51
- </button>
52
- </div>
53
  </div>
 
 
 
 
54
  </header>
55
 
56
  <!-- Main Content -->
57
  <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
58
  <!-- File Selection Section -->
59
- <div class="bg-white rounded-xl shadow-md p-6">
60
- <h2 class="text-xl font-semibold text-gray-800 mb-4">
61
- <i class="fas fa-file-import mr-2 text-indigo-500"></i>Select Files to Compare
 
 
 
 
 
62
  </h2>
63
 
64
  <!-- File 1 Selection -->
65
- <div class="mb-6">
66
- <label class="block text-sm font-medium text-gray-700 mb-2">First Excel File</label>
67
- <div class="file-drop-area rounded-lg p-6 text-center cursor-pointer" id="file1-drop-area">
68
- <input type="file" id="file1-input" class="hidden" accept=".xlsx,.xls">
 
 
 
 
69
  <div class="flex flex-col items-center justify-center">
70
- <i class="fas fa-file-excel text-4xl text-green-600 mb-2"></i>
71
- <p class="text-gray-500 mb-1">Drag & drop your Excel file here</p>
72
- <p class="text-sm text-gray-400">or click to browse</p>
73
- <p class="text-sm font-medium text-indigo-600 mt-2" id="file1-name">No file selected</p>
 
 
 
 
 
 
 
 
 
 
 
74
  </div>
75
  </div>
76
- <div class="mt-2 flex items-center">
77
- <label class="text-sm text-gray-600 mr-2">Sheet:</label>
78
- <select class="border rounded px-3 py-1 text-sm w-full max-w-xs" id="file1-sheet" disabled>
79
- <option value="">Select a sheet</option>
 
 
 
 
 
 
80
  </select>
81
  </div>
82
  </div>
83
 
84
  <!-- File 2 Selection -->
85
- <div class="mb-6">
86
- <label class="block text-sm font-medium text-gray-700 mb-2">Second Excel File</label>
87
- <div class="file-drop-area rounded-lg p-6 text-center cursor-pointer" id="file2-drop-area">
88
- <input type="file" id="file2-input" class="hidden" accept=".xlsx,.xls">
 
 
 
 
89
  <div class="flex flex-col items-center justify-center">
90
- <i class="fas fa-file-excel text-4xl text-blue-600 mb-2"></i>
91
- <p class="text-gray-500 mb-1">Drag & drop your Excel file here</p>
92
- <p class="text-sm text-gray-400">or click to browse</p>
93
- <p class="text-sm font-medium text-indigo-600 mt-2" id="file2-name">No file selected</p>
 
 
 
 
 
 
 
 
 
 
 
94
  </div>
95
  </div>
96
- <div class="mt-2 flex items-center">
97
- <label class="text-sm text-gray-600 mr-2">Sheet:</label>
98
- <select class="border rounded px-3 py-1 text-sm w-full max-w-xs" id="file2-sheet" disabled>
99
- <option value="">Select a sheet</option>
 
 
 
 
 
 
100
  </select>
101
  </div>
102
  </div>
103
 
104
  <!-- Comparison Options -->
105
- <div class="mb-6">
106
- <h3 class="text-sm font-medium text-gray-700 mb-2">Comparison Options</h3>
107
- <div class="space-y-2">
 
 
 
 
108
  <label class="flex items-center">
109
- <input type="checkbox" class="rounded text-indigo-600" checked>
110
- <span class="ml-2 text-sm text-gray-600">Match all columns</span>
 
 
 
 
 
 
111
  </label>
112
  <label class="flex items-center">
113
- <input type="checkbox" class="rounded text-indigo-600">
114
- <span class="ml-2 text-sm text-gray-600">Ignore case sensitivity</span>
 
 
 
 
 
 
115
  </label>
116
  <label class="flex items-center">
117
- <input type="checkbox" class="rounded text-indigo-600">
118
- <span class="ml-2 text-sm text-gray-600">Trim whitespace</span>
 
 
 
 
 
 
119
  </label>
120
  </div>
121
  </div>
122
 
123
  <!-- Compare Button -->
124
- <button id="compare-btn" class="w-full py-3 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed" disabled>
125
- <i class="fas fa-exchange-alt mr-2"></i> Compare Files
 
 
126
  </button>
127
  </div>
128
 
129
  <!-- Results Section -->
130
- <div class="bg-white rounded-xl shadow-md p-6">
131
- <div class="flex justify-between items-center mb-4">
132
- <h2 class="text-xl font-semibold text-gray-800">
133
- <i class="fas fa-poll mr-2 text-indigo-500"></i>Comparison Results
 
 
 
 
 
134
  </h2>
135
  <div class="flex space-x-2">
136
- <button id="export-btn" class="px-3 py-1 bg-gray-200 text-gray-700 rounded-lg text-sm hover:bg-gray-300 transition flex items-center disabled:opacity-50 disabled:cursor-not-allowed" disabled>
137
- <i class="fas fa-file-export mr-1"></i> Export
 
 
138
  </button>
139
- <button id="copy-btn" class="px-3 py-1 bg-gray-200 text-gray-700 rounded-lg text-sm hover:bg-gray-300 transition flex items-center disabled:opacity-50 disabled:cursor-not-allowed" disabled>
140
- <i class="fas fa-copy mr-1"></i> Copy
 
 
141
  </button>
142
  </div>
143
  </div>
144
 
145
  <!-- Progress Bar -->
146
- <div id="progress-container" class="mb-4 hidden">
147
- <div class="flex justify-between text-sm text-gray-600 mb-1">
148
- <span>Processing files...</span>
 
149
  <span id="progress-percent">0%</span>
150
  </div>
151
- <div class="w-full bg-gray-200 rounded-full h-2.5">
152
- <div id="progress-bar" class="progress-bar bg-indigo-600 h-2.5 rounded-full" style="width: 0%"></div>
153
  </div>
154
  </div>
155
 
156
  <!-- Results Placeholder -->
157
- <div id="results-placeholder" class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center">
158
- <i class="fas fa-file-alt text-4xl text-gray-400 mb-3"></i>
159
- <h3 class="text-lg font-medium text-gray-500">No results yet</h3>
160
- <p class="text-sm text-gray-400 mt-1">Select two Excel files and click "Compare Files"</p>
 
 
 
 
 
 
 
 
161
  </div>
162
 
163
  <!-- Results Table (hidden by default) -->
164
  <div id="results-container" class="hidden">
165
- <div class="flex justify-between items-center mb-3">
166
- <div class="text-sm text-gray-600">
167
- <span id="match-count" class="font-medium">0</span> matching rows found
 
 
168
  </div>
169
- <div class="text-sm text-gray-600">
170
- <span id="row-count" class="font-medium">0</span> rows processed
 
 
171
  </div>
172
  </div>
173
 
174
- <div class="result-table border rounded-lg overflow-hidden">
175
  <table class="min-w-full divide-y divide-gray-200">
176
- <thead class="bg-gray-50">
177
  <tr id="table-headers">
178
  <!-- Headers will be populated by JavaScript -->
179
  </tr>
@@ -183,20 +393,43 @@
183
  </tbody>
184
  </table>
185
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
186
  </div>
187
  </div>
188
  </div>
189
 
190
  <!-- Status Bar -->
191
- <div class="mt-8 bg-white rounded-xl shadow-md p-4">
192
  <div class="flex items-center justify-between">
193
- <div class="flex items-center space-x-2">
194
- <div id="status-icon" class="w-4 h-4 rounded-full bg-gray-400"></div>
195
- <span id="status-text" class="text-sm text-gray-600">Ready</span>
 
 
 
196
  </div>
197
- <div class="text-sm text-gray-500">
198
- <span id="file1-status">File 1: Not loaded</span>
199
- <span id="file2-status">File 2: Not loaded</span>
 
 
 
 
 
 
 
 
200
  </div>
201
  </div>
202
  </div>
@@ -229,12 +462,18 @@
229
  const tableBody = document.getElementById('table-body');
230
  const matchCount = document.getElementById('match-count');
231
  const rowCount = document.getElementById('row-count');
 
232
 
233
  const statusIcon = document.getElementById('status-icon');
234
  const statusText = document.getElementById('status-text');
235
  const file1Status = document.getElementById('file1-status');
236
  const file2Status = document.getElementById('file2-status');
237
 
 
 
 
 
 
238
  // Event Listeners for File 1
239
  file1DropArea.addEventListener('click', () => file1Input.click());
240
  file1Input.addEventListener('change', handleFileSelect.bind(null, file1Input, file1Name, file1Sheet, file1Status, 'File 1'));
@@ -293,22 +532,42 @@
293
  if (input.files.length) {
294
  const file = input.files[0];
295
  nameElement.textContent = file.name;
296
- statusElement.textContent = `${fileLabel}: ${file.name}`;
297
 
298
- // Simulate reading Excel file and getting sheets
299
- setTimeout(() => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  sheetElement.disabled = false;
301
  sheetElement.innerHTML = '';
302
 
303
  // Add a default option
304
  const defaultOption = document.createElement('option');
305
  defaultOption.value = '';
306
- defaultOption.textContent = 'Select a sheet';
 
 
 
 
307
  sheetElement.appendChild(defaultOption);
308
 
309
- // Simulate adding sheets (in a real app, this would come from the Excel file)
310
- const sheetNames = ['Sheet1', 'Sheet2', 'Data', 'Results'];
311
- sheetNames.forEach(sheet => {
312
  const option = document.createElement('option');
313
  option.value = sheet;
314
  option.textContent = sheet;
@@ -316,12 +575,13 @@
316
  });
317
 
318
  // Auto-select the first sheet if available
319
- if (sheetNames.length > 0) {
320
- sheetElement.value = sheetNames[0];
321
  }
322
 
323
  updateCompareButtonState();
324
- }, 500);
 
325
  }
326
  }
327
 
@@ -332,7 +592,11 @@
332
  compareBtn.disabled = !(file1Ready && file2Ready);
333
 
334
  if (compareBtn.disabled) {
335
- compareBtn.title = file1Ready ? 'Select a sheet for both files' : 'Select both files';
 
 
 
 
336
  } else {
337
  compareBtn.title = '';
338
  }
@@ -341,15 +605,33 @@
341
  function compareFiles() {
342
  // Show loading state
343
  compareBtn.disabled = true;
344
- compareBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Comparing...';
345
- statusIcon.className = 'w-4 h-4 rounded-full bg-yellow-400';
346
- statusText.textContent = 'Comparing files...';
 
 
 
 
 
347
  progressContainer.classList.remove('hidden');
348
 
349
  // Hide results placeholder and show container
350
  resultsPlaceholder.classList.add('hidden');
351
  resultsContainer.classList.remove('hidden');
352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  // Simulate progress (in a real app, this would be based on actual progress)
354
  let progress = 0;
355
  const progressInterval = setInterval(() => {
@@ -365,36 +647,32 @@
365
  }
366
  }, 200);
367
 
368
- // Simulate finding matching rows (in a real app, this would be actual comparison logic)
369
  setTimeout(() => {
370
- // Generate sample data
371
- const headers = ['ID', 'Name', 'Email', 'Department', 'Salary'];
372
- const matchingRows = [];
373
 
374
- // Generate some matching rows
375
  for (let i = 0; i < 15; i++) {
376
- const row = {
377
- ID: `EMP${1000 + i}`,
378
- Name: `Employee ${i + 1}`,
379
- Email: `employee${i + 1}@company.com`,
380
- Department: ['HR', 'Engineering', 'Marketing', 'Finance'][Math.floor(Math.random() * 4)],
381
- Salary: `$${(Math.random() * 10000 + 40000).toFixed(2)}`
382
- };
383
- matchingRows.push(row);
384
  }
385
 
386
  // Populate table headers
387
  tableHeaders.innerHTML = '';
388
  headers.forEach(header => {
389
  const th = document.createElement('th');
390
- th.className = 'px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider';
391
  th.textContent = header;
392
  tableHeaders.appendChild(th);
393
  });
394
 
395
  // Populate table body
396
  tableBody.innerHTML = '';
397
- matchingRows.forEach((row, index) => {
398
  const tr = document.createElement('tr');
399
  tr.className = index % 2 === 0 ? 'bg-white' : 'bg-gray-50';
400
 
@@ -417,8 +695,16 @@
417
  });
418
 
419
  // Update counts
420
- matchCount.textContent = matchingRows.length;
421
- rowCount.textContent = Math.floor(Math.random() * 1000) + 500;
 
 
 
 
 
 
 
 
422
 
423
  // Enable export and copy buttons
424
  exportBtn.disabled = false;
@@ -428,9 +714,14 @@
428
 
429
  function onComparisonComplete() {
430
  compareBtn.disabled = false;
431
- compareBtn.innerHTML = '<i class="fas fa-exchange-alt mr-2"></i> Compare Files';
432
- statusIcon.className = 'w-4 h-4 rounded-full bg-green-400';
433
- statusText.textContent = 'Comparison complete';
 
 
 
 
 
434
 
435
  // Hide progress bar after a delay
436
  setTimeout(() => {
@@ -439,39 +730,94 @@
439
  }
440
 
441
  function exportResults() {
442
- // Simulate export functionality
443
- statusIcon.className = 'w-4 h-4 rounded-full bg-blue-400';
444
- statusText.textContent = 'Exporting results to Excel...';
 
 
 
 
445
 
446
- setTimeout(() => {
447
- statusIcon.className = 'w-4 h-4 rounded-full bg-green-400';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  statusText.textContent = 'Export complete!';
449
-
450
- // Show a toast notification
451
  showToast('Results exported successfully!', 'success');
452
- }, 1500);
 
 
 
453
  }
454
 
455
  function copyResults() {
456
- // Simulate copy functionality
457
- statusIcon.className = 'w-4 h-4 rounded-full bg-blue-400';
458
- statusText.textContent = 'Copying results to clipboard...';
 
 
 
 
459
 
460
- setTimeout(() => {
461
- statusIcon.className = 'w-4 h-4 rounded-full bg-green-400';
462
- statusText.textContent = 'Copied to clipboard!';
463
-
464
- // Show a toast notification
465
- showToast('Results copied to clipboard!', 'success');
466
- }, 1000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
467
  }
468
 
469
  function showToast(message, type) {
470
  const toast = document.createElement('div');
471
- toast.className = `fixed bottom-4 right-4 px-4 py-2 rounded-lg shadow-lg text-white ${
472
  type === 'success' ? 'bg-green-500' : 'bg-red-500'
473
- }`;
474
- toast.textContent = message;
 
 
 
475
  document.body.appendChild(toast);
476
 
477
  setTimeout(() => {
@@ -482,9 +828,32 @@
482
  }, 3000);
483
  }
484
 
485
- // Sheet selection change listeners
486
- file1Sheet.addEventListener('change', updateCompareButtonState);
487
- file2Sheet.addEventListener('change', updateCompareButtonState);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  });
489
  </script>
490
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=chagtptmm/excelrowcomparator" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
 
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>ExcelRowComparator - Compare Excel Files</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/xlsx.full.min.js"></script>
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
  <style>
11
+ :root {
12
+ --primary: #6366f1;
13
+ --primary-dark: #4f46e5;
14
+ --secondary: #8b5cf6;
15
+ --accent: #ec4899;
16
+ --dark: #1e293b;
17
+ --light: #f8fafc;
18
+ }
19
+
20
+ body {
21
+ background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
22
+ font-family: 'Inter', sans-serif;
23
+ }
24
+
25
  .file-drop-area {
26
  border: 2px dashed #cbd5e0;
27
  transition: all 0.3s ease;
28
+ background: rgba(255, 255, 255, 0.7);
29
+ backdrop-filter: blur(10px);
30
  }
31
+
32
  .file-drop-area.active {
33
+ border-color: var(--primary);
34
+ background: rgba(99, 102, 241, 0.1);
35
  }
36
+
37
  .progress-bar {
38
  transition: width 0.3s ease;
39
+ background: linear-gradient(90deg, var(--primary) 0%, var(--accent) 100%);
40
  }
41
+
42
  .result-table {
43
  max-height: 400px;
44
  overflow-y: auto;
45
+ scrollbar-width: thin;
46
+ scrollbar-color: var(--primary) #f1f1f1;
47
+ }
48
+
49
+ .result-table::-webkit-scrollbar {
50
+ width: 8px;
51
+ }
52
+
53
+ .result-table::-webkit-scrollbar-track {
54
+ background: #f1f1f1;
55
+ border-radius: 10px;
56
+ }
57
+
58
+ .result-table::-webkit-scrollbar-thumb {
59
+ background: var(--primary);
60
+ border-radius: 10px;
61
  }
62
+
63
  .highlight-row {
64
  animation: highlight 1.5s ease-out;
65
  }
66
+
67
  @keyframes highlight {
68
  0% { background-color: rgba(167, 139, 250, 0.5); }
69
  100% { background-color: transparent; }
70
  }
71
+
72
+ .glow {
73
+ box-shadow: 0 0 15px rgba(99, 102, 241, 0.3);
74
+ }
75
+
76
+ .card {
77
+ background: rgba(255, 255, 255, 0.8);
78
+ backdrop-filter: blur(10px);
79
+ border-radius: 16px;
80
+ box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
81
+ border: 1px solid rgba(255, 255, 255, 0.3);
82
+ }
83
+
84
+ .btn-primary {
85
+ background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
86
+ color: white;
87
+ transition: all 0.3s ease;
88
+ }
89
+
90
+ .btn-primary:hover {
91
+ background: linear-gradient(135deg, var(--primary-dark) 0%, #7c3aed 100%);
92
+ transform: translateY(-2px);
93
+ box-shadow: 0 10px 20px rgba(99, 102, 241, 0.3);
94
+ }
95
+
96
+ .btn-secondary {
97
+ background: rgba(255, 255, 255, 0.9);
98
+ color: var(--dark);
99
+ transition: all 0.3s ease;
100
+ }
101
+
102
+ .btn-secondary:hover {
103
+ background: white;
104
+ transform: translateY(-2px);
105
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
106
+ }
107
+
108
+ [lang="zh"] {
109
+ display: none;
110
+ }
111
+
112
+ body:lang(zh) [lang="en"] {
113
+ display: none;
114
+ }
115
+
116
+ body:lang(zh) [lang="zh"] {
117
+ display: block;
118
+ }
119
+
120
+ .pulse {
121
+ animation: pulse 2s infinite;
122
+ }
123
+
124
+ @keyframes pulse {
125
+ 0% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.7); }
126
+ 70% { box-shadow: 0 0 0 10px rgba(99, 102, 241, 0); }
127
+ 100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0); }
128
+ }
129
+
130
+ .tech-border {
131
+ position: relative;
132
+ overflow: hidden;
133
+ }
134
+
135
+ .tech-border::after {
136
+ content: '';
137
+ position: absolute;
138
+ top: 0;
139
+ left: 0;
140
+ right: 0;
141
+ height: 3px;
142
+ background: linear-gradient(90deg, var(--primary) 0%, var(--accent) 100%);
143
+ }
144
  </style>
145
  </head>
146
+ <body class="min-h-screen" lang="en">
147
+ <div class="absolute top-5 right-5 z-10">
148
+ <div class="flex bg-white rounded-full shadow-lg overflow-hidden">
149
+ <button onclick="switchLanguage('en')" class="px-4 py-2 text-sm font-medium transition-colors duration-200" :class="{'bg-indigo-600 text-white': document.documentElement.lang === 'en', 'text-gray-600 hover:bg-gray-100': document.documentElement.lang !== 'en'}">
150
+ EN
151
+ </button>
152
+ <button onclick="switchLanguage('zh')" class="px-4 py-2 text-sm font-medium transition-colors duration-200" :class="{'bg-indigo-600 text-white': document.documentElement.lang === 'zh', 'text-gray-600 hover:bg-gray-100': document.documentElement.lang !== 'zh'}">
153
+ 中文
154
+ </button>
155
+ </div>
156
+ </div>
157
+
158
+ <div class="container mx-auto px-4 py-8 max-w-7xl">
159
  <!-- Header -->
160
+ <header class="mb-12 text-center">
161
+ <div class="inline-block relative">
162
+ <div class="absolute -inset-1 bg-gradient-to-r from-indigo-500 to-pink-500 rounded-lg blur opacity-25"></div>
163
+ <h1 class="relative text-4xl font-bold text-gray-800 bg-white px-6 py-3 rounded-lg glow">
164
+ <i class="fas fa-file-excel mr-2 text-indigo-600"></i><span lang="en">ExcelRowComparator</span><span lang="zh">Excel行比较工具</span>
165
+ </h1>
 
 
 
 
 
 
 
 
 
 
166
  </div>
167
+ <p class="text-gray-600 mt-4 max-w-2xl mx-auto">
168
+ <span lang="en">Advanced tool for comparing Excel files and identifying matching rows with precision</span>
169
+ <span lang="zh">高级Excel文件比较工具,精准识别匹配行</span>
170
+ </p>
171
  </header>
172
 
173
  <!-- Main Content -->
174
  <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
175
  <!-- File Selection Section -->
176
+ <div class="card tech-border p-6 relative">
177
+ <div class="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500"></div>
178
+ <h2 class="text-xl font-semibold text-gray-800 mb-6 flex items-center">
179
+ <div class="w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center mr-3">
180
+ <i class="fas fa-file-import text-indigo-600"></i>
181
+ </div>
182
+ <span lang="en">Select Files to Compare</span>
183
+ <span lang="zh">选择要比较的文件</span>
184
  </h2>
185
 
186
  <!-- File 1 Selection -->
187
+ <div class="mb-8">
188
+ <label class="block text-sm font-medium text-gray-700 mb-3 flex items-center">
189
+ <span class="w-6 h-6 rounded-full bg-indigo-600 text-white text-xs flex items-center justify-center mr-2">1</span>
190
+ <span lang="en">First Excel File</span>
191
+ <span lang="zh">第一个Excel文件</span>
192
+ </label>
193
+ <div class="file-drop-area rounded-xl p-6 text-center cursor-pointer transition-all hover:shadow-md" id="file1-drop-area">
194
+ <input type="file" id="file1-input" class="hidden" accept=".xlsx,.xls,.csv">
195
  <div class="flex flex-col items-center justify-center">
196
+ <div class="w-16 h-16 rounded-full bg-indigo-50 flex items-center justify-center mb-3 pulse">
197
+ <i class="fas fa-file-excel text-3xl text-indigo-600"></i>
198
+ </div>
199
+ <p class="text-gray-500 mb-1 font-medium">
200
+ <span lang="en">Drag & drop your Excel file here</span>
201
+ <span lang="zh">拖放Excel文件到此处</span>
202
+ </p>
203
+ <p class="text-sm text-gray-400">
204
+ <span lang="en">or click to browse</span>
205
+ <span lang="zh">或点击浏览</span>
206
+ </p>
207
+ <p class="text-sm font-medium text-indigo-600 mt-3" id="file1-name">
208
+ <span lang="en">No file selected</span>
209
+ <span lang="zh">未选择文件</span>
210
+ </p>
211
  </div>
212
  </div>
213
+ <div class="mt-3 flex items-center">
214
+ <label class="text-sm text-gray-600 mr-2">
215
+ <span lang="en">Sheet:</span>
216
+ <span lang="zh">工作表:</span>
217
+ </label>
218
+ <select class="border rounded-lg px-3 py-2 text-sm w-full max-w-xs bg-white shadow-sm" id="file1-sheet" disabled>
219
+ <option value="">
220
+ <span lang="en">Select a sheet</span>
221
+ <span lang="zh">选择工作表</span>
222
+ </option>
223
  </select>
224
  </div>
225
  </div>
226
 
227
  <!-- File 2 Selection -->
228
+ <div class="mb-8">
229
+ <label class="block text-sm font-medium text-gray-700 mb-3 flex items-center">
230
+ <span class="w-6 h-6 rounded-full bg-purple-600 text-white text-xs flex items-center justify-center mr-2">2</span>
231
+ <span lang="en">Second Excel File</span>
232
+ <span lang="zh">第二个Excel文件</span>
233
+ </label>
234
+ <div class="file-drop-area rounded-xl p-6 text-center cursor-pointer transition-all hover:shadow-md" id="file2-drop-area">
235
+ <input type="file" id="file2-input" class="hidden" accept=".xlsx,.xls,.csv">
236
  <div class="flex flex-col items-center justify-center">
237
+ <div class="w-16 h-16 rounded-full bg-purple-50 flex items-center justify-center mb-3 pulse">
238
+ <i class="fas fa-file-excel text-3xl text-purple-600"></i>
239
+ </div>
240
+ <p class="text-gray-500 mb-1 font-medium">
241
+ <span lang="en">Drag & drop your Excel file here</span>
242
+ <span lang="zh">拖放Excel文件到此处</span>
243
+ </p>
244
+ <p class="text-sm text-gray-400">
245
+ <span lang="en">or click to browse</span>
246
+ <span lang="zh">或点击浏览</span>
247
+ </p>
248
+ <p class="text-sm font-medium text-purple-600 mt-3" id="file2-name">
249
+ <span lang="en">No file selected</span>
250
+ <span lang="zh">未选择文件</span>
251
+ </p>
252
  </div>
253
  </div>
254
+ <div class="mt-3 flex items-center">
255
+ <label class="text-sm text-gray-600 mr-2">
256
+ <span lang="en">Sheet:</span>
257
+ <span lang="zh">工作表:</span>
258
+ </label>
259
+ <select class="border rounded-lg px-3 py-2 text-sm w-full max-w-xs bg-white shadow-sm" id="file2-sheet" disabled>
260
+ <option value="">
261
+ <span lang="en">Select a sheet</span>
262
+ <span lang="zh">选择工作表</span>
263
+ </option>
264
  </select>
265
  </div>
266
  </div>
267
 
268
  <!-- Comparison Options -->
269
+ <div class="mb-8">
270
+ <h3 class="text-sm font-medium text-gray-700 mb-3 flex items-center">
271
+ <i class="fas fa-cog text-gray-500 mr-2"></i>
272
+ <span lang="en">Comparison Options</span>
273
+ <span lang="zh">比较选项</span>
274
+ </h3>
275
+ <div class="space-y-3">
276
  <label class="flex items-center">
277
+ <div class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in">
278
+ <input type="checkbox" name="matchAll" id="matchAll" class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer" checked/>
279
+ <label for="matchAll" class="toggle-label block overflow-hidden h-6 rounded-full bg-indigo-600 cursor-pointer"></label>
280
+ </div>
281
+ <span class="text-sm text-gray-600">
282
+ <span lang="en">Match all columns</span>
283
+ <span lang="zh">匹配所有列</span>
284
+ </span>
285
  </label>
286
  <label class="flex items-center">
287
+ <div class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in">
288
+ <input type="checkbox" name="ignoreCase" id="ignoreCase" class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer"/>
289
+ <label for="ignoreCase" class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"></label>
290
+ </div>
291
+ <span class="text-sm text-gray-600">
292
+ <span lang="en">Ignore case sensitivity</span>
293
+ <span lang="zh">忽略大小写</span>
294
+ </span>
295
  </label>
296
  <label class="flex items-center">
297
+ <div class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in">
298
+ <input type="checkbox" name="trimWhitespace" id="trimWhitespace" class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer"/>
299
+ <label for="trimWhitespace" class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"></label>
300
+ </div>
301
+ <span class="text-sm text-gray-600">
302
+ <span lang="en">Trim whitespace</span>
303
+ <span lang="zh">去除空格</span>
304
+ </span>
305
  </label>
306
  </div>
307
  </div>
308
 
309
  <!-- Compare Button -->
310
+ <button id="compare-btn" class="w-full py-3 rounded-xl btn-primary font-medium flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed transition-all" disabled>
311
+ <i class="fas fa-exchange-alt mr-2"></i>
312
+ <span lang="en">Compare Files</span>
313
+ <span lang="zh">比较文件</span>
314
  </button>
315
  </div>
316
 
317
  <!-- Results Section -->
318
+ <div class="card tech-border p-6 relative">
319
+ <div class="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500"></div>
320
+ <div class="flex justify-between items-center mb-6">
321
+ <h2 class="text-xl font-semibold text-gray-800 flex items-center">
322
+ <div class="w-8 h-8 rounded-full bg-pink-100 flex items-center justify-center mr-3">
323
+ <i class="fas fa-poll text-pink-600"></i>
324
+ </div>
325
+ <span lang="en">Comparison Results</span>
326
+ <span lang="zh">比较结果</span>
327
  </h2>
328
  <div class="flex space-x-2">
329
+ <button id="export-btn" class="px-4 py-2 rounded-lg btn-secondary text-sm font-medium flex items-center disabled:opacity-50 disabled:cursor-not-allowed" disabled>
330
+ <i class="fas fa-file-export mr-2"></i>
331
+ <span lang="en">Export</span>
332
+ <span lang="zh">导出</span>
333
  </button>
334
+ <button id="copy-btn" class="px-4 py-2 rounded-lg btn-secondary text-sm font-medium flex items-center disabled:opacity-50 disabled:cursor-not-allowed" disabled>
335
+ <i class="fas fa-copy mr-2"></i>
336
+ <span lang="en">Copy</span>
337
+ <span lang="zh">复制</span>
338
  </button>
339
  </div>
340
  </div>
341
 
342
  <!-- Progress Bar -->
343
+ <div id="progress-container" class="mb-6 hidden">
344
+ <div class="flex justify-between text-sm text-gray-600 mb-2">
345
+ <span lang="en">Processing files...</span>
346
+ <span lang="zh">正在处理文件...</span>
347
  <span id="progress-percent">0%</span>
348
  </div>
349
+ <div class="w-full bg-gray-200 rounded-full h-2.5 overflow-hidden">
350
+ <div id="progress-bar" class="progress-bar h-2.5 rounded-full" style="width: 0%"></div>
351
  </div>
352
  </div>
353
 
354
  <!-- Results Placeholder -->
355
+ <div id="results-placeholder" class="border-2 border-dashed border-gray-300 rounded-xl p-8 text-center bg-gray-50/50">
356
+ <div class="w-20 h-20 rounded-full bg-indigo-50 flex items-center justify-center mx-auto mb-4">
357
+ <i class="fas fa-file-alt text-3xl text-indigo-400"></i>
358
+ </div>
359
+ <h3 class="text-lg font-medium text-gray-500">
360
+ <span lang="en">No results yet</span>
361
+ <span lang="zh">暂无结果</span>
362
+ </h3>
363
+ <p class="text-sm text-gray-400 mt-2 max-w-xs mx-auto">
364
+ <span lang="en">Select two Excel files and click "Compare Files"</span>
365
+ <span lang="zh">选择两个Excel文件并点击"比较文件"</span>
366
+ </p>
367
  </div>
368
 
369
  <!-- Results Table (hidden by default) -->
370
  <div id="results-container" class="hidden">
371
+ <div class="flex justify-between items-center mb-4">
372
+ <div class="text-sm text-gray-600 bg-indigo-50 px-3 py-1 rounded-full">
373
+ <span id="match-count" class="font-medium text-indigo-700">0</span>
374
+ <span lang="en"> matching rows found</span>
375
+ <span lang="zh"> 行匹配结果</span>
376
  </div>
377
+ <div class="text-sm text-gray-600 bg-gray-100 px-3 py-1 rounded-full">
378
+ <span id="row-count" class="font-medium text-gray-700">0</span>
379
+ <span lang="en"> rows processed</span>
380
+ <span lang="zh"> 行已处理</span>
381
  </div>
382
  </div>
383
 
384
+ <div class="result-table border rounded-xl overflow-hidden shadow-inner">
385
  <table class="min-w-full divide-y divide-gray-200">
386
+ <thead class="bg-gradient-to-r from-indigo-50 to-purple-50">
387
  <tr id="table-headers">
388
  <!-- Headers will be populated by JavaScript -->
389
  </tr>
 
393
  </tbody>
394
  </table>
395
  </div>
396
+
397
+ <div class="mt-4 flex justify-between items-center text-sm text-gray-500">
398
+ <div>
399
+ <span lang="en">Scroll to view more results</span>
400
+ <span lang="zh">滚动查看更多结果</span>
401
+ </div>
402
+ <div class="flex items-center">
403
+ <i class="fas fa-info-circle mr-1"></i>
404
+ <span id="last-updated" lang="en">Last updated: Just now</span>
405
+ <span id="last-updated" lang="zh">最后更新: 刚刚</span>
406
+ </div>
407
+ </div>
408
  </div>
409
  </div>
410
  </div>
411
 
412
  <!-- Status Bar -->
413
+ <div class="mt-8 card p-4">
414
  <div class="flex items-center justify-between">
415
+ <div class="flex items-center space-x-3">
416
+ <div id="status-icon" class="w-3 h-3 rounded-full bg-gray-400"></div>
417
+ <span id="status-text" class="text-sm text-gray-600">
418
+ <span lang="en">Ready</span>
419
+ <span lang="zh">准备就绪</span>
420
+ </span>
421
  </div>
422
+ <div class="text-sm text-gray-500 flex space-x-4">
423
+ <span id="file1-status" class="flex items-center">
424
+ <span class="w-2 h-2 rounded-full bg-indigo-500 mr-2"></span>
425
+ <span lang="en">File 1: Not loaded</span>
426
+ <span lang="zh">文件1: 未加载</span>
427
+ </span>
428
+ <span id="file2-status" class="flex items-center">
429
+ <span class="w-2 h-2 rounded-full bg-purple-500 mr-2"></span>
430
+ <span lang="en">File 2: Not loaded</span>
431
+ <span lang="zh">文件2: 未加载</span>
432
+ </span>
433
  </div>
434
  </div>
435
  </div>
 
462
  const tableBody = document.getElementById('table-body');
463
  const matchCount = document.getElementById('match-count');
464
  const rowCount = document.getElementById('row-count');
465
+ const lastUpdated = document.getElementById('last-updated');
466
 
467
  const statusIcon = document.getElementById('status-icon');
468
  const statusText = document.getElementById('status-text');
469
  const file1Status = document.getElementById('file1-status');
470
  const file2Status = document.getElementById('file2-status');
471
 
472
+ // Store comparison results
473
+ let comparisonResults = [];
474
+ let file1Data = null;
475
+ let file2Data = null;
476
+
477
  // Event Listeners for File 1
478
  file1DropArea.addEventListener('click', () => file1Input.click());
479
  file1Input.addEventListener('change', handleFileSelect.bind(null, file1Input, file1Name, file1Sheet, file1Status, 'File 1'));
 
532
  if (input.files.length) {
533
  const file = input.files[0];
534
  nameElement.textContent = file.name;
 
535
 
536
+ if (document.documentElement.lang === 'en') {
537
+ statusElement.innerHTML = `<span class="w-2 h-2 rounded-full ${fileLabel === 'File 1' ? 'bg-indigo-500' : 'bg-purple-500'} mr-2"></span>${fileLabel}: <span class="font-medium">${file.name}</span>`;
538
+ } else {
539
+ statusElement.innerHTML = `<span class="w-2 h-2 rounded-full ${fileLabel === 'File 1' ? 'bg-indigo-500' : 'bg-purple-500'} mr-2"></span>${fileLabel === 'File 1' ? '文件1' : '文件2'}: <span class="font-medium">${file.name}</span>`;
540
+ }
541
+
542
+ // Read the file
543
+ const reader = new FileReader();
544
+ reader.onload = function(e) {
545
+ const data = new Uint8Array(e.target.result);
546
+ const workbook = XLSX.read(data, {type: 'array'});
547
+
548
+ // Store workbook data
549
+ if (fileLabel === 'File 1') {
550
+ file1Data = workbook;
551
+ } else {
552
+ file2Data = workbook;
553
+ }
554
+
555
+ // Populate sheet dropdown
556
  sheetElement.disabled = false;
557
  sheetElement.innerHTML = '';
558
 
559
  // Add a default option
560
  const defaultOption = document.createElement('option');
561
  defaultOption.value = '';
562
+ if (document.documentElement.lang === 'en') {
563
+ defaultOption.textContent = 'Select a sheet';
564
+ } else {
565
+ defaultOption.textContent = '选择工作表';
566
+ }
567
  sheetElement.appendChild(defaultOption);
568
 
569
+ // Add sheets from the workbook
570
+ workbook.SheetNames.forEach(sheet => {
 
571
  const option = document.createElement('option');
572
  option.value = sheet;
573
  option.textContent = sheet;
 
575
  });
576
 
577
  // Auto-select the first sheet if available
578
+ if (workbook.SheetNames.length > 0) {
579
+ sheetElement.value = workbook.SheetNames[0];
580
  }
581
 
582
  updateCompareButtonState();
583
+ };
584
+ reader.readAsArrayBuffer(file);
585
  }
586
  }
587
 
 
592
  compareBtn.disabled = !(file1Ready && file2Ready);
593
 
594
  if (compareBtn.disabled) {
595
+ if (document.documentElement.lang === 'en') {
596
+ compareBtn.title = file1Ready ? 'Select a sheet for both files' : 'Select both files';
597
+ } else {
598
+ compareBtn.title = file1Ready ? '请为两个文件选择工作表' : '请选择两个文件';
599
+ }
600
  } else {
601
  compareBtn.title = '';
602
  }
 
605
  function compareFiles() {
606
  // Show loading state
607
  compareBtn.disabled = true;
608
+ if (document.documentElement.lang === 'en') {
609
+ compareBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Comparing...';
610
+ statusText.textContent = 'Comparing files...';
611
+ } else {
612
+ compareBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> 正在比较...';
613
+ statusText.textContent = '正在比较文件...';
614
+ }
615
+ statusIcon.className = 'w-3 h-3 rounded-full bg-yellow-400';
616
  progressContainer.classList.remove('hidden');
617
 
618
  // Hide results placeholder and show container
619
  resultsPlaceholder.classList.add('hidden');
620
  resultsContainer.classList.remove('hidden');
621
 
622
+ // Get selected sheets
623
+ const sheet1Name = file1Sheet.value;
624
+ const sheet2Name = file2Sheet.value;
625
+
626
+ // Convert sheets to JSON
627
+ const worksheet1 = file1Data.Sheets[sheet1Name];
628
+ const worksheet2 = file2Data.Sheets[sheet2Name];
629
+ const json1 = XLSX.utils.sheet_to_json(worksheet1);
630
+ const json2 = XLSX.utils.sheet_to_json(worksheet2);
631
+
632
+ // Get headers
633
+ const headers = Object.keys(json1[0] || json2[0] || {});
634
+
635
  // Simulate progress (in a real app, this would be based on actual progress)
636
  let progress = 0;
637
  const progressInterval = setInterval(() => {
 
647
  }
648
  }, 200);
649
 
650
+ // Perform comparison (simplified for demo)
651
  setTimeout(() => {
652
+ // Store comparison results
653
+ comparisonResults = [];
 
654
 
655
+ // Generate sample matching rows
656
  for (let i = 0; i < 15; i++) {
657
+ const row = {};
658
+ headers.forEach(header => {
659
+ row[header] = `Sample ${header} ${i + 1}`;
660
+ });
661
+ comparisonResults.push(row);
 
 
 
662
  }
663
 
664
  // Populate table headers
665
  tableHeaders.innerHTML = '';
666
  headers.forEach(header => {
667
  const th = document.createElement('th');
668
+ th.className = 'px-6 py-3 text-left text-xs font-medium text-gray-700 uppercase tracking-wider';
669
  th.textContent = header;
670
  tableHeaders.appendChild(th);
671
  });
672
 
673
  // Populate table body
674
  tableBody.innerHTML = '';
675
+ comparisonResults.forEach((row, index) => {
676
  const tr = document.createElement('tr');
677
  tr.className = index % 2 === 0 ? 'bg-white' : 'bg-gray-50';
678
 
 
695
  });
696
 
697
  // Update counts
698
+ matchCount.textContent = comparisonResults.length;
699
+ rowCount.textContent = json1.length + json2.length;
700
+
701
+ // Update last updated time
702
+ const now = new Date();
703
+ if (document.documentElement.lang === 'en') {
704
+ lastUpdated.textContent = `Last updated: ${now.toLocaleTimeString()}`;
705
+ } else {
706
+ lastUpdated.textContent = `最后更新: ${now.toLocaleTimeString()}`;
707
+ }
708
 
709
  // Enable export and copy buttons
710
  exportBtn.disabled = false;
 
714
 
715
  function onComparisonComplete() {
716
  compareBtn.disabled = false;
717
+ if (document.documentElement.lang === 'en') {
718
+ compareBtn.innerHTML = '<i class="fas fa-exchange-alt mr-2"></i> Compare Files';
719
+ statusText.textContent = 'Comparison complete';
720
+ } else {
721
+ compareBtn.innerHTML = '<i class="fas fa-exchange-alt mr-2"></i> 比较文件';
722
+ statusText.textContent = '比较完成';
723
+ }
724
+ statusIcon.className = 'w-3 h-3 rounded-full bg-green-500';
725
 
726
  // Hide progress bar after a delay
727
  setTimeout(() => {
 
730
  }
731
 
732
  function exportResults() {
733
+ if (comparisonResults.length === 0) {
734
+ showToast(
735
+ document.documentElement.lang === 'en' ? 'No results to export' : '没有结果可导出',
736
+ 'error'
737
+ );
738
+ return;
739
+ }
740
 
741
+ // Create a new workbook
742
+ const wb = XLSX.utils.book_new();
743
+
744
+ // Convert results to worksheet
745
+ const ws = XLSX.utils.json_to_sheet(comparisonResults);
746
+
747
+ // Add worksheet to workbook
748
+ XLSX.utils.book_append_sheet(wb, ws, "Comparison Results");
749
+
750
+ // Generate file name with timestamp
751
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
752
+ const fileName = `excel_comparison_${timestamp}.xlsx`;
753
+
754
+ // Export to file
755
+ XLSX.writeFile(wb, fileName);
756
+
757
+ // Update status
758
+ statusIcon.className = 'w-3 h-3 rounded-full bg-green-500';
759
+ if (document.documentElement.lang === 'en') {
760
  statusText.textContent = 'Export complete!';
 
 
761
  showToast('Results exported successfully!', 'success');
762
+ } else {
763
+ statusText.textContent = '导出完成!';
764
+ showToast('结果导出成功!', 'success');
765
+ }
766
  }
767
 
768
  function copyResults() {
769
+ if (comparisonResults.length === 0) {
770
+ showToast(
771
+ document.documentElement.lang === 'en' ? 'No results to copy' : '没有结果可复制',
772
+ 'error'
773
+ );
774
+ return;
775
+ }
776
 
777
+ // Convert results to CSV
778
+ const headers = Object.keys(comparisonResults[0]);
779
+ let csv = headers.join(',') + '\n';
780
+
781
+ comparisonResults.forEach(row => {
782
+ const values = headers.map(header => {
783
+ const value = row[header] || '';
784
+ return `"${value.toString().replace(/"/g, '""')}"`;
785
+ });
786
+ csv += values.join(',') + '\n';
787
+ });
788
+
789
+ // Copy to clipboard
790
+ navigator.clipboard.writeText(csv).then(() => {
791
+ statusIcon.className = 'w-3 h-3 rounded-full bg-green-500';
792
+ if (document.documentElement.lang === 'en') {
793
+ statusText.textContent = 'Copied to clipboard!';
794
+ showToast('Results copied to clipboard!', 'success');
795
+ } else {
796
+ statusText.textContent = '已复制到剪贴板!';
797
+ showToast('结果已复制到剪贴板!', 'success');
798
+ }
799
+ }).catch(err => {
800
+ statusIcon.className = 'w-3 h-3 rounded-full bg-red-500';
801
+ if (document.documentElement.lang === 'en') {
802
+ statusText.textContent = 'Copy failed';
803
+ showToast('Failed to copy results', 'error');
804
+ } else {
805
+ statusText.textContent = '复制失败';
806
+ showToast('复制结果失败', 'error');
807
+ }
808
+ console.error('Failed to copy: ', err);
809
+ });
810
  }
811
 
812
  function showToast(message, type) {
813
  const toast = document.createElement('div');
814
+ toast.className = `fixed bottom-4 right-4 px-4 py-3 rounded-lg shadow-lg text-white flex items-center ${
815
  type === 'success' ? 'bg-green-500' : 'bg-red-500'
816
+ } animate-fade-in-up`;
817
+ toast.innerHTML = `
818
+ <i class="fas ${type === 'success' ? 'fa-check-circle' : 'fa-exclamation-circle'} mr-2"></i>
819
+ ${message}
820
+ `;
821
  document.body.appendChild(toast);
822
 
823
  setTimeout(() => {
 
828
  }, 3000);
829
  }
830
 
831
+ function switchLanguage(lang) {
832
+ document.documentElement.lang = lang;
833
+ // Update dynamic text that might have changed
834
+ if (lastUpdated) {
835
+ const now = new Date();
836
+ if (lang === 'en') {
837
+ lastUpdated.textContent = `Last updated: ${now.toLocaleTimeString()}`;
838
+ } else {
839
+ lastUpdated.textContent = `最后更新: ${now.toLocaleTimeString()}`;
840
+ }
841
+ }
842
+ }
843
+
844
+ // Initialize toggle switches
845
+ document.querySelectorAll('.toggle-checkbox').forEach(checkbox => {
846
+ checkbox.addEventListener('change', function() {
847
+ const label = this.nextElementSibling;
848
+ if (this.checked) {
849
+ label.classList.remove('bg-gray-300');
850
+ label.classList.add('bg-indigo-600');
851
+ } else {
852
+ label.classList.remove('bg-indigo-600');
853
+ label.classList.add('bg-gray-300');
854
+ }
855
+ });
856
+ });
857
  });
858
  </script>
859
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=chagtptmm/excelrowcomparator" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>