Browse Source

完成PDF导出

Purpose 9 months ago
parent
commit
7331a5f502
4 changed files with 262 additions and 77 deletions
  1. 157 4
      package-lock.json
  2. 2 0
      package.json
  3. 72 73
      src/components/DialogOrderExport.vue
  4. 31 0
      src/components/PdfExporter.vue

+ 157 - 4
package-lock.json

@@ -85,6 +85,21 @@
       "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz",
       "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q=="
     },
+    "@babel/runtime": {
+      "version": "7.24.6",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz",
+      "integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==",
+      "requires": {
+        "regenerator-runtime": "^0.14.0"
+      },
+      "dependencies": {
+        "regenerator-runtime": {
+          "version": "0.14.1",
+          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+          "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+        }
+      }
+    },
     "@babel/template": {
       "version": "7.0.0-beta.44",
       "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz",
@@ -183,6 +198,12 @@
       "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==",
       "dev": true
     },
+    "@types/raf": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
+      "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
+      "optional": true
+    },
     "@vue/compiler-sfc": {
       "version": "2.7.16",
       "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
@@ -609,8 +630,7 @@
     "atob": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
-      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
-      "dev": true
+      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
     },
     "autoprefixer": {
       "version": "7.2.6",
@@ -1580,6 +1600,11 @@
         }
       }
     },
+    "base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="
+    },
     "base64-js": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -1843,6 +1868,11 @@
         "electron-to-chromium": "^1.3.30"
       }
     },
+    "btoa": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
+      "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="
+    },
     "buffer": {
       "version": "4.9.2",
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
@@ -2036,6 +2066,36 @@
       "integrity": "sha512-JRW7kAH8PFJzoPCJhLSHgDgKg5348hsQ68aqb+slnzuB5QFERv846oA/mRChmlLAOdEDeOkRn3ynb1gSFnjt3w==",
       "dev": true
     },
+    "canvg": {
+      "version": "3.0.10",
+      "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz",
+      "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==",
+      "optional": true,
+      "requires": {
+        "@babel/runtime": "^7.12.5",
+        "@types/raf": "^3.4.0",
+        "core-js": "^3.8.3",
+        "raf": "^3.4.1",
+        "regenerator-runtime": "^0.13.7",
+        "rgbcolor": "^1.0.1",
+        "stackblur-canvas": "^2.0.0",
+        "svg-pathdata": "^6.0.3"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "3.37.1",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz",
+          "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==",
+          "optional": true
+        },
+        "regenerator-runtime": {
+          "version": "0.13.11",
+          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+          "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+          "optional": true
+        }
+      }
+    },
     "caseless": {
       "version": "0.12.0",
       "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
@@ -2666,6 +2726,14 @@
         }
       }
     },
+    "css-line-break": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
+      "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+      "requires": {
+        "utrie": "^1.0.2"
+      }
+    },
     "css-loader": {
       "version": "0.28.11",
       "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz",
@@ -3944,6 +4012,12 @@
         "domelementtype": "^2.2.0"
       }
     },
+    "dompurify": {
+      "version": "2.5.5",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.5.tgz",
+      "integrity": "sha512-FgbqnEPiv5Vdtwt6Mxl7XSylttCC03cqP5ldNT2z+Kj0nLxPHJH4+1Cyf5Jasxhw93Rl4Oo11qRoUV72fmya2Q==",
+      "optional": true
+    },
     "domutils": {
       "version": "2.8.0",
       "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
@@ -5026,6 +5100,11 @@
         "websocket-driver": ">=0.5.1"
       }
     },
+    "fflate": {
+      "version": "0.4.8",
+      "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
+      "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="
+    },
     "figures": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
@@ -5942,6 +6021,15 @@
         }
       }
     },
+    "html2canvas": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
+      "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+      "requires": {
+        "css-line-break": "^2.1.0",
+        "text-segmentation": "^1.0.3"
+      }
+    },
     "htmlparser2": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
@@ -6731,6 +6819,29 @@
       "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==",
       "dev": true
     },
+    "jspdf": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz",
+      "integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==",
+      "requires": {
+        "@babel/runtime": "^7.14.0",
+        "atob": "^2.1.2",
+        "btoa": "^1.2.1",
+        "canvg": "^3.0.6",
+        "core-js": "^3.6.0",
+        "dompurify": "^2.2.0",
+        "fflate": "^0.4.8",
+        "html2canvas": "^1.0.0-rc.5"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "3.37.1",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz",
+          "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==",
+          "optional": true
+        }
+      }
+    },
     "jsprim": {
       "version": "1.4.2",
       "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
@@ -8309,8 +8420,7 @@
     "performance-now": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
-      "dev": true
+      "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
     },
     "picocolors": {
       "version": "1.0.1",
@@ -10929,6 +11039,15 @@
       "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
       "dev": true
     },
