将ECharts图表插入到Word文档中

news2024/12/29 0:20:21

文章目录

    • 在后端调用JS代码
    • 准备ECharts库
    • 生成Word文档
    • 项目地址
      • 库封装
      • 本文示例 EChartsGen_DocTemplateTool_Sample

如何通过ECharts在后台生成图片,然后插入到Word文档中?

首先要解决一个问题:总所周知,ECharts是前端的一个图表库,如何在后台调用JS代码?这里就要用到PhantomJS了。

PhantomJS是一个基于WebKit的JavaScript API,它使用QtWebKit作为核心浏览器的功能,使用WebKit编译、解释和执行JavaScript代码。任何可以在基于WebKit的浏览器上做的事情,它都能做到。它不仅是一个隐形的浏览器,提供了诸如CSS选择器、支持Web标准、DOM操作、JSON、HTML5、Canvas、SVG等功能,同时也提供了处理文件I/O的操作。

之前写过一个文档模板工具,其中可以通过占位符插入图片。

用PhantomJS生成ECharts图表的Png图片,利用文档模板工具插入图片即可实现这个需求。

下面就来看看如何实现。

在后端调用JS代码

创建一个.netstandard2.1的类库项目。为了方便调用,我们安装一个PhantomJS包装器:NReco.PhantomJS

dotnet add package NReco.PhantomJS --version 1.1.0

这只是一个包装器,因此还需要一个可执行文件,前往官网下载PhantomJS。

因为直接使用编译好的可执行文件,因此需要下载对应的平台版本,这里我下载了Windows以及Linux 64-bit版本。

将下载好的可执行文件解压放置在项目根目录下的libs目录中。

在这里插入图片描述

这样我们可以直接在.net中调用PhantomJS了。

在这里插入图片描述

准备ECharts库

jQuery

下载jquery-3.6.3.min.js: https://code.jquery.com/jquery-3.6.3.min.js

ECharts

下载echarts.min.js: https://github.com/apache/echarts/tree/5.4.3/dist

ECharts转换器

echarts-convert在github上有众多版本,echarts-convert的代码来源于这里:https://github.com/wadezhan/billfeller.github.io/issues/85

这里选择

