CMS数据可视化

项目介绍

  • 应对现在数据可视化的趋势,越来越多企业需要在很多场景(营销数据,生产数据,用户数据)下使用,可视化图表来展示体现数据,让数据更加直观,数据特点更加突出。

  • 项目以班级管理为背景,功能包括学生信息录入,每次成绩录入;并制作可视化看板。

  • 我们以班主任老师的角色注册账号,并登录系统。

  • 为了方便开发,登录后点击页面顶部的“点我初始化数据”按钮,即可为该账号随机增加56名同学(8个小组,每组7人),并为每位学生模拟了3次考试成绩。

  • 后续,可以在学员管理中,增删改学员信息,也可以录入或修改成绩。

重要的三个地址

资源说明

  • 仓库地址:https://gitee.com/lv-chao123/cms

    # 克隆master分支
    git clone git@gitee.com:lv-chao123/cms.git
    或
    git clone https://gitee.com/lv-chao123/cms.git
    

课程目标

  • 实战Ajax在项目中的应用
  • 掌握echarts的基本使用
  • 增强对数据的处理能力,增强编程能力

Echarts-介绍

ECharts,一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖矢量图形库 ZRender,提供直观,交互丰富,可高度个性化定制的数据可视化图表。

大白话:

  • 是一个JS插件
  • 性能好可流畅运行PC与移动设备
  • 兼容主流浏览器
  • 提供很多常用图表,且可定制

Echarts-体验

官方教程:五分钟上手ECharts

自己步骤:

  • 下载echarts https://echarts.apache.org/zh/download.html
  • 引入echarts dist/echarts.min.js
  • 准备一个具备大小(宽高)的 DOM
<div id="main" style="width: 600px;height:400px;"></div>
  • 初始化echart实例
var myChart = echarts.init(document.getElementById('main'));
  • 指定图表的配置项和数据 (根据文档提供示例找到option)
var option = {
    xAxis: {
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
        type: 'value'
    },
    series: [{
        data: [820, 932, 901, 934, 1290, 1330, 1320],
        type: 'line'
    }]
};
  • 使用刚指定的配置项和数据显示图表
myChart.setOption(option);

Echarts-基础配置

需要了解的主要配置:series xAxis yAxis grid tooltip title legend color

  • series
    • 系列列表。每个系列通过 type 决定自己的图表类型
    • 大白话:图标数据,指定什么类型的图标,可以多个图表重叠。
  • xAxis:直角坐标系 grid 中的 x 轴
  • yAxis:直角坐标系 grid 中的 y 轴
  • grid:直角坐标系内绘图网格
  • title:标题组件
  • tooltip:提示框组件
  • legend:图例组件
  • color:调色盘颜色列表

演示代码:

var option = {
    xAxis: {
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
        type: 'value'
    },
    series: [{
        data: [820, 932, 901, 934, 1290, 1330, 1320],
        type: 'line',
        name:'线形图'
    },
    {
        data: [22, 333, 111, 222, 444, 666, 777],
        type: 'bar',
        name:'饼状图'
    }],
    grid: {
        show: true
    },
    title: {
        text: '标题'
    },
    tooltip: {
        padding: 20
    },
    legend: {
        data: ['线形图']
    },
    color: ['red','green']
};

Echarts-饼图

步骤分析

  1. 封装好函数,为后续传入真实数据做准备
  2. 初始化echarts
  3. 设置配置项,空的 option 即可
  4. 创建图表
  5. 查找官方示例
  6. 按需求,自定义配置图表

第一步:echarts基本步骤

function pieChart() {
    let myChart = echarts.init(document.querySelector('.pie'));
	let option = {};
	myChart.setOption(option);
}

第二步:参照官方示例

  • 只留下series系列数据配置,其他全部删除。

第三步:自定义配置

  • 增加标题,标题颜色 #6d767e
  • 增加鼠标移入提示。(比如:“各地学员分布 北京市 12人 占比6.8%”)
  • 系列数据
    • 修改 name 为 ‘各地学员分布’
    • 饼图,内圈半径 10%,外圈半径 60%
    • 居中显示
    • 面积模式
    • 扇形轮廓圆角(4px)

完成后的配置项如下:

