【第一个Vue上手小项目Day5】单独封装Echarts图表,后端Lambda进行数据筛选

10 篇文章 2 订阅

一、折线图+饼图

前端效果:
在这里插入图片描述

二、Vue挂载echarts,单独封装

  1. 项目路径下安装echarts(原版百度图表库)和vue-echarts(比较轻量)

    cnpm install echarts vue-echarts

  2. 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中:

  1. 可以this.$chart.line1('linechart1');直接调用,对应方法和挂载点名称就行;
  2. 注意挂载点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种方式:

  1. 在前端封装:
    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中
    在这里插入图片描述

    这样的处理是可行的,但不建议在前端做过多的数据处理:

    1. 实际项目中,“前端” 可能不会、或者很难实现数据分类统计处理;
    2. 既然我们要做到前后端分离,当然也要实现功能分离。

    因此,我建议在后端做好分离接口!参考方式2

  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
}

五、总结&源码

  1. 更多echarts模板

    参考: echarts官方示例

  2. 本次遇到一个低级错误
    在后端java循环时:

    • for(int i = 0; i < key.length; i++){}
      怎么都跳不出循环,我很苦恼,报错400,返回结果一直为null

    • for(String keyword: key){}
      改用之后,错误解决!

  3. 源码Github

    Vue:Day5
    Springboot:Day8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值