Django结合Vue实现前端页面导出为PDF1

简介: Django结合Vue实现前端页面导出为PDF

Django结合Vue实现前端页面导出为PDF


测试环境

Win 10

Python 3.5.4

Django-2.0.13.tar.gz

官方下载地址:

https://www.djangoproject.com/download/2.0.13/tarball/

pdfkit-0.6.1.tar.gz

下载地址:

https://pypi.org/project/pdfkit/

https://files.pythonhosted.org/packages/a1/98/6988328f72fe3be4cbfcb6cbfc3066a00bf111ca7821a83dd0ce56e2cf57/pdfkit-0.6.1.tar.gz  

django REST framework-3.9.4

下载地址:

https://github.com/encode/django-rest-framework

wkhtmltox_v0.12.5.zip

下载地址:

https://wkhtmltopdf.org/downloads.html

https://downloads.wkhtmltopdf.org/0.12/0.12.5/wkhtmltox-0.12.5-1.msvc2015-win64.exe  

axios 0.18.0  

echarts 4.2.1  

element-ui: 2.8.2  

Vue 3.1.0  

 

需求描述

如下,要将一个包含echarts图表,elementUI table的测试报告页面导出为PDF文档,页面包含以下类型的元素

image.png

image.png

 

解决方案

最开始采用“html2canvas和jsPDF”直接前端导出,发现存在问题,只能导出可视区内容,并且是类似截图一样的效果,无法获取翻页数据,然后考虑后台导出,前端通过js获取报告容器元素innerHtml,传递给后台,后台根据这个html元素导出为pdf,发现还是存在问题,echarts图片无法导出,另外,翻页组件等也会被导出,还有就是表格翻页数据无法获取,页面样式缺失等。

最终解决方案:

后台编写好html模板(包含用到的样式、样式链接等),收到请求时读取该模板文件为html文本。从数据库读取前端用到的表格数据,然后替换至模板中对应位置的模板变量;通过echars api先由 js把echarts图表转为base64编码数据,然后随其它导出文件必要参数信息发送到后台,后台接收后转base64编码为图片,然后替换模板中对应的模板变量,这样以后,通过pdfkit类库把模板html文本导出为pdf。最后,删除生成的图片,并且把pdf以blob数据类型返回给前端,供前端下载。

 

pdfkit api使用简介

基础用法

import pdfkit

 