let option = {
  tooltip: {
    // {a} 表示series中的name
    // {b} 表示数据中的series.data中的name
    // {c} 表示每一项的值
    // {d} 表示百分比
    formatter: '{a} <br />{b} <strong>{c}</strong>人 占比{d}%'
  },
  title: {
    text: '籍贯 Hometown',
    textStyle: {
      color: '#6d767e' // 标题演示
    },
  },
  series: [
    {
      name: '各地学员分布',
      type: 'pie', // pie 表示饼图
      radius: ['10%', '65%'], // 内外圈的半径
      center: ['50%', '50%'], // 中心点
      roseType: 'area', // area表示面积模式,radius表示半径模式
      itemStyle: { // 每一项的设置
        borderRadius: 4, // 扇形边缘圆角设置
      },
      data: [
        { value: 40, name: '北京' },
        { value: 38, name: '山东' },
        { value: 32, name: '上海' },
        { value: 30, name: '江苏' },
        { value: 28, name: '河北' },
        { value: 26, name: '山西' },
        { value: 22, name: '河南' },
        { value: 18, name: '辽宁' }
      ]
    }
  ]
};

Echarts-折线图

步骤分析

  1. 封装好函数,为后续传入真实数据做准备
  2. 初始化echarts
  3. 设置配置项,空的option 即可
  4. 创建图表
  5. 查找官方示例
  6. 按需求,自定义配置图表

第一步:echarts基本步骤

function lineChart() {
  let myChart = echarts.init(document.querySelector('.line'));
  let option = {};
  myChart.setOption(option);
}

第二步:参照官方示例

  • tooltip – 输入移入的提示
  • title – 标题
  • xAxis – x轴
  • yAxis – y轴
  • dataZoom – 数据缩放组件
  • series – 系列数据

以上配置项留下,其他删除

第三步:自定义配置

  • 将官方示例中除了option之外的其他代码删除,并自己添加X轴数据和series中的数据。

  • 系列数据

    • 增加一条线
    • 修改 name 为 ‘期望薪资’ 和 ‘实际薪资’
    • 线的拐点为平滑拐点
    • 线条和X轴对齐位置,无特殊标记 symbol: 'none'
  • 分析数据缩放组件 dataZoom

  • 增加标题,标题颜色 #6d767e

  • 分析tooltip(官方示例已带)。

  • 增加图例,距离顶部20px。

  • 分析坐标轴留白策略 boundaryGap

完成后的配置项 option 如下:

let option = {
  // 图例
  legend: {
    top: 20,
  },
  // 鼠标移入的提示
  tooltip: {
    trigger: 'axis', // 轴触发
    position: function (pt) {
      // pt是一个数组,pt[0]表示横坐标位置,'10%'表示Y轴方向始终保持距离顶部10%的距离
      // 所以意思是,提示框的位置会跟随鼠标左右移动,但纵向上的位置始终保持不变。
      return [pt[0], '10%']; 
    }
  },
  // 标题
  title: {
    text: '薪资 Salary',
    textStyle: {
      color: '#6d767e'
    }
  },
  xAxis: {
    type: 'category',
    boundaryGap: false, // x轴两边的留白策略,false表示不留空白
    data: ['张三', '李四', '张飞', '赵云', '狗哥', '张三', '李四', '张飞', '赵云', '狗哥', '张三', '李四', '张飞', '赵云', '狗哥', '张三', '李四', '张飞', '赵云', '狗哥']
  },
  yAxis: {
    type: 'value',
    // Y轴类型为value,则留白策略指的是对数据的延伸。
    // 比如,图表中的数据最大值是17000,则Y轴最大数字大约是 17000 + 17000 * 50%
    boundaryGap: [0, '50%'],
  },
  // 数据缩放组件
  dataZoom: [
    // {
    //   type: 'inside', // 将拖动的条内置到轴里面,看不见了,但是可以拖动
    //   start: 0,
    //   end: 10
    // },
    {
      type: 'slider', // 拖动条显示到轴的外面(默认就是slider类型)
      start: 0, // 拖动滑块起始位置(这是一个百分比)
      end: 15 // 拖动滑块结束位置(这是一个百分比)
    }
  ],
  // 数据部分
  series: [
    {
      name: '期望薪资',
      type: 'line',
      smooth: true, // 表示使用平滑曲线
      symbol: 'none', // 线上拐点位置的样式,none表示没有;也可以是实心圆、空心圆、方块.....
      itemStyle: { // 单独控制这条线的颜色
        color: '#ee6666'
      },
      data: [8300, 9600, 15000, 17000, 12000, 8300, 9600, 15000, 17000, 12000, 8300, 9600, 15000, 17000, 12000, 8300, 9600, 15000, 17000, 12000]
    },
    {
      name: '实际薪资',
      type: 'line',
      smooth: true,
      symbol: 'none',
      itemStyle: { // 单独控制这条线的颜色
        color: '#5470c6'
      },
      data: [9600, 15000, 17000, 12000, 8300, 9600, 15000, 17000, 12000, 8300, 9600, 15000, 17000, 12000, 8300, 9600, 15000, 17000, 12000, 13000]
    }
  ]
};

