vue中前端不使用后端接口,导出pdf文件

项目需要不使用接口的情况下,导出页面生成pdf文件(带样式)
文件效果:
在这里插入图片描述

下载需要的插件依赖

	// 下载文件所需要的依赖
	npm install html2canvas jspdf
	// 在文中引入
	import html2Canvas from 'html2canvas';
	import JsPDF from 'jspdf';

在js文件中添加(重要代码)

import html2Canvas from 'html2canvas';
import JsPDF from 'jspdf';
// const logoImg = require('@/assets/export-logo.png');
// const logoBase64 = new Image();
// logoBase64.src = logoImg;

/**
 * [获取页面导出的pdf文件]
 * @param   {[Object]}  options  [导出pdf配置项,包括一个title属性设置文件名,以及query属性设置获取元素的条件]
 */
export const htmlToPdf = (options) => {
  return new Promise((resolve) => {
    const title = options.title || '标题'; // 导出文件名,默认为“标题”
    const children = document.querySelectorAll('.' + options.className || '.pdf-cell');
    let canvas = [];
    let i = 0;

    function toCanvas () {
      if (children.length >= 1) {
        html2Canvas(children[i], { scale: 3 }).then((res) => {
          // 计算每个dom的高度,方便后面计算分页
          res.imgWidth = 595.28;
          res.imgHeight = (592.28 / res.width) * res.height;
          canvas.push(res);
          i++;
          if (canvas.length === children.length) {
            paging();
            toPdf();
          } else {
            toCanvas();
          }
        });
      } else {
        throw new Error('导出节点列表为空');
      }
    }

    /**
     * [根据dom的高度初步进行分页,会将canvas组装为一个二维数组]
     */
    function paging () {
      const imgArr = [[]];
      let pageH = 0; // 页面的高度
      let allH = 0; // 当前组所有dom的高度和
      let j = 0;
      for (let k = 0; k < canvas.length; k++) {
        // 涉及到k--的操作,使用for循环方便
        pageH += canvas[k].imgHeight;
        console.log(pageH, canvas[k].imgHeight);
        if (pageH > 841.89 && canvas[k].imgHeight < 841.89) {
          // 当某个页面装不下下一个dom时,则分页
          imgArr[j][0].allH = allH - canvas[k].imgHeight;
          allH = pageH = 0;
          k--;
          j++;
          imgArr.push([]);
        } else {
          if (canvas[k].imgHeight > 841.89) {
            // 特殊情况:某个dom高度大于了页面高度,特殊处理
            canvas[k].topH = 841.89 - (pageH - canvas[k].imgHeight); // 该dom顶部距离页面上方的距离
            pageH = (2 * canvas[k].imgHeight - pageH) % 841.89;
            canvas[k].pageH = pageH; // 该dom底部距离页面上方的距离
          }
          imgArr[j].push(canvas[k]);
          allH += canvas[k].imgHeight;
        }
        if (k === canvas.length - 1) imgArr[j][0].allH = allH;
      }
      canvas = imgArr;
    }
    /**
     * [生成PDF文件]
     */
    function toPdf () {
      const PDF = new JsPDF('', 'pt', 'a4');

      canvas.forEach((page, index) => {
        let allH = page[0].allH;
        // let position = 40; // pdf页面偏移
        let position = 0; // pdf页面偏移
        if (index !== 0 && allH <= 841.89) {
          PDF.addPage();
        }
        page.forEach((img) => {
          // PDF.addImage(logoBase64, 'PNG', 10, 10, 575.28, 81 / (1920 / 575.28));
          if (img.imgHeight < 841.89) {
            // 当某个dom高度小于页面宽度,直接添加图片
            PDF.addImage(img.toDataURL('image/jpeg', 1.0), 'JPEG', 0, position, img.imgWidth, img.imgHeight);
            position += img.imgHeight;
            allH -= img.imgHeight;
          } else {
            // 当某个dom高度大于页面宽度,则需另行处理
            while (allH > 0) {
              PDF.addImage(img.toDataURL('image/jpeg', 1.0), 'JPEG', 0, position, img.imgWidth, img.imgHeight);
              allH -= img.topH || 841.89;
              position -= img.topH || 841.89;
              img.topH = 0;
              if (allH > 0) {
                PDF.addPage();
              }
            }
            position = img.pageH;
          }
        });
      });
      // 是否将生成的pdf发送给后端接口
      if(!options.fileUpload){
        PDF.save(title + '.pdf');
        resolve(true);
      }else{
        let pdfData = PDF.output('datauristring') // 获取base64Pdf
        let file = dataURLtoFile(pdfData, title || '标题' + '.pdf') // 将base64文件转化为流,上传oss
        resolve(file);
      }
    }
    function dataURLtoFile (dataurl, filename) {
      let arr = dataurl.split(',')
      let mime = arr[0].match(/:(.*?);/)[1]
      let bstr = atob(arr[1])
      let n = bstr.length
      let u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      return new File([u8arr], filename, { type: mime })
    }
    toCanvas();
  });
};