pdfkit.from_url('https://www.w3school.com.cn, 'out.pdf')

pdfkit.from_file('test.html', 'out.pdf')

pdfkit.from_string('Hello!', 'out.pdf')

 

 

可以通过传递多个url、文件来生成pdf文件:

pdfkit.from_url(['https://www.w3school.com.cn', 'www.cnblogs.com'], 'out.pdf')

如上,将会把访问两个网站后打开的内容按网站在list中的顺序,写入out.pdf,也可以不带https://、http://,如下

pdfkit.from_url(['www.w3school.com.cn', 'www.cnblogs.com'], 'out.pdf')

pdfkit.from_file(['file1.html', 'file2.html'], 'out.pdf')

 

可以通过打开的文件来生成PDF

with open('file.html') as f:

pdfkit.from_file(f, 'out.pdf')

 

也可以不输出到文件,直接保存到内存中,以便后续处理

pdf = pdfkit.from_url('www.w3school.com.cn ', False)

 

默认的,pdfkit会显示所有wkhtmltopdf的输出,可以通过添加options参数,并设置quiet的值(quiet除外,还有很多其他选项可设置,具体参考官方文档),如下::

options = {

'quiet': ''

}

pdfkit.from_url('https://www.w3school.com.cn, 'out.pdf', options=options)

 

此外还可以为要生成的pdf添加css样式,特别适合css样式采用“外联样式”的目标对象。

#单个CSS样式文件

css = 'example.css'

pdfkit.from_file('file.html', options=options, css=css)

 

# 多个css样式

css = ['example.css', 'example2.css']

pdfkit.from_file('file.html', options=options, css=css)

 

添加configuration参数,如下,指定wkhtmltopdf安装路径

config = pdfkit.configuration(wkhtmltopdf='/opt/bin/wkhtmltopdf')

pdfkit.from_string(html_string, output_file, configuration=config)

 

更多详情参考官方文档

https://pypi.org/project/pdfkit/

 

实现步骤

1.安装wkhtmltox

安装完成后,找到安装目录下wkhtmltopdf.exe所在路径(例中为D:\Program Files\wkhtmltopdf\bin\wkhtmlpdf.exe),添加到系统环境变量path中(实践时发现,即便是配置了环境变量,运行时也会报错:提示:No wkhtmltopdf executable found: "b''"

 

解决方案:

如下,生成pdf前指定wkhtmltopdf.exe路径

config = pdfkit.configuration(wkhtmltopdf='/opt/bin/wkhtmltopdf')

pdfkit.from_string(html_string, output_file, configuration=config)

 

2.安装pdfkit

3.前端请求下载报告

仅保留关键代码

<script>

export default {

return {

echartPicIdDict: {}, // 存放echart图表ID 数据格式为: {" echartPicUniqueName":"echartPicUUID" },比如 {"doughnut-pie-chart":"xdfasfafafadfafafafafdasf" } // 创建Echarts图表时需要指定一个id,例中创建每个echart图表时,都会生成一个UUID作为该echart图表的id,并且会把该UUID保存到this.echartPicIdDict。

reportId: "", // 存放用户所选择的测试报告ID

...略

}

},

 

 

methods: {

...略

// 下载报告

downloadSprintTestReport() {

try {

...略

 

let echartBase64Info = {}; // 存放通过getDataURL获取的echarts图表base64编码信息

 

 

// 获取echart图表base64编码后的数据信息

for (let key in this.echartPicIdDict) {

// let echartObj = this.$echarts.getInstanceById(this.echartPicIdDict[key]); // 结果 echartObj=undefined

let echartDomObj = document.getElementById(this.echartPicIdDict[key]);

if (echartDomObj) {

const picBase64Data = echartDomObj.getDataURL(); //返回数据格式:data:image/png;base64,base64编码数据

echartBase64Info[key] = picBase64Data;

}

}

}

 

 

// 发送下载报告请求

downloadSprintTestReportRequest({

reportId: this.reportInfo.id,

sprintId: this.reportInfo.sprintId,

...略

echartBase64Info: echartBase64Info

})

.then(res => {

let link = document.createElement("a");

let blob = new Blob([res.data], {

type: res.headers["content-type"]

});

 

 

link.style.display = "none";

link.href = window.URL.createObjectURL(blob);

// 下载文件名无法通过后台响应获取,因为获取不到Content-Disposition响应头

link.setAttribute("download", this.reportInfo.title + ".pdf");

 

 

document.body.appendChild(link);

link.click();

document.body.removeChild(link);

})

.catch(res => {

if (

Object.prototype.toString.call(res.response.data) ==

"[object Blob]"

) {

let reader = new FileReader();

reader.onload = e => {

let responseData = JSON.parse(e.target.result);

if (responseData.msg) {

this.$message.error(

res.msg || res.message + ":" + responseData.msg

);

} else {

this.$message.error(

res.msg || res.message + ":" + responseData.detail

);

}

};

reader.readAsText(res.response.data);

} else {

this.$message.error(res.msg || res.message);

}

});

} catch (err) {

this.$message.error(res.message);

}

},

 

}

</script>

 

4、 后端编写模板

<!DOCTYPE HTML>

<html>

<head>

<meta charset="UTF-8" />

<!-- elementUI -->

<!-- 引入样式 -->

<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css" />

<!-- 引入组件库 -->

<script src="https://unpkg.com/element-ui/lib/index.js"></script>

<style>

...略

.plan-info {

border-width: 1px;

border-style: solid;

background: rgba(241, 239, 239, 0.438);

border-color: rgb(204, 206, 206);

 

}

 

.plan-info .plan-info-table-td {

text-align: center;

padding-top: 3px;

padding-bottom: 3px;

font-size: 14px;

}

 

.plan-info .plan-info-table-td-div {

display: inline;

}

...略

 

</style>

</head>

<body>

...略

<div class="sprint-test-report-detail">

<span style="font-weight: bold;">测试计划:</span>

<div class="plan-info">

<table>

<thead>

<tr>

<th style="border: none; width: 6%; height: 0px;">ID</th>

<th style="border: none; width: 20%; height: 0px;">计划名称</th>

<th style="border: none; width: 10%; height: 0px;">预估开始日期</th>

<th style="border: none; width: 10%; height: 0px;">实际开始时间</th>

<th style="border: none; width: 10%; height: 0px;">预估完成日期</th>

<th style="border: none; width: 10%; height: 0px;">实际完成时间</th>

<th style="border: none; width: 25%; height: 0px;">关联组别</th>

<th style="border: none; width: 9%; height: 0px;">测试环境</th>

</tr>

</thead>

<tbody>

${relatedPlans}

</tbody>

</table>

</div>

</div>

<div class="sprint-test-report-detail">

<span style="font-weight: bold;">测试范围:</span>

<div>

<span>${test_scope}</span>

</div>

</div>

<div class="sprint-test-report-detail">

<span style="font-weight: bold;">测试统计</span>

<div>

<div>

<img src="${defect_status_pie}" />

</div>

...略

</div>

...略

</div>

</body>

</html>

 


目录
相关文章
|
15天前
|
监控 JavaScript 前端开发
使用Vue.js开发员工上网行为监控的实时数据展示页面
使用Vue.js开发的实时员工上网行为监控页面,展示员工访问的网站、应用和时间等数据。页面响应式设计,适应不同设备。通过Vue组件显示实时数据,如`&lt;li v-for=&quot;activity in activities&quot;&gt;`循环渲染。数据定时更新,利用Vue的生命周期钩子和axios从服务器获取并自动刷新,确保数据的时效性。该页面有助于管理者即时了解员工网络活动,保障企业网络安全和资源管理。
75 5
|
8天前
|
前端开发 安全 UED
【项目实战】从终端到浏览器:实现 ANSI 字体在前端页面的彩色展示
在学习和工作中,我们经常需要使用日志来记录程序的运行状态和调试信息。而为了更好地区分不同的日志等级,我们可以使用不同的颜色来呈现,使其更加醒目和易于阅读。 在下图运行结果中,我们使用了 colorlog 库来实现彩色日志输出。通过定义不同日志等级对应的颜色,我们可以在控制台中以彩色的方式显示日志信息。例如,DEBUG 级别的日志使用白色,INFO 级别的日志使用绿色,WARNING 级别的日志使用黄色,ERROR 级别的日志使用红色,CRITICAL 级别的日志使用蓝色。
|
18天前
|
缓存 前端开发 jenkins
Serverless 应用引擎产品使用合集之前端的项目部署在镜像里时,页面总是自动刷新,是什么导致的
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
22天前
|
JavaScript 前端开发 数据安全/隐私保护
vue3+ts+elementplus写一个登录页面教程
【6月更文挑战第3天】本文介绍了如何使用 Vue 3 和 TypeScript 创建一个登录页面。首先,需安装 Vue CLI,然后创建新项目并启用 TypeScript 支持。接着,创建 `Login.vue` 组件,设计登录表单,包括用户账号、密码和验证码字段,并实现相关验证规则。页面样式包括背景、登录框和按钮等元素的布局与样式。最后,展示了`&lt;script&gt;`部分的代码,包括表单验证逻辑、生成验证码的函数以及登录提交处理。文章还提供了一个登录页面的截图和完整代码示例。
145 1
|
7天前
|
存储 前端开发 搜索推荐
Web前端网站(一) - 登录页面及账号密码验证
页面背景动态是烟花和文字特效与缓缓下落的雪花相结合,在登录表单的旁边还有五个白色光圈以不规则的方式环绕,当鼠标靠近时,会发出彩色的光芒~~~
24 1
Web前端网站(一) - 登录页面及账号密码验证
|
4天前
|
JSON 前端开发 JavaScript
SSMP整合案例第五步 在前端页面上拿到service层调数据库里的数据后列表
SSMP整合案例第五步 在前端页面上拿到service层调数据库里的数据后列表
10 2
|
4天前
|
缓存 负载均衡 前端开发
SSMP整合案例第八步 前端页面的分页功能完善与维护和实现条件查询
SSMP整合案例第八步 前端页面的分页功能完善与维护和实现条件查询
9 1
|
4天前
|
前端开发 JavaScript Java
SSMP整合案例第六步 在前端页面上利用axios和element-ui与后端交互实现增删改
SSMP整合案例第六步 在前端页面上利用axios和element-ui与后端交互实现增删改
9 1
|
22天前
|
JavaScript
|
22天前
|
存储 JavaScript 前端开发
vue在页面使用Vue.prototype全局变量
【6月更文挑战第3天】Vue.prototype 是 Vue 实例的原型对象,用于添加全局属性和方法。通过修改 Vue.prototype,所有 Vue 实例都能访问这些属性和方法。例如,可在 mai
27 1