Echarts-柱状图

步骤分析

  1. 封装好函数,为后续传入真实数据做准备
  2. 初始化echarts
  3. 设置配置项,空的option 即可
  4. 创建图表
  5. 查找官方示例
  6. 按需求,自定义配置图表

第一步:echarts基本步骤

function barChart() {
  let myChart = echarts.init(document.querySelector('.barChart'));
  let option = {}
  myChart.setOption(option);
}

第二步:参照官方示例

  • tooltip 提示组件
  • legend 图例
  • xAxis x轴
  • yAxis y轴
  • series 系列数据

以上几个配置项留下,其他删除。

第三步:自定义配置

  • 修改X轴及series中的数据

    ['1组', '2组', '3组', '4组', '5组', '6组', '7组']
    [83, 57, 90, 78, 66, 76, 77, 87, 69, 92, 88, 78]
    [2, 1, 3, 4, 2, 5, 2, 2, 4, 1, 6, 2]
    [3, 2, 1, 5, 1, 2, 3, 4, 5, 2, 2, 4]
    [3, 2, 1, 5, 1, 2, 3, 4, 5, 2, 2, 4]
    
  • 多个Y轴

    • 第一个y轴(索引0)表示平均分,范围0~100,根据数字10,将Y轴分为10份
    • 第二个y轴(索引1)表示人数,范围0~10(根据班级情况而定),根据数字1,将y轴分为10份。
  • 系列数据

    • 增加至 4 组数据,并修改每组 name
    • 修改每个柱子的宽度为 15px
    • 让平均分使用第一个Y轴(yAxisIndex: 0),让人数使用第二个Y轴(yAxisIndex: 1
  • 调整网格(图表的宽高)

    • 上下 30px,左右 7%
  • 分析tooltip(官方示例已带)

let option = {
  // 网格(整个图表区域设置)
  grid: {
    top: 30,
    bottom: 30,
    left: '7%',
    right: '7%'
  },
  // 鼠标移入的提示
  tooltip: {
    trigger: 'axis', // 触发方式,axis表示轴触发,item表示每一项
    axisPointer: {   // 坐标轴指示器配置项
      // 十字准星指示器,其他选项有 line、shadow、none(这里配合x轴的设置,组成的十字准星)
      type: 'cross', 
      crossStyle: {
        color: '#999'
      }
    }
  },
  // 图例
  legend: {},
  // X轴
  xAxis: [
    {
      type: 'category',
      data: ['1组', '2组', '3组', '4组', '5组', '6组', '7组'],
      axisPointer: { // 坐标轴指示器为阴影,配合tooltip中的设置,组成十字准星
        type: 'shadow'
      }
    }
  ],
  // Y轴
  yAxis: [
    {
      type: 'value',
      min: 0, // y轴数据最小值
      max: 100, // y轴数据最大值
      interval: 10, // step步长,把y轴的数据分成多少份
      axisLabel: { // Y轴文字设置
        formatter: '{value}分', // Y轴文字
      }
    },
    {
      type: 'value',
      min: 0,
      max: 10,
      interval: 1,
      axisLabel: {
        formatter: '{value}人'
      }
    }
  ],
  // 数据部分(4组数据)
  series: [
    {
      name: '平均分',
      type: 'bar',
      data: [83, 57, 90, 78, 66, 76, 77, 87, 69, 92, 88, 78],
      barWidth: '15',
    },
    {
      name: '低于60分人数',
      type: 'bar',
      data: [2, 1, 3, 4, 2, 5, 2, 2, 4, 1, 6, 2],
      barWidth: '15',
      yAxisIndex: 1, // Y轴索引,1表示使用第2个Y轴
    },
    {
      name: '60到80分之间',
      type: 'bar',
      yAxisIndex: 1, // Y轴索引,1表示使用第2个Y轴
      barWidth: '15',
      data: [1, 4, 2, 4, 5, 2, 1, 3, 3, 2, 2, 4]
    }
    ,
    {
      name: '高于80分人数',
      type: 'bar',
      yAxisIndex: 1, // Y轴索引,1表示使用第2个Y轴
      barWidth: '15',
      data: [3, 2, 1, 5, 1, 2, 3, 4, 5, 2, 2, 4]
    }
  ]
};

Echarts社区

社区就是一些,活跃的echart使用者,交流和贡献定制好的图表的地方。

在这里可以找到一些基于echart的高度定制好的图表,相当于基于jquery开发的插件,这里是基于echarts开发的第三方的图表。

社区示例:https://www.makeapie.com/explore.html

Apache 接手echarts后,社区无限期停服,以下为代替地址!

https://www.makeapie.cn/echarts

http://ppchart.com/#/
https://www.isqqw.com/#/homepage
http://analysis.datains.cn/finance-admin/#/chartLib/all

Echarts-使用社区的示例

项目中使用的社区示例地址:https://www.makeapie.com/editor.html?c=xD4a1EBnvW

重点:

  • 使用社区示例,必须要查看示例引入了哪些外部js文件。

实现步骤:

  • 第一需要下载china.js提供中国地图的js文件
  • 导入后,直接使用社区提供的配置即可。
  • 自行修改
    • 将背景色改为 白色
    • 将 视觉映射组件(visualMap)中的 show 改为 false
    • 其他自行自愿修改。

必须知道的结论:

  • 哪些数据和哪些数据是对应的,必须一致
  • 哪些数据能多,能错
  • 哪些数据不能多,不能错

插件介绍

提示框插件

  • 提示框插件有很多,不同的框架中选择的也不一样。
  • 案例中,提示框使用的是 toastr 插件。(挂的是GitHub链接,可能打不开)
  • 总结它的使用步骤如下:

使用步骤:

  1. 加载 toastr.css 和 toastr.js 文件

  2. 全局配置。为方便,我们将下面的配置放到 assets/utils/toastr.js 中,使用时,加载这个配置文件即可

    toastr.options = {
      // "closeButton": false,
      // "debug": false,
      // "newestOnTop": false,
      // "progressBar": false,
      "positionClass": "toast-top-right", // 提示框位置,这里填类名
      // "preventDuplicates": false,
      // "onclick": null,
      "showDuration": "300",              // 提示框渐显所用时间
      "hideDuration": "300",              // 提示框隐藏渐隐时间
      "timeOut": "2000",                  // 提示框持续时间
      // "extendedTimeOut": "1000",
      // "showEasing": "swing",
      // "hideEasing": "linear",
      // "showMethod": "fadeIn",
      // "hideMethod": "fadeOut"
    }
    
  3. 调用方法,直接使用

    toastr.info('提示信息');                // 普通提示
    toastr.success('提示信息');             // 成功提示
    toastr.warning('提示信息');             // 警告提示
    toastr.error('提示信息');               // 错误提示
    

配置Axios项目根路径

  • 目前echarts图表中的数据都是假数据

  • 如果获取真数据就需要调用接口

  • 这就需要至少要完成注册、登录、初始化数据等接口的调用才行

  • 项目中接口根路径是相同的,见前文 重要的三个地址

  • 所以,在 assets/utils 文件夹,创建axios的配置文件,取名 common.js 。代码如下:

    axios.defaults.baseURL = 'http://www.itcbc.com:8000';
    

    这里之所以放到 assets/utils 文件夹中,是因为 utils 在编程中常用来放工具函数。而request是请求的意思。

注册账号

切换登录和注册的盒子

登录和注册同在 login.html 中,因为定位的原因重叠在一起了。我们可以通过JS实现切换两个盒子

// 切换两个盒子
document.querySelector('.login a').addEventLister('click', function () {
    document.querySelector('.login').style.display = 'none';
    document.querySelector('.register').style.display = 'block';
})
document.querySelector('.register a').addEventLister('click', function () {
    document.querySelector('.login').style.display = 'block';
    document.querySelector('.register').style.display = 'none';
})

表单验证

完成注册

当表单验证通过后,根据接口文档,获取输入框的账号和密码,Ajax提交账号和密码

document.querySelector('.register form').addEventListener('submit', function (e) {
  e.preventDefault();
  // 通过验证,这里的代码将会执行。我们将Ajax请求的代码放到这里即可
  let data = $(this).serialize();
  // console.log(data);
  axios.post('/api/register', data).then(({ data: res }) => {
    // console.log(res);
    if (res.code === 0) { // 我们只考虑成功的情况即可,失败的情况后续统一使用拦截器处理
      toastr.success(res.message); // 使用插件提示消息
      document.querySelector('.register input').value = ''; // 清空输入框
      document.querySelector('.register a').click(); // 切换至登录的盒子
    }
  })
});

登录功能

  • 因为前文已经完成了注册,所以这里的登录功能极为简单
  • 问题是,登录如果成功,该做什么?
    • 将服务器响应的token存储到本地存储(关于token的说明见下文的 JWT身份认证
    • 跳转到 index.html 页面 (页面跳转,只考虑两个html之间的相对关系,不考虑js在哪里)

登录的代码如下:

document.querySelector('.login form').addEventListener('submit', function (e) {
    e.preventDefault();
    //提交逻辑
    let data = $(this).serialize();
    // console.log(data);
    axios.post('/api/login', data).then(({ data: res }) => {
        // console.log(res);
        if (res.code === 0) {
            localStorage.setItem('token', res.token);
            location.href = './index.html'
        }
    })
});

JWT身份认证

什么是jwt身份认证

在前后端分离模式的开发中,服务器如何知道来访者的身份呢?

  • 在登录后,服务器会响应给用户一个 令牌 (token)
  • 令牌中会包括该用户的id等唯一标识
  • 浏览器收到令牌后,自己保存
  • 下次请求其他接口时,(在请求头中)携带这个令牌去请求
  • 这样服务器就知道来访者的身份了,服务器就会为该用户开发接口的访问权限,并处理该用户的数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qIkHcOv-1651502872223)(Readme/image-20211106094103312.png)]

