Exportación de Datos de Tablas y JSON a Archivos Excel en Vue

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');

Etiquetas: Vue Excel FileSaver XLSX JavaScript

Publicado el 6-16 04:20