在需要的文件中引入

// 我的是放在utils文件夹中
import { htmlToPdf } from '@/utils/downPdf'

// 点击之后下载pdf文件
 async downLoad () {
       const loading = this.$loading({
        lock: true, // 文件下载时禁用页面上所有的交互操作
        text: '正在下载请稍等',
        spinner: 'el-icon-loading', // 文件下载时的的加载图标
        background: 'rgba(0, 0, 0, 0.7)',
      });
       // className表示的是只有具有指定类型的元素才会被转化成pdf文件
	  // title: '文件下载': 这个参数指定了生成的 PDF 文件的标题
      // 如果元素过多时,pdf将以 pdfData-centent 为间隔,进行分页分割
      const data = await htmlToPdf({ className: 'pdfData-centent', title: '报表', fileUpload: false });
      if (data) this.$message.success('下载成功')
      loading.close();
    },

详细代码

<template>
  <div class="container">
    <!-- 设置分页的类名 -->
    <div class="page">
      <div class="title" @click="downLoad">测试文件下载</div>
      <a href="/api/uploads/20230731/4dbbe8b13342145d34e99ce00.webp.jpg" download="">下载</a>
      <div class="content">
        <div class="content-text">
          <div class="tip">一致性 Consistency</div>
          <p> 与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;</p>
          <p>在界面中一致:所有的元素和结构需保持一致,比如:设计样式、图标和文本、元素的位置等。</p>
          <div class="tip">效率 Efficiency</div>
          <p>简化流程:设计简洁直观的操作流程;</p>
          <p>清晰明确:语言表达清晰且表意明确,让用户快速理解进而作出决策;</p>
          <p>帮助用户识别:界面简单直白,让用户快速识别而非回忆,减少用户记忆负担。</p>
        </div>
        <div class="imgFile">
          <img src="./img/1.png" class="img1" alt="">
          <img src="./img/2.png" class="img2" alt="">
        </div>
        <div class="table">
          <el-table :data="tableData" style="width: 80%;margin-left:10%">
            <el-table-column prop="date" label="日期" width="180">
            </el-table-column>
            <el-table-column prop="name" label="姓名" width="180">
            </el-table-column>
            <el-table-column prop="address" label="地址">
            </el-table-column>
          </el-table>
        </div>
        <div class="echarts">
          <div id="echart" style="width:100%;height:100%">
          </div>
        </div>
        <div class="echarts">
          <div id="myEcharts" style="width:100%;height:100%"></div>
        </div>
        <div class="echarts">
          <div id="echartsBar" style="width:100%;height:100%"></div>
        </div>
      </div>
      
    </div>
   
  </div>
  