这样,就明白为什么登录后,要将token保存到本地存储中了。

全局配置请求头

由于除了登录和注册接口外,其他所有接口都需要身份认证(都需要我们提供令牌),所以我们可以在 request.js 中,全局配置请求头。

axios.defaults.headers.common['Authorization'] = localStorage.getItem('token');

利用令牌控制页面的访问权限

浏览器端,可以通过合理使用令牌,控制页面的访问权限。

比如,用户默认只能访问登录页,如果不登录就不能访问首页,怎么做?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WrHXcA70-1651502872225)(Readme/image-20211107100520701.png)]

第一个判断:判断本地存储是否有token

<!-- index.html -->
<!-- 本地存储有token,则说明用户登录了;没有token,则说明用户没有登录,不允许访问首页 -->
<script>
  if (localStorage.getItem('token') === null) location.href = './login.html'
</script>

上述判断只能判断token有没有,但不能判断token的真假,所以需要发送Ajax请求,根据服务器响应结果再次判断

第二个判断:根据服务器响应结果,判断token是否是假token或者过期的token

  • 如果token值是正确的,是没有过期的,则服务器响应 code===0
  • 如果token是错误的获取过期的,则服务器响应 code===1 && message==='身份认证失败'
// request.js 中,使用响应拦截器,拦截响应结果进行判断
// 如果响应结果中 code === 1 && message === '身份认证失败' 则表示浏览器使用了无效的token
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
}, function (error) {
    // 对响应错误做点什么
    if (error.response) {
        if (error.response.data.message === '身份认证失败') {
            localStorage.removeItem('token');
            location.href = './login.html'
        }
    }
    return Promise.reject(error);
});

