目录
一、折线图+饼图
前端效果:
二、Vue挂载echarts,单独封装
- 项目路径下安装echarts(原版百度图表库)和vue-echarts(比较轻量)
cnpm install echarts vue-echarts
- src目录下新建:echarts包,在里面新建index.js和目录myEcharts(myEcharts.js)
index.js
是echarts配置,待会儿会注入到main.js全局中
这里如果是部分引入,我们以后可以这样封装配置,现在为了方便,
在后面学习中使用import echarts from 'echarts'
,忽略注释中的配置import Vue from 'vue' // import * as echarts from 'echarts/lib/echarts'; // // 引入 echarts 主模块。 // import 'echarts/lib/echarts' // // 引入柱形图 // import 'echarts/lib/chart/bar' // 引入提示框组件、标题组件、工具箱、legend组件。 // import 'echarts/lib/component/tooltip' // import 'echarts/lib/component/title' // import 'echarts/lib/component/toolbox' // import 'echarts/lib/component/legend' // // Vue.prototype.$echarts =echarts; //自定义js import myEcharts from '@/echarts/myEcharts/myEcharts.js' Vue.use(myEcharts)
main.js
,这里就可以全局使用myEcharts.js中定义的图表了import './echarts'// 引入表格
配置就算完成了!
三、静态图表
挂载两个静态图表:
因为前面在main.js中进行过配置,在vue中:
- 可以
this.$chart.line1('linechart1');
直接调用,对应方法和挂载点名称就行; - 注意挂载点div必须包含容器的大小,因为echarts会自动适应容器大小,不设置就默认不显示。
XXXX.vue
挂载点:这里是两张图并列:
<template>
<div>
<el-row :gutter="24">
<el-col :span="12">
<el-card shadow="always">
<!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
<div id="linechart1" style="float:left;width:100%;height: 300px"></div>
</el-card>
</el-col>
<el-col :span="12">
<el-card shadow="always">
<div id="piechart2" style="float:left;width:100%;height: 300px"></div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {}
},
mounted() {
this.$chart.line1('linechart1');
this.$chart.pie1('piechart2');
}
}
</script>
<style scoped>
</style>
具体的myEcharts.js
静态封装:折线图&饼图
/**
* 各种画echarts图表的方法都封装在这里
* 注意:这里echarts没有采用按需引入的方式,只是为了方便学习
*/
import echarts from 'echarts'
const install = function(Vue) {
Object.defineProperties(Vue.prototype, {
$chart: {
get() {
return {
//画一条简单的线
line1: function(id) {
this.chart = echarts.init(document.getElementById(id));
this.chart.clear(); //释放缓存
const optionData = {
title: {
text: '直线:月使用率', //主标题
subtext: '纯属虚构', //副标题
x: 'left', //x轴方向对齐方式
},
xAxis: {
type: 'category',
data: ['Day1','Day2','Day3','Day4','Day5'],//usersName,
},
yAxis: {
type: 'value'
},
series: [{
data: [560,720,680,760,850],//usersAge,
type: 'line',
smooth: true
}]
};
this.chart.setOption(optionData);
},
//画一个饼图
pie1: function(id) {
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById(id));
// 绘制图表
myChart.setOption({
title: {
text: '饼图:某站点用户访问来源', //主标题
subtext: '纯属虚构', //副标题
x: 'left', //x轴方向对齐方式
},
tooltip: {
trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
legend: {
orient: 'vertical',
bottom: 'bottom',
data: ['直接访问','邮件营销','联盟广告','视频广告','搜索引擎']
},
series: [{
name: '访问来源',
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: [{
value: 335,
name: '直接访问'
},
{
value: 310,
name: '邮件营销'
},
{
value: 234,
name: '联盟广告'
},
{
value: 135,
name: '视频广告'
},
{
value: 1548,
name: '搜索引擎'
}
],
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}]
});
},
}
}
}
})
}
export default {
install
}
静态数据封装就算完成了!!还是比较简单,接下来难点是如何提取关键信息进行动态演示!
四、动态图表
4.1 思路
后端数据往往不可能是前面静态图表数据标准的格式:一个name对应一个value;
【表格】中所需要的内容:
- 标签 -》data: [‘Day1’,‘Day2’,‘Day3’,‘Day4’,‘Day5’]
- 数据统计结果 -》data: [560,720,680,760,850],
【实际】请求结果:通过jpa查询返回数据库中所有数据集
【需求】如果我们只需要age分布图或者地域分布图,那么我们有2种方式:
-
在前端封装:
ajax获取的依然是上述完整数据,单独封装每个列:const usersName = []; const usersAge = []; const usersAddress = []; this.$ajax.get('/user/aop/list').then((res) => { //axios发送get请求 const users = res.data.data; if (users != null) { for (var i = 0; i < users.length; i++) { usersName[i] = users[i].name; usersAge[i] = users[i].age; usersAddress[i] = users[i].address; } console.log(usersName); //再分类统计。。。 } else { this.$message({ type: 'error', message: '查询失败', showClose: true }) } })
假如
封装的是name姓名
,效果如下,然后在前端js中进行分类
,再把数据写入echarts的option中
。
这样的处理是可行的,但不建议在前端做过多的数据处理:
- 实际项目中,“前端” 可能不会、或者很难实现数据分类统计处理;
- 既然我们要做到前后端分离,当然也要实现功能分离。
因此,我建议在后端做好分离接口!参考方式2
-
在后端封装:
用循环方式写入新的集合中:@RequestMapping(value = "/aop/addressList", method = RequestMethod.GET) public Result aopAddressList(Exception e) { List<UserEntity> userList = userJPA.findAll(); List<String> AddressList = new ArrayList<>(); if(userList!=null){ for(UserEntity m:userList){ AddressList.add(m.getAddress()); } return ResultGenerator.genSuccessResult(AddressList); }else { return ResultGenerator.genFailResult(e.getMessage()); } }
浏览器请求:
http://localhost:8090/user/aop/addressList
,返回接口结果:{“code”:200,“data”:[“重庆”,“重庆”,“北京”,“北京”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”,“上海”],“message”:“SUCCESS”,“success”:true}
4.2 后端数据处理
现在我们请求得到了只有地址数据集
,离我们的图表数据还差一个统计分类
:
【目标】:
{“code”:200,“data”:{“address”:[“重庆”,“北京”,“上海”],“num”:[2,2,32]},“message”:“SUCCESS”,“success”:true}
需要将不同的类别进行统计分类,这里用到了lambda表达式
(1)lambda分类原理
lambda的用法参考:通过标签分类原理将数据流分类筛选,保留符合筛选条件的结果。
@RequestMapping(value = "/aop/addressList", method = RequestMethod.GET)
public Result aopAgeList(Exception e) {
List<UserEntity> userList = userJPA.findAll();
List<String> AddressList = new ArrayList<>();
//List<Integer> AgeList = null;
// 需要筛选的条件:从AddressList中筛选出地址为“重庆”的用户
AddressList.add("重庆");
List<UserEntity> result = null;
result = userList.stream()
.filter((UserEntity s) -> AddressList.contains(s.getAddress()))
.collect(Collectors.toList());
// 打印原有stuList集合中的数据
System.out.println("原有userList集合中的数据");
userList.forEach((UserEntity s) -> System.out.println(s.getName() + "--->" + s.getAddress()));
// 打印过滤筛选后的result结果
System.out.println("过滤筛选后的result结果");
result.forEach((UserEntity s) -> System.out.println(s.getName() + "--->" + s.getAddress()));
if(userList!=null){
for(UserEntity m:userList){
AddressList.add(m.getAddress());
}
return ResultGenerator.genSuccessResult(AddressList);
}else {
return ResultGenerator.genFailResult(e.getMessage());
}
}
运行效果:通过lambda,我们可以看到,将重庆
的结果筛选出来了,类似于关键字查询:
(2)数据筛选
先筛选地址:
@RequestMapping(value = "/aop/addressList", method = RequestMethod.GET)
public Result aopAgeList(Exception e) {
List<UserEntity> userList = userJPA.findAll();
List<String> AddressList = new ArrayList<>();
//List<Integer> AgeList = null;
if(userList!=null){
// 需要筛选的条件:
AddressList.add("重庆");
List<UserEntity> result1 = null;
result1 = userList.stream()
.filter((UserEntity s) -> AddressList.contains(s.getAddress()))
.collect(Collectors.toList());
AddressList.clear();
AddressList.add("北京");
List<UserEntity> result2 = null;
result2 = userList.stream()
.filter((UserEntity s) -> AddressList.contains(s.getAddress()))
.collect(Collectors.toList());
AddressList.clear();
AddressList.add("上海");
List<UserEntity> result3 = null;
result3 = userList.stream()
.filter((UserEntity s) -> AddressList.contains(s.getAddress()))
.collect(Collectors.toList());
AddressList.clear();
if(result1.size()!=0&&result2.size()!=0&&result3.size()!=0) {
AddressList.add("重庆");
AddressList.add(String.valueOf(result1.size()));
AddressList.add("北京");
AddressList.add(String.valueOf(result2.size()));
AddressList.add("上海");
AddressList.add(String.valueOf(result3.size()));
}
return ResultGenerator.genSuccessResult(AddressList);
}else {
return ResultGenerator.genFailResult(e.getMessage());
}
}
结果如图:就是我们想要的数据啦!
{“code”:200,“data”:[“重庆”,“2”,“北京”,“2”,“上海”,“32”],“message”:“SUCCESS”,“success”:true}
(3)优化
对前面的data进行分类统计后,返回字符串格式,不美观,再对接口进行优化:
{“code”:200,“data”:{“address”:[“重庆”,“北京”,“上海”],“num”:[2,2,32]},“message”:“SUCCESS”,“success”:true}
{“code”:200,“data”:{“num”:[2,1,32,1],“age”:[19,20,21,22]},“message”:“SUCCESS”,“success”:true}
最终版:
Controller完整项目地址
@RequestMapping(value = "/aop/addressList", method = RequestMethod.GET)
public Result aopAddressList(Exception e) {
List<UserEntity> userList = userJPA.findAll();
if(userList!=null){
String[] key= {"重庆","北京","上海"};//筛选条件
ArrayList num = new ArrayList();//数据统计
for(String keyword: key){
List<String> AddressList = new ArrayList<>();//每次都会清空筛选条件
AddressList.add(keyword);// 需要筛选的条件
List<UserEntity> result = userList.stream()
.filter((UserEntity s) -> AddressList.contains(s.getAddress()))
.collect(Collectors.toList());
num.add(result.size());//符合条件的统计结果添加到数据统计num[]中
log.info(String.valueOf("address:"+key +"num:"+ num));//前面要加上@Slf4j标签即可使用
}
JSONObject map = new JSONObject();
map.put("address", key);//存入map中
map.put("num", num);
log.info(String.valueOf(map));
return ResultGenerator.genSuccessResult(map);//aop异常结果封装
}else {
return ResultGenerator.genFailResult(e.getMessage());
}
}
@RequestMapping(value = "/aop/ageList", method = RequestMethod.GET)
public Result aopAgeList(Exception e) {
List<UserEntity> userList = userJPA.findAll();
if(userList!=null){
Integer[] key= {19,20,21,22};
ArrayList num = new ArrayList();
for(Integer keyword: key){
List<Integer> AgeList = new ArrayList<>();
AgeList.add(keyword);// 需要筛选的条件
List<UserEntity> result = userList.stream()
.filter((UserEntity s) -> AgeList.contains(s.getAge()))
.collect(Collectors.toList());
num.add(result.size());
log.info(String.valueOf("age:"+key +"num:"+ num));
}
JSONObject map = new JSONObject();
map.put("age", key);
map.put("num", num);
log.info(String.valueOf(map));
return ResultGenerator.genSuccessResult(map);
}else {
return ResultGenerator.genFailResult(e.getMessage());
}
}
4.3 前端调用接口
效果:
myEcharts.js
: js完整项目地址
/**
* 各种画echarts图表的方法都封装在这里
* 注意:这里echarts没有采用按需引入的方式,只是为了方便学习
*/
import echarts from 'echarts'
import axios from 'axios'
const install = function(Vue) {
Object.defineProperties(Vue.prototype, {
$chart: {
get() {
return {
//画一条简单的线
line1: function(id) {
this.chart = echarts.init(document.getElementById(id));
this.chart.clear(); //释放缓存
var value = [];
var age = [];
axios.get('/user/aop/ageList').then((res) => { //axios发送get请求
if (res.data != null) {
var data = res.data.data;
for (var i = 0; i < data.age.length; i++) {
age[i] = data.age[i];
value[i] = data.num[i];
}
//console.log("age:"+age+"\t value:"+value);
//绘图
var optionData = {
title: {
text: '直线:年龄分段', //主标题
subtext: '后台数据', //副标题
x: 'left', //x轴方向对齐方式
},
xAxis: {
type: 'category',
data: age,
},
yAxis: {
type: 'value'
},
series: [{
data: value,
type: 'line',
smooth: true
}]
};
this.chart.setOption(optionData);
} else {
this.$message({
type: 'error',
message: '查询失败',
showClose: true
})
}
})
},
//画一个饼图
pie1: function(id) {
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById(id));
var value = [];
var myaddress = [];
var addressAndvalue =[];
axios.get('/user/aop/addressList').then((res) => { //axios发送get请求
if (res.data != null) {
//console.log(res.data);
var data = res.data.data;
for (var i = 0; i < data.num.length; i++) {
myaddress[i] = data.address[i];
value[i] = data.num[i];
addressAndvalue[i] = {"name":data.address[i],"value":data.num[i]};
}
console.log("address:"+myaddress+"\t value:"+value);
console.log(addressAndvalue);
// 绘制图表
myChart.setOption({
title: {
text: '饼图:地域分布', //主标题
subtext: '结合后台', //副标题
x: 'left', //x轴方向对齐方式
},
tooltip: {
trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
legend: {
orient: 'vertical',
bottom: 'bottom',
data: myaddress //['直接访问','邮件营销','联盟广告','视频广告','搜索引擎']
},
series: [{
name: '地址分布',
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: addressAndvalue,
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}]
});
} else {
this.$message({
type: 'error',
message: '查询失败',
showClose: true
})
}
})//ajax
},
}
}
}
})
}
export default {
install
}
五、总结&源码
-
更多echarts模板
参考: echarts官方示例
-
本次遇到一个低级错误
在后端java循环时:-
for(
int i = 0; i < key.length; i++
){}
怎么都跳不出循环,我很苦恼,报错400,返回结果一直为null -
for(
String keyword: key
){}
改用之后,错误解决!
-
-
源码Github