KEXEL/git
0
1<!DOCTYPE html>2<html lang="pt-br">3 4<head>5 <meta charset="UTF-8">6 <meta name="viewport" content="width=device-width, initial-scale=1.0">7 <title>XLSX File Reader & Editor</title>8 <!-- Bootstrap 5 CSS -->9 <link href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css" rel="stylesheet">10 <!-- Bootstrap Icons -->11 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1/font/bootstrap-icons.css">12 <!-- XLSX Library -->13 <script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>14 <!-- FileSaver.js -->15 <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>16 <style>17 .dropzone {18 border: 2px dashed #0d6efd;19 transition: all 0.3s ease;20 cursor: pointer;21 }22 23 .dropzone.active {24 border-color: #198754;25 background-color: rgba(25, 135, 84, 0.05);26 }27 28 .table-container {29 /*max-height: 70vh;*/30 overflow-y: auto;31 }32 33 .editable-cell {34 min-width: 120px;35 border: 1px solid #dee2e6;36 padding: 0.5rem;37 }38 39 .editable-cell:focus {40 outline: 2px solid #0d6efd;41 background-color: rgba(13, 110, 253, 0.1);42 }43 44 .sheet-tab {45 cursor: pointer;46 padding: 0.5rem 1rem;47 border-radius: 0.375rem;48 margin-right: 0.5rem;49 background-color: #f8f9fa;50 transition: all 0.2s ease;51 }52 53 .sheet-tab:hover {54 background-color: #e9ecef;55 }56 57 .sheet-tab.active {58 background-color: #0d6efd;59 color: white;60 }61 62 .file-info {63 background-color: #f8f9fa;64 border-radius: 0.375rem;65 padding: 1rem;66 }67 68 .how-to-use-card {69 height: 100%;70 }71 </style>72</head>73 74<body class="bg-light">75 <div class="container py-5">76 <div class="text-center mb-5">77 <h1 class="display-5 fw-bold text-dark mb-3">Excel File Reader & Editor</h1>78 <p class="lead text-muted">Upload, view, edit and export Excel files directly in your browser</p>79 </div>80 81 <div class="shadow-sm mb-5">82 <div class="">83 <div class="row">84 85 <!-- File Upload Section -->86 <div class="col-md">87 <div id="dropzone" class="dropzone rounded-0 p-3 text-center mb-4">88 <div class="d-flex flex-column align-items-center justify-content-center">89 <i class="bi bi-file-earmark-excel text-primary fs-1 mb-3"></i>90 <p class="text-muted mb-2">Drag & drop your Excel file here</p>91 <p class="text-muted small mb-3">or</p>92 <!--<label for="fileInput" class="btn btn-primary">93 <i class="bi bi-upload me-2"></i> Browse Files94 </label>-->95 <input type="file" id="fileInput" class="d-none" accept=".xlsx, .xls, .csv" />96 </div>97 </div>98 99 100 </div>101 <div class="col-md-3">102 <h5 class="fw-bold mb-3">File Information</h5>103 <div class="file-info">104 <div class="d-flex align-items-center mb-2">105 <i class="bi bi-file-earmark text-muted me-2"></i>106 <span id="fileName" class="text-dark">No file selected</span>107 </div>108 <div class="d-flex align-items-center mb-2">109 <i class="bi bi-collection text-muted me-2"></i>110 <span id="sheetCount" class="text-dark">0 sheets</span>111 </div>112 <div class="d-flex align-items-center">113 <i class="bi bi-table text-muted me-2"></i>114 <span id="rowCount" class="text-dark">0 rows</span>115 </div>116 </div>117 </div>118 119 120 <!-- Sheet Navigation -->121 <div class="col-ms">122 <div id="sheetTabs" class="d-flex overflow-auto py-2 mb-3">123 <!-- Sheets will be added here dynamically -->124 </div>125 126 <!-- Table Container -->127 <div id="tableContainer" class="table-container border rounded-0 overflow-hidden">128 <div class="text-center py-5 text-muted" id="emptyState">129 <i class="bi bi-file-earmark-excel display-4 opacity-25 mb-3"></i>130 <p class="h5">Upload an Excel file to get started</p>131 </div>132 <table id="dataTable" class="table table-bordered m-0 d-none overflow-auto">133 <!-- Table content will be generated dynamically -->134 </table>135 </div>136 137 <!-- Action Buttons -->138 <div class="d-flex flex-wrap gap-2 mt-4">139 <button id="exportBtn" class="btn btn-success" disabled>140 <i class="bi bi-download me-2"></i> Export Excel141 </button>142 <button id="addRowBtn" class="btn btn-primary" disabled>143 <i class="bi bi-plus me-2"></i> Add Row144 </button>145 <button id="addColBtn" class="btn btn-primary" disabled>146 <i class="bi bi-plus me-2"></i> Add Column147 </button>148 <button id="resetBtn" class="btn btn-secondary">149 <i class="bi bi-arrow-counterclockwise me-2"></i> Reset150 </button>151 </div>152 </div>153 </div>154 </div>155 </div>156 157 <!-- Instructions -->158 <div class="card shadow-sm">159 <div class="card-body p-4">160 <h2 class="h4 fw-bold mb-4">How to use</h2>161 <div class="row g-4">162 <div class="col-md-4">163 <div class="card bg-primary bg-opacity-10 how-to-use-card">164 <div class="card-body">165 <div class="d-flex align-items-center mb-3">166 <div class="bg-primary bg-opacity-25 p-2 rounded-circle me-3">167 <i class="bi bi-upload text-primary"></i>168 </div>169 <h3 class="h6 fw-bold mb-0 text-primary">Upload</h3>170 </div>171 <p class="card-text text-muted">Drag & drop or click to upload your Excel file (.xlsx, .xls, .csv)</p>172 </div>173 </div>174 </div>175 <div class="col-md-4">176 <div class="card bg-success bg-opacity-10 how-to-use-card">177 <div class="card-body">178 <div class="d-flex align-items-center mb-3">179 <div class="bg-success bg-opacity-25 p-2 rounded-circle me-3">180 <i class="bi bi-pencil text-success"></i>181 </div>182 <h3 class="h6 fw-bold mb-0 text-success">Edit</h3>183 </div>184 <p class="card-text text-muted">Click on any cell to edit its content. Add/remove rows and columns as needed.</p>185 </div>186 </div>187 </div>188 <div class="col-md-4">189 <div class="card bg-info bg-opacity-10 how-to-use-card">190 <div class="card-body">191 <div class="d-flex align-items-center mb-3">192 <div class="bg-info bg-opacity-25 p-2 rounded-circle me-3">193 <i class="bi bi-download text-info"></i>194 </div>195 <h3 class="h6 fw-bold mb-0 text-info">Export</h3>196 </div>197 <p class="card-text text-muted">Download your edited file back to Excel format when you're done.</p>198 </div>199 </div>200 </div>201 </div>202 </div>203 </div>204</div>205 206 <!-- Bootstrap 5 JS Bundle with Popper -->207 <script src="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/js/bootstrap.bundle.min.js"></script>208 209 <script>210 document.addEventListener("DOMContentLoaded", function () {211 // DOM Elements212 const fileInput = document.getElementById("fileInput");213 const dropzone = document.getElementById("dropzone");214 const fileName = document.getElementById("fileName");215 const sheetCount = document.getElementById("sheetCount");216 const rowCount = document.getElementById("rowCount");217 const tableContainer = document.getElementById("tableContainer");218 const dataTable = document.getElementById("dataTable");219 const emptyState = document.getElementById("emptyState");220 const sheetTabs = document.getElementById("sheetTabs");221 const exportBtn = document.getElementById("exportBtn");222 const addRowBtn = document.getElementById("addRowBtn");223 const addColBtn = document.getElementById("addColBtn");224 const resetBtn = document.getElementById("resetBtn");225 226 // Global variables227 let workbook = null;228 let currentSheetName = "";229 let sheetData = [];230 let headers = [];231 232 // Event Listeners233 fileInput.addEventListener("change", handleFileSelect);234 dropzone.addEventListener("click", () => fileInput.click());235 dropzone.addEventListener("dragover", handleDragOver);236 dropzone.addEventListener("dragleave", handleDragLeave);237 dropzone.addEventListener("drop", handleDrop);238 exportBtn.addEventListener("click", exportToExcel);239 addRowBtn.addEventListener("click", addNewRow);240 addColBtn.addEventListener("click", addNewColumn);241 resetBtn.addEventListener("click", resetApp);242 243 // Functions244 function handleFileSelect(e) {245 const file = e.target.files[0];246 if (!file) return;247 248 processExcelFile(file);249 }250 251 function handleDragOver(e) {252 e.preventDefault();253 e.stopPropagation();254 dropzone.classList.add("active");255 }256 257 function handleDragLeave(e) {258 e.preventDefault();259 e.stopPropagation();260 dropzone.classList.remove("active");261 }262 263 function handleDrop(e) {264 e.preventDefault();265 e.stopPropagation();266 dropzone.classList.remove("active");267 268 const file = e.dataTransfer.files[0];269 if (!file) return;270 271 processExcelFile(file);272 }273 274 function processExcelFile(file) {275 fileName.textContent = file.name;276 277 const reader = new FileReader();278 reader.onload = function (e) {279 const data = new Uint8Array(e.target.result);280 workbook = XLSX.read(data, { type: "array" });281 282 updateSheetInfo();283 renderSheetTabs();284 loadFirstSheet();285 enableButtons();286 };287 reader.readAsArrayBuffer(file);288 }289 290 function updateSheetInfo() {291 if (!workbook) return;292 293 sheetCount.textContent = `${workbook.SheetNames.length} sheet${294 workbook.SheetNames.length !== 1 ? "s" : ""295 }`;296 }297 298 function renderSheetTabs() {299 if (!workbook) return;300 301 sheetTabs.innerHTML = "";302 workbook.SheetNames.forEach((sheetName) => {303 const tab = document.createElement("div");304 tab.className = "sheet-tab";305 tab.textContent = sheetName;306 tab.addEventListener("click", () => switchSheet(sheetName));307 308 if (sheetName === workbook.SheetNames[0]) {309 tab.classList.add("active");310 }311 312 sheetTabs.appendChild(tab);313 });314 }315 316 function switchSheet(sheetName) {317 if (!workbook || currentSheetName === sheetName) return;318 319 // Update active tab320 document.querySelectorAll(".sheet-tab").forEach((tab) => {321 tab.classList.remove("active");322 if (tab.textContent === sheetName) {323 tab.classList.add("active");324 }325 });326 327 currentSheetName = sheetName;328 loadSheetData();329 renderTable();330 }331 332 function loadFirstSheet() {333 if (!workbook || workbook.SheetNames.length === 0) return;334 335 currentSheetName = workbook.SheetNames[0];336 loadSheetData();337 renderTable();338 }339 340 function loadSheetData() {341 if (!workbook || !currentSheetName) return;342 343 const worksheet = workbook.Sheets[currentSheetName];344 sheetData = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: "" });345 346 if (sheetData.length > 0) {347 headers = sheetData[0];348 rowCount.textContent = `${sheetData.length - 1} row${349 sheetData.length - 1 !== 1 ? "s" : ""350 }`;351 } else {352 headers = [];353 rowCount.textContent = "0 rows";354 }355 }356 357 function renderTable() {358 if (!sheetData || sheetData.length === 0) {359 emptyState.classList.remove("d-none");360 dataTable.classList.add("d-none");361 return;362 }363 364 emptyState.classList.add("d-none");365 dataTable.classList.remove("d-none");366 367 // Clear existing table368 dataTable.innerHTML = "";369 370 // Create header row371 const thead = document.createElement("thead");372 const headerRow = document.createElement("tr");373 headerRow.className = "table-light";374 375 // Add empty cell for corner376 const cornerCell = document.createElement("th");377 cornerCell.className = "text-center";378 cornerCell.style.padding = "0px 45px";379 headerRow.appendChild(cornerCell);380 381 // Add column headers382 headers.forEach((header, colIndex) => {383 const th = document.createElement("th");384 th.className = "text-nowrap";385 th.textContent = header || `Column ${colIndex + 1}`;386 387 // Add delete column button388 const deleteBtn = document.createElement("button");389 deleteBtn.className = "btn btn-sm btn-link text-danger ms-2 p-0";390 deleteBtn.innerHTML = '<i class="bi bi-x"></i>';391 deleteBtn.addEventListener("click", () => deleteColumn(colIndex));392 393 th.appendChild(deleteBtn);394 headerRow.appendChild(th);395 });396 397 // Add new column header398 const newColHeader = document.createElement("th");399 newColHeader.className = "text-center";400 newColHeader.style.width = "50px";401 402 const addColBtn = document.createElement("button");403 addColBtn.className = "btn btn-sm btn-link text-primary p-0";404 addColBtn.innerHTML = '<i class="bi bi-plus"></i>';405 addColBtn.addEventListener("click", addNewColumn);406 407 newColHeader.appendChild(addColBtn);408 headerRow.appendChild(newColHeader);409 410 thead.appendChild(headerRow);411 dataTable.appendChild(thead);412 413 // Create table body414 const tbody = document.createElement("tbody");415 416 // Add data rows417 for (let rowIndex = 1; rowIndex < sheetData.length; rowIndex++) {418 const row = document.createElement("tr");419 420 // Add row number cell421 const rowNumCell = document.createElement("td");422 rowNumCell.className = "text-center text-muted";423 rowNumCell.textContent = rowIndex;424 425 // Add delete row button426 const deleteRowBtn = document.createElement("button");427 deleteRowBtn.className = "btn btn-sm btn-link text-danger ms-2 p-0";428 deleteRowBtn.innerHTML = '<i class="bi bi-x"></i>';429 deleteRowBtn.addEventListener("click", () => deleteRow(rowIndex));430 431 rowNumCell.appendChild(deleteRowBtn);432 row.appendChild(rowNumCell);433 434 // Add data cells435 for (let colIndex = 0; colIndex < headers.length; colIndex++) {436 const cell = document.createElement("td");437 cell.className = "editable-cell";438 cell.contentEditable = true;439 440 // Set cell value (handle cases where row might be shorter than headers)441 const cellValue =442 sheetData[rowIndex][colIndex] !== undefined443 ? sheetData[rowIndex][colIndex]444 : "";445 cell.textContent = cellValue;446 447 // Add event listener to save changes448 cell.addEventListener("blur", () => {449 if (!sheetData[rowIndex]) {450 sheetData[rowIndex] = new Array(headers.length).fill("");451 }452 sheetData[rowIndex][colIndex] = cell.textContent;453 });454 455 row.appendChild(cell);456 }457 458 // Add empty cell for new column459 const newColCell = document.createElement("td");460 newColCell.className = "text-center";461 row.appendChild(newColCell);462 463 tbody.appendChild(row);464 }465 466 // Add new row button467 const newRow = document.createElement("tr");468 const newRowCell = document.createElement("td");469 newRowCell.colSpan = headers.length + 2;470 newRowCell.className = "text-center py-3";471 472 const addRowBtn = document.createElement("button");473 addRowBtn.className = "btn btn-sm btn-primary";474 addRowBtn.innerHTML = '<i class="bi bi-plus me-2"></i>Add Row';475 addRowBtn.addEventListener("click", addNewRow);476 477 newRowCell.appendChild(addRowBtn);478 newRow.appendChild(newRowCell);479 tbody.appendChild(newRow);480 481 dataTable.appendChild(tbody);482 }483 484 function addNewRow() {485 if (!sheetData) return;486 487 const newRow = new Array(headers.length).fill("");488 sheetData.push(newRow);489 rowCount.textContent = `${sheetData.length - 1} row${490 sheetData.length - 1 !== 1 ? "s" : ""491 }`;492 renderTable();493 }494 495 function addNewColumn() {496 if (!headers) return;497 498 const newHeader = `Column ${headers.length + 1}`;499 headers.push(newHeader);500 501 // Add empty value for the new column in each row502 for (let i = 0; i < sheetData.length; i++) {503 sheetData[i].push("");504 }505 506 renderTable();507 }508 509 function deleteRow(rowIndex) {510 if (!sheetData || rowIndex < 1 || rowIndex >= sheetData.length) return;511 512 sheetData.splice(rowIndex, 1);513 rowCount.textContent = `${sheetData.length - 1} row${514 sheetData.length - 1 !== 1 ? "s" : ""515 }`;516 renderTable();517 }518 519 function deleteColumn(colIndex) {520 if (!headers || colIndex < 0 || colIndex >= headers.length) return;521 522 headers.splice(colIndex, 1);523 524 // Remove the column from each row525 for (let i = 0; i < sheetData.length; i++) {526 if (sheetData[i].length > colIndex) {527 sheetData[i].splice(colIndex, 1);528 }529 }530 531 renderTable();532 }533 534 function exportToExcel() {535 if (!workbook || !currentSheetName) return;536 537 // Update the current sheet with edited data538 const worksheet = XLSX.utils.aoa_to_sheet(sheetData);539 workbook.Sheets[currentSheetName] = worksheet;540 541 // Generate Excel file542 const excelBuffer = XLSX.write(workbook, {543 bookType: "xlsx",544 type: "array"545 });546 const blob = new Blob([excelBuffer], {547 type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"548 });549 550 // Suggest a filename551 let exportName = "edited_" + (fileName.textContent || "spreadsheet");552 if (!exportName.endsWith(".xlsx")) {553 exportName += ".xlsx";554 }555 556 // Save the file557 saveAs(blob, exportName);558 }559 560 function resetApp() {561 // Reset UI562 fileName.textContent = "No file selected";563 sheetCount.textContent = "0 sheets";564 rowCount.textContent = "0 rows";565 566 emptyState.classList.remove("d-none");567 dataTable.classList.add("d-none");568 sheetTabs.innerHTML = "";569 570 // Reset data571 workbook = null;572 currentSheetName = "";573 sheetData = [];574 headers = [];575 576 // Disable buttons577 exportBtn.disabled = true;578 addRowBtn.disabled = true;579 addColBtn.disabled = true;580 581 // Reset file input582 fileInput.value = "";583 }584 585 function enableButtons() {586 exportBtn.disabled = false;587 addRowBtn.disabled = false;588 addColBtn.disabled = false;589 }590});591 592 </script>593</body>594 595</html>