统一处理错误提示

在上述响应拦截器的基础之上,顺便添加响应错误提示。

所有接口响应的结果有两种:

  • 响应状态码 小于 400,并且 code===1,比如登录账号密码错误。这样的响应进入响应拦截器中的第一个函数。
  • 响应状态码 大于等于 400,并且 code === 1 ,比如身份认证失败。这样的响应进入响应拦截器中的第二个函数。

所以分别提示

// request.js 中,使用响应拦截器,拦截响应结果进行判断
// 如果响应结果中 code === 1 && message === '身份认证失败' 则表示浏览器使用了无效的token
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    if (response.data.code === 1) {
        toastr.warning(response.data.message)
    }
    return response;
}, function (error) {
    // 对响应错误做点什么
    if (error.response) {
        if (error.response.data.message === '身份认证失败') {
            localStorage.removeItem('token');
            location.href = './login.html'
        } else {
            toastr.error(error.response.data.message);
        }
    }
    return Promise.reject(error);
});

退出登录

一般来说,退出需要做的事和登录后做的事刚好相反。

  • 登录后,在本地存储了token;退出时,移除这个token
  • 登录后,跳转到了 index.html 页面;退出时,跳转到 login.html
// ------------------------ 退出登录 -------------------
document.querySelector('.logout a').addEventLister('click', function () {
  if (!confirm('确定要退出登录吗?')) return;
  localStorage.removeItem('token');
  location.href = './login.html';
})