</template>
<script>
import * as echarts from "echarts";
import { htmlToPdf } from '@/utils/downPdf'
export default {

  name: '',

  props: {},

  components: {},

  data () {
    return {
      tableData: [{
        date: '2016-05-02',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1518 弄'
      }, {
        date: '2016-05-04',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1517 弄'
      }, {
        date: '2016-05-01',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1519 弄'
      }, {
        date: '2016-05-03',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1516 弄'
      }, {
        date: '2016-05-03',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1516 弄'
      }, {
        date: '2016-05-03',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1516 弄'
      }]
    }
  },

  computed: {},

  watch: {},
  created () { },

  methods: {
    init () {
      const echart = echarts.init(document.getElementById('echart'))
      echart.setOption({
        xAxis: {
          type: 'category',
          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        },
        yAxis: {
          type: 'value'
        },
        series: [
          {
            data: [150, 230, 224, 218, 135, 147, 260],
            type: 'line'
          }
        ]
      })

    },
    getInit () {
      const myEcharts = echarts.init(document.getElementById("myEcharts"))
      myEcharts.setOption({
        tooltip: {
          trigger: 'item'
        },
        legend: {
          top: '5%',
          left: 'center'
        },
        series: [
          {
            name: 'Access From',
            type: 'pie',
            radius: ['40%', '70%'],
            avoidLabelOverlap: false,
            itemStyle: {
              borderRadius: 10,
              borderColor: '#fff',
              borderWidth: 2
            },
            label: {
              show: false,
              position: 'center'
            },
            emphasis: {
              label: {
                show: true,
                fontSize: 40,
                fontWeight: 'bold'
              }
            },
            labelLine: {
              show: false
            },
            data: [
              { value: 1048, name: 'Search Engine' },
              { value: 735, name: 'Direct' },
              { value: 580, name: 'Email' },
              { value: 484, name: 'Union Ads' },
              { value: 300, name: 'Video Ads' }
            ]
          }
        ]
      })
      const echartsBar = echarts.init(document.getElementById("echartsBar"))
      echartsBar.setOption({
        xAxis: {
          type: 'category',
          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        },
        yAxis: {
          type: 'value'
        },
        series: [
          {
            data: [120, 200, 150, 80, 70, 110, 130],
            type: 'bar',
            barWidth: "10"
          }
        ]
      })
    },
    async downLoad () {
      const loading = this.$loading({
        lock: true,
        text: '正在下载请稍等',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)',
      });
      // 如果元素过多时,pdf将以 pdfData-centent 为间隔,进行分页分割
      const data = await htmlToPdf({ className: 'pdfData-centent', title: '报表', fileUpload: false });
      if (data) this.$message.success('下载成功')
      loading.close();
    },
  },

  mounted () {
    this.init();
    this.getInit()
  },
}
</script> 

<style scoped lang="less">
.container {
  padding: 20px;
  .title {
    padding-top: 50px;
    text-align: center;
    color: rgb(20, 80, 80);
    font-size: 18px;
    cursor: pointer;
  }
  .content {
    display: flex;
    width: 100%;
    flex-wrap: wrap;
    div {
      margin-top: 10px;
    }
    .content-text {
      text-indent: 2em;
      width: 50%;

      .tip {
        font-size: 24px;
        color: rgb(50, 19, 92);
      }
    }
    .imgFile {
      width: 50%;
      display: flex;
      justify-content: center;
      align-items: center;
      .img1 {
        width: 200px;
        height: 200px;
      }
      .img2 {
        width: 220px;
        height: 200px;
      }
    }
    .table {
      width: 50%;
    }
    .echarts {
      width: 50%;
      height: 400px;
    }
  }
}
</style>
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Vue 前端调用后端接口导出 Excel 文件,可以采用以下步骤: 1. 在后端编写导出 Excel 的接口,通常采用 Spring MVC 或者 Express 等框架,在接口使用 Apache POI 或者其他 Excel 库生成 Excel 文件,并将生成的文件以流的形式返回给前端。 2. 在前端编写调用后端接口的代码,通常可以使用 axios 或者其他 HTTP 请求库来发送请求,并将返回的文件流保存到本地,同时进行下载操作。 以下是一个简单的 Vue 组件示例代码: ```html <template> <div> <button @click="exportExcel">导出 Excel</button> </div> </template> <script> import axios from 'axios' export default { methods: { exportExcel() { axios({ url: '/api/export', method: 'GET', responseType: 'blob', }).then((response) => { const url = window.URL.createObjectURL(new Blob([response.data])) const link = document.createElement('a') link.href = url link.setAttribute('download', 'export.xlsx') document.body.appendChild(link) link.click() }) }, }, } </script> ``` 以上代码,我们定义了一个 `exportExcel` 方法,当用户点击按钮时会触发该方法。在方法,我们使用 axios 发送 GET 请求,并将响应类型设置为 blob,这样可以保留文件流的形式。在获取到响应后,我们将其转换为 URL 对象,创建一个下载链接,并触发点击操作,从而实现文件的下载。我们可以将请求的 URL 设置为后端提供的接口,例如 `'/api/export'`,从而实现与后端的交互。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值