Как скачать компонент Vue в формате PDF в Nuxt 3 без уязвимых пакетов?

Вопрос или проблема

У меня есть большая жестко закодированная таблица (более 300 элементов таблички) с множеством стилей в моем компоненте RiskGrid.vue, который находится на странице risk.vue. Компонент RiskGrid.vue также содержит более мелкие компоненты под названием RiskGridTable.vue. У меня есть маленькая кнопка на странице risk.vue, которая должна запускать функцию для загрузки компонента RiskGrid.vue (со всеми его стилями) в виде PDF-файла.

Я пытался использовать пакеты html2pdf, vue-html2pdf и jspdf, но у них есть известные уязвимости, и они не дали мне желаемых результатов. Есть ли лучшие способы загрузить этот компонент в формате PDF, или я неправильно использовал эти пакеты? PDF должен содержать заголовок, описание и таблицу.

Вот некоторые фрагменты кода, чтобы вы могли представить мою структуру:

risk.vue:

<TabPanel value="0" class="tab-panel">
    <div ref="rizikSazetakRef">
        <RiskGrid v-if="vrstaIzracuna == 'Djelatnost'" :tip="'RZ'" class="rizik-sazetak" />
        <span v-else>
            <font-awesome-icon icon="info-circle" style="margin-right: 5px;" />
            Выберите тип расчета
        </span>
    </div>
</TabPanel>
<button class="action-icon" @click="downloadRizikSazetak">
    <font-awesome-icon icon="download" size="lg" />
</button>

<script setup>
const downloadRizikSazetak = () => {
    // код для загрузки pdf
};
</script>

Фрагмент кода из RiskGrid.vue:

<template>
    <div class="rizik-sazetak">
        <div v-if="showPopup" class="success-popup">
            <font-awesome-icon :icon="'circle-exclamation'" />
            <span>{{ message }}</span>
            <div class="progress-bar" />
        </div>
        <div class="sazetak">
            <div ref="rizikSazetakRef" class="grid-table">
                <!-- больше кода -->
                <div class="grid-item vertical lightgreen db-l">Изменения ветра</div>
                <div class="grid-item table-header">Первый-ал</div>
                <div class="grid-item table-header">Первый-бл</div>
                <div class="grid-item table-header">Первый-кл</div>
                <div class="grid-item table-header">Первый-дл</div>
                <div class="grid-item table-item nb-b">
                    <RiskGridTable :pozicija="12" :data="structuredData['12']" class="sazetak-table" />
                </div>
                <!-- больше кода -->
            </div>
            <LegendaBoja v-if="structuredData" class="legenda" />
        </div>
    </div>
</template>

<style scoped>
/* больше кода */
.vertical {
    /* writing-mode: vertical-lr; */
    writing-mode: sideways-lr;
    text-orientation: upright;
    text-align: center;
    text-wrap: wrap;
    font-size: 14px;
    padding: 5px;

    border-right: none;
    border-left: none;
    border-bottom: none;
}

.grid-item:nth-child(57) {
    grid-row: 7;
    grid-column: 12;
}

.grid-item:nth-child(58) {
    grid-row-start: 4;
    grid-column-start: 13;
    grid-row-end: 8;
    grid-column-end: 20;
}

.grid-item:nth-child(59) {
    grid-column: 20;
    grid-row: 4 / 8;
}
/* больше кода */
</style>

RiskGridTable.vue:

<template>
    <div>
        <div v-if="data" class="grid">
            <div class="_grid-item first-div" :class="getColorClass(data[0].history)">
                <span class="first" :style="getFontSize(data[0].history)">
                    {{ data[0].history }}
                </span>
            </div>
            <div class="_grid-item db-s" :class="getColorClass(data[0].p0_4_5)" :style="getFontSize(data[0].p0_4_5)">
                {{ data[0].p0_4_5 }}
            </div>
            <div class="_grid-item" :class="getColorClass(data[0].p0_8_5)" :style="getFontSize(data[0].p0_8_5)">
                {{ data[0].p0_8_5 }}
            </div>
            <!-- больше кода -->
        </div>
    </div>