初始化数据

为了减少手动录入数据的时间,特别设计了此接口。调用此接口将为你随机添加56名同学,并分为8个组。并为每位同学随机添加了三次成绩。

// ------------------------ 初始化数据 -------------------
document.querySelector('.init').addEventLister('click', function () {
  axios.get('/init/data').then(({ data: res }) => {
    if (res.code === 0) {
      toastr.success(res.message);
    }
  })
})

班级概况

  • 查阅接口文档,发现服务器已经将数据计算整理完毕,提供了现成的接口。
  • 所以我们前端开发者,只需要调用接口,并将数据展示到页面中即可。
// 获取班级概况数据
axios.get('/student/overview').then(({ data: res }) => {
  // console.log(res);
  let { code, data } = res;
  if (code === 0) {
    document.querySelector('.overview .total').innerHTML = data.total;
    document.querySelector('.overview .avgAge').innerHTML = data.avgAge;
    document.querySelector('.overview .avgSalary').innerHTML = data.avgSalary;
    document.querySelector('.overview .proportion').innerHTML = data.proportion;
  }
})

柱状图使用接口数据

  • 这个接口和班级概况一样,服务器已经将数据整理完毕。
  • 所以我们前端开发者,只需要调用接口获取数据,并将数据传递给 echarts 即可。
  • 不过,接口需要一个 batch 参数(考试的次数),所以需要先将下拉菜单处理一下。

创建柱状图的函数,设置形参接收数据。并把数据应用到X轴及series中。

// 形参,直接解构赋值
+function barChart({ avgScore, group, gt60, gt80, lt60 }) {
  ...... 省略其他代码
  let option = {
    xAxis: {
      type: 'category',
+      data: group, // X轴使用组名 1组 2组 3组 ......
    },
    series: [
      {
+      data: avgScore, // 第一个柱子,使用平均分
        type: 'bar',
        barWidth: '15%',
        name: '平均分',
        yAxisIndex: 0,
      },
      {
+      data: lt60, // 第二个柱子,使用小于60分人数数据
        type: 'bar',
        barWidth: '15%',
        name: '低于60分人数',
        yAxisIndex: 1,
      },
      {
+      data: gt60, // 第三个柱子,使用大于60小于80分人数数据
        type: 'bar',
        barWidth: '15%',
        name: '60~80分人数',
        yAxisIndex: 1,
      },
      {
+      data: gt80, // 第四个柱子,使用大于80分人数数据
        type: 'bar',
        barWidth: '15%',
        name: '80分以上人数',
        yAxisIndex: 1,
      }
    ]
  }
}

- barChart(); // 这里就不要再调用函数了

折线图使用接口数据

为了断链数据处理能力。所以并没有提供 “返回折线图数据” 的接口。我们需要调用获取学生接口,然后自己处理数据。

调用“获取学生”接口,然后处理