+    "raf": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
+      "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
+      "optional": true,
+      "requires": {
+        "performance-now": "^2.1.0"
+      }
+    },
     "randombytes": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -11366,6 +11485,12 @@
       "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==",
       "dev": true
     },
+    "rgbcolor": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
+      "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
+      "optional": true
+    },
     "right-align": {
       "version": "0.1.3",
       "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
@@ -12344,6 +12469,12 @@
       "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
       "dev": true
     },
+    "stackblur-canvas": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
+      "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
+      "optional": true
+    },
     "stackframe": {
       "version": "1.3.4",
       "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
@@ -12604,6 +12735,12 @@
       "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
       "dev": true
     },
+    "svg-pathdata": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
+      "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
+      "optional": true
+    },
     "svgo": {
       "version": "0.7.2",
       "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz",
@@ -12650,6 +12787,14 @@
         "inherits": "2"
       }
     },
+    "text-segmentation": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
+      "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+      "requires": {
+        "utrie": "^1.0.2"
+      }
+    },
     "text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -13278,6 +13423,14 @@
       "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
       "dev": true
     },
+    "utrie": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
+      "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+      "requires": {
+        "base64-arraybuffer": "^1.0.2"
+      }
+    },
     "uuid": {
       "version": "3.4.0",
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",

+ 2 - 0
package.json

@@ -15,6 +15,8 @@
     "babel-polyfill": "^6.26.0",
     "echarts": "^5.5.0",
     "element-ui": "^2.15.6",
+    "html2canvas": "^1.4.1",
+    "jspdf": "^2.5.1",
     "qs": "^6.12.1",
     "vue": "^2.5.2",
     "vue-barcode": "^1.3.0",

+ 72 - 73
src/components/DialogOrderExport.vue

@@ -2,83 +2,81 @@
   <el-dialog
     :visible.sync="visible"
     append-to-body
-    width="1000px"
+    width="297mm"
   >
-    <div class="overflow-auto" style="height:500px;margin:0 -20px">
-      <div class="flex column layout-gap" style="min-height:100%;padding:20px;color:#000000;">
-        <div class="text-center" style="font-size:16px;font-weight:600;">标签需求订单</div>
-        <div class="flex valign-center">
-          <div>订单编号:</div>
-          <VueBarcode v-if="data" :value="data.orderNo" format="CODE128" :height="40" :font-size="14"></VueBarcode>
+    <pdf-exporter ref="pdfExporter" class="flex column layout-gap" style="min-height:100%;height:500px;margin:0 -20px;padding:20px;color:#000000;">
+      <div class="text-center" style="font-size:16px;font-weight:600;">标签需求订单</div>
+      <div class="flex valign-center">
+        <div>订单编号:</div>
+        <VueBarcode v-if="data" :value="data.orderNo" format="CODE128" :height="40" :font-size="14"></VueBarcode>
+      </div>
+      <div style="width:100%;">
+        <table cellspacing="0" cellpadding="0" border="0" class="origin-table">
+          <colgroup>
+            <col />
+            <col width="250" />
+            <col />
+            <col />
+            <col />
+            <col />
+            <col />
+            <col />
+          </colgroup>
+          <thead>
+            <tr>
+              <th><div class="cell">序号</div></th>
+              <th><div class="cell">供应商名称</div></th>
+              <th><div class="cell">申领人员</div></th>
+              <th><div class="cell">标签类型</div></th>
+              <th><div class="cell">创建日期</div></th>
+              <th><div class="cell">需求数量</div></th>
+              <th><div class="cell">单价(元)</div></th>
+              <th><div class="cell">小计(元)</div></th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr v-if="data">
+              <td align="center"><div class="cell">{{ 1 }}</div></td>
+              <td align="center"><div class="cell">{{ data.providerName }}</div></td>
+              <td align="center"><div class="cell">{{ data.applicantName }}</div></td>
+              <td align="center"><div class="cell">{{ data. tagTypeName}}</div></td>
+              <td align="center"><div class="cell">{{ data.formatCreateTime }}</div></td>
+              <td align="center"><div class="cell">{{ data.number }}</div></td>
+              <td align="center"><div class="cell">{{ data.unitPrice }}</div></td>
+              <td align="center"><div class="cell">{{ data.subtotal }}</div></td>
+            </tr>
+            <tr>
+              <td><div class="cell"></div></td>
+              <td><div class="cell"></div></td>
+              <td><div class="cell"></div></td>
+              <td><div class="cell"></div></td>
+              <td><div class="cell"></div></td>
+              <td><div class="cell"></div></td>
+              <td><div class="cell"></div></td>
+              <td><div class="cell"></div></td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+      <div class="flex layout-gap" style="padding-top:20px;">
+        <div class="flex flex-1" style="gap:5px">
+          <label>制单人:</label>
+          <div class="flex-1 fit-size"></div>
         </div>
-        <div style="width:100%;">
-          <table cellspacing="0" cellpadding="0" border="0" class="origin-table">
-            <colgroup>
-              <col />
-              <col width="250" />
-              <col />
-              <col />
-              <col />
-              <col />
-              <col />
-              <col />
-            </colgroup>
-            <thead>
-              <tr>
-                <th><div class="cell">序号</div></th>
-                <th><div class="cell">供应商名称</div></th>
-                <th><div class="cell">申领人员</div></th>
-                <th><div class="cell">标签类型</div></th>
-                <th><div class="cell">创建日期</div></th>
-                <th><div class="cell">需求数量</div></th>
-                <th><div class="cell">单价(元)</div></th>
-                <th><div class="cell">小计(元)</div></th>
-              </tr>
-            </thead>
-            <tbody>
-              <tr v-if="data">
-                <td align="center"><div class="cell">{{ 1 }}</div></td>
-                <td align="center"><div class="cell">{{ data.providerName }}</div></td>
-                <td align="center"><div class="cell">{{ data.applicantName }}</div></td>
-                <td align="center"><div class="cell">{{ data. tagTypeName}}</div></td>
-                <td align="center"><div class="cell">{{ data.formatCreateTime }}</div></td>
-                <td align="center"><div class="cell">{{ data.number }}</div></td>
-                <td align="center"><div class="cell">{{ data.unitPrice }}</div></td>
-                <td align="center"><div class="cell">{{ data.subtotal }}</div></td>
-              </tr>
-              <tr>
-                <td><div class="cell"></div></td>
-                <td><div class="cell"></div></td>
-                <td><div class="cell"></div></td>
-                <td><div class="cell"></div></td>
-                <td><div class="cell"></div></td>
-                <td><div class="cell"></div></td>
-                <td><div class="cell"></div></td>
-                <td><div class="cell"></div></td>
-              </tr>
-            </tbody>
-          </table>
+        <div class="flex flex-1" style="gap:5px">
+          <label>日期:</label>
+          <div class="flex-1 fit-size"></div>
         </div>
-        <div class="flex layout-gap" style="padding-top:20px;">
-          <div class="flex flex-1" style="gap:5px">
-            <label>制单人:</label>
-            <div class="flex-1 fit-size"></div>
-          </div>
-          <div class="flex flex-1" style="gap:5px">
-            <label>日期:</label>
-            <div class="flex-1 fit-size"></div>
-          </div>
-          <div class="flex flex-1" style="gap:5px">
-            <label>需求单位(签章):</label>
-            <div class="flex-1 fit-size"></div>
-          </div>
-          <div class="flex" style="gap:5px">
-            <label>日期:</label>
-            <div style="width:100px"></div>
-          </div>
+        <div class="flex flex-1" style="gap:5px">
+          <label>需求单位(签章):</label>
+          <div class="flex-1 fit-size"></div>
+        </div>
+        <div class="flex" style="gap:5px">
+          <label>日期:</label>
+          <div style="width:100px"></div>
         </div>
       </div>
-    </div>
+    </pdf-exporter>
 
     <template #footer>
       <div class="flex valign-center align-right">
@@ -113,7 +111,8 @@ export default {
       this.visible = true
     },
     onExportPdfBtnClick () {
-
+      const { data } = this
+      this.$refs.pdfExporter.export(`标签需求订单_${data.orderNo}_${data.providerName}_${data.tagTypeName}`)
     }
   }
 }

+ 31 - 0
src/components/PdfExporter.vue

@@ -0,0 +1,31 @@
+<template>
+  <div><slot /></div>
+</template>
+
+<script>
+import html2canvas from 'html2canvas'
+import JsPDF from 'jspdf'
+
+export default {
+  name: 'PdfExporter',
+  methods: {
+    async export (exportName) {
+      const wrapperEl = this.$el
+      const canvas = await html2canvas(wrapperEl)
+      const imgData = canvas.toDataURL('image/png')
+      const doc = new JsPDF({
+        orientation: 'l',
+        unit: 'px',
+        format: 'a4'
+      })
+      const imgProps = doc.getImageProperties(imgData)
+      const pdfWidth = doc.internal.pageSize.getWidth()
+      const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width
+      doc.addImage(imgData, 'PNG', 0, 30, pdfWidth, pdfHeight)
+      doc.save(`${exportName}.pdf`)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped></style>