Я пытаюсь сделать систему отчетов с диаграммами, нарисованными vue-chartjs, и я хотел загрузить их в виде PDF или любого файла.
Вот как выглядит App.vue
<template>
<div>
<div id = "printSection" ref = "document">
<AreaChart />
<PieChart />
...
</div>
<button @click = "printSection">Print Shit</button>
</div>
</template>
<script>
import AreaChart from "./SuperAdmin/AreaChart";
import PieChart from "./SuperAdmin/PieChart";
...
//import { Printd } from "printd"; //tried with this one
//import html2pdf from 'html2pdf.js'; // and this one
export default {
name: "App",
components: {
AreaChart,
PieChart,
},
data() {
return { };
},
mounted() {
var d = new Printd();
},
methods: {
printSomething () { // this was for printd
this.d.print( this.$el, this.cssText)
},
exportToPDF () { // this was for html2pdf
html2pdf()
html2pdf(this.$refs.document, {
margin: 1,
filename: 'document.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { dpi: 192, letterRendering: true },
jsPDF: { unit: 'in', orientation: 'landscape' }
})
},
printSection() {
this.$htmlToPaper("printSection"); // this was for htmltopaper
}
}
};
</script>
<style>
...
</style>
Итак, как вы можете видеть, я попробовал 3 пакета для преобразования диаграмм в PDF-файлы, но они всегда печатают пустые документы, за исключением html2pdf, который разрезает графики пополам.
Вот один из моих графиков:
PieChart.vue
<script>
import { Pie } from "vue-chartjs";
import displayshit from '../../service/Hello'
import labels from 'chartjs-plugin-labels'
//import { plugins } from 'chart.js';
export default {
extends: Pie,
async mounted() {
var data = [];
var new_data = [];
var Type = ['Mobile','Card','Both']
var sum = 0 ;
data = (await displayshit.displayanothershit()).data.message;
var Mobile_number = data.find(element=> JSON.stringify(element._id) == JSON.stringify([Type[0]])) || 0
var Card_number = data.find(element=> JSON.stringify(element._id) == JSON.stringify([Type[1]])) || 0
var Both_number = data.find(element=> element._id.length == 2) || 0
new_data.push(Mobile_number.count || 0)
new_data.push(Card_number.count || 0)
new_data.push(Both_number.count || 0)
this.gradient = this.$refs.canvas
.getContext("2d")
.createLinearGradient(0, 0, 0, 450);
this.gradient2 = this.$refs.canvas
.getContext("2d")
.createLinearGradient(0, 0, 0, 450);
this.gradient.addColorStop(0, "rgba(255, 0,0, 0.5)");
this.gradient.addColorStop(0.5, "rgba(255, 0, 0, 0.25)");
this.gradient.addColorStop(1, "rgba(255, 0, 0, 0)");
this.gradient2.addColorStop(0, "rgba(0, 231, 255, 0.9)");
this.gradient2.addColorStop(0.5, "rgba(0, 231, 255, 0.25)");
this.gradient2.addColorStop(1, "rgba(0, 231, 255, 0)");
this.renderChart(
{
labels: Type,
datasets: [
{
backgroundColor: [this.gradient, this.gradient2, "#00D8FF"],
data: new_data
},
],
},
{ responsive: true, maintainAspectRatio: false,plugins: {
labels: {render: 'label',
}
}}
);
},
};
</script>
Я хочу преобразовать диаграммы в файлы PDF, но я также хочу иметь возможность выбирать, какие диаграммы включать в PDF.
Вот песочница с моим кодом.
Кроме того, если бы вы могли объяснить, как реактивно обновлять диаграммы, это было бы здорово.
Вот, я выпустил свой код в песочницу - (codeandbox.io/s/quizzical-pond-v15jz?file=/src/App.vue) пожалуйста, помогите мне
Наконец-то я нашел решение, см. мой ответ ниже. Извините, что так долго, но на самом деле это было значительно сложнее, чем я думал.
По-видимому, (локально) преобразование диаграмм (в PDF-файлы) из chart.js на самом деле не так просто, а преобразование диаграмм из vue-chartjs еще сложнее. К счастью, есть (по крайней мере) два способа сделать это, хотя они не очень оптимальны.
image-charts.com предоставляет API, который может принимать данные диаграммы в формате chart.js и преобразовывать их в изображение, но, похоже, не может преобразовать их в PDF.
Например, вы можете передать эти данные в виде URL-адреса диаграммам изображений:
https://image-charts.com/chart.js/2.8.0?
bkg=white
&c= {
type: 'line',
data: { labels: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug'],
datasets: [
{
backgroundColor: 'rgba(255,150,150,0.5)',
borderColor:'rgb(255,150,150)',
data:[-23,64,21,53,-39,-30,28,-10],
label:'Dataset',fill:'origin'
}
]
}
}
... и вы получите это обратно:
Его можно использовать бесплатно, без ограничения скорости, но в правом верхнем углу есть водяной знак, как вы можете видеть на изображении выше. Бесплатный план включает (в настоящее время):
... но вы можете проверить их цены для получения дополнительной информации и других планов. Также ознакомьтесь с их документацией для получения дополнительной информации и поиграйте в их песочнице, чтобы убедиться, что это то, что вам нужно.
В качестве альтернативы, Quickchart.io также предоставляет простой в использовании API, который принимает данные диаграммы и выводит PDF (или другие форматы) для использования. Просто отправьте запрос в конечную точку /chart
с параметрами данных/форматирования диаграммы, и вы получите вывод в формате PDF:
https://quickchart.io/chart?
format=PDF
&bkg=white
&c= {
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [
{ label: 'Users', data: [50, 60, 70, 180] },
{ label: 'Revenue', data: [100, 200, 300, 400] }
]
}
}
Несмотря на то, что водяного знака нет, и вы получаете вывод в формате PDF, диаграммы изображений, вероятно, по-прежнему являются лучшим решением, если ваше приложение обслуживает много пользователей, и вы не хотите платить за премиальные планы, поскольку они (диаграммы изображений) не не ограничивайте запросы.
Для подхода без внешнего API, который может работать автономно и локально, вы можете использовать html2canvas и jsPDF для преобразования диаграмм в PDF-файлы. Это значительно сложнее, особенно с vue-chartjs, потому что вам нужно специально поместить диаграмму в контейнер размером 594 на 459 пикселей (для бумаги формата Letter) и убедиться, что параметры диаграммы установлены на
{ responsive: true, maintainAspectRatio: false }
...или изображение будет либо растянуто, либо сжато, либо слишком маленькое, либо слишком большое, например:
Но, если вы сделаете все это, вы можете получить несколько довольно красивых PDF-файлов с диаграммами:
Я думаю, что самый простой способ экспортировать диаграммы в PDF-файлы — это создать модуль, который можно импортировать в любое место, где вам нужно их экспортировать:
экспортер.js
import html2canvas from "html2canvas";
import jsPDF from "jspdf";
export default class Exporter {
constructor(charts) {
this.charts = charts;
this.doc = new jsPDF("landscape", "px", "letter");
}
export_pdf() {
return new Promise((resolve, reject) => {
try {
this.charts.forEach(async (chart, index, charts) => {
this.doc = await this.create_page(chart, this.doc);
if (index === charts.length - 1) resolve(this.doc); // this.doc.save("charts.pdf"); // <-- at the end of the loop; display PDF
this.doc.addPage("letter");
});
} catch (error) {
reject(error);
}
});
}
async create_page(chart, doc) {
const canvas = await html2canvas(chart, {
scrollY: -window.scrollY,
scale: 5 // <-- this is to increase the quality. don't raise this past 5 as it doesn't get much better and just takes longer to process
});
const image = canvas.toDataURL("image/jpeg", 1.0),
pageWidth = doc.internal.pageSize.getWidth(),
pageHeight = doc.internal.pageSize.getHeight(),
ratio = (pageWidth - 50) / canvas.width,
canvasWidth = canvas.width * ratio,
canvasHeight = canvas.height * ratio,
marginX = (pageWidth - canvasWidth) / 2,
marginY = (pageHeight - canvasHeight) / 2;
doc.addImage(image, "JPEG", marginX, marginY, canvasWidth, canvasHeight);
return doc;
}
}
Вы можете импортировать это в App.vue или куда угодно, где у вас есть диаграммы, и создать новый экземпляр Exporter
с массивом элементов диаграммы для экспорта, затем вызвать для него метод export_pdf()
и делать с PDF все, что хотите:
let area = document.getElementById("area");
let line = document.getElementById("line");
const exp = new Exporter([line, area]);
exp.export_pdf().then((pdf) => pdf.save("charts.pdf"));
Таким образом, вы можете отображать столько диаграмм, сколько хотите.
Пример App.vue:
import Exporter from "@/assets/exporter.js";
...
export default {
methods: {
exportToPDF() {
let bar = document.getElementById("bar");
let radar = document.getElementById("radar");
let pie = document.getElementById("pie");
let area = document.getElementById("area");
let line = document.getElementById("line");
const exp = new Exporter([line, bar, radar, pie, area]);
exp.export_pdf().then((pdf) => pdf.save("charts.pdf"));
}
...
}
Теперь обратите внимание, что, как упоминалось выше, элемент диаграммы, который вы передаете Exporter
, должен содержаться в элементе размером 594 на 459 пикселей, поскольку это размер бумаги и оптимальный размер для диаграммы. Но тогда вы не сможете изменить размер диаграммы на своем веб-сайте или что-то подобное, поэтому вы можете создать две идентичные диаграммы, одну из которых вы можете делать все, что хотите, а другую, содержащуюся в div справа размер, но размещен за пределами экрана, поэтому пользователь его не видит.
Что-то вроде этого:
<template>
<BarChart />
<div class = "hidden">
<BarChart id = "bar" />
</div>
</template>
<script>
export default { STUFF GOES HERE}
</script>
<style>
.hidden {
width: 594px !important;
height: 459px !important;
position: absolute !important;
left: -600px !important;
}
</style>
Другая диаграмма скрыта слева, где вы ее не видите.
Обе диаграммы будут заполнены одними и теми же данными, но при экспорте в PDF внеэкранная диаграмма будет преобразована в PDF, а не в тот, который увидит пользователь. Это слишком сложно, но это единственный способ, который я мог найти. Если вы попытаетесь скрыть диаграммы с помощью v-if или v-show, когда вы покажете скрытый график, другой по какой-то причине будет удален. Возможно, вы могли бы создать один компонент, который будет создавать две диаграммы вместе, чтобы упростить задачу, но это потребует дополнительной работы.
Также обратите внимание, что вам, возможно, придется увеличить расстояние, на которое скрытая диаграмма перемещается влево, если она все еще отображается на экране.
Обязательно установите зависимости:
npm i html2canavs jspdf
Я создал модуль node.js для exporter.js, который вы можете установить и использовать в качестве альтернативы. См. здесь инструкции по установке и дополнительную информацию.
На самом деле это довольно просто. vue-chartjs имеет два миксина, которые вы можете использовать для достижения этой цели: reactiveProp
и reactiveData
. Если вы хотите передать динамические данные в компонент диаграммы, который вы создали в качестве реквизита, вы можете использовать reactiveProp
:
Linechart.js
import { Line, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default {
extends: Line,
mixins: [reactiveProp],
props: ['options'],
mounted () {
// this.chartData is created in the mixin.
// If you want to pass options please create a local options object
this.renderChart(this.chartData, this.options)
}
}
// from https://vue-chartjs.org/guide/#updating-charts
Везде, где вы хотите отобразить диаграмму, просто импортируйте Linechart.js и зарегистрируйте его как компонент:
import LineChart from './LineChart.js'
export default {
components: {
LineChart
},
...
}
... затем поместите диаграмму в свой шаблон и передайте данные через опору chart-data
:
<line-chart :chart-data = "datacollection"></line-chart>
Он будет автоматически обновляться при изменении datacollection
.
Примеры кода и много более подробной информации об этом доступны в документации.
Посмотрите демонстрацию обновления диаграмм и преобразования их в PDF-файлы здесь.
Чтобы вы знали, такие компании, как Dropbox, Microsoft, Spotify и Grab, предпочитают использовать image-charts.com (более быстрый по всему миру и более надежный) вместо quickchart.io :) // Отказ от ответственности: я основатель IC!
@FGRibreau - я не вижу способа для диаграмм изображений вывести PDF-файл. Возможно ли это сделать или поддерживаются только изображения?
в настоящее время мы не поддерживаем вывод PDF, но как только клиент попросит об этом, мы реализуем его на следующий день!
Не могли бы вы создать репозиторий или codeandbox, который демонстрирует вашу проблему, чтобы мы могли помочь вам лучше? Предоставленный вами код сложно использовать для воспроизведения проблемы, так как он имеет зависимости, которые вы не включили.