(function () {
    var system = require('system');
    var fs = require('fs');
    var config = {
        // define the location of js files
        JQUERY: 'jquery-3.6.3.min.js',
        //ESL: 'esl.js',
        ECHARTS: 'echarts.min.js',
        // default container width and height
        DEFAULT_WIDTH: '1920',
        DEFAULT_HEIGHT: '800'
    }, parseParams, render, pick, usage;

    usage = function () {
        console.log("\nUsage: phantomjs echarts-convert.js -options options -outfile filename -width width -height height"
            + "OR"
            + "Usage: phantomjs echarts-convert.js -infile URL -outfile filename -width width -height height\n");
    };

    pick = function () {
        var args = arguments, i, arg, length = args.length;
        for (i = 0; i < length; i += 1) {
            arg = args[i];
            if (arg !== undefined && arg !== null && arg !== 'null' && arg != '0') {
                return arg;
            }
        }
    };

    parseParams = function () {
        var map = {}, i, key;
        console.log("--logs:\n")
        console.log(system.args)
        if (system.args.length < 2) {
            usage();
            phantom.exit();
        }
        for (i = 0; i < system.args.length; i += 1) {
            if (system.args[i].charAt(0) === '-') {
                key = system.args[i].substr(1, i.length);
                if (key === 'infile') {
                    // get string from file
                    // force translate the key from infile to options.
                    key = 'options';
                    try {
                        map[key] = fs.read(system.args[i + 1]).replace(/^\s+/, '');
                    } catch (e) {
                        console.log('Error: cannot find file, ' + system.args[i + 1]);
                        phantom.exit();
                    }
                } else {

                    map[key] = system.args[i + 1].replace(/^\s+/, '');
                }
            }
        }
        return map;
    };

    render = function (params) {
        var page = require('webpage').create(), createChart;

        var bodyMale = config.SVG_MALE;
        page.onConsoleMessage = function (msg) {
            console.log(msg);
        };

        page.onAlert = function (msg) {
            console.log(msg);
        };

        createChart = function (inputOption, width, height, config) {
            var counter = 0;
            function decrementImgCounter() {
                counter -= 1;
                if (counter < 1) {
                    console.log(messages.imagesLoaded);
                }
            }

            function loadScript(varStr, codeStr) {
                var script = $('<script>').attr('type', 'text/javascript');
                script.html('var ' + varStr + ' = ' + codeStr);
                document.getElementsByTagName("head")[0].appendChild(script[0]);
                if (window[varStr] !== undefined) {
                    console.log('Echarts.' + varStr + ' has been parsed');
                }
            }

            function loadImages() {
                var images = $('image'), i, img;
                if (./Assets/images.length > 0) {
                    counter = images.length;
                    for (i = 0; i < images.length; i += 1) {
                        img = new Image();
                        img.onload = img.onerror = decrementImgCounter;
                        img.src = images[i].getAttribute('href');
                    }
                } else {
                    console.log('The images have been loaded');
                }
            }
            // load opitons
            if (inputOption != 'undefined') {
                // parse the options
                loadScript('options', inputOption);
                // disable the animation
                options.animation = false;
            }

            // we render the image, so we need set background to white.
            $(document.body).css('backgroundColor', 'white');
            var container = $("<div>").appendTo(document.body);
            container.attr('id', 'container');
            container.css({
                width: width,
                height: height
            });
            // render the chart
            var myChart = echarts.init(container[0]);
            myChart.setOption(options);
            // load images
            loadImages();
            return myChart.getDataURL();
        };

        // parse the params
        page.open("about:blank", function (status) {
            // inject the dependency js
            page.injectJs(config.ESL);
            page.injectJs(config.JQUERY);
            page.injectJs(config.ECHARTS);


            var width = pick(params.width, config.DEFAULT_WIDTH);
            var height = pick(params.height, config.DEFAULT_HEIGHT);

            // create the chart
            var base64 = page.evaluate(createChart, params.options, width, height, config);
            //fs.write("base64.txt", base64);
            // define the clip-rectangle
            page.clipRect = {
                top: 0,
                left: 0,
                width: width,

                height: height
            };
            // render the image
            page.render(params.outfile);
            console.log('render complete:' + params.outfile);
            // exit
            phantom.exit();
        });
    };
    // get the args
    var params = parseParams();

    // validate the params
    if (params.options === undefined || params.options.length === 0) {
        console.log("ERROR: No options or infile found.");
        usage();
        phantom.exit();
    }
    // set the default out file
    if (params.outfile === undefined) {
        var tmpDir = fs.workingDirectory + '/tmp';
        // exists tmpDir and is it writable?
        if (!fs.exists(tmpDir)) {
            try {
                fs.makeDirectory(tmpDir);
            } catch (e) {
                console.log('ERROR: Cannot make tmp directory');
            }
        }
        params.outfile = tmpDir + "/" + new Date().getTime() + ".png";
    }

    // render the image
    render(params);
}());

将上述文件放在项目根目录下的js目录中。

在这里插入图片描述

我们来测试一下是否能生成一个简单的ECharts图表。

创建一个option.json

在这里插入图片描述

首先指定一个option,在官方示例 https://echarts.apache.org/examples/zh/index.html 中,随意找一个柱状图的sample,复制option对象内容到新创建的option.json文件中

