
一、项目工程结构

二.项目依赖说明
项目的pom.xml配置,属性配置 见第一篇
三.项目代码说明
(1)echarts-pojo模块(数据模型)
package com.lhf.springboot.echarts.pojo;
import lombok.Data;
/**
* @ClassName: PieData
* @Author: liuhefei
* @Description: TODD
*/
@Data
public class PieData {
private String[] types;
private String[] datas;
private String title;
}(2)controler模块(API接口)
package com.lhf.springboot.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lhf.springboot.common.JsonResult;
import com.lhf.springboot.echarts.pojo.BarData;
import com.lhf.springboot.echarts.pojo.LinesData;
import com.lhf.springboot.echarts.pojo.PieData;
import com.lhf.springboot.util.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.time.FastDateFormat;
import org.jboss.logging.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.awt.*;
import java.io.File;
import java.io.FileOutputStream;
import java.text.DecimalFormat;
import java.util.*;
import java.util.List;
/**
* @ClassName: EchartsTemplateController
* @Author: liuhefei
* @Description: 使用模板生成饼图
*/
@Api(value = "Freemarker模板生成Echarts图表Api接口", tags = "模板生成图表Api-1")
@RequestMapping("/template")
@RestController
public class EchartsTemplateController {
private final static Logger logger = Logger.getLogger(EchartsController.class);
@Value("${img-url}")
private String imgUrl;
@Value("${img-url-path}")
private String imgUrlPath;
@Value("${request-url}")
private String requestUrl;
private static final String path = FreemarkerUtil.class.getClassLoader().getResource("").getPath();
private String temPath = path + "templates";
@ApiOperation("模板生成饼图, html文件格式")
@RequestMapping(value = "/pie", method = RequestMethod.POST)
public JsonResult createPieFtl(@RequestBody PieData pieData) {
String title = pieData.getTitle();
String[] types = pieData.getTypes();
String[] datas = pieData.getDatas();
if (title == null || types == null || datas == null) {
return new JsonResult(-1, "参数异常");
}
if(types.length != datas.length){
return new JsonResult(-1, "数据不对应");
}
//组装参数
Double data = null;
Double sum = 0.0;
DecimalFormat df = new DecimalFormat(".00");
List<String> stringList = new ArrayList<>();
String bfb = null;
String typeParam = null;
List<Map<String, Object>> listMap = new ArrayList<>();
for(int i = 0;i<types.length;i++){
data = Double.valueOf(datas[i]);
System.out.println("data = " + data);
sum += Double.valueOf(data);
}
System.out.println("sum = " + sum);
for(int i =0 ;i<types.length;i++){
data = Double.valueOf(datas[i]);
bfb = String.valueOf(df.format(data/sum * 100));
System.out.println("bfb = " + bfb);
typeParam = types[i] + ":" + datas[i] + "(" + bfb + "%)";
System.out.println(typeParam);
//1.组装types
stringList.add(typeParam);
//2.组装datas
Map<String, Object> mapStr = new HashMap<>();
mapStr.put("name", typeParam);
mapStr.put("value", datas[i]);
listMap.add(mapStr);
}
System.out.println(stringList);
System.out.println(listMap);
try {
//组装参数
HashMap<String, Object> pieDatas = new HashMap<>();
pieDatas.put("title", title);
//pieDatas.put("types", JSON.toJSONString(types));
pieDatas.put("types", JSON.toJSONString(stringList));
pieDatas.put("datas", JSON.toJSONString(listMap));
String option = FreemarkerUtil.generateString("pieOption1.ftl", temPath, pieDatas);
System.out.println("option = " + option);
Map<String, Object> pieOption = new HashMap<>();
pieOption.put("option", option);
String htmlPie = FreemarkerUtil.generateString("pieOption.ftl", temPath, pieOption);
System.out.println("htmlPie = " + htmlPie);
//写入html
String nowStr = FastDateFormat.getInstance("yyyyMMddHHmmss").format(new Date());
String imageName = "pie"+nowStr;
File htmlFile = new File(imgUrl+imageName+".html");
if(!htmlFile.exists()){
htmlFile.createNewFile();
}
byte bytes[] = new byte[1024];
bytes = htmlPie.getBytes();
int b = bytes.length; //字节长度
FileOutputStream fos = new FileOutputStream(htmlFile);
fos.write(bytes, 0, b);
fos.write(bytes);
fos.close();
} catch (Exception e) {
logger.error("发生了异常," + e.getMessage());
return new JsonResult(-1, "Fail");
}
return new JsonResult(1, "SUCCESS");
}
@ApiOperation("模板生成饼图")
@RequestMapping(value = "/pieImg", method = RequestMethod.POST)
public JsonResult createPieFtlImage(@RequestBody PieData pieData) {
String title = pieData.getTitle();
String[] types = pieData.getTypes();
String[] datas = pieData.getDatas();
if (title == null || types == null || datas == null) {
return new JsonResult(-1, "参数异常");
}
if(types.length != datas.length){
return new JsonResult(-1, "数据不对应");
}
//组装参数
Double data = null;
Double sum = 0.0;
DecimalFormat df = new DecimalFormat(".00");
List<String> stringList = new ArrayList<>();
String bfb = null;
String typeParam = null;
List<Map<String, Object>> listMap = new ArrayList<>();
for(int i = 0;i<types.length;i++){
data = Double.valueOf(datas[i]);
System.out.println("data = " + data);
sum += Double.valueOf(data);
}
System.out.println("sum = " + sum);
for(int i =0 ;i<types.length;i++){
data = Double.valueOf(datas[i]);
bfb = String.valueOf(df.format(data/sum * 100));
System.out.println("bfb = " + bfb);
typeParam = types[i] + ":" + datas[i] + "(" + bfb + "%)";
System.out.println(typeParam);
//1.组装types
stringList.add(typeParam);
//2.组装datas
Map<String, Object> mapStr = new HashMap<>();
mapStr.put("name", typeParam);
mapStr.put("value", datas[i]);
listMap.add(mapStr);
}
System.out.println(stringList);
System.out.println(listMap);
String oldFilePath = null;
String newFilePath = null;
try {
//组装参数
HashMap<String, Object> pieDatas = new HashMap<>();
pieDatas.put("title", title);
//pieDatas.put("types", JSON.toJSONString(types));
pieDatas.put("types", JSON.toJSONString(stringList));
pieDatas.put("datas", JSON.toJSONString(listMap));
String option = FreemarkerUtil.generateString("pieOption1.ftl", temPath, pieDatas);
System.out.println("option = " + option);
long nowStr = Calendar.getInstance().getTimeInMillis();
String imageName = "pie"+nowStr+ RandomUtils.getRandomString(4)+".png";
Map<String, Object> opt = (Map<String, Object>) JSONObject.parse(option);
PhantomJS js = new PhantomJS();
js.setOpt(opt);
js.setReqMethod("echarts");
js.setFile(imgUrlPath+imageName);
PhantomJSUtil.phantomJS(requestUrl, JSON.parseObject(JSON.toJSONString(js)));
oldFilePath = imgUrlPath+imageName;
newFilePath = imgUrlPath+"new"+imageName;
logger.info("oldFilePath = " + oldFilePath);
logger.info("newFilePath = " + newFilePath);
String logoText = "霜花似雪";
//添加水印
ImageUtil.markImageByText(logoText, oldFilePath, newFilePath, -15, new Color(190,190,190), "png");
} catch (Exception e) {
logger.error("发生了异常," + e.getMessage());
return new JsonResult(-1, "Fail");
}
return new JsonResult(1, "SUCCESS");
}
}三、插件环境配置说明
插件环境说明见第一篇文章
其他重要说明:
生成饼图的命令:
D:\>cd D:\softpack\echarts\phantomjs-2.1.1-windows\bin D:\softpack\echarts\phantomjs-2.1.1-windows\bin>phantomjs D:\softpack\echarts\phantomjs-2.1.1-windows\bin\echarts-util.js -s -p 6668 或者phantomjs D:\softpack\echarts\phantomjs-2.1.1-windows\bin\echarts-util-one.js -s -p 6668

