打开前端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> {@Autowiredprivate 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 {@Autowiredprivate 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
- 报表的最终展示效果如下图所示: