PDF导出功能方案

@记录PDF开发过程

pdf导出功能开发方案

  公司监控新的需求需要导出pdf功能,现在平台暂时不支持,后端不愿意代码写pdf,所以只好找点工具用用。

1. 方案一:使用html转换pdf的方法

1.1. 流程:

  具体流程如图所示,忽略巡检触发的过程,生成PDF的流程可以总结为:go-gin模块渲染html,通过转换工具将html转为pdf。

在这里插入图片描述

1.2. 转换工具:

  转换工具有很多,phatomjs、wkhtmltopdf和pd4ml等等。

  • phatomjs不再维护,而且页面大小不好设置;
  • wkhtmltopdf命令多,坑也多,对应静态模版还阔以;
  • pd4ml得api好用,但是有水印。

  综上所述,优先选择wkhtmltopdf,具体依赖见文件夹。

  附(转换命令)

## 1.转换命令
wkhtmltopdf https://www.csdn.net /Users/kevin/Desktop/csdn.pdf

  这些工具都有通病,不能转换动态渲染的页面,类似react框架等等,这就产生一个问题,生成所需html模版应该如何摆放。

1.3. html模版的摆放问题

  这里也是有两种方法:

  • 需要支持html,直接放在文件夹下,通过部署脚本将静态文件移动到指定位置,但是css等内容经常改动,这样脚本变动太多;
  • 转成bindata,放在监控模块中,这样可以直接通过内部网络访问,还不会造成安全问题。
    两种方案中,最好都把对html的支持方在监控服务中,因为监控服务也支持了gin,不需要添加太多的依赖。

  附(生成bindata方法):

## 1.添加依赖
go get github.com/jteeuwen/go-bindata/...
go get github.com/elazarl/go-bindata-assetfs/...
## 2.转换文件
>go-bindata-assetfs -o ./modules/api/app/static/static.go -pkg=static ./modules/api/app/static/...
1.4. html模版

  需要简单写一写的那种,css和js可能要前端支援,瞎逼写个页面,转换出来,长下面这样:
在这里插入图片描述

2. 方案二:前端添加pdf导出功能

2.1 现成功能

  参考链接:https://www.cnblogs.com/alannxu/p/10982952.html

  试了下,是能用得,可是前端又不同意,我也是难得。

<template>
  <div class="pdf-preview-container">
    <div
      class="page-container"
      ref="container"
      v-for="page in docPages"
      :style="{
        height: `${pageHeight}px`
      }"
      :key="page" v-show='isLoaded'>
      <canvas v-if="renderList.includes(page)">
      </canvas>
    </div>
    <div class="notice" v-show="!isLoaded">
      <h4>加载中...</h4>
    </div>
  </div>
</template>

<script>
// import pdfJS from 'pdfjs-dist'

export default {
  props: {
    url: {
      type: String,
      required: true
    },
    renderPages: {
      type: Number,
      default: 5
    },
    customScroll: {
      type: Boolean,
      default: false
    },
    offsetHeight: {
      type: Number,
      default: 0
    }
  },
  data() {
    return {
      doc: null,
      docPages: 0,
      currentPage: 0,
      pageHeight: 0,
      renderList: [],
      show: false,
      notice: '',
      timer: 0,
      isLoaded:true
    };
  },
  watch: {
    url: {
      immediate: true,
      handler() {
        this.getPDFFile();
      }
    }
  },
  created() {
    if (!this.customScroll) {
      document.addEventListener('scroll', this.scroll);
    }
  },
  beforeDestroy() {
    document.removeEventListener('scroll', this.scroll);
  },
  methods: {
    getPDFFile() {
      if (!this.url) return;
      this.currentPage = 0;
    //   pdfJS.getDocument(this.url).then(pdf => {
    //     this.isLoaded = true;
    //     this.doc = pdf
    //     this.docPages = pdf.numPages
    //     this.$nextTick(() => {
    //       this.docPages && this.scrollToPage(1)
    //     })
    //   })
    },
    scrollToPage(pageNo) {
      if (this.currentPage === pageNo) return;
      this.currentPage = pageNo;
      let list = [];
      for (let page = pageNo - this.renderPages; page <= pageNo + this.renderPages; page++) {
        list.push(page);
      }
      list = list.filter(page => page <= this.docPages && page >= 1);
      this.$nextTick(() => {
        this.renderList = list;
        this.renderList.forEach(page => {
          this.renderPage(page);
        });
      });
    },
    // 渲染page
    renderPage(pageNo) {
      this.doc.getPage(pageNo).then(page => {
        let container = this.$refs.container[pageNo - 1];
        if (!container) return;
        let canvas = container.querySelector('canvas');
        if (!canvas || canvas.__rendered) return;
        let ctx = canvas.getContext('2d');
        let dpr = window.devicePixelRatio || 1;
        let bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
        let ratio = dpr / bsr;
        let rect = container.getBoundingClientRect();
        let viewport = page.getViewport(1);
        let width = rect.width;
        let height = width / viewport.width * viewport.height;
        canvas.style.width = `${width}px`;
        canvas.style.height = `${height}px`;
        this.pageHeight = height;
        canvas.height = height * ratio;
        canvas.width = width * ratio;
        ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
        page.render({
          canvasContext: ctx,
          viewport: page.getViewport(width/viewport.width)
        });
        canvas.__rendered = true;
      });
    },
    scroll() {
      this.checkRender(document.documentElement);
    },
    checkRender(el) {
      if (!this.pageHeight) return;
      let scrollTop = el.scrollTop;
      if (el === document.documentElement) {
        scrollTop = el.scrollTop || window.pageYOffset || document.body.scrollTop;
      }
      let page = Math.floor((scrollTop - this.offsetHeight) / this.pageHeight);
      page = Math.max(page, 1);
      page = Math.min(page, this.docPages);
      this.scrollToPage(page);
    }
  }
};
</script>

<style lang="scss" scoped>
.notice{
  position: fixed;
  background:#000;
  color: #fff;
  left: 2rem;
  top: 5rem;
  right: 2rem;
}
</style>

后记,最后还是选了方案一。。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

L.南瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值