翠屏区智慧广电示范区
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1184 lines
45 KiB

<template>
<!-- 应急广播数据中心 -->
<div class="eb-container" ref="appRef">
<!-- 背景框 -->
<div class="bg">
<MainTitle class="title" :title="title"></MainTitle>
</div>
<gis-map
class="gismap"
pointType="应急广播"
:point-list="pointList"
:lngLat="lngLat"
list-item-key="name"
@currentPoint="currentPointFn"
>
<template v-slot:content>
<div class="left-box">
<div class="left-one">
<div class="title-box-samll">
<span>资源状态展示</span>
</div>
<div class="databox">
<div class="line-title-box">
<div
class="line-title-box-item"
v-for="(item, index) in statusItemList"
:key="index"
>
<div class="line-item-box">
<div
class="line-item-color"
:style="{
background:
index % 2 === 0
? '#01FF94'
: '#01A4FF',
}"
></div>
<div class="line-item-white"></div>
</div>
<div class="line-item-title">
{{ item.title }}
</div>
<div class="line-item-number">
<CountTo
:startVal="0"
:endVal="Number(item.number)"
:duration="2000"
/>
</div>
</div>
</div>
<div class="table-box">
<div class="table-title-box">
<div
class="table-title-item"
style="width: 28%; text-align: left"
>
地区
</div>
<div
class="table-title-item"
style="width: 18%"
>
前端在线
</div>
<div
class="table-title-item"
style="width: 18%"
>
前端离线
</div>
<div
class="table-title-item"
style="width: 18%"
>
终端在线
</div>
<div
class="table-title-item"
style="width: 18%"
>
终端离线
</div>
</div>
<div
class="table-body-box"
style="height: 280px"
>
<vue-seamless-scroll
:data="statusTableData"
:class-option="optionHoverVertical"
>
<div>
<div
v-for="(
item, index
) in statusTableData"
:key="index"
class="table-body-item"
>
<div
class="table-info-item"
style="
width: 28%;
text-align: left;
"
>
{{ item.districtName }}
</div>
<div
class="table-info-item"
style="width: 18%"
>
<CountTo
:startVal="0"
:endVal="
Number(
item.frontOnlineNum
)
"
:duration="2000"
/>
</div>
<div
class="table-info-item"
style="width: 18%"
>
<CountTo
:startVal="0"
:endVal="
Number(
item.frontOfflineNum
)
"
:duration="2000"
/>
</div>
<div
class="table-info-item"
style="width: 18%"
>
<CountTo
:startVal="0"
:endVal="
Number(
item.terminalOnlineNum
)
"
:duration="2000"
/>
</div>
<div
class="table-info-item"
style="width: 18%"
>
<CountTo
:startVal="0"
:endVal="
Number(
item.terminalOfflineNum
)
"
:duration="2000"
/>
</div>
</div>
</div>
</vue-seamless-scroll>
</div>
</div>
</div>
</div>
<div class="left-two">
<div class="title-box-samll">
<span>设备终端</span>
</div>
<div class="databox">
<div class="table-body-box" style="height: 100%">
<vue-seamless-scroll
:class-option="optionHoverVertical"
:data="deviceList"
>
<div
class="data-item-box"
v-for="(item, index) in deviceList"
:key="index"
>
<div style="display: flex">
<div class="data-name">
{{ item.name }}
</div>
</div>
<div class="data-status">
<div
class="point"
:style="{
backgroundColor:
item.status == '0'
? 'red'
: 'green',
}"
></div>
<div class="status">
{{
item.status == "0"
? "离线"
: "在线"
}}
</div>
</div>
</div>
</vue-seamless-scroll>
</div>
</div>
</div>
</div>
<div class="right">
<div class="right-one">
<div class="title-box-samll">
<span>近六月事件类型统计</span>
</div>
<div class="databox" id="lineChart"></div>
</div>
<div class="right-two">
<div class="title-box-samll">
<span>近六月事件级别统计</span>
</div>
<div class="databox" id="barChart"></div>
</div>
<div class="right-three">
<div class="title-box-samll">
<span>最新应急信息</span>
</div>
<div class="databox">
<div class="table-title-box">
<div
class="table-title-item"
style="width: 20%; text-align: left"
>
事件类别
</div>
<div
class="table-title-item"
style="width: 30%"
>
事件标题
</div>
<div
class="table-title-item"
style="width: 30%"
>
发布单位
</div>
<div
class="table-title-item"
style="width: 20%; text-align: right"
>
调度状态
</div>
</div>
<div class="table-body-box" style="height: 394px">
<vue-seamless-scroll
:data="infoTableData"
:class-option="optionHoverVertical"
>
<div>
<div
v-for="(
item, index
) in infoTableData"
:key="index"
class="table-body-item"
>
<div
class="table-info-item"
style="
width: 20%;
text-align: left;
"
>
{{ item.eventTypeName }}
</div>
<div
class="table-info-item"
style="width: 30%"
>
{{ item.msgTitle }}
</div>
<div
class="table-info-item"
style="width: 30%"
>
{{ item.senderName }}
</div>
<div
class="table-info-item"
style="
width: 20%;
text-align: right;
"
:style="{
color:
item.status == 1
? 'green'
: 'red',
}"
>
{{
item.status == 1
? "已调度"
: "未调度"
}}
</div>
</div>
</div>
</vue-seamless-scroll>
</div>
</div>
</div>
</div>
<div class="page-bottom-bg"></div>
<div class="page-left-bg"></div>
<div class="page-right-bg"></div>
</template>
</gis-map>
<CommonModal v-model="pointState" title="设备详情" width="500">
<div
style="
width: 100%;
display: flex;
flex-direction: column;
padding: 10px;
"
>
<div
style="
display: flex;
align-items: center;
margin-bottom: 20px;
"
>
<span style="font-size: 20px"
>名称{{ currentPoint.name }}</span
>
</div>
<div
style="
display: flex;
align-items: center;
margin-bottom: 20px;
"
>
<span style="font-size: 20px"
>资源编码{{ currentPoint.resourceCode }}</span
>
</div>
<div
style="
display: flex;
align-items: center;
margin-bottom: 20px;
"
>
<span style="font-size: 20px"
>安装位置{{ currentPoint.location }}</span
>
</div>
<div
style="
display: flex;
align-items: center;
margin-bottom: 20px;
"
>
<span style="font-size: 20px"
>安装时间{{ currentPoint.installTime }}</span
>
</div>
<div
style="
display: flex;
align-items: center;
margin-bottom: 20px;
"
>
<span style="font-size: 20px"
>经度{{ currentPoint.longitude }}</span
>
</div>
<div
style="
display: flex;
align-items: center;
margin-bottom: 20px;
"
>
<span style="font-size: 20px"
>纬度{{ currentPoint.latitude }}</span
>
</div>
<div
style="
display: flex;
align-items: center;
margin-bottom: 20px;
"
>
<span style="font-size: 20px">在线状态</span>
<Tag :color="currentPoint.status == '1' ? 'green' : 'red'">{{ currentPoint.status == "1" ? "在线" : "离线" }}</Tag>
</div>
</div>
</CommonModal>
</div>
</template>
<script>
import { useIndex } from "../../utils/utilsDramAdmin";
import MainTitle from "./components/MainTitle.vue";
import gisMap from "./components/gisMapOfCounty.vue";
import vueSeamlessScroll from "vue-seamless-scroll";
import CountTo from "vue-count-to";
import * as echarts from "echarts";
import { echartsFocus, echartsClear } from "@/utils/autoEcharts";
import CommonModal from "@/views/common-components/common-modal.vue";
const EVENT_TYPE_MAP = {
水旱: "floodDroughtValue",
气象: "weatherValue",
地震: "earthquakeValue",
地质: "geologyValue",
卫生: "hygieneValue",
其他: "otherValue",
};
const WARNING_LEVELS = [
"红色预警",
"橙色预警",
"黄色预警",
"蓝色预警",
"未知预警",
];
export default {
name: "EmergencyScreen",
components: { MainTitle, gisMap, vueSeamlessScroll, CountTo, CommonModal },
data() {
return {
title: "应急广播数据中心",
pointList: [],
pointState: false,
currentPoint: {},
lngLat: [],
lineChartData: {},
statusItemList: [
{ title: "前端在线(个)", number: 0 },
{ title: "前端离线(个)", number: 0 },
{ title: "终端在线(个)", number: 0 },
{ title: "终端离线(个)", number: 0 },
],
statusTableData: [],
deviceList: [],
infoTableData: [],
optionHoverVertical: {
step: 0.3,
limitMoveNum: 2,
hoverStop: false,
direction: 1,
openWatch: true,
singleHeight: 0,
singleWidth: 0,
waitTime: 1000,
},
barChart: null,
lineChart: null,
};
},
mounted() {
const { calcRate, windowDraw } = useIndex(this.$refs.appRef);
calcRate();
windowDraw();
this.getInitTotalNum();
this.findStatus();
this.findSixMonthTypeLine();
this.findSixMonthLevelBar();
this.findTopFiveInfos();
},
beforeDestroy() {
echartsClear();
this.lineChart.distroy();
this.barChart.distroy();
},
methods: {
assembleData(data) {
let list = [];
if (data && data.length > 0) {
data.forEach((item) => {
if (item.children && item.children.length > 0) {
this.assembleData(item.children);
} else {
let dataItem = { ...item };
dataItem.lnglat = [item.longitude, item.latitude];
list.push(dataItem);
}
});
}
return list;
},
// 查询终端
getInitTotalNum() {
this.$http
.post(
"/broadcast/queryBroadcastDeviceStatus",
this.common.request({})
)
.then((res) => {
if (res.data.code == 200) {
this.deviceList = res.data.data;
this.pointList = this.assembleData(res.data.data);
}
});
},
// 资源状态
findStatus() {
this.$http
.post(
"/broadcast/queryBroadcastResourceStatus",
this.common.request({})
)
.then((res) => {
if (res.data.code === 200) {
this.statusTableData = res.data.data;
this.updateStatusItemList(res.data.data);
}
});
},
updateStatusItemList(data) {
const [
frontOnlineNum,
frontOfflineNum,
terminalOnlineNum,
terminalOfflineNum,
] = data.reduce(
(pre, cur) => {
pre[0] += Number(cur.frontOnlineNum);
pre[1] += Number(cur.frontOfflineNum);
pre[2] += Number(cur.terminalOnlineNum);
pre[3] += Number(cur.terminalOfflineNum);
return pre;
},
[0, 0, 0, 0]
);
this.statusItemList[0].number = frontOnlineNum;
this.statusItemList[1].number = frontOfflineNum;
this.statusItemList[2].number = terminalOnlineNum;
this.statusItemList[3].number = terminalOfflineNum;
},
// 近六月事件类型统计
findSixMonthTypeLine() {
this.$http
.post(
"/broadcast/queryBroadcastEventType",
this.common.request({})
)
.then((res) => {
if (res.data.code === 200) {
const lineChartData = this.processLineChartData(
res.data.data
);
this.initLineChart(lineChartData);
}
});
},
processLineChartData(data) {
return data.reduce(
(pre, cur) => {
pre.axis.push(cur.month);
pre.series = Object.keys(EVENT_TYPE_MAP).reduce(
(series, type) => {
series.push({
name: type,
value: data.map(
(item) => item[EVENT_TYPE_MAP[type]]
),
});
return series;
},
[]
);
return pre;
},
{ axis: [], series: [] }
);
},
// 近六月事件级别统计
findSixMonthLevelBar() {
this.$http
.post(
"/broadcast/queryBroadcastEventLevel",
this.common.request({})
)
.then((res) => {
if (res.data.code === 200) {
this.initBarChart(res.data.data);
}
});
},
// 最新应急消息
findTopFiveInfos() {
this.$http
.post(
"/broadcast/queryBroadcastAlarmMessage",
this.common.request({})
)
.then((res) => {
if (res.data.code === 200) {
this.infoTableData = res.data.data;
}
});
},
initLineChart(lineChartData) {
this.lineChart = echarts.init(document.getElementById("lineChart"));
const option = this.getLineChartOption(lineChartData);
this.lineChart.setOption(option);
echartsFocus(this.lineChart, lineChartData.axis.length);
},
getLineChartOption(lineChartData) {
const { axis, series } = lineChartData;
const legendData = series.map((item) => item.name);
const seriesData = series.map((item) => ({
name: item.name,
type: "line",
symbol: "circle",
showAllSymbol: true,
symbolSize: 0,
smooth: true,
lineStyle: { normal: { width: 2 } },
tooltip: { show: true },
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{ offset: 0, color: "rgba(233, 233, 233, .4)" },
{ offset: 1, color: "rgba(233, 233, 233, 0)" },
],
false
),
},
},
data: item.value,
}));
return {
tooltip: { trigger: "axis", axisPointer: { type: "line" } },
legend: {
orient: "horizontal",
top: "0%",
icon: "circle",
textStyle: { fontWeight: "normal", color: "#FFF" },
data: legendData,
},
grid: { top: "18%", left: "8%", right: "7%", bottom: "12%" },
xAxis: [
{
type: "category",
boundaryGap: false,
axisLine: {
show: true,
lineStyle: { color: "rgba(172, 215, 255, 0.24)" },
},
axisLabel: {
textStyle: { color: "#99A8BD", fontSize: 12 },
},
splitLine: { show: false },
axisTick: { show: false },
data: axis,
},
],
yAxis: [
{
min: 0,
splitLine: {
show: true,
lineStyle: {
type: "dashed",
color: "rgba(172, 215, 255, 0.24)",
},
},
axisLine: { show: false },
axisLabel: {
show: true,
textStyle: { color: "#99A8BD", fontSize: 12 },
},
axisTick: { show: false },
},
],
series: seriesData,
};
},
initBarChart(emergency) {
this.barChart = echarts.init(document.getElementById("barChart"));
const option = this.getBarChartOption(emergency);
this.barChart.setOption(option);
echartsFocus(this.barChart, emergency.length);
},
getBarChartOption(emergency) {
const zzx1 = emergency.map((item) => item.month);
const red = emergency.map((item) => item.redNum);
const orange = emergency.map((item) => item.orangeNum);
const yellow = emergency.map((item) => item.yellowNum);
const blue = emergency.map((item) => item.blueNum);
const unknownData = emergency.map((item) => item.unKnownNum);
const seriesData = WARNING_LEVELS.map((level, index) => ({
name: level,
type: "bar",
barWidth: "8",
itemStyle: {
normal: {
opacity: 0.7,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: this.getColorByLevel(index) },
{
offset: 1,
color: this.getColorByLevel(index, true),
},
]),
},
},
label: {
show: false,
position: ["18", "-18"],
color: "#00f8ff",
fontSize: 12,
},
data: this.getDataByLevel(
index,
red,
orange,
yellow,
blue,
unknownData
),
}));
return {
tooltip: { trigger: "axis", axisPointer: { type: "shadow" } },
grid: {
left: "2%",
right: "4%",
bottom: "4%",
top: "16%",
containLabel: true,
},
legend: {
data: WARNING_LEVELS,
top: 0,
textStyle: { color: "#99A8BD", fontSize: 12 },
itemWidth: 14,
itemHeight: 10,
},
xAxis: {
type: "category",
data: zzx1,
axisLine: { show: true, lineStyle: { color: "#233653" } },
axisLabel: {
textStyle: {
fontFamily: "Microsoft YaHei",
color: "#99A8BD",
fontWeight: "normal",
fontSize: 12,
},
},
},
yAxis: {
type: "value",
minInterval: 15,
axisLine: { show: false },
splitLine: {
show: true,
lineStyle: { color: "#192a44", type: "dashed" },
},
axisLabel: {
formatter: "{value}",
color: "#99A8BD",
fontSize: 12,
},
},
series: seriesData,
};
},
getColorByLevel(index, isEnd = false) {
const colors = [
["rgba(245,69,69, 1)", "rgba(245,69,69, 0.2)"],
["rgba(255,148,56, 1)", "rgba(255,148,56, 0.2)"],
["rgba(245,211,47, 1)", "rgba(245,211,47, 0.2)"],
["rgba(115,221,255, 1)", "rgba(115,221,255, 0.2)"],
["rgba(161,161,161, 1)", "rgba(161,161,161, 0.2)"],
];
return isEnd ? colors[index][1] : colors[index][0];
},
getDataByLevel(index, red, orange, yellow, blue, unknownData) {
const data = [red, orange, yellow, blue, unknownData];
return data[index];
},
currentPointFn(data) {
this.currentPoint = data;
this.pointState = true;
},
},
};
</script>
<style lang="less" scoped>
@import "../../styles/pbStyle.less";
/deep/.ivu-tree {
width: 340px;
height: 848px;
}
/deep/.ivu-tree-arrow {
color: #d6e2f2;
}
/deep/.ivu-tree-title {
font-family: "PingFang SC";
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0em;
text-align: left;
color: #ffffff;
}
.eb-container {
width: 1920px;
height: 1080px;
transform: scale(var(--scale)) translate(-50%, -50%);
transform-origin: 0 0;
position: absolute;
left: 50%;
top: 50%;
background-image: url("../../assets/largeScreen/page-bg.png");
background-size: 100%;
.gismap {
.left-box {
position: fixed;
top: 100px;
left: 50px;
width: 458px;
height: 920px;
display: flex;
flex-direction: column;
.left-one {
width: 100%;
height: 50%;
display: flex;
flex-direction: column;
.databox {
.content-pub-style(100%, calc(100% - 44px));
color: #ffffff;
display: flex;
flex-direction: column;
.line-title-box {
width: 100%;
height: 80px;
display: flex;
flex-direction: row;
.line-title-box-item {
width: 25%;
height: 100%;
padding: 10px;
font-family: shiShangHeiTi;
.line-item-box {
width: 100%;
height: 5px;
display: flex;
flex-direction: row;
.line-item-color {
width: 30%;
height: 100%;
border-radius: 5px;
}
.line-item-white {
width: 60%;
height: 100%;
border-radius: 5px;
background-color: rgba(1, 206, 255, 0.16);
margin-left: 5px;
}
}
.line-item-title {
width: 100%;
margin-top: 5px;
font-size: 14px;
line-height: 22px;
}
.line-item-number {
width: 100%;
font-size: 32px;
line-height: 40px;
text-shadow: 0px 3px 4px #ffffff;
}
}
}
.table-box {
width: 100%;
padding: 10px 10px 10px 15px;
font-family: shiShangHeiTi;
}
}
}
.left-two {
width: 100%;
height: 50%;
display: flex;
flex-direction: column;
.databox {
.content-pub-style(100%, calc(100% - 44px));
color: #ffffff;
font-family: shiShangHeiTi;
padding: 15px;
}
}
}
.table-title-box {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
.table-title-item {
text-align: center;
font-size: 12px;
line-height: 28px;
color: #c2c4c7;
letter-spacing: 2px;
}
}
.table-body-box {
width: 100%;
overflow: hidden;
.data-item-box {
width: 100%;
height: 40px;
display: flex;
justify-content: space-between;
flex-direction: row;
}
.data-name {
/*margin-left: 16px;*/
font-size: 14px;
line-height: 40px;
}
.data-status {
display: flex;
.point {
width: 6px;
height: 6px;
margin-top: 17px;
border-radius: 3px;
background-color: red;
}
.status {
margin-left: 10px;
font-size: 14px;
line-height: 40px;
}
}
.table-body-item {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
.table-info-item {
text-align: center;
font-size: 14px;
line-height: 36px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.right {
width: 458px;
// height: 922px;
position: fixed;
top: 100px;
right: 50px;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
.right-one {
width: 100%;
height: 300px;
display: flex;
flex-direction: column;
align-items: center;
.databox {
.content-pub-style(100%, calc(100% - 44px));
display: flex;
padding-top: 20px;
flex-direction: column;
align-items: center;
}
}
.right-two {
width: 100%;
height: 300px;
display: flex;
flex-direction: column;
align-items: center;
.databox {
.content-pub-style(100%, calc(100% - 44px));
display: flex;
padding-top: 20px;
flex-direction: column;
align-items: center;
}
}
.right-three {
width: 100%;
height: 320px;
display: flex;
flex-direction: column;
align-items: center;
.databox {
.content-pub-style(100%, calc(100% - 44px));
display: flex;
flex-direction: column;
align-items: center;
font-family: shiShangHeiTi;
color: #ffffff;
padding: 10px 15px;
}
}
// .right-three {
// margin-top: 10px;
// width: 100%;
// height: 300px;
// display: flex;
// flex-direction: column;
// justify-content: space-between;
// align-items: center;
// .min-title {
// width: 100%;
// height: 46px;
// background: url("../../assets/screenView/minTitle.png") no-repeat;
// background-size: 100% 100%;
// display: flex;
// align-items: center;
// span {
// font-family: "YouSheBiaoTiHei";
// font-size: 22px;
// font-weight: 400;
// line-height: 29px;
// letter-spacing: 0em;
// text-align: left;
// background: linear-gradient(to top, #5982B5, #ECF4FE);
// -webkit-background-clip: text;
// color: transparent;
// margin-left: 44px;
// }
// }
// .databox {
// flex: 1;
// width: 100%;
// margin-top: 10px;
// border: 1px solid #556b7c;
// background-color: rgba(29, 37, 47, 0.5);
// display: flex;
// padding-top: 20px;
// flex-direction: column;
// align-items: center;
// .titleBox {
// width: 100%;
// height: 40px;
// display: grid;
// background-color: #072e5b;
// grid-template-columns: repeat(4, 25%);
// grid-template-rows: 100%;
// span {
// display: flex;
// justify-content: center;
// align-items: center;
// font-size: 16px;
// color: #ffffff;
// }
// }
// .scrollBox {
// width: 100%;
// flex: 1;
// overflow: hidden;
// .contentList {
// width: 100%;
// display: flex;
// flex-direction: column;
// .content-item {
// width: 100%;
// height: 30px;
// display: grid;
// grid-template-columns: repeat(4, 25%);
// grid-template-rows: 100%;
// padding: 0 3px;
// span {
// text-align: center;
// line-height: 30px;
// color: #ffffff;
// font-size: 14px;
// display: inline-block;
// white-space: nowrap;
// overflow: hidden;
// text-overflow: ellipsis;
// }
// }
// }
// }
// }
// }
}
}
.bg {
width: 1920px;
height: 1080px;
display: flex;
flex-direction: column;
align-items: center;
pointer-events: none;
.title {
pointer-events: all;
}
}
}
</style>