前端实用技术 之报表的打包下载

前端实用技术之图表的打包下载

现在大部分系统图表的生成和包括打包下载依赖后端的报表服务,由于后端报表服务可定制化成都差,扩展困难,风格和样式单一,也很难做出炫酷的效果。本文讲述一下前端实现报表的批量打包下载。

实现过程

1.实现报表展现(如果不需要展现报表的话,这步跳过)。
2.把这些报表放到zip里面。
3.把打包好的zip文件前端生成下载链接。
传统的报表应该是有定制化表头以及水印等等信息,要实现这些功能需要前端开发者去调研,在我调研的这些图表工具中比较倾向于百度的eCharts,功能和效果应该可以满足大部分的需求。打包工具比较好用的是zip.js。

选择eCharts的理由: (1)丰富的图表类型
(2)多个坐标系的支持 (3)丰富的api文档 (4)深度的交互式数据探索 (5)大数据量的展现 (6)多维数据的支持以及丰富的视觉编码手段 (7)动态数据 (8)绚丽的特效 (9)最重要的一点,好用还开源免费

eCharts安装和使用请查看官网或者github

需要注意如果需要表头表尾插入图片logo等功能的需要3.7.2及以上版本。

准备开始:

1、eChart制作图表

要实实现以上报表的展现,我首先去想到的是eCharts有没有表尾的配置,实际eCharts是没有的,但是他可以配置多个title,所以我通过配置两个title,控制title在报表的位置来实现表头表尾效果(页眉页脚也是一样的思路)。还有一个技术点是图标和样式应该怎么去实现,恰好eCharts在title标签内可以配置rich(图片的插入和自定义在新版本才会有,目前最新版本3.8.4,如果不支持的话请升级版本) eCharts配置例子(因为一般的报表的表头表尾都有统一规则所以这我写的是一个公共的方法)

function getChartTitle(subtext, title, officeName) {  
    return [{
        left: 30,
        text: title,
        textStyle: {height: 28, fontSize: 20, fontFamily: fontFamily, fontWeight: 600},
        subtext: subtext,
        subtextStyle: {
            left: 30,
            lineHeight: 20,
            fontSize: 14,
            fontFamily: fontFamily,// 自定义的字体
            fontWeight: 400,
            rich: {lineHeight: 30}
        },
        x: '10px',
        y: '30px',
        textAlign: 'left'
    }, {
        text: '{a|由' + officeName + '通过}' + "{b|}" + '{x|\|}' + '{c|大数据中心}' + '{a|生成}',
        bottom: '20px',
        z: 2,
        right: '-80px',
        textAlign: 'center',
        textStyle: {
            rich: {
                a: {
                    height: 20,
                    fontSize: 14,
                    fontFamily: fontFamily,
                    fontWeight: 400,
                    color: '#a9a9a9',
                    lineHeight: 20,
                    padding: [3, 4, 5, 6]
                },
                b: {backgroundColor: {image: '你的图片的base64或者Url'}, height: 18}, c: {
                    height: 20,
                    fontSize: 14,
                    fontFamily: fontFamily,
                    fontWeight: 400,
                    color: '#ed6c00', padding: [3, 0, 5, 6]
                }, x: {
                    height: 20,
                    fontSize: 14,
                    fontFamily: fontFamily,
                    fontWeight: 400,
                    padding: [3, 0, 5, 6],
                    color: '#ececec'
                }
            }
        }
    }]
}

如果我们的下载就是所见即所得的eCharts的图表,那么我们只需要把每个图的eCharts的图的base64直接取到放到前端的数组里。如果我们看到的和下载的不一致的化,那我们就要考虑一个一个的去渲染图表,然后取到图表以后马上去销毁这个实力,为了页面的流畅程度,一个个加载取值然后销毁是一个必要操作。 经验告诉我们eCharts的实例如果单页超过10的话,会有明显卡顿感。来看一下,分步加载取图的代码。

function initDownChart(option, height) {  
                var height = height || 1500
                var id = (new Date()).getTime()
                $('#hide-visual').append('<div  hidden  style="width: 1000px;height:' + height + 'px" id=' + id + '></div>')
                var instance = echarts.init(document.getElementById(id));
                instance.setOption(option);
                var defer = $q.defer();
                setTimeout(function () {
                    defer.resolve({
                        dataUrl: instance.getDataURL({
                            pixelRatio: 1,
                            backgroundColor: 'white'
                        })
                    })
                    instance.dispose();
                    $('#' + id).remove();
                }, 0)
                return defer.promise;
  }

注意:eCharts实例化必须要在有高度和宽度的容器。

2、将取到的图片压缩到zip并生成链接下载

使用的是zip.js来实现图片的压缩到zip,它本身自己的调用方法并不好用,因此我做了一层封装,以下是封装代码。

        function createTempFile(callback) {
            var tmpFilename = "tmp.zip";
            requestFileSystem(window.EMPORARY, 4 * 1024 * 1024 * 1024, function(filesystem) {
                function create() {
                    filesystem.root.getFile(tmpFilename, {
                        create: true
                    }, function(zipFile) {
                        callback(zipFile);
                    });
                }

                filesystem.root.getFile(tmpFilename, null, function(entry) {
                    entry.remove(create, create);
                }, create);
            });
        }


        function addFiles(files, options) {
            //调用addFiles的时候直接调用addFiles(files,{onEnd:function(){},onAdd:function(file){}.....)
            var addIndex = 0;

            function nextFile() {
                var file = files[addIndex];
                if (options.onAdd) {
                    options.onAdd(file)
                }
                zipWriter.add(file.name + '.png', new zip.Data64URIReader(file.dataUrl), function() {
                    addIndex++;
                    if (addIndex < files.length)
                        nextFile();
                    else
                        options.onEnd();
                }, options.onprogress ? options.onprogress : '');
            }

            function createZipWriter() {
                zip.createWriter(writer, function(writer) {
                    zipWriter = writer;
                    if (options.onInit) {
                        options.onInit();
                    }
                    nextFile();
                }, onerror);
            }

            if (zipWriter)
                nextFile();
            else if (creationMethod == "Blob") {
                writer = new zip.BlobWriter();
                createZipWriter();
            } else {
                createTempFile(function(fileEntry) {
                    zipFileEntry = fileEntry;
                    writer = new zip.FileWriter(zipFileEntry);
                    createZipWriter();
                });
            }
        }


        function getBlobURL(callback) {
            zipWriter.close(function(blob) {
                var blobURL = URL.createObjectURL(blob);
                callback(blobURL);
                zipWriter = null;
            });

        }

        function getBlob(callback) {
            zipWriter.close(callback);
        }

` 注:Blob方式是通用的,通过文件流的方式压缩下载,requestFileSystem这种方式也是可以的,但是这种方式是操作本地文件的方式做的,本质是把生成的zip放到硬盘的缓存的临时文件中,然后调用浏览器自己的native api获取这个文件的流,然后下载。好处是不会占用过多系统内存,缺点是只有chrome目前是完美支持的。

李文杰

前端程序设计,人丑、家穷、没文化 myGit: https://github.com/JosnLee

北京市朝阳区成寿寺