</template>

Ответ или решение

Для скачивания компонента Vue в формате PDF в Nuxt 3 без использования небезопасных пакетов можно воспользоваться библиотекой pdf-lib, которая не имеет известных уязвимостей и предоставляет достаточно широкий функционал для работы с PDF-документами.

Вот как можно реализовать данную функциональность для вашего компонента RiskGrid.vue, принимая во внимание вашу текущую структуру.

Шаги для реализации

  1. Установите pdf-lib:
npm install pdf-lib
  1. Создайте функцию для сборки и скачивания PDF:
    В вашем компоненте risk.vue мы добавим логику для генерации PDF-документа при нажатии кнопки.
<template>
  <TabPanel value="0" class="tab-panel">
    <div ref="rizikSazetakRef">
      <RiskGrid v-if="vrstaIzracuna == 'Djelatnost'" :tip="'RZ'" class="rizik-sazetak" />
      <span v-else>
        <font-awesome-icon icon="info-circle" style="margin-right: 5px;" />
        Nije odabrana vrsta izračuna
      </span>
    </div>
  </TabPanel>
  <button class="action-icon" @click="downloadRizikSazetak">
    <font-awesome-icon icon="download" size="lg" />
  </button>
</template>

<script setup>
import { PDFDocument, rgb } from 'pdf-lib';
import { ref } from 'vue';

const rizikSazetakRef = ref(null);

const downloadRizikSazetak = async () => {
  // Создание нового PDF-документа
  const pdfDoc = await PDFDocument.create();
  const page = pdfDoc.addPage([600, 800]); // Укажите размер страницы

  // Получение HTML содержимого
  const element = rizikSazetakRef.value;
  if (element) {
    // Подготовка изображения из HTML
    const svg = element.outerHTML; // Можно конвертировать ваш HTML в PNG или SVG
    const pngImageBytes = await fetch('data:image/svg+xml;base64,' + btoa(svg)).then(res => res.arrayBuffer());

    const pngImage = await pdfDoc.embedPng(pngImageBytes);
    const pngDims = pngImage.scale(1);

    // Вставка изображения на страницу
    page.drawImage(pngImage, {
      x: 0,
      y: page.getHeight() - pngDims.height,
      width: pngDims.width,
      height: pngDims.height,
    });

    // Сохранение PDF-документа
    const pdfBytes = await pdfDoc.save();
    const blob = new Blob([pdfBytes], { type: 'application/pdf' });
    const link = document.createElement('a');
    link.href = URL.createObjectURL(blob);
    link.download = 'RiskGrid.pdf';
    link.click();
  }
};
</script>

Объяснение кода

  • В данном коде мы используем библиотеку pdf-lib, чтобы создать новый PDF-документ.
  • Мы получаем HTML-содержимое из компонента RiskGrid.vue, конвертируем его в изображение, а затем добавляем это изображение на страницу PDF.
  • После создания PDF-документа мы вызываем метод save, чтобы получить байты PDF и создаем Blob, чтобы инициировать скачивание.

Замечания

  • Убедитесь, что у вас правильно настроены стили и структура RiskGrid.vue, чтобы они корректно отображались в сквозном SVG.
  • Логика преобразования HTML в изображение должна быть дополнительно реализована, так как данный код просто извлекает HTML и требует дальнейшей обработки (например, с использованием html2canvas для конвертации HTML в PNG или SVG).

Заключение

Данный подход позволяет вам создать PDF-документ без использования небезопасных пакетов, предоставляя возможность сохранить все стили и структуру вашего компонента. Интеграция приведенного выше кода в ваш проект позволит осуществить загрузку компонента в формате PDF.

Оцените материал
Добавить комментарий

Капча загружается...