打开前端Vue项目:kongguan_web,完成前端src/components/echart/SectorFlightChart.vue页面设计,使用ECharts插件实现柱状图和饼状图
- 在src/components目录下创建echart目录,完成src/components/echart/SectorFlightChart.vue 的页面div设计:
<template xmlns:el-col="http://www.w3.org/1999/html">
<div class="home">
<div id="barFlightChart" class="chart" />
</div>
</template>
... 接下文 ...
- 初始化数据,代码如下:
... 接上文 ...
<script>
import {findATCTime} from "../../api/chartdata/chartdata";
export default {
name: "SectorFlightChart",
data() {
return {
sectorData: [],
barChartData: [],
barChartAxis: [],
sectorCharData: [],
sectorCharOneData: [],
sectorChartAxis: ['K', 'S', 'E', 'P', 'G'],
chartOption: {},
myChart: {},
}
},
mounted() {
this.initChart();
this.loadData();
},
... 接下文 ...
- 初始化ECharts,代码如下:
... 接上文 ...
methods: {
initChart() {
this.myChart = this.$echarts.init(document.getElementById("barFlightChart"));
this.chartOption = {
baseOption: {
timeline: {
axisType: 'category',
// realtime: false,
// loop: false,
autoPlay: true,
// currentIndex: 2,
playInterval: 1000,
// controlStyle: {
// position: 'left'
// },
lineStyle: {color: "#bcc9d7", width: 1},
controlStyle: {showPlayBtn: !1, showPrevBtn: !1, showNextBtn: !1},
checkpointStyle: {color: "#f19326", symbol: "circle", symbolSize: 10, borderWidth: 0},
itemStyle: {normal: {color: "#419ae7"}},
},
title: {
text: "扇区架次数动态循环展示",
subtext: "",
top:18,
left: 26,
textStyle: {
color: "#000000"
},
},
tooltip: {
trigger: "item",
padding: 10,
backgroundColor: "#222",
borderColor: "#777",
borderWidth: 1,
},
angleAxis: {
type: "category",
axisTick: {show: !1},
axisLine: {show: !0, lineStyle: {color: "#d2dde7"}},
axisLabel: {color: "#d2dde7"},
data: ["G区", "K区", "E区", "P区", "S区"],
z: 10
},
radiusAxis: {
min: 0,
axisLine: {show: !1, lineStyle: {color: "#000", opacity: .3}},
axisLabel: {show: !1, color: "#000"},
axisTick: {show: !1},
splitLine: {lineStyle: {color: "#d2dde7"}},
splitArea: {show: !1, areaStyle: {color: "rgb(1, 10, 63)", opacity: .8}}
},
grid: {left: "10%", right: "50%", top: "10%", bottom: "9%", containLabel: !1},
polar: {center: ["75%", "45%"], radius: "50%"},
xAxis: [{
type: 'value',
boundaryGap: [0, 0.01],
splitLine: {
show: false
},
show: false,
axisLine: { //横轴样式
lineStyle: {},
},
position:'top'
}],
yAxis: [{
type: 'category',
data: this.barChartAxis,
inverse:true,
axisLine: { //纵轴样式
lineStyle: {
color: '#73777d'
}
},
axisLabel: {
rotate: -45
}
}],
series: [{
type: "bar",
coordinateSystem: "polar",
name: "扇区",
center: ["75%", "45%"],
stack: "a",
itemStyle: {
normal: {
color: function (t) {
return ["#51b8f9", "#7d92ff", "#5fccc3", "#f19326", "#f258b6"][t.dataIndex]
}, label: {show: !0, position: "top", formatter: "{b}\n{c}"}
}
}
}, {
name: "本日架次数",
type: "bar",
barWidth: 8,
radius: 90,
avoidLabelOverlap: !1,
label: {
normal: {show: !1, position: "outside", formatter: "{c}"},
emphasis: {show: !0, textStyle: {fontSize: "12", fontWeight: "normal"}}
},
labelLine: {normal: {show: !1}},
itemStyle: {normal: {color: "#51b8f9"}, emphasis: {color: "#f19326"}}
}]
}
}
this.myChart.setOption(this.chartOption);
},
... 接下文 ...
- 加载整理数据,然后拼装ECharts专用的options对象,代码如下:
//加载数据
loadData() {
findATCTime().then(data => {
if (data.isSuccess) {
this.formatData(data.result);
} else {
this.$message.error("数据获取失败");
}
});
},
//整理数据
formatData(data) {
let timeLineData = [];
let barDataArr = [];
let pieDataArr = [];
let optionArr = [];
for (let i = 0; i < data.length; i++) {
let dayItemData = data[i];
timeLineData.push(i + 1);
let dayFlightSum = 0;
let dayFlightDetail = [];
for (let j = 0; j < dayItemData.length; j++) {
dayFlightDetail.push(dayItemData[j][this.sectorChartAxis[j]]);
dayFlightSum = dayFlightSum + parseInt(dayItemData[j][this.sectorChartAxis[j]]);
}
pieDataArr.push(dayFlightDetail);
barDataArr.push(dayFlightSum);
}
//拼装 echart专用的options对象
for (let i = 0; i < timeLineData.length; i++) {
optionArr.push({
series: [{data: pieDataArr[i]}, {data: barDataArr}],
yAxis: [{data: timeLineData, nameTextStyle: {fontSize: 4, align: "center"},axisLabel:{formatter:'第{value}天'}}]
})
}
this.chartOption.baseOption.timeline.data = timeLineData;
this.chartOption.options = optionArr;
this.refreshChart();
}
refreshChart() {
this.myChart.setOption(this.chartOption);
}
}
}
</script>
- 页面样式,代码如下:
<style scoped>
.home {
height: 700px;
overflow: auto;
background-color: #ffffff;
border: 1px solid #ebedf2;
border-radius: 10px;
box-shadow: 3px 3px 3px 3px #ebedf2;
}
.home::-webkit-scrollbar {
display: none;
}
.chart {
height: 680px;
}
</style>
- 加载数据时,会调用src/api/chartdata/chartdata.js中定义的findATCTime方法,向服务端发送GET请求,获取扇区架次数动态统计,chartdata.js的完整代码如下:
import request from '../../utils/request'
const baseUrl = "/api"
/**
* 扇区架次数动态统计
*/
export function findATCTime() {
return request({
url: baseUrl + "/atc/findATCTime",
method: "GET"
})
}
/**
* 获取各个扇区通话饱和度
*/
export function findCallSaturation() {
return request({
url: baseUrl + "/callSaturation/findCallSaturation",
method: "GET"
})
}
export function annualWarningStatisticsByCategory() {
return request({
url: baseUrl + "/warnFlightHistory/annualWarningStatisticsByCategory",
method: "GET"
})
}
export function getAirPortCount() {
return request({
url: baseUrl + "/company/getAirPortCount",
method: "GET"
})
}
/**
* 获取从青岛起飞航班数前十的航线
* @returns {AxiosPromise}
*/
export function findByLimit() {
return request({
url: baseUrl + "/airLine/findByLimit",
method: "GET"
})
}
2、后端的实现,打开后端项目:BigData-KongGuan
- 编写实体类com/qrsoft/entity/Atc.java(前面任务时,已经创建过)
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("atc_number")
public class Atc implements Serializable {
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
@TableField(value = "ACID")
private String acId;
@TableField(value = "ATC_TIME")
private String atcTime;
@TableField(value = "EXECUTE_DATE")
private String executeDate;
@TableField(value = "PLAN_SECTOR_NAME")
private String planSectorName;
@TableField(exist = false)
private String count;
}
- 编写数据访问类com/qrsoft/mapper/AtcMapper.java,添加findATCTime()方法和findATCTime2()方法,AtcMapper类的完整代码如下:
import com.qrsoft.entity.Atc;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface AtcMapper extends BaseMapper<Atc> {
@Select("select PLAN_SECTOR_NAME,COUNT(*) as count from atc_number GROUP BY PLAN_SECTOR_NAME;")
List<Atc> findSectorSortie();
@Select("select EXECUTE_DATE from atc_number group by EXECUTE_DATE order by EXECUTE_DATE desc limit 19;")
List<String> findATCTime();
@Select("select PLAN_SECTOR_NAME,count(*) as count from atc_number where EXECUTE_DATE = #{executeTime} and PLAN_SECTOR_NAME = #{sectorName}")
Atc findATCTime2(String executeTime,String sectorName);
}
- 编写Service类com/qrsoft/service/AtcService.java,添加findATCTime()方法,AtcService类的完整代码如下:
package com.qrsoft.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qrsoft.common.Result;
import com.qrsoft.common.ResultConstants;
import com.qrsoft.entity.Atc;
import com.qrsoft.entity.MultiRadar;
import com.qrsoft.mapper.AtcMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Service
public class AtcService extends ServiceImpl<AtcMapper, Atc> {
@Autowired
private MultiRadarService multiRadarService;
/**
* 查询所有扇区航班架次
*/
public Result findSectorSortie() {
List<Atc> sectorSortie = baseMapper.findSectorSortie();
return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS, sectorSortie);
}
/**
* 根据扇区号查询架次
* @param planSectorName
*/
public Result findLocusCount(String planSectorName) {
QueryWrapper<MultiRadar> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("section",planSectorName);
int count = multiRadarService.count(queryWrapper);
return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS, count);
}
/**
* 扇区架次数动态统计(饼状图)
*/
public Result findATCTime() {
List<String> sectorName = new ArrayList<>();
sectorName.add("K");
sectorName.add("S");
sectorName.add("E");
sectorName.add("P");
sectorName.add("G");
List<String> executeTime = baseMapper.findATCTime();
List list = new ArrayList();
for (int i = 0; executeTime.size() > i; i++) {
ArrayList<Object> objects = new ArrayList<>();
for (int j = 0; sectorName.size() > j; j++) {
Atc atcTime2 = baseMapper.findATCTime2(executeTime.get(i), sectorName.get(j));
HashMap<String, Object> map = new HashMap<>();
if (atcTime2.getPlanSectorName() != null) {
map.put(atcTime2.getPlanSectorName(), atcTime2.getCount());
}else {
map.put(sectorName.get(j),0);
}
objects.add(map);
}
list.add(objects);
}
return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS, list);
}
}
- 编写扇区操作的控制器类com/qrsoft/controller/AtcController.java,添加findATCTime()方法,AtcController类的完整代码如下:
package com.qrsoft.controller;
import com.qrsoft.common.Result;
import com.qrsoft.service.AtcService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "扇区操作类")
@RestController
@RequestMapping("/api/atc")
public class AtcController {
@Autowired
private AtcService service;
/**
* 获取各扇区航班数
*/
@ApiOperation(value = "获取各扇区航班数")
@GetMapping("/findSectorSortie")
public Result findSectorSortie(){
return service.findSectorSortie();
}
/**
* 根据扇区名称获取该扇区航班数
* @param planSectorName
*/
@ApiOperation(value = "根据扇区名称获取该扇区航班数")
@GetMapping("/findLocusCount")
public Result findLocusCount(@RequestParam String planSectorName){
return service.findLocusCount(planSectorName);
}
/**
* 扇区架次数动态统计(饼状图)
*/
@ApiOperation(value = "扇区架次数动态统计(饼状图)")
@GetMapping("/findATCTime")
public Result findATCTime(){
return service.findATCTime();
}
}
3、实现前端的报表展示
- 回顾前面已经完成的src/components/echart/SectorFlightChart.vue页面,在页面中绑定数据的核心代码为:
//加载数据
loadData() {
findATCTime().then(data => {
if (data.isSuccess) {
this.formatData(data.result);
} else {
this.$message.error("数据获取失败");
}
});
},
//整理数据
formatData(data) {
let timeLineData = [];
let barDataArr = [];
let pieDataArr = [];
let optionArr = [];
for (let i = 0; i < data.length; i++) {
let dayItemData = data[i];
timeLineData.push(i + 1);
let dayFlightSum = 0;
let dayFlightDetail = [];
for (let j = 0; j < dayItemData.length; j++) {
dayFlightDetail.push(dayItemData[j][this.sectorChartAxis[j]]);
dayFlightSum = dayFlightSum + parseInt(dayItemData[j][this.sectorChartAxis[j]]);
}
pieDataArr.push(dayFlightDetail);
barDataArr.push(dayFlightSum);
}
//拼装 echart专用的options对象
for (let i = 0; i < timeLineData.length; i++) {
optionArr.push({
series: [{data: pieDataArr[i]}, {data: barDataArr}],
yAxis: [{data: timeLineData, nameTextStyle: {fontSize: 4, align: "center"},axisLabel:{formatter:'第{value}天'}}]
})
}
this.chartOption.baseOption.timeline.data = timeLineData;
this.chartOption.options = optionArr;
this.refreshChart();
}
,
//重新绑定数据
refreshChart() {
this.myChart.setOption(this.chartOption);
}
- 在src/views/Home/Index.vue引入SectorFlightChart组件,代码如下:
... 略 ...
import AirLine from "../../components/AirLine";
import Section from "../../components/Section";
import Delay from "../../components/Delay";
import WarnStatistice from "../../components/WarnStatistice";
import SectorFlightChart from "../../components/echart/SectorFlightChart";
import {hasPermission} from "../../utils/permission";
export default {
data() {
return {
};
},
mounted() {
},
components: {AirLine,Section,Delay,WarnStatistice,SectorFlightChart},
methods: {
isShow(permission){
return hasPermission(permission);
}
}
... 略 ...
- 在src/views/Home/Index.vue添加“扇区架次动态展示”组件,代码如下:
<el-row :gutter="30" v-show="isShow('/section/detail')">
<el-col :span="16" align="center">
<SectorFlightChart/>
</el-col>
// ... 略 ...
</el-row>
注意:在上面代码中【 v-show="isShow('/section/detail')" 】属性的作用是判断当前登录的用户是否有权限显示当前内容,如果当前登录的用户没有权限,则不会显示当前内容,新用户的权限需要到MySQL数据库中进行设置。
这里有两种方式,可以显示当前内容:
1)去掉【 v-show="isShow('/section/detail')" 】属性,即不判断是否有权限显示。
2)需要使用有权限的用户登录才能显示,或到数据库中分配权限。
参照任务“动态航线图”进行设置。
例如我们前面使用的用户admin,该用户没有权限显示,所以使用admin用户登录系统时是不会显示当前内容的,如果要进行权限设置,可以进入MySQL安装节点(node3节点),然后进入数据库,为admin用户授权。
[root@node3 ~]# mysql -uroot -p123456
mysql> use kongguan;
先查看角色表中,“管理员”的ID:
修改sys_auth表,添加一个【/section/detail】权限:
mysql> insert into sys_auth(auth_name,auth_code,menu_url) values('show detail','/section/detail','/section/detail');
修改role_auth表,将权限授权给“管理员”角色:
mysql>insert into role_auth(role_id,auth_id) values(3,198);
- src/views/Home/Index.vue的完整代码如下:
<template>
<div class="index">
<el-row :gutter="30" v-show="isShow('/flight/airline')">
<el-col :span=24 align="center">
<AirLine/>
</el-col>
</el-row>
<el-row :gutter="30" v-show="isShow('/flight/section')">
<el-col :span="24" align="center">
<Section/>
</el-col>
</el-row>
<el-row :gutter="30" v-show="isShow('/flight/delay')">
<el-col :span="16" align="center">
<Delay/>
</el-col>
<el-col :span="8" align="center">
<year-warning-chart/>
</el-col>
</el-row>
<el-row :gutter="30" v-show="isShow('/section/warning')">
<el-col :span="12" align="center">
<air-port-count-chart/>
</el-col>
<el-col :span="12" align="center">
<WarnStatistice/>
</el-col>
</el-row>
<el-row :gutter="30" v-show="isShow('/section/detail')">
<el-col :span="16" align="center">
<SectorFlightChart/>
</el-col>
<el-col :span="8" align="center">
<sector-call-chart/>
</el-col>
</el-row>
</div>
</template>
<script>
import AirLine from "../../components/AirLine";
import Section from "../../components/Section";
import WarnStatistice from "../../components/WarnStatistice";
import Delay from "../../components/Delay";
import {hasPermission} from "../../utils/permission";
import SectorFlightChart from "../../components/echart/SectorFlightChart";
export default {
data() {
return {
};
},
mounted() {
},
components: {AirLine, Section, WarnStatistice, Delay,SectorFlightChart},
methods: {
isShow(permission){
return hasPermission(permission);
}
}
};
</script>
<style scoped>
.index {
height: 100%;
overflow: auto;
padding-left: 44px;
padding-right: 44px
}
.index::-webkit-scrollbar {
display: none;
}
.caseClass {
background: url('../../assets/images/index-bg.png') no-repeat;
background-size: cover;
margin-top: 20px;
height: 284px;
}
.el-button {
background: transparent;
}
</style>
- 确保Hadoop、Spark、Kafka、Redis、MySQL等服务均已经正常启动,如果没有正常启动,请参照前面的安装部署任务,完成这些服务的启动。
例如:查看MySQL是否正常启动。
- 启动后端项目 BigData-KongGuan
- 启动前端项目 kongguan_web
- 报表的最终展示效果如下图所示: