我一个前端程序员,最近老大要我出一个报表UI,并跟我说要支持pdf导出,因为这个报表数据回头要发给部门老大看,pdf显得正式点,然后,我就开始了各种找插件之旅。。。
首先,介绍下我们的项目背景:UI使用datatable。当我去看datatable官网的时候,很高兴,有现成的导出插件:datatable导出插件,简直让我欣喜若狂呀,不过如果这么简单就把需求做好了,那我也就没有下文可写了。
datatable 毕竟是老外写的,所以并不支持中文,即:导出的中文都是乱码。作为中国人,是不是有点不可忍?(对于我来说,是的!!!)
既然datatable现有插件不支持中文导出pdf,我首先想到的是放弃使用他的现有插件。百度一下,放眼望去出现比较多的词语就是tableExport。So,这应该是一款比较成熟的插件了吧,直接用应该没什么问题。
那就用下tableExport试试吧
跑去tableExport官网荡一下,直接使用官网tableExport的 js文件,然鹅。。。中文还是有乱码。tableExport也是老外写的吧?
不过还好,咱们CSDN上有好几个热心的朋友都贴出来解决方案。
看了好几个博文的解决方案,解决办法就是需要引入中文字体vfs_fonts.js。
然鹅,我的悲剧就是:生成vfs_fonts.js失败(莫非是我用的mac最新版本?),更让我崩溃的是:在咱们CSDN上下载的近30M的vfs_fonts.js文件,竟然运行的时候报错,让我情何以堪?各种泪崩呀。
细心的我,发现:不管是tableExport 还是datatable extension,他们都使用了pdfmake.js。vsf_fonts.js设置下自己想要的字体,pafmake.js就能导出相应的pdf无乱码文件。值得高兴的是,我也找到了一个中文的vsf_fonts.js,虽然是方正姚体,不过,能实现中文导出pdf无乱码,所以就不管三七二十一,用了再说。
代码实现很简单,js引入pdfmake.js和vfs_fonts.js就可以。HTML页面代码设置也很简单:就页面中放了一个下载的按钮而已
代码如下
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>my first export PDFtitle>
<script src="pdfmake.min.js">script>
<script src="方正vfs_fonts.js">script>
<script>
function down(data) {
var dd = {
content: [
data,
'Another paragraph, this time a little bit longer to make sure, this line will be divided into at least two lines'
],
defaultStyle: {
font: '方正姚体'
}
};
pdfMake.fonts = {
Roboto: {
normal: 'Roboto-Regular.ttf',
bold: 'Roboto-Medium.ttf',
italics: 'Roboto-Italic.ttf',
bolditalics: 'Roboto-Italic.ttf'
},
方正姚体: {
normal: 'FZYTK.TTF',
bold: 'FZYTK.TTF',
italics: 'FZYTK.TTF',
bolditalics: 'FZYTK.TTF',
}
};
pdfMake.createPdf(dd).download();
}
script>
head>
<body>
<button onclick="down('pdf文件显示中文')">下载button>
body>
html>
OK, 到了这里,不难发现,当我们配置好了响应的vsf_fonts.js,然后使用pdfMake.fonts配置下响应的字体,那么导出的pdf就没有乱码了。实现了pdf中文导出无乱码,我的心情可以用雀跃来形容了。然后对pdfmake.js就迫不及待的想了解更多,于是找到了他们官网pdfmake官网 PS:正好我的办公网段刚开通了国外,访问pdfmake官网完全没有压力,网速嘎嘎哒。。。
稍微浏览下pdfmake的docs,我惊呆了:pdfmake竟然功能如此强大,style、table、list、header、footer…各种排版样式,各种强大的功能都有,普通的table导出更不是问题了,即使领导要求pdf导出文件需要这个那个标红、这个那个显示背景颜色,这些通通不是事。
为什么回到datatable extension呢? 因为datatable extension已经把要导出的内容写好了,我们要导出pdf文件,直接把导出内容用createPdf()方法导出即可。
一起来读一读来看看datatable extension的源码:button.html5.js。
js有点基础的读起来应该问题不大,而这个button.html5.js也就一千多行代码,其中导出pdf文件相关的目测才300多行,不信你们看看:
有了之前咱们导出pdf中文无乱码的经验,那在button.html5.js先找到createPdf()这个方法,然后再该方法之前增加咱们相应的pdfMake.fonts
_pdfMake().fonts = {
Roboto: {
normal: 'Roboto-Regular.ttf',
bold: 'Roboto-Medium.ttf',
italics: 'Roboto-Italic.ttf',
bolditalics: 'Roboto-Italic.ttf'
},
方正姚体: {
normal: 'FZYTK.TTF',
bold: 'FZYTK.TTF',
italics: 'FZYTK.TTF',
bolditalics: 'FZYTK.TTF',
},
// 微软雅黑: {
// normal: '微软雅黑.ttf',
// bold: '微软雅黑.ttf',
// italics: '微软雅黑.ttf',
// bolditalics: '微软雅黑.ttf',
// }
};
var pdf = _pdfMake().createPdf( doc );
在代码中可以看到,我给微软雅黑字体预留了个位置,以后我的vsf_font.js有了微软雅黑之后就可以直接使用微软雅黑字体了。
从以上代码可以看到,createPdf该方法里边的doc就是生成pdf文件的配置了,咱们再花点精力往直前看看doc具体是什么
OK,如果你刚才在pdfmake官网有留意的话,不难发现,这就是标准的pdfmake导出文件的配置对象,并且使用的table布局导出。当然最后的defaultStyle中的font: ‘方正姚体’就是我配置好的文件导出的字体。
如果想再深入看看导出的内容,可以看看table>body的rows
从图片中可以看到,rows就是datable extension给我们生成的表格的所有内容,如果你不想逐行读代码,可以直接console.log(rows)看看里边的内容具体是什么,跟自己要导出的内容是否对的上(肯定是对的上的啦。。。)
前端页面写一个简单的table,然后咱们来试试效果吧。
table是用datatable生成的,代码如下:
$('.datatable').DataTable({
'dom': 'Btirlp',
"sPaginationType": "full_numbers",
'buttons': [{
"extend": 'pdfHtml5',
'title': '候选人详细说明', //导出文件名字
'text': '导出table数据pdf文件', //定义导出excel按钮的文字
"aButtons": "true",
'download': 'open',//直接在窗口打开
// 'orientation': 'landscape',
// 'pageSize': 'LEGAL',
'messageTop': '副标题位置信息'
}],
// "searching": true
"bDestroy": true,
// "bServerSide": true,
// "sAjaxSource": "",
"data": [{ id: 1, name: '小明1', desc: '年龄45,至今未婚,有房有车' }, { id: 2, name: '小明2', desc: '年龄45,至今未婚,有房有车' }, { id: 3, name: '小明3', desc: '年龄45,至今未婚,有房有车' }, { id: 4, name: '小明4', desc: '年龄45,至今未婚,有房有车' }, { id: 5, name: '小明5', desc: '年龄45,至今未婚,有房有车' }, { id: 6, name: '小明6', desc: '年龄45,至今未婚,有房有车' }, { id: 7, name: '小明7', desc: '年龄45,至今未婚,有房有车' }, { id: 8, name: '小明8', desc: '年龄45,至今未婚,有房有车' }, { id: 9, name: '小明9', desc: '年龄45,至今未婚,有房有车' }, { id: 10, name: '小明10', desc: '年龄45,至今未婚,有房有车' }, { id: 11, name: '小明1', desc: '年龄45,至今未婚,有房有车' }, ],
"aoColumns": [{
"sTitle": "id",
// "sWidth": "10%",
"mDataProp": "id"
}, {
"sTitle": "名称",
// "sWidth": "10%",
"mDataProp": "name"
}, {
"sTitle": "描述",
// "sWidth": "15%",
"mDataProp": "desc"
}],
"oLanguage": {
"sProcessing": "处理中...",
"sLengthMenu": "显示 _MENU_ 项结果",
"sZeroRecords": "没有匹配结果",
"sInfo": "显示第 _START_ 至 _END_ 项结果,共 _TOTAL_ 项",
"sInfoEmpty": "显示第 0 至 0 项结果,共 0 项",
"sInfoFiltered": "(由 _MAX_ 项结果过滤)",
"sInfoPostFix": "",
"sSearch": "搜索:",
"sUrl": "",
"sEmptyTable": "表中数据为空",
"sLoadingRecords": "载入中...",
"sInfoThousands": ",",
"oPaginate": {
"sFirst": "首页",
"sPrevious": "上页",
"sNext": "下页",
"sLast": "末页"
},
"oAria": {
"sSortAscending": ": 以升序排列此列",
"sSortDescending": ": 以降序排列此列"
}
},
"aoColumnDefs": []
});
其中buttons的配置参考datatable extension的文档去好好看看咯datatables.net/extensions/buttons/examples
导出pdf无中文乱码结果:
想要完整代码的,从下边链接进去下载吧
datatable、pdf中文无乱码导出
刚刚前面有提到,我们极有可能遇到:某个数据要标红显示的需求,那么我们来实现一下吧,让表格第五行的第二列标红.
要实现需求,本质上就是定位到我们需求的位置,然后对需求内容样式更改下即可。要定位到需求位置,就在rows里边去找吧。
生成rows的时候,遍历了一下data.body。OK,显然,我们在data.body遍历的时候去定位就可以了
代码如下
for ( var i=0, ien=data.body.length ; iif(i==4){
rows.push( $.map( data.body[i], function ( d, key ) {
if(key == 1){
return {
text: typeof d === 'string' ? d : d+'',
style: i % 2 ? 'tableBodyEven' : {fillColor: '#f3f3f3',color:'red'}
};
}else {
return {
text: typeof d === 'string' ? d : d+'',
style: i % 2 ? 'tableBodyEven' : 'tableBodyOdd'
};
}
} ) );
}else{
rows.push( $.map( data.body[i], function ( d ) {
return {
text: typeof d === 'string' ? d : d+'',
style: i % 2 ? 'tableBodyEven' : 'tableBodyOdd'
};
} ) );
}
}
经过了以上从懵懵懂懂,到最后的应用游刃有余,我想说的不仅仅是一个pdf中文无乱码导出前端JS功能,更重要的是我们对技术追求的学习力。技术是一个无边的海阳,我们不可能掌握所有的技术。尤其是我们在工作的时候,更多的是需要什么,然后去学习什么。
包括目前现有的各种插件,当其中某一种部分不能满足我们需求的时候,不妨来读读源码,甚至可以尝试改改源码,轻而易举就能把需求漂亮完成咯