{
  "tooltip": {
    "trigger": "axis",
    "axisPointer": {
      "type": "shadow"
    }
  },
  "grid": {
    "left": "3%",
    "right": "4%",
    "bottom": "3%",
    "containLabel": true
  },
  "xAxis": [
    {
      "type": "category",
      "data": [ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" ],
      "axisTick": {
        "alignWithLabel": true
      }
    }
  ],
  "yAxis": [
    {
      "type": "value"
    }
  ],
  "series": [
    {
      "name": "Direct",
      "type": "bar",
      "barWidth": "60%",
      "data": [ 10, 52, 200, 334, 390, 330, 220 ]
    }
  ]
}

Program.cs中调用ECharts转换器:

static void Main(string[] args)
{
    var phantomJS = new PhantomJS();

    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
    {
        phantomJS.ToolPath = Path.Combine(basePath, "libs\\phantomjs-2.1.1-windows\\bin");

    }
    else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
    {
        phantomJS.ToolPath = Path.Combine(basePath, "libs\\phantomjs-2.1.1-linux-x86_64\\bin");

    }
    var scriptPath = Path.Combine(basePath, "js\\echarts-converts.js");

    var optionPath = Path.Combine(basePath, "js\\option.json");

    phantomJS.OutputReceived += (sender, e) =>
    {
        Console.WriteLine("PhantomJS output: {0}", e.Data);
    };
    
    phantomJS.Run(scriptPath, new string[] { "-infile", optionPath });
    phantomJS.Abort();

}

打印如下

在这里插入图片描述

打开输出路径看到生成的图片。

在这里插入图片描述

生成Word文档

为了方便集成,我加.NET中构件ECharts中可能用的全部数据结构。
这里感谢https://github.com/idoku/EChartsSDK这个项目,代码基本都是从这里拷贝过来的。

这样可以通过指定ChartOption对象,生成图片。

var option = new ChartOption()
    {
        title = new List<Title>()
        {
            new Title (){
            text=title, left="center"}
        },
        tooltip = new ToolTip(),
        legend = new Legend()
        {
            orient = OrientType.vertical,
            left = "left"
        },
        series = new object[]
        {
            new {
                name= "Access From",
                type="pie",
                data=new object[]
                    {
                        new  { value= failedCount, name="异常" },
                        new  { value= passCount, name="正常" },
                    }
            }
        }
    }

根据Document Template Tool图片占位符格式:#字段名称[宽度,高度]#,

在上一章的Sample基础上,在ReportTemplate2.docx中添加图片占位符。

在这里插入图片描述

生成后的文档如下:

在这里插入图片描述

项目地址

库封装

https://github.com/jevonsflash/EChartsGen

本文示例 EChartsGen_DocTemplateTool_Sample

https://github.com/jevonsflash/EChartsGen/tree/master/EChartsGen_DocTemplateTool_Sample

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1219916.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

asp.net实验室设备管理系统VS开发sqlserver数据库web结构c#编程计算机网页源码项目

一、源码特点 asp.net实验室设备管理系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 asp.net实验室设备管理系统1 二、功能介绍 本系统使用Microsoft Visual Studio 2019为开发工具&#xff0c;SQL …

国鑫受邀出席2023松山湖软件和信息服务业高质量发展大会

为推动粤港澳大湾区的软件和先进制造产业的融合发展&#xff0c;“2023松山湖软件和信息服务业高质量发展大会”于今日在松山湖畔隆重举办&#xff0c;会议以“推动软件和制造业深度融合发展&#xff0c;打造软件和信息服务业集聚高地”为主题&#xff0c;聚焦工业软件应用、智…

解析CAD图纸出现乱码的原因及解决方法

解析CAD图纸出现乱码的原因及解决方法 CAD&#xff08;计算机辅助设计&#xff09;是现代工程设计中不可或缺的工具&#xff0c;它能够帮助工程师们高效地完成复杂的设计任务。然而&#xff0c;有时在使用CAD软件过程中&#xff0c;可能会遇到图纸出现乱码的问题&#xff0c;影…

【python零基础入门学习】python进阶篇之数据库连接-PyMysql-全都是干货-一起来学习吧!!!

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

解决因跨域导致使用a标签下载文件download属性失效无法自定义命名的问题

问题背景&#xff1a; 在使用a标签下载文件时&#xff0c;download属性可以更改下载的文件名。 // 下载a.exe,并采用默认命名 <a href"/images/a.exe" download>点击下载</a>// 将a.exe改名为b.exe下载 <a href"/images/a.exe" download&…

什么是日志分析?为什么IT管理员需要日志分析?

在现在大数据时代&#xff0c;大量的数据被生成和记录&#xff0c;无论是企业还是个人&#xff0c;都在不断产生各种日志。日志记录了系统、应用程序、网络等多个领域的活动和事件信息&#xff0c;它们对于解决问题、监控和优化系统、还原事件等都非常重要。而这些海量的日志数…

粉够荣获淘宝联盟理事会常务理事,共绘联盟生态新篇章

淘宝联盟区域理事会于2021年成立&#xff0c;首届成立成都、广州、武汉&#xff0c;服务近2000个领军淘宝客企业&#xff0c;作为区域生态与官方交流重要枢纽&#xff0c;理事会举办近百场交流分享会&#xff0c;带动淘客跨域跨业态交流成长。 2023年9月7日第二届淘宝联盟理事…

操作系统 day11(进程调度时机、切换、调度方式)

进程调度的时机 这里的主动放弃指的是—内中断&#xff08;异常、例外&#xff09;&#xff0c;中断信号来自CPU内部。而被动放弃指的是—外中断&#xff08;中断&#xff09;&#xff0c;中断信号来自CPU外部 如果该进程还没退出临界区&#xff08;还没解锁&#xff09;就进行…

Spring Boot中使用MongoDB完成数据存储

我们在开发中用到的数据存储工具有许多种&#xff0c;我们常见的数据存储工具包括&#xff1a; 关系性数据库&#xff1a;使用表格来存储数据&#xff0c;支持事务和索引。&#xff08;如&#xff1a;MySQL&#xff0c;Oracle&#xff0c;SQL Server等&#xff09;。NoSQL数据…

信创环境下高级威胁攻击层出不穷,信息化负责人该如何增强对抗与防御能力?

11月15日&#xff0c;以“加快推进智慧校园建设 赋能为党育才为党献策”为主题的2023年华东地区党校&#xff08;行政学院&#xff09;信息化和图书馆工作高质量发展专题研讨班顺利举办。 作为国内云原生安全领导厂商&#xff0c;安全狗受邀出席活动。 厦门服云信息科技有限公司…

无需添加udid,ios企业证书的自助生成方法

我们开发uniapp的app的时候&#xff0c;需要苹果证书去打包。 假如申请的是个人或company类型的苹果开发者账号&#xff0c;必须上架才能安装&#xff0c;异常的麻烦&#xff0c;但是有一些app&#xff0c;比如企业内部使用的app&#xff0c;是不需要上架苹果应用市场的。 假…

2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-B卷

2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-B卷 2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-B卷A模块基础设施设置/安全加固&#xff08;200分&#xff09;A-1&#xff1a;登录安全加固&#xff08;Windows, Linux&#xff09;A-2&#…

【科技素养】蓝桥杯STEMA 科技素养组模拟练习试卷4

单选题 1、Soda is sold in packs of 6、12 and 24 cans.What is the minimum number of packs needed to buy exactly 90cans of soda? A、4 B、5 C、6 D、8 答案&#xff1a;B 2、蓝桥杯STEMA考试中心发送有同样内容、重量、体积的试卷&#xff0c;从北京10箱和上海6箱…

数据结构详细笔记——图

文章目录 图的定义图的存储邻接矩阵法邻接表法邻接矩阵法与邻接表法的区别 图的基本操作图的遍历广度优先遍历&#xff08;BFS&#xff09;深度优先遍历&#xff08;DFS&#xff09;图的遍历和图的连通性 图的定义 图G由顶点集V和边集E组成&#xff0c;记为G(V,E)&#xff0c;…

uniapp-轮播图点击预览功能

实现效果 点击后打开预览图 实现代码 <swiper v-if"this.bannerList.length > 1" class"swiper" autoplay"true" duration"500" interval"2000" change"changeSwiper"><swiper-item class"swip…

vue下载xlsx表格

vue下载xlsx表格 // 导入依赖库 import XLSX from xlsx; import FileSaver from file-saver; methods:{btn(){let date new Date()let Y date.getFullYear() -let M (date.getMonth() 1 < 10 ? 0 (date.getMonth() 1) : date.getMonth() 1) -let D (date.getDat…

抖音小店没有流量?如何快速起店?实操经验分享!

我是电商珠珠 做抖音小店最重要的就是流量&#xff0c;店铺没有流量也就意味着销量上不去&#xff0c;最后只能被平台清退。 我做抖音小店也已经快三年的时间了&#xff0c;这种情况我也遇到过&#xff0c;接下来给大家讲讲解决方案。 第一步&#xff0c;选品 品是整个店铺…

电子商务税收问题:跨境电商的挑战与解决

随着电子商务的崛起&#xff0c;跨境电商已经成为全球贸易的主要动力之一。然而&#xff0c;电子商务的快速发展也带来了一系列税收问题&#xff0c;尤其是涉及跨境交易的税收问题。本文将深入探讨跨境电商所面临的税收挑战&#xff0c;以及政府和国际组织正在采取的解决方案。…

Matlab通信仿真系列——信号处理函数

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 本节目录 一、Matlab信号产生函数…