Вопрос или проблема
У меня есть большая жестко закодированная таблица (более 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
, принимая во внимание вашу текущую структуру.
Шаги для реализации
- Установите
pdf-lib
:
npm install pdf-lib
- Создайте функцию для сборки и скачивания 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.