En aplicaciones Vue, se pueden utilizar las bibliotecas file-saver y xlsx para exportar datos de tablas HTML o estructuras JSON a archivos Excel.
Instalación de paquetes npm
Ejecute el siguiente comando para instalar las dependencias necesarias:
npm install file-saver xlsx
Importación en el componente Vue
import FileSaver from 'file-saver';
import XLSX from 'xlsx';
Función para exportar tablas HTML a Excel
Define una función que capture el contenido de una tabla y lo convierta en un archivo Excel. Se utiliza raw: true para evitar modificaciones automáticas en los formatos, como fechas.
function exportarTablaExcel(seleccionador, nombreArchivo) {
const elementoTabla = document.querySelector(seleccionador);
const libro = XLSX.utils.table_to_book(elementoTabla, { raw: true });
const datosBinarios = XLSX.write(libro, { bookType: 'xlsx', bookSST: true, type: 'array' });
try {
const blob = new Blob([datosBinarios], { type: 'application/octet-stream' });
const nombreCompleto = nombreArchivo + formatearFecha(new Date(), '_yyyyMMddhhmmssSSS') + '.xlsx';
FileSaver.saveAs(blob, nombreCompleto);
} catch (error) {
if (typeof console !== 'undefined') {
console.error('Error al exportar:', error);
}
}
return datosBinarios;
}
La función formatearFecha se asume como un auxiliar para generar marcas de tiempo únicas en los nombres de archivo.
Función para exportar datos JSON a Excel
Para convertir datos JSON, se construye una hoja de cálculo a partir de matrices y se ajusta el ancho de columnas automáticamente. Esto incluye funciones auxiliares para el manejo de celdas y rengos.
function generarArregloDesdeTabla(elementoTabla) {
const resultado = [];
const filas = elementoTabla.querySelectorAll('tr');
const rangos = [];
for (let i = 0; i < filas.length; i++) {
const filaActual = [];
const celdas = filas[i].querySelectorAll('td');
for (let j = 0; j < celdas.length; j++) {
const celda = celdas[j];
const colspan = parseInt(celda.getAttribute('colspan')) || 1;
const rowspan = parseInt(celda.getAttribute('rowspan')) || 1;
let valorCelda = celda.innerText;
if (valorCelda !== '' && !isNaN(valorCelda)) {
valorCelda = Number(valorCelda);
}
rangos.forEach(rango => {
if (i >= rango.inicio.fila && i <= rango.fin.fila && filaActual.length >= rango.inicio.columna && filaActual.length <= rango.fin.columna) {
for (let k = 0; k <= rango.fin.columna - rango.inicio.columna; k++) {
filaActual.push(null);
}
}
});
if (rowspan > 1 || colspan > 1) {
rangos.push({
inicio: { fila: i, columna: filaActual.length },
fin: { fila: i + rowspan - 1, columna: filaActual.length + colspan - 1 }
});
}
filaActual.push(valorCelda !== '' ? valorCelda : null);
if (colspan > 1) {
for (let l = 1; l < colspan; l++) {
filaActual.push(null);
}
}
}
resultado.push(filaActual);
}
return { datos: resultado, rangos: rangos };
}
function convertirFechaANumero(fecha) {
const epoch = Date.parse(fecha);
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}
function crearHojaDesdeMatriz(matrizDatos) {
const hoja = {};
const rango = { inicio: { fila: Infinity, columna: Infinity }, fin: { fila: 0, columna: 0 } };
for (let i = 0; i < matrizDatos.length; i++) {
for (let j = 0; j < matrizDatos[i].length; j++) {
if (rango.inicio.fila > i) rango.inicio.fila = i;
if (rango.inicio.columna > j) rango.inicio.columna = j;
if (rango.fin.fila < i) rango.fin.fila = i;
if (rango.fin.columna < j) rango.fin.columna = j;
const celda = { valor: matrizDatos[i][j] };
if (celda.valor === null) continue;
const referenciaCelda = XLSX.utils.encode_cell({ c: j, r: i });
if (typeof celda.valor === 'number') {
celda.tipo = 'n';
} else if (typeof celda.valor === 'boolean') {
celda.tipo = 'b';
} else if (celda.valor instanceof Date) {
celda.tipo = 'n';
celda.formato = XLSX.SSF._table[14];
celda.valor = convertirFechaANumero(celda.valor);
} else {
celda.tipo = 's';
}
hoja[referenciaCelda] = { v: celda.valor, t: celda.tipo, z: celda.formato };
}
}
if (rango.inicio.columna !== Infinity) {
hoja['!ref'] = XLSX.utils.encode_range({ s: { c: rango.inicio.columna, r: rango.inicio.fila }, e: { c: rango.fin.columna, r: rango.fin.fila } });
}
return hoja;
}
function LibroDeTrabajo() {
this.hojas = [];
this.datosHojas = {};
}
function convertirCadenaABuffer(cadena) {
const buffer = new ArrayBuffer(cadena.length);
const vista = new Uint8Array(buffer);
for (let i = 0; i < cadena.length; i++) {
vista[i] = cadena.charCodeAt(i) & 0xFF;
}
return buffer;
}
export function exportarJsonExcel(encabezados, datosMatriz, tituloArchivo) {
const datosCompletos = [encabezados, ...datosMatriz];
const nombreHoja = 'Hoja1';
const libro = new LibroDeTrabajo();
const hoja = crearHojaDesdeMatriz(datosCompletos);
const anchosColumnas = [];
for (let i = 0; i < datosCompletos[0].length; i++) {
let maxAncho = 10;
for (let j = 0; j < datosCompletos.length; j++) {
const valor = datosCompletos[j][i];
if (valor !== null && valor !== undefined) {
const longitud = valor.toString().length;
const esUnicode = valor.toString().charCodeAt(0) > 255;
const anchoCalculado = esUnicode ? longitud * 2 : longitud;
if (anchoCalculado > maxAncho) {
maxAncho = anchoCalculado;
}
}
}
anchosColumnas.push({ wch: maxAncho });
}
hoja['!cols'] = anchosColumnas;
libro.hojas.push(nombreHoja);
libro.datosHojas[nombreHoja] = hoja;
const opcionesEscritura = { bookType: 'xlsx', bookSST: false, type: 'binary' };
const datosBinarios = XLSX.write(libro, opcionesEscritura);
const nombreFinal = (tituloArchivo || 'datos') + formatearFecha(new Date(), '_yyyyMMddhhmmssSSS') + '.xlsx';
const blob = new Blob([convertirCadenaABuffer(datosBinarios)], { type: 'application/octet-stream' });
FileSaver.saveAs(blob, nombreFinal);
}
Métodos de uso
Para exporatr una tabla HTML, asigne un selector CSS (como una clase o ID) a la tabla y invoque la función exportarTablaExcel:
exportarTablaExcel('#miTabla', 'reporte_ventas');
Para expotrar datos JSON, transforme los datos en una matriz y llame a exportarJsonExcel:
const encabezados = ['Nombre', 'Edad', 'Ciudad'];
const datos = [
['Juan', 25, 'Madrid'],
['Ana', 30, 'Barcelona']
];
exportarJsonExcel(encabezados, datos, 'lista_usuarios');