// 获取学员信息  ==> 1、提取期望薪资和实际薪资,做折线图;  2、提取地区,做饼图;  3、提取地区和经纬度,做地图
axios.get('/student/list').then(({ data: res }) => {
  let { code, data } = res;
  if (code === 0) {
    // console.log(data);
    // 折线图所需数据,检查折线图需要的数据格式
    // (x轴需要一个数组,放姓名)
    // (两条线分别需要一个数组,放期望薪资和实际薪资)
    let lineData = {
      xAxis: [],
      salary: [],
      truesalary: []
    };
    // 遍历data,将所需的数据取出,放到前面定义好的数组中
    data.forEach(item => {
      lineData.xAxis.push(item.name);
      lineData.salary.push(item.salary);
      lineData.truesalary.push(item.truesalary);
    });
    
    // 遍历结束,得到我们所需的数据
    // 数据处理好,调用图表函数
    lineChart(lineData);
  }
})

折线图函数 lineChart 中,设置形参接收数据并使用。

+function lineChart({ xAxis, salary, truesalary }) {
  let myChart = echarts.init($('.line')[0]);
  let option = {
    ...... 其他配置项省略
    xAxis: {
      type: 'category',
      boundaryGap: false,
+      data: xAxis
    },
    // 数据部分
    series: [
      {
        name: '期望薪资',
        type: 'line',
        smooth: true, // 表示使用平滑曲线
        symbol: 'none', // 线上拐点位置的样式,none表示没有;也可以是实心圆、空心圆、方块.....
        itemStyle: { // 单独控制这条线的颜色
          color: '#ee6666'
        },
+        data: salary
      },
      {
        name: '实际薪资',
        type: 'line',
        smooth: true,
        symbol: 'none',
        itemStyle: { // 单独控制这条线的颜色
          color: '#5470c6'
        },
+        data: truesalary
      }
    ]
  };
  myChart.setOption(option);
}
- lineChart(); // 这里不要再调用函数了

地图使用接口数据

地图中使用的数据,只有地名和经纬度。这些数据在 “获取学生”接口中都有,但还是需要我们自己整理。锻炼数据处理能力。

由于前面已经调用过获取学生接口了,所以不用重新发送请求获取数据了,只需要处理即可。

// 分析地图需要什么格式的数据。
// 需要两组数据
// 第一组数据
let chinaGeoCoordMap = {
  '北京市': [116.4551, 40.2539],
  '内蒙古': [110.3467, 41.4899],
   "吉林": [125.8154, 44.2584],
  // ......
}
// 第二组数据
let chinaDatas = [
  [{
    name: '黑龙江11',
    value: 0
  }], [{
    name: '内蒙古',
    value: 0
  }],
  // ......
]

上述数据,用的是省的名字,实际上全部用市的名字或者县的名字都可以。只要对应上即可。

我们案例中用县的名字。

当请求到学生数据之后

// 获取学员信息  ==> 1、提取期望薪资和实际薪资,做折线图;  2、提取地区,做饼图;  3、提取地区和经纬度,做地图
axios.get('/student/list').then(({ data: res }) => {
  let { code, data } = res;
  if (code === 0) {
    // console.log(data);
    // 折线图所需数据,检查折线图需要的数据格式
    // (x轴需要一个数组,放姓名)
    // (两条线分别需要一个数组,放期望薪资和实际薪资)
    let lineData = {
      xAxis: [],
      salary: [],
      truesalary: []
    };
+    // 地图,第一组数据
+    let chinaGeoCoordMap = { '北京市': [116.4551, 40.2539] };
+    // 地图,第二组数据
+    let chinaDatas = []
    // 遍历data,将所需的数据取出,放到前面定义好的数组中
    data.forEach(item => {
      lineData.xAxis.push(item.name);
      lineData.salary.push(item.salary);
      lineData.truesalary.push(item.truesalary);
      
+      chinaGeoCoordMap[item.county] = [item.jing, item.wei];
+      chinaDatas.push([{name: item.county, value: 0}]);
    });
    
    // 遍历结束,得到我们所需的数据
    // 数据处理好,调用图表函数
    lineChart(lineData);
+    mapChart(chinaGeoCoordMap, chinaDatas);
  }
})

地图函数中使用数据:

