Преобразование диаграмм из vue-chartjs в PDF

Я пытаюсь создать систему отчетов с диаграммами, нарисованными с помощью 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.

Вот песочница с моим кодом.

Кроме того, если бы вы могли объяснить, как обновлять диаграммы реактивно, это было бы здорово.


person user139595    schedule 21.12.2020    source источник
comment
Не могли бы вы создать репо или codeandbox, которые демонстрируют вашу проблему, чтобы мы могли помочь вам лучше? Предоставленный вами код сложно использовать для воспроизведения проблемы, поскольку он имеет зависимости, которые вы не включили.   -  person marsnebulasoup    schedule 22.12.2020
comment
Вот и все, я выпустил свой код в песочнице - (codesandbox.io/s/quizzical-pond-v15jz?file=/src/App.vue) пожалуйста, помогите мне   -  person user139595    schedule 22.12.2020
comment
Наконец-то я нашел решение, см. Мой ответ ниже. Извините, это заняло так много времени, но на самом деле это было намного сложнее, чем я думал.   -  person marsnebulasoup    schedule 23.12.2020


Ответы (1)


По-видимому, (локально) преобразование диаграмм (в PDF-файлы) из chart.js на самом деле не так просто, а преобразование диаграмм из vue-chartjs еще сложнее. К счастью, есть (по крайней мере) два способа сделать это, хотя они не очень оптимальны.

1) Легкий путь (и)

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'
                }
            ]
        }
       }

... и вы получите это обратно:

image-charts-demo

Его можно использовать бесплатно, без ограничения скорости, но в правом верхнем углу есть водяной знак, как вы можете видеть на изображении выше. Бесплатный план включает (в настоящее время):

  • Неограниченное создание диаграмм
  • Водяной знак
  • Задержка запроса менее секунды
  • Премиум-сеть Google
  • CDN по всему миру
  • Поддержка WebP и Gif

... но вы можете ознакомиться с их ценами для получения дополнительной информации и других планов. Также ознакомьтесь с их документами для получения дополнительной информации и поиграйте в их песочница, чтобы узнать, что вам нужно.

В качестве альтернативы 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 имеет ограничение скорости, если вы оплатить премиальные планы. Также имеется модуль node.js / JS, который вы можете импортировать и использовать в своем проекте здесь.

Хотя водяного знака нет и вы получаете PDF-файл, графические диаграммы по-прежнему, вероятно, являются лучшим решением, если ваше приложение обслуживает множество пользователей, и вам не хочется платить за премиальные планы, поскольку они (графические диаграммы) не Запросы на ограничение скорости.

2) Свободный и местный и слишком сложный путь

Для подхода без внешнего API, который может работать автономно и локально, вы можете использовать html2canvas и jsPDF для визуализации диаграмм в PDF-файлы. Это значительно сложнее, особенно с vue-chartjs, потому что вам нужно специально поместить диаграмму в контейнер размером 594 на 459 пикселей (для бумаги формата Letter) и убедиться, что параметры диаграммы установлены на

{ responsive: true, maintainAspectRatio: false }

... или изображение будет либо растянутым, либо сжатым, либо слишком маленьким или слишком большим, например:

сбой 1  failure 2 отказ 3  отказ 4

Но, если вы сделаете все это, вы можете получить довольно красивые PDF-файлы с диаграммами:

успех 1  успех 2

Я думаю, что самый простой способ экспортировать диаграммы в PDF-файлы - это создать модуль, который можно будет импортировать туда, где вам нужно их экспортировать:

exporter.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>

example1

Другой график скрыт слева, где вы его не видите.

Обе диаграммы будут заполнены одними и теми же данными, но при экспорте в PDF внеэкранная диаграмма будет преобразована в PDF, а не в тот, который увидит пользователь. Это слишком сложно, но это единственный способ, который я смог найти. Если вы попытаетесь скрыть диаграммы с помощью v-if или v-show, при отображении скрытой диаграммы по какой-то причине будет удалена другая. Возможно, вы могли бы создать один компонент, который бы создавал две диаграммы вместе, чтобы упростить задачу, но это потребует дополнительной работы.

Также обратите внимание, что вам может потребоваться увеличить расстояние, на которое скрытая диаграмма перемещается влево, если она все еще отображается на экране.

Обязательно установите зависимости:

npm i html2canavs jspdf

Я создал модуль node.js для exporter.js, который вы можете установить и использовать в качестве альтернативы. См. здесь, чтобы получить инструкции по установке и дополнительную информацию.

Как обновить диаграммы с помощью vue-chartjs

На самом деле это довольно просто. 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 здесь.

person marsnebulasoup    schedule 23.12.2020
comment
Просто чтобы вы знали, такие компании, как Dropbox, Microsoft, Spotify и Grab, предпочитают использовать image-charts.com (более быстрый во всем мире и более надежный) вместо quickchart.io :) // отказ от ответственности: я основатель IC! - person FGRibreau; 24.12.2020
comment
@FGRibreau - Безлимитное бесплатное создание графиков ... вау. Я обязательно обновлю свой ответ этим. Спасибо! - person marsnebulasoup; 24.12.2020
comment
@FGRibreau - Я не вижу способа вывода PDF-файлов с помощью графических диаграмм. Возможно ли это сделать или поддерживаются только изображения? - person marsnebulasoup; 28.12.2020
comment
в настоящее время мы не поддерживаем вывод в формате PDF, но как только заказчик попросит об этом, мы реализуем его на следующий день! - person FGRibreau; 28.12.2020