CodeBox CodeBox

【コピペするだけ】Nuxt (Vue.js) × pdfmakeで納品書を作成する方法

Vue / Nuxt
けい

概要

クライアントサイド(Nuxt.js)でPDFの納品書を作成する機会があったので、その作成方法とテンプレートを紹介します。今回作成する納品書はこちらです(PDFに記載されている情報は全て架空の情報です。)

コード全体

全体のコードはgithubに反映していますので、クローンすることで動かすこともできます。今回使用している日本語フォントは、こちらのgithubからダウンロードできます。

日本語フォントは「GenShinGothic-Normal.ttf」と「GenShinGothic-Bold.ttf」をサブセット化(不要なものを省く)したもので、元々は8MBぐらいあったのを2MBぐらいに圧縮しています。

[Tutorial.vueの内容]

<template>
  <button
    style="
      padding: 10px 20px;
      margin: 10px;
      background-color: blue;
      color: white;
    "
    @click="outputPDF"
  >
    PDFを出力する
  </button>
</template>


<script>
/* eslint-disable */
import pdfFonts from "~/static/vfs_fonts";


export default {
  methods: {
    toDataURL(url, callback) {
      let xhr = new XMLHttpRequest();
      xhr.onload = () => {
        let reader = new FileReader();
        reader.onloadend = () => {
          callback(reader.result);
        };
        reader.readAsDataURL(xhr.response);
      };
      xhr.open("GET", url);
      xhr.responseType = "blob";
      xhr.send();
    },


    outputPDF() {
      this.toDataURL(require("~/assets/images/logo.jpeg"), (dataUrl) => {
        pdfMake.vfs = pdfFonts.pdfMake.vfs;
        pdfMake.fonts = {
          GenShin: {
            normal: "genshin-normal.ttf",
            bold: "genshin-bold.ttf",
          },
          English: {
            normal:
              "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.66/fonts/Roboto/Roboto-Regular.ttf",
            bold: "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.66/fonts/Roboto/Roboto-Medium.ttf",
          },
        };
        const defaultStyle = "GenShin";


        const docDefinition = {
          defaultStyle: {
            font: defaultStyle,
          },
          content: [],
        };


        const itemData = [
          [
            {
              text: "サンプル商品1",
              font: "GenShin",
              fontSize: 9,
              alignment: "right",
            },
            {
              text: "A-12345",
              font: "GenShin",
              fontSize: 9,
              alignment: "right",
            },
            { text: "S", font: "GenShin", fontSize: 9, alignment: "right" },
            { text: "Black", font: "GenShin", fontSize: 9, alignment: "right" },
            { text: "100", font: "GenShin", fontSize: 9, alignment: "right" },
            { text: "¥999", font: "GenShin", fontSize: 9, alignment: "right" },
            {
              text: "¥99,900",
              font: "GenShin",
              fontSize: 9,
              alignment: "right",
            },
          ],
          [
            {
              text: "サンプル商品2",
              font: "GenShin",
              fontSize: 9,
              alignment: "right",
            },
            {
              text: "A-23456",
              font: "GenShin",
              fontSize: 9,
              alignment: "right",
            },
            { text: "M", font: "GenShin", fontSize: 9, alignment: "right" },
            { text: "Blue", font: "GenShin", fontSize: 9, alignment: "right" },
            { text: "100", font: "GenShin", fontSize: 9, alignment: "right" },
            { text: "¥999", font: "GenShin", fontSize: 9, alignment: "right" },
            {
              text: "¥99,900",
              font: "GenShin",
              fontSize: 9,
              alignment: "right",
            },
          ],
          [
            {
              text: "サンプル商品3",
              font: "GenShin",
              fontSize: 9,
              alignment: "right",
            },
            {
              text: "A-34567",
              font: "GenShin",
              fontSize: 9,
              alignment: "right",
            },
            { text: "L", font: "GenShin", fontSize: 9, alignment: "right" },
            { text: "Green", font: "GenShin", fontSize: 9, alignment: "right" },
            { text: "100", font: "GenShin", fontSize: 9, alignment: "right" },
            { text: "¥999", font: "GenShin", fontSize: 9, alignment: "right" },
            {
              text: "¥99,900",
              font: "GenShin",
              fontSize: 9,
              alignment: "right",
            },
          ],
        ];


        const content = [
          {
            text: "納品書",
            font: "GenShin",
            fontSize: 18,
            alignment: "center",
            margin: [0, 0, 0, 30],
          },
          {
            columns: [
              {
                width: "60%",
                margin: [0, 0, 0, 30],
                layout: {
                  paddingTop: function (i, node) {
                    return 0.5;
                  },
                  paddingBottom: function (i, node) {
                    return 0.5;
                  },
                },
                table: {
                  headerRows: 1,
                  widths: ["*"],
                  body: [
                    [
                      {
                        text: "株式会社〇〇",
                        font: "GenShin",
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                        margin: [0, 0, 0, 0],
                      },
                    ],
                    [
                      {
                        text: `〒999-9999`,
                        font: "GenShin",
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                        margin: [0, 0, 0, 0],
                      },
                    ],
                    [
                      {
                        text: `〇〇県 〇〇市 98-128-89`,
                        font: "GenShin",
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                        margin: [0, 0, 0, 0],
                      },
                    ],
                    [
                      {
                        text: `090-8888-9999`,
                        font: "GenShin",
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                        margin: [0, 0, 0, 0],
                      },
                    ],
                    [
                      {
                        text: `test@example.com`,
                        font: "GenShin",
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                        margin: [0, 0, 0, 0],
                      },
                    ],
                  ],
                },
              },
              {
                layout: {
                  vLineColor: "#333333",
                  hLineColor: "#333333",
                  hLineWidth: function (i, node) {
                    return 1.5;
                  },
                  vLineWidth: function (i, node) {
                    return 1.5;
                  },
                },
                table: {
                  headerRows: 1,
                  widths: ["*", "*"],
                  body: [
                    [
                      { text: "納品書番号", fontSize: 10 },
                      { text: "0123456789", fontSize: 10 },
                    ],
                    [
                      { text: "発効日", fontSize: 10 },
                      { text: "2021/12/31", fontSize: 10 },
                    ],
                  ],
                },
              },
            ],
          },


          {
            columns: [
              {
                width: "60%",
                margin: [0, 0, 0, 10],
                layout: {
                  paddingTop: function (i, node) {
                    return 1;
                  },
                  paddingBottom: function (i, node) {
                    return 1;
                  },
                },
                table: {
                  headerRows: 1,
                  widths: ["*"],
                  body: [
                    [
                      {
                        text: `クライアント様`,
                        font: "GenShin",
                        fontSize: 14,
                        bold: true,
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                        margin: [0, 0, 0, 2],
                      },
                    ],
                    [
                      {
                        text: `この度はお買い上げ頂き誠にありがとうございます。`,
                        font: "GenShin",
                        fontSize: 10,
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                        margin: [0, 0, 0, 0],
                      },
                    ],
                    [
                      {
                        text: `下記内容にて納品させていただきます。`,
                        fontSize: 10,
                        font: "GenShin",
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                        margin: [0, 0, 0, 20],
                      },
                    ],
                    [
                      {
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                        columns: [
                          {
                            width: "80%",
                            layout: {
                              paddingTop: function (i, node) {
                                return 1;
                              },
                              paddingBottom: function (i, node) {
                                return 1;
                              },
                            },
                            table: {
                              headerRows: 1,
                              widths: ["*", "*"],
                              body: [
                                [
                                  {
                                    width: "auto",
                                    text: "ご請求金額",
                                    font: "GenShin",
                                    bold: true,
                                    fontSize: 14,
                                  },
                                  {
                                    width: "auto",
                                    text: "¥ 300,000",
                                    font: "GenShin",
                                    fontSize: 14,
                                    bold: true,
                                  },
                                ],
                              ],
                            },
                          },
                        ],
                      },
                    ],
                  ],
                },
              },
              {
                layout: {
                  paddingTop: function (i, node) {
                    return 1;
                  },
                  paddingBottom: function (i, node) {
                    return 1;
                  },
                },
                table: {
                  headerRows: 1,
                  widths: ["*"],
                  body: [
                    [
                      {
                        image: dataUrl,
                        width: 150,
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                        margin: [0, 0, 0, 2],
                      },
                    ],
                    [
                      {
                        text: "〇〇株式会社",
                        font: "GenShin",
                        fontSize: 10,
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                        margin: [0, 3, 0, 0],
                      },
                    ],
                    [
                      {
                        text: "〒777-6666",
                        font: "GenShin",
                        fontSize: 10,
                        margin: [0, 0, 0, 0],
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                      },
                    ],
                    [
                      {
                        text: "〇〇県 〇〇市 123-5678",
                        font: "GenShin",
                        fontSize: 10,
                        margin: [0, 0, 0, 0],
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                      },
                    ],
                    [
                      {
                        text: "〇〇丁目 〇〇ビル10F",
                        font: "GenShin",
                        fontSize: 10,
                        margin: [0, 0, 0, 0],
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                      },
                    ],
                    [
                      {
                        text: "info@example.jp",
                        font: "English",
                        fontSize: 10,
                        margin: [0, 0, 0, 1],
                        borderColor: [
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                          "#ffffff",
                        ],
                      },
                    ],
                  ],
                },
              },
            ],
          },


          {
            layout: {
              hLineWidth: function (i, node) {
                return 1.5;
              },
            },
            table: {
              widths: ["100%"],
              body: [
                [
                  {
                    text: "",
                    margin: [0, 10, 0, 0],
                    borderColor: ["#ffffff", "#ffffff", "#ffffff", "#333333"],
                  },
                ],
              ],
            },
          },


          {
            layout: {
              vLineColor: "#333333",
              hLineColor: "#333333",
              hLineWidth: function (i, node) {
                return 0.2;
              },
              vLineWidth: function (i, node) {
                return 0.2;
              },
            },
            margin: [0, 20, 0, 0],
            table: {
              headerRows: 2,
              widths: [120, 100, "auto", "auto", "auto", "auto", 100],
              body: [
                [
                  {
                    text: "商品名",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                  {
                    text: "商品コード",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                  {
                    text: "サイズ",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                  {
                    text: "カラー",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                  {
                    text: "個数",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                  {
                    text: "単価 (税込) ",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                  {
                    text: "合計金額 (税込) ",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                ],
                ...itemData,
                [
                  { text: "", font: "GenShin", fontSize: 9 },
                  { text: "", font: "GenShin", fontSize: 9 },
                  { text: "", font: "GenShin", fontSize: 9 },
                  { text: "", font: "GenShin", fontSize: 9 },
                  { text: "", font: "GenShin", fontSize: 9 },
                  {
                    text: "商品合計",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                  {
                    text:299,700`,
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "right",
                  },
                ],
                [
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  {
                    text: "送料",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                  {
                    text: "¥1,000",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "right",
                  },
                ],
                [
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin", fontSize: 9 },
                  {
                    text: "代引き手数料",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                  {
                    text:300`,
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "right",
                  },
                ],
                [
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin", fontSize: 9 },
                  {
                    text: "クーポン割引",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                  {
                    text: "¥1,000",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "right",
                  },
                ],
                [
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin" },
                  { text: "", font: "GenShin", fontSize: 9 },
                  {
                    text: "総合計金額",
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "center",
                  },
                  {
                    text:300,000`,
                    font: "GenShin",
                    fontSize: 9,
                    alignment: "right",
                  },
                ],
              ],
            },
          },
        ];
        docDefinition.content = docDefinition.content.concat(content);
        pdfMake.createPdf(docDefinition).open();
      });
    },
  },
};
/* eslint-enable */
</script>

<style scoped>
</style>


ABOUT ME

けい
ベンチャーのフロントエンジニア。 主にVueとTypescriptを使っています。ライターのための文字数カウントアプリ:https://easy-count.vercel.app/