+function mapChart(chinaGeoCoordMap, chinaDatas) {
- var chinaGeoCoordMap = {.......}
- var chinaDatas = [......]
  let myChart = echarts.init($('.map')[0]);
  var convertData = function (data) {
    var res = [];
    for (var i = 0; i < data.length; i++) {
      var dataItem = data[i];
      var fromCoord = chinaGeoCoordMap[dataItem[0].name];
+      var toCoord = [116.4551, 40.2539]; // 目标点 经纬度(北京市)
      if (fromCoord && toCoord) {
        res.push([{
          coord: fromCoord,
          value: dataItem[0].value
        }, {
          coord: toCoord,
        }]);
      }
    }
    return res;
  };
  var series = [];
+  [['北京市', chinaDatas]].forEach(function (item, i) { // 写好北京市,目标点
   // 其他代码省略,无需修改 
}

- mapChart(); // 这里不要再调用函数了

饼图使用接口数据

饼图使用的数据格式如下

data: [
  { name: '河北省', value: 12 },
  { name: '广东省', value: 12 },
  { name: '上海市', value: 12 },
  { name: '江西省', value: 12 },
  // ......
]

饼图使用的数据,也需要通过 获取学生 接口得到,并需要自行处理。

// 获取学员信息  ==> 1、提取期望薪资和实际薪资,做折线图;  2、提取地区,做饼图;  3、提取地区和经纬度,做地图
axios.get('/student/list').then(({ data: res }) => {
  let { code, data } = res;
  if (code === 0) {
    // console.log(data);
    // 折线图所需数据,检查折线图需要的数据格式
    // (x轴需要一个数组,放姓名)
    // (两条线分别需要一个数组,放期望薪资和实际薪资)
    let lineData = {
      xAxis: [],
      salary: [],
      truesalary: []
    };
    // 地图,第一组数据
    let chinaGeoCoordMap = { '北京市': [116.4551, 40.2539] };
    // 地图,第二组数据
    let chinaDatas = []
    
+    // 饼图数据
+    let pieData = [];
    
    // 遍历data,将所需的数据取出,放到前面定义好的数组中
    data.forEach(item => {
      lineData.xAxis.push(item.name);
      lineData.salary.push(item.salary);
      lineData.truesalary.push(item.truesalary);
      
      chinaGeoCoordMap[item.county] = [item.jing, item.wei];
      chinaDatas.push([{name: item.county, value: 0}]);
      
+      let i;
+      if ((i = pieData.findIndex(v => v.name === item.province)) >= 0) {
+        pieData[i].value++;
+      } else {
+        pieData.push({ name: item.province, value: 1 });
+      }
    });
    
    // 遍历结束,得到我们所需的数据
    // 数据处理好,调用图表函数
    lineChart(lineData);
    mapChart(chinaGeoCoordMap, chinaDatas);
+    pieChart(pieData);
  }
})

饼图函数,接收数据并使用:

+function pieChart(pieData) {
  let myChart = echarts.init($('.pie')[0]);
  let option = {
    series: [
      {
        name: '各地人员分布',
        type: 'pie', // pie 表示饼图
        radius: ['10%', '65%'], // 内外圈的半径
        center: ['50%', '50%'], // 中心点
        roseType: 'area', // area表示面积模式,radius表示半径模式
        itemStyle: { // 每一项的设置
          borderRadius: 4, // 扇形边缘圆角设置
        },
+        data: pieData
      }
    ]
  };
}

data.forEach(item => {
lineData.xAxis.push(item.name);
lineData.salary.push(item.salary);
lineData.truesalary.push(item.truesalary);

  chinaGeoCoordMap[item.county] = [item.jing, item.wei];
  chinaDatas.push([{name: item.county, value: 0}]);
  •  let i;
    
  •  if ((i = pieData.findIndex(v => v.name === item.province)) >= 0) {
    
  •    pieData[i].value++;
    
  •  } else {
    
  •    pieData.push({ name: item.province, value: 1 });
    
  •  }
    

    });

    // 遍历结束,得到我们所需的数据
    // 数据处理好,调用图表函数
    lineChart(lineData);
    mapChart(chinaGeoCoordMap, chinaDatas);

  • pieChart(pieData);
    }
    })


饼图函数,接收数据并使用:

```diff
+function pieChart(pieData) {
  let myChart = echarts.init($('.pie')[0]);
  let option = {
    series: [
      {
        name: '各地人员分布',
        type: 'pie', // pie 表示饼图
        radius: ['10%', '65%'], // 内外圈的半径
        center: ['50%', '50%'], // 中心点
        roseType: 'area', // area表示面积模式,radius表示半径模式
        itemStyle: { // 每一项的设置
          borderRadius: 4, // 扇形边缘圆角设置
        },
+        data: pieData
      }
    ]
  };
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值