可视化项目案例(数据Echart可视化显示)
文章目录
四、行业信息模块
4.1 行业需求描述
从多个维度统计行业信息
- 统计各个城市招聘人数,以饼状图方式展示个城市人数比例
- 统计热门行业信息,以柱状图的方式展示前10个热门行业的招聘人数
- 统计销售行业、服务业、生产制造行业招聘情况,使用面积图参考招聘这3个行业情况。
4.2 表结构分析
4.3.统计各个城市招聘人数
1) 统计各个城市招聘人数需求分析
统计各个城市招聘人数,以饼状图方式展示个城市人数比例
2) 统计各城市招聘接口
后端代码实现
IndustryController
package com.lagou.service.impl;
import com.lagou.common.ServerResponse;
import com.lagou.service.IIndustryService;
import com.lagou.vo.ConverterVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service("iIndustryService")
public class IIndustryServiceImpl implements IIndustryService {
@Autowired
private IdustryMapper mapper;
/**
* 统计各个城市招聘情况
* @return
*/
@Override
public ServerResponse<Map<String, Object>> countCityData() {
//1.查询各个城市招聘情况 , 创建一个对象封装查询的数据 list
List<ConverterVo> cityPositionList = mapper.countCityPosition();
//2.创建一个Map<String,Object> 存放总数据
Map<String , Object> map = new HashMap<>();
//3.创建3个List集合 cityNamesList 存放城市名称 ; thridList 前三名城市数据 dataList 存放是个城市数据
List<String> cityNamesList = new ArrayList<>();
List<ConverterVo> thridList = new ArrayList<>();
List<ConverterVo> dataList = new ArrayList<>();
//4.循环list ,给cityNamesList thridList dataList 封装数据
for (ConverterVo converterVo : cityPositionList) {
//设置城市名称
cityNamesList.add(converterVo.getName());
//设置thridList
if(thridList.size() < 3 ){
thridList.add(converterVo);
}
//设置dataList
dataList.add(converterVo);
}
//5.把3个list的数据村道map中
map.put("cityNamesList",cityNamesList);
map.put("thridList",thridList);
map.put("dataList",dataList);
//6.把map中的数据放到serverresponse中返回
return ServerResponse.createBySuccessData(map);
}
}
IIndustryService
package com.lagou.service;
import com.lagou.common.ServerResponse;
import java.util.Map;
public interface IIndustryService {
/**
* 统计各城市招聘人数
* @return
*/
ServerResponse<Map<String, Object>> countCityData();
}
IndustryServiceImpl
package com.lagou.service.impl;
import com.lagou.common.ServerResponse;
import com.lagou.service.IIndustryService;
import com.lagou.vo.ConverterVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service("iIndustryService")
public class IndustryServiceImpl implements IIndustryService {
@Autowired
private IdustryMapper mapper;
/**
* 统计各个城市招聘情况
* @return
*/
@Override
public ServerResponse<Map<String, Object>> countCityData() {
//1.查询各个城市招聘情况 , 创建一个对象封装查询的数据 list
List<ConverterVo> cityPositionList = mapper.countCityPosition();
//2.创建一个Map<String,Object> 存放总数据
Map<String , Object> map = new HashMap<>();
//3.创建3个List集合 cityNamesList 存放城市名称 ; thridList 前三名城市数据 dataList 存放是个城市数据
List<String> cityNamesList = new ArrayList<>();
List<ConverterVo> thridList = new ArrayList<>();
List<ConverterVo> dataList = new ArrayList<>();
//4.循环list ,给cityNamesList thridList dataList 封装数据
for (ConverterVo converterVo : cityPositionList) {
//设置城市名称
cityNamesList.add(converterVo.getName());
//设置thridList
if(thridList.size() < 3 ){
thridList.add(converterVo);
}
//设置dataList
dataList.add(converterVo);
}
//5.把3个list的数据村道map中
map.put("cityNamesList",cityNamesList);
map.put("thridList",thridList);
map.put("dataList",dataList);
//6.把map中的数据放到serverresponse中返回
return ServerResponse.createBySuccessData(map);
}
}
ConverterVo ,目的封装查询的数据 list
package com.lagou.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data //get、set
@AllArgsConstructor //有参构造
@NoArgsConstructor //无参构造
public class ConverterVo {
private String name;
private String value;
}
IdustryMapper
package com.lagou.mapper;
import com.lagou.vo.ConverterVo;
import java.util.List;
public interface IdustryMapper {
/**
* 统计各个城市招聘情况
* @return
*/
public List<ConverterVo> countCityPosition();
}
IdustryMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.lagou.mapper.IdustryMapper">
<!-- 1.统计各个城市招聘人数情况 top10 -->
<select id="countCityPosition" resultType="ConverterVo">
SELECT position_city as name ,COUNT(*) as value FROM position_info_v2
WHERE position_city != ''
GROUP BY position_city ORDER BY value DESC
LIMIT 10
</select>
</mapper>
3)前后台联调
编写页面recruitment.html
a.引入echats.js文件
<script src="dist/js/echarts.min.js"></script>
b.创建一个div展示区域
<div class="panel-body">
<div id="main" style="height: 700px"></div>
</div>
c.前台发送异步ajax请求
<script>
//1.创建echats对象
var myChart = echarts.init(document.getElementById("main"));
//2.发送ajax请求 获取后台的数据
$.post("/lg_visualization/industry/countCityPosition.do",function(data){
//3.在回调函数中设置option
myChart.setOption({
tooltip: {
//提示组件
trigger: 'item', //触发类型: item 如果是散点图 饼图
formatter: '{a} <br/>{b}: {c} ({d}%)' //代表提示悬浮窗的格式 {a} {b} {c} 分别代表系列名 数据名 数值
},
legend: {
//图例组件
orient: 'vertical', //代表布局方式 vertical 水平布局
left: 10, //图例举例容器左边10像素
data: data.data.cityNamesList
},
series: [ //系列列表
{
name: '各个城市招聘人数', //系列名称
type: 'pie', //图表类型 pie饼图
selectedMode: 'single', //选中模式 : single 选择单个
radius: [0, '30%'], //代表图表的半径
label: {
//饼图标签上的信息
position: 'inner' // 饼图扇区内部展示信息
},
labelLine: {
//标签视觉的引导线
show: false //关闭视觉引导线
},
data: data.data.thridList //内部饼图的数据
},
{
name: '各个城市招聘人数',
type: 'pie',
radius: ['40%', '55%'], //外层的饼图
label: {
formatter: '{a|{a}}{abg|}\n{hr|}\n {b|{b}:}{c} {per|{d}%} ',
backgroundColor: '#eee',
borderColor: '#aaa',
borderWidth: 1,
borderRadius: 4,
rich: {
//自定义富文本样式
a: {
color: '#999',
lineHeight: 22,
align: 'center'
},
hr: {
borderColor: '#aaa',
width: '100%',
borderWidth: 0.5,
height: 0
},
b: {
fontSize: 16,
lineHeight: 33
},
per: {
color: '#eee',
backgroundColor: '#334455',
padding: [2, 4],
borderRadius: 2
}
}
},
data: data.data.dataList //外层饼图的数据
}
]
})
},"json");
</script>
4.4.热门行业统计
1)热门行业统计需求分析
同一个各个行业招聘人数top10 ,显示柱状图,具体如下:
2) 热门行业招聘信息接口
- 请求路径:industryTop.do
- 请求方法:post
- 请求接口
http://localhost:8080/lg_visualization/industry/industryTop.do
后台代码实现
IndustryController
/**
* 2.热门行业top10
*/
@RequestMapping(value = "industryTop.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse<Map<String,Object>> getIndustryTop(){
ServerResponse<Map<String,Object>> response = iIndustryService.getIndustryTop10();
return response;
}
IIndustryService
/**
* 2.热门行业top10
*/
ServerResponse<Map<String, Object>> getIndustryTop10();
IndustryServiceImpl
/**
* 2.热门行业top10
*/
@Override
public ServerResponse<Map<String, Object>> getIndustryTop10() {
//1.查询最热门的10个行业 List<IndustryVo>
List<IndustryVo> list = mapper.countIndustryTop();
//2.创建map 封装所有数据
Map<String,Object> map = new HashMap<>();
//3.创建两个list namesList 放行业名称 countList 行业数量
List<String> industryNames = new ArrayList<>();
List<Long> countList = new ArrayList<>();
//4.循环给list设置数据
for (IndustryVo industryVo : list) {
industryNames.add(industryVo.getPositionName());
countList.add(industryVo.getNum());
}
//5.list存在map中
map.put("names",industryNames);
map.put("count",countList);
//6.map放到ServerResponse
return ServerResponse.createBySuccessData(map);
}
IdustryMapper
/**
* 2.热门行业top10
*/
List<IndustryVo> countIndustryTop();
IdustryMapper.xml
<!-- 2.统计热门行业top10 -->
<select id="countIndustryTop" resultType="IndustryVo">
SELECT DISTINCT position_type_name positionName, SUM(position_num) AS num
FROM position_type_info_v2
GROUP BY position_type_name ORDER BY num DESC LIMIT 10;
</select>
- 前后台联调
编写页面industryTop.html
a.引入echats.js文件
<script src="dist/js/echarts.min.js"></script>
b.创建一个div展示区域
<div class="panel-body">
<div id="main" style="height: 700px"></div>
</div>
c.前台发送异步ajax请求
<script>
//1.创建echarts对象
var mychart = echarts.init(document.getElementById("main"));
//2.发送ajax请求
$.post("/lg_visualization/industry/industryTop.do",function(data){
//3.在回调函数中设置 option
mychart.setOption({
tooltip: {
//提示组件
trigger: 'axis', //如果是柱状图或折线图触发
axisPointer: {
//坐标轴指示配置项
type: 'shadow' // 默认值 line shadow 阴影指示器
}
},
grid: {
//直角坐标系网络 ,网络中绘制折线图 柱状图 ...
left: '3%', //组件举例左侧容器举例
right: '4%',
bottom: '3%',
containLabel: true //grid 区域是否有坐标轴的刻度
},
xAxis: {
type: 'value', //数值轴
boundaryGap: [0, 0.01]
},
yAxis: {
type: 'category', //类目轴
data: data.data.names
},
series: [
{
name: '招聘数量',
type: 'bar', //柱状图
data: data.data.count,
label: {
//图形上添加文字标签
show:true , //是否在标签上展示
position: 'insideRight' //标签数据展示位置
}
}
]
});
},"json")
</script>
4.5 热门行业招聘对比统计
4.5.1 热门行业招聘对比统计需求分析
使用面积图展示销售业、服务业、生产制造业的招聘数量图表
4.5.2 热门行业招聘对比统计接口
请求路径:industryCompare.do
请求方法:post
请求接口:
http://localhost:8080/lg_visualization/industry/industryCompare.do
后台代码实现
IndustryController
/**
* 3.热门行业对比图
*/
@RequestMapping(value = "industryCompare.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse<Map<String,Object>> getIndustryCompare(){
ServerResponse<Map<String,Object>> response = iIndustryService.getIndustryCompare();
return response;
}
IIndustryService
ServerResponse<Map<String, Object>> getIndustryCompare();
Const
package com.lagou.common;
public class Const {
public static final String[] INDUSTRY = {
"销售","服务业","生产制造"};
public static final String START_DATE = "2020-06-04 00:00:00";
public static final String END_DATE = "2020-06-09 23:59:59";
public static final String[] DATE_TIME = {
"2020-06-04","2020-06-05","2020-06-06","2020-06-07","2020-06-08","2020-06-09"};
}
IndustryServiceImpl
/**
* 3.热门行业对比图
* 统计 热门行业(销售 服务业 生产制造) 在2020-6-4 到2020-6-9 招聘信息对比
*/
@Override
public ServerResponse<Map<String, Object>> getIndustryCompare() {
//1.创建一个MAP集合,封装所有数据
Map<String,Object> map = new HashMap<>();
//2.循环行业
for (String industryName: Const.INDUSTRY
) {
//创建List<Long> 存放行业每天的招聘情况
List<Long> countNumList = new ArrayList<>();
List<IndustryVo> dataList = mapper.getIndustryData(industryName,Const.START_DATE,Const.END_DATE);
//4.循环dataList ,目的是给countNumList 设置数据
for (IndustryVo industryVo :dataList
) {
countNumList.add(industryVo.getNum());
}
//5.判断行业,给MAP设置值
if(industryName.equals(Const.INDUSTRY[0])){
map.put("sell",countNumList);
}else if(industryName.equals(Const.INDUSTRY[1])){
map.put("server",countNumList);
}else if(industryName.equals(Const.INDUSTRY[2])){
map.put("pro",countNumList);
}
map.put("dateTime",Const.DATE_TIME);
map.put("industryNameList",Const.INDUSTRY)
}
return ServerResponse.createBySuccessData(map);
}
IdustryMapper
/**
* 3.热门行业对比图
*/
List<IndustryVo> getIndustryData(@Param("industryName") String industryName,@Param("startDate") String startDate,@Param("endDate") String endDate);
IdustryMapper.xml
这里需要注意 >= 用 & gt;= ;<= 用 & lt;=
<!-- 3.热门行业对比图 -->
<select id="getIndustryData" resultType="IndustryVo">
SELECT position_type_name AS positionName, SUM(position_num) AS num , DATE_FORMAT(update_time,'%Y-%m-%d') AS theDay
FROM position_type_info_v2
WHERE position_type_name = #{industryName}
AND update_time >= #{startDate} AND update_time <= #{endDate}
GROUP BY theDay;
</select>
4.5.3 前后台联调
编写页面industry.html
1)创建一个div展示区域
<div class="panel-body">
<div id="main" style="height: 670px"></div>
</div>
2)前台发送异步ajax请求
<script>
//1.创建echart对象
var mychart = echarts.init(document.getElementById("main"));
//2.发送ajax请求
$.post("/lg_visualization/industry/industryCompare.do",function(data){
//3.设置option
mychart.setOption({
title: {
text: '统计行业招聘对比'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
legend: {
data: data.data.industryNameList
},
toolbox: {
feature: {
saveAsImage: {
}
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: data.data.dateTime
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: '销售',
type: 'line',
stack: '总量',
smooth : true, //是否平滑展示
areaStyle: {
},
data: data.data.sell,
label: {
//给图形上添加文本标签
normal :{
show : true, //是否显示标签
position : 'top' //数据展示的位置
}
}
},
{
name: '服务业',
type: 'line',
stack: '总量',
smooth : true, //是否平滑展示
areaStyle: {
},
data: data.data.server,
label: {
//给图形上添加文本标签
normal :{
show : true, //是否显示标签
position : 'top' //数据展示的位置
}
}
},
{
name: '生产制造',
type: 'line',
stack: '总量',
smooth : true, //是否平滑展示
areaStyle: {
},
data: data.data.pro,
label: {
//给图形上添加文本标签
normal :{
show : true, //是否显示标签
position : 'top' //数据展示的位置
}
}
}
]
});
},"json")
</script>
五.应聘者信息统计
5.1 各个学历下的岗位统计需求分析
通过柱状图和饼状图统计公司需要的就业人员学历情况,及各个学历占比情况
5.2 统计学历对应岗位接口
后台代码实现
EduController
package com.lagou.controller;
import com.lagou.common.ServerResponse;
import com.lagou.service.IEduService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Map;
@Controller
@RequestMapping("/edu")
public class EduController {
@Autowired
private IEduService service;
/**
* 获取不同学历下的岗位数量
*/
@RequestMapping("eduCount.do")
@ResponseBody
public ServerResponse<Map<String,Object>> getEduCount(){
ServerResponse<Map<String,Object>> response = service.getEduData();
return response;
}
}
IEduService
package com.lagou.service;
import com.lagou.common.ServerResponse;
import java.util.Map;
public interface IEduService {
/**
* 统计不同学历下的岗位数量
* @return
*/
public ServerResponse<Map<String, Object>> getEduData();
}
EduServiceImpl
package com.lagou.service.impl;
import com.lagou.common.ServerResponse;
import com.lagou.mapper.EduMapper;
import com.lagou.service.IEduService;
import com.lagou.vo.ConverterVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class EduServiceImpl implements IEduService {
@Autowired
private EduMapper mapper;
/**
* 统计不同学历对应的岗位数量
* @return
*/
@Override
public ServerResponse<Map<String, Object>> getEduData() {
//1.创建一个Map集合存放数据
Map<String,Object> dataMap = new HashMap<>();
//2.查询数据库, List<ConverterVo>
// 结果视图封装到ConverterVo中
List<ConverterVo> list = mapper.getEdu();
//3.创建两个List集合 namesList 存放名称集合 countList 存放不同的学历招聘个数
// 下面的数量, 对应ConverterVo中的属性类型, 写成String类型, Mybatis会自动转换
List<String> namesList = new ArrayList<>();
List<String> countList = new ArrayList<>();
//4.循环List<ConverterVo> ; 把数据给nameList countList设置
for (ConverterVo vo : list) {
namesList.add(vo.getName());
countList.add(vo.getValue());
}
//5.把3个list存放到map
dataMap.put("names",namesList);
dataMap.put("countNum",countList);
dataMap.put("eduData",list);
//6.把map放到ServerResponse返回
return ServerResponse.createBySuccessData(dataMap);
}
}
EduMapper
package com.lagou.mapper;
import com.lagou.vo.ConverterVo;
import java.util.List;
public interface EduMapper {
/**
* 统计不同学历下的岗位
* @return
*/
List<ConverterVo> getEdu();
}
EduMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.lagou.mapper.EduMapper">
<!-- 1.统计不同学历下的岗位数量 -->
<!--下面的 edu 和 count 必须要分别起别名, 和ConverterVo中的属性名一致, 才自动封装-->
<select id="getEdu" resultType="ConverterVo">
SELECT edu name, COUNT(*) value
FROM position_info_v2
WHERE edu IN ('中专/中技','初中及以下','博士','大专','本科','硕士','高中')
GROUP BY edu
</select>
</mapper>
前后台联调
编写页面edu.html
1)创建一个div展示区域
<div class="panel-body" >
<div id="main1" style="height: 550px"></div>
</div>
<script>
//1.创建2个echarts对象
var myChart1 = echarts.init(document.getElementById("main1"));
var myChart2 = echarts.init(document.getElementById("main2"));
//2.发送ajax请求
$.post("/lg_visualization/edu/eduCount.do",function(data){
//3.回调函数中,设置两个echarts option
myChart1.setOption({
color: ['#3398DB'],
tooltip: {
trigger: 'axis',
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true //grid 区域是否包含坐标轴的刻度标签
},
xAxis: [
{
type: 'category',
data: data.data.names,
axisTick: {
//配置坐标轴刻度
alignWithLabel: true //true 有效,保证刻度线和标签对齐
}
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: '岗位人数',
type: 'bar', //柱状图
label: {
//图上添加文字
show: true,
position: 'inside'
},
barWidth: '60%',
data: data.data.countNum
}
]
});
myChart2.setOption({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
data: data.data.names
},
series: [
{
name: '学历占比',
type: 'pie',
radius: '55%', //代表饼图的半径
center: ['50%', '60%'], //饼图圆心的坐标
data: data.data.eduData,
emphasis: {
//高亮扇区和标签的样式
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
});
},"json")
</script>
六、全国招聘信息
6.1.全国各省招聘需求分析
以地图的方式展示每个省份的招聘人数
6.2. 统计各全国各省招聘接口
- 请求路径:mapData
- 请求方法:post
- 请求接口:http://localhost:8080/lg_visualization/map/mapData.do
- 请求参数:无
- 响应参数
- 成功响应数据
{
"status":0,
"data": [
{
"name": "北京",
"value": 255
},
{
"name": "上海",
"value": 121
},
{
"name": "天津",
"value": 150
},
{
"name": "重庆",
"value": 85
},
{
"name": "黑龙江",
"value": 16
},
...
{
"name": "台湾",
"value": 0
}
]
}
后台代码实现
sql
-- 1. 查询每个城市有多少招聘岗位
SELECT position_city cName, COUNT(*) num
FROM position_info_v2
WHERE position_city != ''
GROUP BY position_city
ORDER BY num DESC;
-- 2. 查询每个省有多少个城市
SELECT p.`ProvinceID`, p.`NAME` NAME, c.`NAME` cName
FROM Province p
LEFT JOIN City c
ON c.`ProvinceID`=p.`ProvinceID`;
Controller
package com.lagou.controller;
import com.lagou.common.ServerResponse;
import com.lagou.service.ICountryMapService;
import com.lagou.vo.ProvinceVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
@RequestMapping("/map")
public class CountryMapController {
@Autowired
private ICountryMapService service;
/**
* 统计各省的招聘人数
*/
@RequestMapping(value = "mapData.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse<List<ProvinceVo>> countProvinceData(){
ServerResponse<List<ProvinceVo>> response = service.getMapData();
return response;
}
}
将各个市的招聘人数汇总为个省招聘人数
a.封装省份:ProvinceVo
package com.lagou.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 省的视图对象
* 作用1: 查找数据库的时候,数据映射
* 作用2: 把省份 对应的招聘人数,返回给前台 long 招聘人数
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProvinceVo {
@JsonIgnore //代表pid不返回给前台,相当于@JsonIgnoreProperties(value = "{pid}")
private int pid; //省编号
private String name; //省名称
private long value; //该省份的招聘人数
@JsonIgnore
private List<CityVo> city;
}
b.封装城市 CityVo
package com.lagou.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 城市的视图对象
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CityVo {
private int cid; //城市的id
private String cName; //城市名称
private long num; //城市招聘数量
}
c.定义转换器类CityProvinceConverter,将CityVo中的人数累加到ProvinceVo中
package com.lagou.common;
import com.lagou.vo.CityVo;
import com.lagou.vo.ProvinceVo;
import java.util.ArrayList;
import java.util.List;
/**
* 转换器类
* 将CityVo中的人数累加到ProvinceVo中
*/
public class CityProvinceConverter {
/**
* 作用: cityList 中的招聘数 ===> provinceVoList 中的value值中
* @param cityList
* @param provinceVoList
* @return
*/
public static List<ProvinceVo> converter(List<CityVo> cityList, List<ProvinceVo> provinceVoList) {
//cityList [ {北京 255 }, {上海 300 , 杭州 100} ]
//provinceVoList [{浙江 0 [杭州,宁波..]} ,{浙江 0 [杭州,宁波..]}]
//1.循环省
flag: for (ProvinceVo provinceVo : provinceVoList) {
//统计每一个省的招聘数量
long sumCount = 0;
String provinceVoName = provinceVo.getName();
List<CityVo> pCityList = provinceVo.getCity();
//用来装省中的城市的名称
List<String> pCityNameList = new ArrayList<>();
for (CityVo cityVo : pCityList) {
//填充城市的名称
pCityNameList.add(cityVo.getCName());
}
//循环cityList
for (CityVo cityVo : cityList) {
//判断有些有些省的名称不要是 北京 上海 还有天津 重庆...
if(provinceVoName.equals("北京") && cityVo.getCName().equals("北京")){
//给北京设置招聘人数
provinceVo.setValue(cityVo.getNum());
continue flag;
} else if(provinceVoName.equals("上海") && cityVo.getCName().equals("上海")){
provinceVo.setValue(cityVo.getNum());
continue flag;
} else if(provinceVoName.equals("天津") && cityVo.getCName().equals("天津")){
provinceVo.setValue(cityVo.getNum());
continue flag;
} else if(provinceVoName.equals("重庆") && cityVo.getCName().equals("重庆")){
provinceVo.setValue(cityVo.getNum());
continue flag;
} else {
//如果省份的名称集合包含城市的名字,累加sumCount 设置给provinceVo
if(pCityNameList.contains(cityVo.getCName())){
sumCount += cityVo.getNum();
}
}
provinceVo.setValue(sumCount);
}
}
return provinceVoList;
}
}
ICountryMapService
package com.lagou.service;
import com.lagou.common.ServerResponse;
import com.lagou.vo.ProvinceVo;
import java.util.List;
public interface ICountryMapService {
/**
* 统计各个省份的招聘人数
* @return
*/
ServerResponse<List<ProvinceVo>> getMapData();
}
CountryMapServiceImpl
package com.lagou.service.impl;
import com.lagou.common.CityProvinceConverter;
import com.lagou.common.ServerResponse;
import com.lagou.mapper.CountryMapMapper;
import com.lagou.service.ICountryMapService;
import com.lagou.vo.CityVo;
import com.lagou.vo.ProvinceVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 统计各个省份的招聘人数
*/
@Service
public class CountryMapServiceImpl implements ICountryMapService {
@Autowired
private CountryMapMapper mapper;
/**
* 统计各省招聘人数
* @return
*/
@Override
public ServerResponse<List<ProvinceVo>> getMapData() {
//1.查询各个省份有哪些城市 List<ProvinceVo> 没有城市, 招聘数量0
List<ProvinceVo> provinceVoList = mapper.queryProvinceCity();
//2.查询每个城市招聘数量 List<CityVo> 有城市招聘数量
List<CityVo> cityList = mapper.queryCityNum();
//3.调用转换类的转换方法,传递 List<ProvinceVo> List<CityVo> ---> CityVo中的人数累加到ProvinceVo中
List<ProvinceVo> provinceVos = CityProvinceConverter.converter(cityList,provinceVoList);
//4.返回ServerResponse ,把List<ProvinceVo>放到response中
return ServerResponse.createBySuccessData(provinceVos);
}
}
CountryMapMapper
package com.lagou.mapper;
import com.lagou.vo.CityVo;
import com.lagou.vo.ProvinceVo;
import java.util.List;
public interface CountryMapMapper {
/**
* 查询各个省份有哪些城市
* @return
*/
public List<ProvinceVo> queryProvinceCity() ;
/**
* 查询每个城市招聘数量
* @return
*/
List<CityVo> queryCityNum();
}
CountryMapMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.lagou.mapper.CountryMapMapper">
<!-- 身份城市一对多关系映射 -->
<!-- autoMapping="true" 作用是属性和字段名相同时, 下面可以不进行该映射配置 -->
<resultMap id="provinceResultMap" type="ProvinceVo" autoMapping="true">
<!-- 主键映射 -->
<id column="ProvinceID" property="pid"/>
<!-- 普通字段映射 autoMapping="true"时,下面属性名和字段名相同,可以省略 -->
<result column="name" property="name"/>
<!-- 映射集合数据类型 -->
<collection property="city" javaType="list" ofType="CityVo" autoMapping="true">
<!-- 主键映射 -->
<id column="CityID" property="cid" />
</collection>
</resultMap>
<!-- 1.查询省份有多少个城市 -->
<select id="queryProvinceCity" resultMap="provinceResultMap">
SELECT p.ProvinceID , p.Name NAME , c.NAME cName
FROM Province p
LEFT JOIN City c
ON c.ProvinceID = p.ProvinceID
</select>
<!-- 2.城市有多少个招聘岗位-->
<select id="queryCityNum" resultType="CityVo">
SELECT position_city cName ,COUNT(*) num
FROM position_info_v2
WHERE position_city != ''
GROUP BY position_city ORDER BY num DESC
</select>
</mapper>
前后台联调
编写页面map.html
1)创建一个div展示区域
<div class="panel-wrapper collapse in" >
<!--图表区域-->
<div id="main" style="width: 800px; height: 700px; margin-left: 300px" ></div>
</div>
<script>
var myChart = echarts.init(document.getElementById('main'));
//执行图表的配置和数据
var titleName = "2020年全国各个城市招聘数量";
var mapName = "china";
$.post("/lg_visualization/map/mapData.do",function(data){
myChart.setOption({
title: {
text: titleName,
x: 'center',
textStyle: {
fontSize: 24
}
},
tooltip: {
//提示框组件
trigger: "item", //数据项图形触发,主要在散点图,饼图等无类目轴的图表中使用
//提示框浮层内容格式器,支持字符串模板和回调函数两种形式。
formatter: function (params) {
return params.name + "招聘人数:" + params.value + "人";
}
},
toolbox: {
feature: {
//各工具配置项
saveAsImage: {
} //保存为图片
}
},
visualMap: {
//是视觉映射组件
show: true, //是否显示 visualMap-piecewise 组件
left: "left",//visualMap 组件离容器左侧的距离
top: "bottom", //visualMap 组件离容器上侧的距离
seriesIndex: [0], //指定取哪个系列的数据,即哪个系列的
type: "piecewise", //分段型视觉映射组件
pieces: [ //分段式视觉映射组件的每一段的范围
{
ge : 30000}, //设置大于10000人数
{
ge :10000 , lte: 29999}, //设置人数1000-9999
{
ge : 5000, lte : 9999} ,
{
ge : 1000 , lte : 4999},
{
ge : 0 , lte : 999}
],
inRange : {
//定义 在选中范围中 的视觉元素
color : ['rgb(168,188,180)','rgb(141,188,180)', 'rgb(200,200,160)' , 'rgb(249,207,178)' , 'rgb(254,57,101)']
},
textStyle: {
color: "#000000"
}
},
geo: {
//地理坐标系组件
show: true, //是否显示地理坐标系组件
map: mapName, //地图类型。
label: {
//图形上的文本标签,可用于说明图形的一些数据信息
normal: {
show: true,
fontSize: 12
},
emphasis: {
show: true
}
},
roam: false, //是否开启鼠标缩放和平移漫游。默认不开启
itemStyle : {
normal: {
//普通状态下样式
areaColor: "#FFFFFF",
borderColor: "#666666",
},
emphasis: {
//高亮状态下的多边形和标签样式。
areaColor: "#0099CC" //地图区域的颜色。
}
}
},
series: [
{
type: "map",
map: mapName,
geoIndex: 0, // geoIndex 指定一个 geo 组件
animation: false, //是否开启动画。
data:data.data
},
]
})
})
</script>
6.3.首页大屏展示图表
实现方式:
创建bigSreen.html ,在该页面使用iframe引入 recruitment.html , map.html , industryCompare.html
下面是bigSreen.html 代码细节
bigSreen.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Elmer I Fast build Admin dashboard for any platform</title>
<meta name="description" content="Elmer is a Dashboard & Admin Site Responsive Template by hencework." />
<meta name="keywords" content="admin, admin dashboard, admin template, cms, crm, Elmer Admin, Elmeradmin, premium admin templates, responsive admin, sass, panel, software, ui, dwh, web app, application" />
<meta name="author" content="hencework"/>
<!-- Morris Charts CSS -->
<link href="vendors/bower_components/morris.js/morris.css" rel="stylesheet" type="text/css"/>
<!-- Calendar CSS -->
<link href="vendors/bower_components/fullcalendar/dist/fullcalendar.css" rel="stylesheet" type="text/css"/>
<!-- bootstrap-select CSS -->
<link href="vendors/bower_components/bootstrap-select/dist/css/bootstrap-select.min.css" rel="stylesheet" type="text/css"/>
<!-- switchery CSS -->
<link href="vendors/bower_components/switchery/dist/switchery.min.css" rel="stylesheet" type="text/css"/>
<!-- Data table CSS -->
<link href="vendors/bower_components/datatables/media/css/jquery.dataTables.min.css" rel="stylesheet" type="text/css"/>
<!-- Custom CSS -->
<link href="dist/css/style.css" rel="stylesheet" type="text/css">
</head>
<body>
<!--Preloader-->
<div class="preloader-it">
<div class="la-anim-1"></div>
</div>
<!--/Preloader-->
<div class="wrapper theme-1-active pimary-color-blue" id="app">
<!-- Main Content -->
<!-- Row -->
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<div class="panel panel-default card-view panel-refresh">
<div class="refresh-container">
<div class="la-anim-1"></div>
</div>
<div class="panel-heading">
<div class="pull-left">
<h6 class="panel-title txt-dark">各个城市招聘统计</h6>
</div>
<div class="pull-right">
<a href="#" class="pull-left inline-block refresh mr-15">
<i class="zmdi zmdi-replay"></i>
</a>
</div>
<div class="clearfix"></div>
</div>
<div class="panel-wrapper collapse in">
<div class="panel-body">
<!-- 1.引入recruitment2 各个城市招聘信息 -->
<iframe src="recruitment2.html" style="width: 600px;height: 378px" scrolling="no"></iframe>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<div class="panel panel-default card-view panel-refresh">
<div class="refresh-container">
<div class="la-anim-1"></div>
</div>
<div class="panel-heading">
<div class="pull-left">
<h6 class="panel-title txt-dark">全国各省招聘人数统计</h6>
</div>
<div class="pull-right">
<a href="#" class="pull-left inline-block refresh mr-15">
<i class="zmdi zmdi-replay"></i>
</a>
</div>
<div class="clearfix"></div>
</div>
<div class="panel-wrapper collapse in">
<div class="panel-body">
<!-- 2.引入各个城市招聘地图 -->
<iframe src="map2.html" style="width: 600px;height: 378px" scrolling="no"></iframe>
</div>
</div>
</div>
</div>
</div>
<!-- /Row -->
<!-- Row -->
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<!-- 3.引入热门行业对比 -->
<iframe src="industryCompare2.html" style="width: 1350px;height: 280px" scrolling="no"></iframe>
</div>
</div>
<!-- /Row -->
<!-- /Main Content -->
</div>
<!-- /#wrapper -->
<!-- JavaScript -->
<!-- jQuery -->
<script src="vendors/bower_components/jquery/dist/jquery.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="vendors/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<!-- Calender JavaScripts -->
<script src="vendors/bower_components/moment/min/moment.min.js"></script>
<script src="vendors/jquery-ui.min.js"></script>
<script src="vendors/bower_components/fullcalendar/dist/fullcalendar.min.js"></script>
<script src="dist/js/fullcalendar-data.js"></script>
<!-- Progressbar Animation JavaScript -->
<script src="vendors/bower_components/waypoints/lib/jquery.waypoints.min.js"></script>
<script src="vendors/bower_components/jquery.counterup/jquery.counterup.min.js"></script>
<!-- Data table JavaScript -->
<script src="vendors/bower_components/datatables/media/js/jquery.dataTables.min.js"></script>
<!-- Slimscroll JavaScript -->
<script src="dist/js/jquery.slimscroll.js"></script>
<!-- ChartJS JavaScript -->
<script src="vendors/chart.js/Chart.min.js"></script>
<!-- Fancy Dropdown JS -->
<script src="dist/js/dropdown-bootstrap-extended.js"></script>
<!-- Sparkline JavaScript -->
<script src="vendors/jquery.sparkline/dist/jquery.sparkline.min.js"></script>
<script src="vendors/bower_components/jquery.easy-pie-chart/dist/jquery.easypiechart.min.js"></script>
<!-- Morris Charts JavaScript -->
<script src="vendors/bower_components/raphael/raphael.min.js"></script>
<script src="vendors/bower_components/morris.js/morris.min.js"></script>
<script src="dist/js/morris-data.js"></script>
<!-- Owl JavaScript -->
<script src="vendors/bower_components/owl.carousel/dist/owl.carousel.min.js"></script>
<!-- Bootstrap Select JavaScript -->
<script src="vendors/bower_components/bootstrap-select/dist/js/bootstrap-select.min.js"></script>
<!-- Switchery JavaScript -->
<script src="vendors/bower_components/switchery/dist/switchery.min.js"></script>
<!-- Init JavaScript -->
<script src="dist/js/init.js"></script>
<script src="dist/js/widgets-data.js"></script>
</body>
</html>