关键文件说明:
echarts-util.js: 此文件用于生成状图、折线图、饼图, 只限于普通的option(option中没有js方法的)
echarts-util-one.js: 此文件用于生成状图、折线图、饼图,生成的柱状图具有颜色渐变的效果,option中含有js方法,实例可以参考bar.txt文件
echarts-util.js代码:
phantom.outputEncoding = "gbk";// 为防止输出中文时出现乱码,可设置输出编码格式,写在最顶部
var params = require('system');// 获取系统参数
var server = require('webserver').create(); // 服务端
var port = params.args[3];// 端口,与启动命令有关,不一定是3
var listen = server.listen(port, function(request, response) {// 监听端口
var args = serverGetArgs(request);// 得到网络请求参数
args.response = response;
methodDis(args);
});
var jslib = {
jquery : phantom.libraryPath + '/lib/jquery-3.2.1.min.js',
echarts : phantom.libraryPath + '/lib/echarts.min.js',
china : phantom.libraryPath + '/lib/china.js',
};
/**
* 请求分发
*
* @author liansh
* @data 2019年9月19日 下午11:32:59
* @param args
*/
function methodDis(args) {
if (args.reqMethod == "table") {
table(args);
} else if (args.reqMethod == "echarts") {
echarts(args);
}
if (args.exit == "true") {
writeResponse(args.response, {
error_no : 0
});
phantom.exit();
}
}
function table(args) {
var page = require('webpage').create();// 打开页面
// 设置分辨率
page.viewportSize = {
width : 1000,
height : 1200
};
// 打开页面
page.open(args.url || 'http://127.0.0.1:8080/hello', function(status) {
if (status == "fail") {
writeResponse(args.response, {
error_no : -1
});
return;
}
page.injectJs(jslib.jquery);
var tableheight = page.evaluate(function() {
return $('body').height() + 20;
});
// 定义剪切范围
page.clipRect = {
top : 0,
left : 0,
width : 1000,
height : tableheight
};
// var base64 = 'data:image/png;base64,' + page.renderBase64('png');
page.render(args.file);// 将整个page保存为文件,可以是png,jpg, gif,pdf
page.close();
writeResponse(args.response, {
error_no : 0
});
});
page.onError = function(msg, trace) {
writeResponse(args.response, {
error_no : -1,
error_info : trace
});
};
}
function echarts(args) {
var page = require('webpage').create(); // 客户端
page.open("about:blank", function(status) {// 空白页
page.injectJs(jslib.jquery);
page.injectJs(jslib.echarts);
page.injectJs(jslib.china);
var pageBody = page.evaluate(function(args) {
// 动态加载js,获取options数据
$('<script>').attr('type', 'text/javascript').html('var options = ' //
+ JSON.stringify(args.opt)).appendTo(document.head);
// 取消动画,否则生成图片过快,会出现无数据
if (options !== undefined) {
options.animation = false;
}
// body背景设置为白色
$(document.body).css('backgroundColor', 'white');
// echarts容器
var container = $("<div>").attr('id', 'container').css({
width : args.width,
height : args.height
}).appendTo(document.body);
var eChart = echarts.init(container[0]);
eChart.setOption(options);
}, args);
// 定义剪切范围
page.clipRect = {
top : 0,
left : 0,
width : args.width - 100,
height : args.height + 10
};
// var base64 = 'data:image/png;base64,' + page.renderBase64('png');
// writeResponse(args.response, {// 返回给http请求
// error_no : 0,
// base64 : base64
// });
page.render(args.file);// 将整个page保存为文件,可以是png,jpg, gif,pdf
page.close();
writeResponse(args.response, {
error_no : 0
});
});
page.onError = function(msg, trace) {
writeResponse(args.response, {
error_no : -1,
error_info : trace
});
};
}
function writeResponse(response, msg) {
response.write(JSON.stringify(msg || {
error_no : 0
}));
response.close();
}
/**
* 获取请求参数
*
* @author liansh
* @data 2019年9月19日 下午11:27:16
* @param request
* @returns
*/
function serverGetArgs(request) {
var args = {};
if ('GET' === request.method) {
var index = request.url.indexOf('?');
if (index !== -1) {
pairs = request.url.substr(index + 1).split("&");
for (var i = 0; i < pairs.length; i++) {
var pos = pairs[i].indexOf('=');
if (pos === -1)
continue;
var key = pairs[i].substring(0, pos);
var value = pairs[i].substring(pos + 1);
// 中文解码,必须写两层
value = decodeURIComponent(decodeURIComponent(value));
args[key] = value;
}
}
} else if ('POST' === request.method) {
args = JSON.parse(request.post);
}
args.width = args.width || 1000;
args.height = args.height || 400;
return args;
};echarts-util-one.js代码:
phantom.outputEncoding = "gbk";// 为防止输出中文时出现乱码,可设置输出编码格式,写在最顶部
var params = require('system');// 获取系统参数
var server = require('webserver').create(); // 服务端
var port = params.args[3];// 端口,与启动命令有关,不一定是3
var listen = server.listen(port, function(request, response) {// 监听端口
var args = serverGetArgs(request);// 得到网络请求参数
args.response = response;
methodDis(args);
});
var jslib = {
jquery : phantom.libraryPath + '/lib/jquery-3.2.1.min.js',
echarts : phantom.libraryPath + '/lib/echarts.min.js',
china : phantom.libraryPath + '/lib/china.js',
};
/**
* 请求分发
*
* @author liansh
* @data 2019年9月19日 下午11:32:59
* @param args
*/
function methodDis(args) {
if (args.reqMethod == "table") {
table(args);
} else if (args.reqMethod == "echarts") {
echarts(args);
}
if (args.exit == "true") {
writeResponse(args.response, {
error_no : 0
});
phantom.exit();
}
}
function table(args) {
var page = require('webpage').create();// 打开页面
// 设置分辨率
page.viewportSize = {
width : 1000,
height : 1200
};
// 打开页面
page.open(args.url || 'http://127.0.0.1:8080/hello', function(status) {
if (status == "fail") {
writeResponse(args.response, {
error_no : -1
});
return;
}
page.injectJs(jslib.jquery);
var tableheight = page.evaluate(function() {
return $('body').height() + 20;
});
// 定义剪切范围
page.clipRect = {
top : 0,
left : 0,
width : 1000,
height : tableheight
};
// var base64 = 'data:image/png;base64,' + page.renderBase64('png');
page.render(args.file);// 将整个page保存为文件,可以是png,jpg, gif,pdf
page.close();
writeResponse(args.response, {
error_no : 0
});
});
page.onError = function(msg, trace) {
writeResponse(args.response, {
error_no : -1,
error_info : trace
});
};
}
function echarts(args) {
var page = require('webpage').create(); // 客户端
page.open("about:blank", function(status) {// 空白页
/**
* 报错{"file":"undefined","line":3,"function":""},{"file":"","line":18,"function":""}
* "file":"undefined" 为所引用的jslib文件路径不对,需要重新检查路径
*/
page.injectJs(jslib.jquery);
page.injectJs(jslib.echarts);
page.injectJs(jslib.china);
var pageBody = page.evaluate(function(args) {
// 动态加载js,获取options数据
//var itemStyle = '{"normal":{"color" : new echarts.graphic.LinearGradient(0, 0, 0, 1, [ '+ '{offset : 0,color : "#83bff6"}, {offset : 0.5,color : "#188df0"}, {offset : 1,color : "#188df0"} ])}}';
var itemStyle = '{"normal":{"color" : new echarts.graphic.LinearGradient(0, 0, 0, 1, [ '+ '{offset : 0,color : "#3370FE"}, {offset : 0.5,color : "#3370FE"}, {offset : 1,color : "#3370FE"} ])}}';
// 动态加载js,获取options数据
$('<script>').attr('type', 'text/javascript').html('var options =' //
+ JSON.stringify(args.opt).replace('\"__itemStyle"', itemStyle)).appendTo(document.head);
// 取消动画,否则生成图片过快,会出现无数据
if (options !== undefined) {
options.animation = false;
}
// body背景设置为白色
$(document.body).css('backgroundColor', 'white');
// echarts容器
var container = $("<div>").attr('id', 'container').css({
width : args.width,
height : args.height
}).appendTo(document.body);
var eChart = echarts.init(container[0]);
eChart.setOption(options);
}, args);
// 定义剪切范围
page.clipRect = {
top : 0,
left : 0,
width : args.width - 100,
height : args.height + 10
};
// var base64 = 'data:image/png;base64,' + page.renderBase64('png');
// writeResponse(args.response, {// 返回给http请求
// error_no : 0,
// base64 : base64
// });
page.render(args.file);// 将整个page保存为文件,可以是png,jpg, gif,pdf
page.close();
writeResponse(args.response, {
error_no : 0
});
});
page.onError = function(msg, trace) {
writeResponse(args.response, {
error_no : -1,
error_info : trace
});
};
}
function writeResponse(response, msg) {
response.write(JSON.stringify(msg || {
error_no : 0
}));
response.close();
}
/**
* 获取请求参数
*
* @author liansh
* @data 2019年9月19日 下午11:27:16
* @param request
* @returns
*/
function serverGetArgs(request) {
var args = {};
if ('GET' === request.method) {
var index = request.url.indexOf('?');
if (index !== -1) {
pairs = request.url.substr(index + 1).split("&");
for (var i = 0; i < pairs.length; i++) {
var pos = pairs[i].indexOf('=');
if (pos === -1)
continue;
var key = pairs[i].substring(0, pos);
var value = pairs[i].substring(pos + 1);
// 中文解码,必须写两层
value = decodeURIComponent(decodeURIComponent(value));
args[key] = value;
}
}
} else if ('POST' === request.method) {
args = JSON.parse(request.post);
}
args.width = args.width || 1000;
args.height = args.height || 400;
return args;
};四、结合Swagger文档测试
环境配置完成之后,启动服务,swagger文档:http://localhost:8095/swagger-ui.html
测试数据:
{
"datas": [
43364, 13899, 12000, 2181, 21798, 1796, 1300
],
"title": "胸罩图例",
"types": [
"A罩杯", "B罩杯", "C罩杯", "D罩杯", "E罩杯", "F罩杯","G罩杯"
]
}效果展示:
(1)生成饼图,html文件格式
<!DOCTYPE html>
<html style="height: 100%">
<head>
<meta charset="utf-8">
</head>
<body style="height: 100%; margin: 0">
<div id="container" style="height: 100%"></div>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts/echarts.min.js"></script>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts-gl/echarts-gl.min.js"></script>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts-stat/ecStat.min.js"></script>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts/extension/dataTool.min.js"></script>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/simplex.js"></script>
<script type="text/javascript">
var dom = document.getElementById("container");
var myChart = echarts.init(dom);
var app = {};
option = null;
option ={
"calculable": true,
"legend": {
"data": ["A罩杯:43364(45.01%)","B罩杯:13899(14.43%)","C罩杯:12000(12.46%)","D罩杯:2181(2.26%)","E罩杯:21798(22.63%)","F罩杯:1796(1.86%)","G罩杯:1300(1.35%)"],
"orient": "vertical",
"x": "left",
"textStyle": {
"color": "red",
"fontSize": 15,
"fontWeight": "bolder"
}
},
"series": [{
"center": ["50%", "60%"],
"data": [{"name":"A罩杯:43364(45.01%)","value":"43364"},{"name":"B罩杯:13899(14.43%)","value":"13899"},{"name":"C罩杯:12000(12.46%)","value":"12000"},{"name":"D罩杯:2181(2.26%)","value":"2181"},{"name":"E罩杯:21798(22.63%)","value":"21798"},{"name":"F罩杯:1796(1.86%)","value":"1796"},{"name":"G罩杯:1300(1.35%)","value":"1300"}],
"name": "胸罩图例",
"radius": "65%",
"type": "pie",
"avoidLabelOverlap": true,
"label": {
"normal": {
"show": true,
"position": "top",
"textStyle": {
"color":"red",
"fontSize": "15",
"fontWeight": "bold"
}
},
"emphasis": {
"show": true,
"textStyle": {
"fontSize": "20",
"fontWeight": "bold"
}
}
},
"labelLine": {
"normal": {
"show": true
}
}
}],
"title": {
"subtext": "",
"text": "胸罩图例",
"x": "center",
"textStyle": {
"color": "green",
"fontSize": 20,
"fontWeight": "bolder"
}
},
"toolbox": {
"feature": {
"mark": {
"lineStyle": {
"color": "#1e90ff",
"type": "dashed",
"width": 2
},
"show": true
},
"dataView": {
"lang": ["数据视图", "关闭", "刷新"],
"readOnly": false,
"show": true,
"title": "数据视图"
},
"magicType": {
"show": true,
"title": {
"bar": "柱形图切换",
"stack": "堆积",
"tiled": "平铺",
"line": "折线图切换"
},
"type": ["pie", "funnel"]
},
"restore": {
"show": true,
"title": "还原"
},
"saveAsImage": {
"lang": ["点击保存"],
"show": true,
"title": "保存为图片",
"type": "png"
}
},
"show": true
},
"tooltip": {
"formatter": "{a} <br/>{b} : {c} ({d}%)",
"trigger": "item"
}
}
;
if (option && typeof option === "object") {
myChart.setOption(option, true);
}
</script>
</body>
</html>(2)生成饼图,图片格式,效果图如下
不带水印:

带水印:

更多关于Echarts图表的使用请读者自行研究,如果遇到问题,欢迎与我交流。需要代码的话,也可以找我!
喜欢的话,请点个赞,感谢你的支持!


随时随地看视频
热门评论
-
qq_慕瓜64892692021-07-06 0
-
qq_慕哥34171622021-04-19 0
-
慕村62030152020-12-07 0
查看全部评论你好,phantomjs是否还有linux版本呢
大佬能分享一下代码地址吗