添加看板

master
夜笙歌 3 months ago
parent e8aa03240f
commit aa76bf92fd

@ -0,0 +1,43 @@
import request from '@/utils/request'
export function realTimeData(query) {
return request({
url: '/ems/board/realTimeData', method: 'get', params: query
})
}
export function deviceOverview(query) {
return request({
url: '/ems/board/deviceOverview', method: 'get', params: query
})
}
export function monthConsumptionRanking(query) {
return request({
url: '/ems/board/monthConsumptionRanking', method: 'get', params: query
})
}
export function peaksValleysConsumption(query) {
return request({
url: '/ems/board/peaksValleysConsumption', method: 'get', params: query
})
}
export function fiveConsumptionStatistics(query) {
return request({
url: '/ems/board/fiveConsumptionStatistics', method: 'get', params: query
})
}
export function energyConsumptionStatistics(query) {
return request({
url: '/ems/board/energyConsumptionStatistics', method: 'get', params: query
})
}
export function realTimeAlarm(query) {
return request({
url: '/ems/board/realTimeAlarm', method: 'get', params: query
})
}

@ -6,7 +6,7 @@
import * as echarts from 'echarts';
export default {
expose: ['setData'],
expose: ['setData', 'getChart'],
data() {
return {
chart: null,
@ -29,6 +29,9 @@ export default {
this.chart = echarts.init(this.$el, 'macarons')
this.chart.setOption(option)
},
getChart() {
return this.chart
}
}
}

@ -9,12 +9,12 @@
<div class="subTitle" style="top: 71.2%;left: 78%">实时预警</div>
<div class="subTitle" style="top: 13.5%;left: 78%">近7天尖峰平谷统计</div>
<div class="sbzl">
<div class="num" style="top: 22%;left: 7%;">200</div>
<div class="num" style="top: 22%;left: 14.3%">200</div>
<div class="num" style="top: 22%;left: 21.7%">200</div>
<div class="num" style="top: 31%;left: 7%">200</div>
<div class="num" style="top: 31%;left: 14.3%">200</div>
<div class="num" style="top: 31%;left: 21.7%">200</div>
<div class="num" style="top: 22%;left: 7%;">{{ deviceOverviewData.deviceSum }}</div>
<div class="num" style="top: 22%;left: 14.3%">{{ deviceOverviewData.waterSum }}</div>
<div class="num" style="top: 22%;left: 21.7%">{{ deviceOverviewData.dnbSum }}</div>
<div class="num" style="top: 31%;left: 7%">{{ deviceOverviewData.airSum }}</div>
<div class="num" style="top: 31%;left: 14.3%">{{ deviceOverviewData.steamSum }}</div>
<div class="num" style="top: 31%;left: 21.7%">{{ deviceOverviewData.nitrogenSum }}</div>
<div class="text" style="top: 22.2%;left: 7%">网关总数</div>
<div class="text" style="top: 22.2%;left: 14.3%">水表总数</div>
<div class="text" style="top: 22.2%;left: 21.7%">电表总数</div>
@ -28,50 +28,648 @@
<div class="text" style="top: 30%;left: 38.5%;">用压缩空气量()</div>
<div class="text" style="top: 31.5%;left: 51.5%;">用蒸汽量()</div>
<div class="text" style="top: 30%;left: 65%;">用氮气量()</div>
<div class="num" style="top: 20.3%;left: 45%">6554</div>
<div class="num" style="top: 20.3%;left: 57%;">6554</div>
<div class="num" style="top: 31.8%;left: 38.5%">6554</div>
<div class="num" style="top: 33.3%;left: 51.5%">6554</div>
<div class="num" style="top: 31.8%;left: 65%">6554</div>
<div class="num" style="top: 20.3%;left: 45%">{{ fiveConsumptionStatisticsData.dnbSum }}</div>
<div class="num" style="top: 20.3%;left: 57%;">{{ fiveConsumptionStatisticsData.waterSum }}</div>
<div class="num" style="top: 31.8%;left: 38.5%">{{ fiveConsumptionStatisticsData.airSum }}</div>
<div class="num" style="top: 33.3%;left: 51.5%">{{ fiveConsumptionStatisticsData.steamSum }}</div>
<div class="num" style="top: 31.8%;left: 65%">{{ fiveConsumptionStatisticsData.nitrogenSum }}</div>
<div class="qs" style="top: 23.3%;left: 45%">
<span>{{ 1 === 1 ? '上升' : '下降' }}9.6%</span>
<div class="img"></div>
<span>{{
fiveConsumptionStatisticsData.dnbRate > 0 ? '上升' : '下降'
}}{{ fiveConsumptionStatisticsData.dnbRate }}%</span>
<div :class="fiveConsumptionStatisticsData.dnbRate > 0 ? 'img':'img1'"></div>
</div>
<div class="qs" style="top: 23.3%;left: 57%;">
<span>{{ 1 === 1 ? '上升' : '下降' }}9.6%</span>
<div class="img"></div>
<span>{{
fiveConsumptionStatisticsData.waterRate > 0 ? '上升' : '下降'
}}{{ fiveConsumptionStatisticsData.waterRate }}%</span>
<div :class="fiveConsumptionStatisticsData.waterRate > 0 ? 'img':'img1'"></div>
</div>
<div class="qs" style="top: 34.8%;left: 38.5%">
<span>{{ 1 === 1 ? '上升' : '下降' }}9.6%</span>
<div class="img"></div>
<span>{{
fiveConsumptionStatisticsData.airRate > 0 ? '上升' : '下降'
}}{{ fiveConsumptionStatisticsData.airRate }}%</span>
<div :class="fiveConsumptionStatisticsData.airRate > 0 ? 'img':'img1'"></div>
</div>
<div class="qs" style="top: 36.3%;left: 51.5%">
<span>{{ 1 === 1 ? '上升' : '下降' }}9.6%</span>
<div class="img"></div>
<span>{{
fiveConsumptionStatisticsData.steamRate > 0 ? '上升' : '下降'
}}{{ fiveConsumptionStatisticsData.steamRate }}%</span>
<div :class="fiveConsumptionStatisticsData.steamRate > 0 ? 'img':'img1'"></div>
</div>
<div class="qs" style="top: 34.8%;left: 65%">
<span>{{ 1 === 1 ? '上升' : '下降' }}9.6%</span>
<div class="img"></div>
<span>{{
fiveConsumptionStatisticsData.nitrogenRate > 0 ? '上升' : '下降'
}}{{ fiveConsumptionStatisticsData.nitrogenRate }}%</span>
<div :class="fiveConsumptionStatisticsData.nitrogenRate > 0 ? 'img':'img1'"></div>
</div>
</div>
<div class="table1">
<div class="h1">
<div class="scrollTable" style="font-weight: bold;width: calc(100% / 6);">
</div>
<div class="scrollTable" style="font-weight: bold;width: calc(100% / 6);">
A区
</div>
<div class="scrollTable" style="font-weight: bold;width: calc(100% / 6);">
A区
</div>
<div class="scrollTable" style="font-weight: bold;width: calc(100% / 6);">
A区
</div>
<div class="scrollTable" style="font-weight: bold;width: calc(100% / 6);">
A区
</div>
<div class="scrollTable" style="font-weight: bold;width: calc(100% / 6);">
合计
</div>
</div>
<vue-seamless-scroll
:class-option="{...chart1TableOption,limitMoveNum:10}"
:data="table1Data"
class="case-item"
style="height: 84%;overflow: hidden;"
>
<div
v-for="(item, index) in table1Data"
:key="index"
>
<div class="T1">
<div class="scrollTable" style="width: calc(100% / 6);">
{{ item.no }}
</div>
<div class="scrollTable" style="width: calc(100% / 6);">
{{ item.no }}
</div>
<div class="scrollTable" style="width: calc(100% / 6);">
{{ item.no }}
</div>
<div class="scrollTable" style="width: calc(100% / 6);">
{{ item.no }}
</div>
<div class="scrollTable" style="width: calc(100% / 6);">
{{ item.no }}
</div>
<div class="scrollTable" style="width: calc(100% / 6);">
{{ item.no }}
</div>
</div>
</div>
</vue-seamless-scroll>
</div>
<div class="table2">
<div class="h2">
<div class="scrollTable" style="font-weight: bold;width: calc(33% - 60px);">
区域
</div>
<div class="scrollTable" style="font-weight: bold;width: calc(66% - 120px);">
设备
</div>
<div class="scrollTable" style="font-weight: bold;width: calc(180px);">
报警时间
</div>
</div>
<vue-seamless-scroll
:class-option="{...chart1TableOption,limitMoveNum:10}"
:data="realTimeAlarmData"
:key="scrollKey2"
class="case-item"
style="height: 84%;overflow: hidden;"
>
<div
v-for="(item, index) in realTimeAlarmData"
:key="index"
>
<div class="T2">
<div class="scrollTable" style="width: calc(33% - 60px);font-size: 0.7vw">
{{ item.deviceName }}
</div>
<div class="scrollTable" style="width: calc(66% - 120px);font-size: 0.7vw">
<span style="color:red">{{ item.monitorName }}</span>:{{ item.alarmData }}
</div>
<div class="scrollTable" style="width: 180px; font-size: 0.7vw">
{{ item.collectTime }}
</div>
</div>
</div>
</vue-seamless-scroll>
</div>
<Chart class="jfpgtjBar" ref="jfpgtjBar"></Chart>
<Chart class="nhtjBar" ref="nhtjBar"></Chart>
<Chart class="jfpgtjPie" ref="jfpgtjPie"></Chart>
<Chart class="ydfxtjPie" ref="ydfxtjPie"></Chart>
<div class="jfpgtjInfo">
<div style="width: 100%"></div>
<div style="white-space: nowrap" v-for="(i,k) in peaksValleysConsumptionData">
<div class="market" :style="`background-color:${colors[k]}`"></div>
<div class="text">
{{ i[0] }}段用电
</div>
<div class="num">
<span :style="`color:${colors[k]};font-weight: bold;font-size:0.9vw`">
{{ i[2] }}
</span>%
</div>
</div>
<div style="width: 100%"></div>
</div>
<div class="ydph">
<template v-for="(i,k) in rankingData.splice(0,5)">
<div class="icon" :class="`icon${k+1}`">
<div class="img"></div>
</div>
<div class="ph" :class="`ph${k+1}`">
<div class="text">{{ i.workUnitName }}</div>
<div class="num">
<span style="color: #25B89A">{{ i.expend }}</span>
<span>kwh</span>
</div>
<div class="schedule">
<div class="schedule1" :style="`width:${i.expend}%`"></div>
</div>
</div>
</template>
</div>
</div>
</div>
</template>
<script>
import vueSeamlessScroll from "vue-seamless-scroll";
import Chart from "@/components/Charts/Chart.vue";
import * as echarts from 'echarts'
import {
deviceOverview, energyConsumptionStatistics,
fiveConsumptionStatistics,
monthConsumptionRanking,
peaksValleysConsumption, realTimeAlarm,
realTimeData
} from "@/api/board";
export default {
name: 'Board',
components: {
vueSeamlessScroll
vueSeamlessScroll,
Chart
},
mounted() {
let num = 900719925474099522349866432465796241098
console.log(num.toString())
data() {
return {
colors: ['#30A0FB', '#00FF89', '#F9E435', '#FB5C2D', '#73c0de'],
chart1TableOption: {
step: 0.5, //
limitMoveNum: 3, // this.dataList.length
hoverStop: true, // stop
direction: 1, // 0 1 2 3
openWatch: true, // dom
singleHeight: 0, // (0) direction => 0/1
singleWidth: 0, // (0) direction => 2/3
waitTime: 0,
autoPlay: false,
navigation: false
},
table1Data: [
{
no: 1
},
{
no: 1
},
],
scrollKey1: '',
scrollKey2: '',
deviceOverviewData: {},
rankingData: [],
statisticsData: [],
peaksValleysConsumptionData: [],
fiveConsumptionStatisticsData: {},
realTimeAlarmData: []
}
},
async mounted() {
deviceOverview().then(res => {
this.deviceOverviewData = res.data
})
monthConsumptionRanking({monitorType: 2}).then(res => {
this.rankingData = res.data.ranking
this.statisticsData = res.data.statistics
this.$refs.ydfxtjPie.setData({
graphic: [
{
type: 'text',
left: 'center',
top: 'center',
style: {
text: '能耗\n占比',
textAlign: 'center',
fill: '#4FB8FF', //
fontSize: 20,
fontWeight: 'bold',
lineHeight: 30
}
}
],
series: [
{
type: 'pie',
radius: ['55%', '70%'],
tooltip: {
show: false
},
label: {
formatter: params => {
return `${params.name}\n${params.value}`
},
color: '#fff'
},
labelLine: {
length: 10,
length2: 10,
},
data: res.data.statistics.map((e, k) => {
return {
value: e.expend,
name: e.workUnitName,
itemStyle: {
color: this.colors[k] + '99',
shadowColor: this.colors[k] + 'cc',
shadowBlur: 20,
shadowOffsetX: 0,
shadowOffsetY: 0
}
}
})
},
]
})
})
peaksValleysConsumption().then(async res => {
let oriData = res.data?.reportList?.reduce((acc, item) => {
if (!acc[item.priceType]) {
acc[item.priceType] = 0;
}
acc[item.priceType] += item.expend;
return acc;
}, {})
let all = Object.values(oriData).reduce((acc, item) => {
return acc + item
}, 0)
let chartData = Object.keys(oriData).map((e) => {
return [e, oriData[e], (oriData[e] / all * 100).toFixed(2)]
})
this.peaksValleysConsumptionData = chartData
let seriesData = []
chartData.forEach(e => {
seriesData.push({
value: all / 100 / chartData.length, name: '',
label: {
show: false
},
tooltip: {
show: false
},
itemStyle: {
color: '#0000'
}
})
seriesData.push({
value: e[1], name: e[0] + '段用电', percentage: e[2]
})
})
this.$refs.jfpgtjPie.setData({
tooltip: {
trigger: 'item',
formatter: (params) => {
return `${params.marker}${params.name}${params.data.percentage}%`
}
},
graphic: [
{
type: 'text',
left: 'center',
top: 'center',
style: {
text: '{num|1111}\n{unit|kwh}',
textAlign: 'center',
rich: {
num: {
fill: '#4FB8FF', //
fontSize: 20,
fontWeight: 'bold',
lineHeight: 30
},
unit: {
fill: '#4FB8FF', //
fontSize: 16,
fontWeight: 'bold'
}
},
}
}
],
series: [
{
name: 'pie1',
type: 'pie',
radius: ['62%', '70%'],
label: {
show: false
},
labelLine: {
show: false
},
tooltip: {
show: false
},
data: seriesData
},
{
name: 'pie2',
type: 'pie',
radius: ['45%', '62%'],
itemStyle: {
opacity: 0.6,
},
label: {
show: false
},
labelLine: {
show: false
},
data: seriesData
}
]
})
await this.$nextTick()
const chart = this.$refs.jfpgtjPie.getChart()
chart.on('mouseover', (params) => {
if (params.seriesType === 'pie') {
const other = params.seriesName === 'pie1' ? 'pie2' : 'pie1'
chart.dispatchAction({
type: 'highlight',
seriesName: other,
name: params.name
})
}
})
chart.on('mouseout', (params) => {
if (params.seriesType === 'pie') {
const other = params.seriesName === 'pie1' ? 'pie2' : 'pie1'
chart.dispatchAction({
type: 'downplay',
seriesName: other,
name: params.name
})
}
})
function groupByDate(data) {
const result = {};
const types = new Set();
data.forEach(item => {
if (!result[item.date]) {
result[item.date] = {date: item.date, data: {}};
}
result[item.date].data[item.priceType] = item.expend;
types.add(item.priceType);
});
return {
grouped: Object.values(result),
types: Array.from(types),
typeCount: types.size
};
}
let barData = groupByDate(res.data?.reportList)?.grouped
let types = groupByDate(res.data?.reportList)?.types
this.$refs.jfpgtjBar.setData({
tooltip: {
trigger: 'axis',
axisPointer: {type: 'shadow'}
},
legend: {
show: false
},
grid: {
left: '5%',
right: '5%',
bottom: '5%',
top: '10%',
containLabel: true
},
yAxis: {
type: 'value',
name: '单位(kwh)',
max: function (value) {
return value.max * 1.2;
},
nameTextStyle: {color: '#ccc'},
axisLabel: {color: '#ccc'},
axisLine: {
show: true
},
splitLine: {
show: true,
lineStyle: {
color: 'rgba(255, 255,255, 0.3)',
type: 'dashed'
}
}
},
xAxis: {
type: 'category',
data: barData.map(e => e.date),
axisLabel: {color: '#ccc'},
splitLine: {
show: true,
lineStyle: {
color: 'rgba(255, 255,255, 0.3)',
type: 'dashed'
}
}
},
series: types.map((e, k) => {
return {
name: e + '段用电',
type: 'bar',
stack: 'total',
label: {
show: true,
position: 'inside',
color: '#000'
},
barWidth: '30px',
data: barData.map(v => {
return v.data[e] || 0
}),
itemStyle: {color: this.colors[k]}
}
})
})
})
fiveConsumptionStatistics().then(res => {
this.fiveConsumptionStatisticsData = res.data
})
energyConsumptionStatistics({monitorType: 2, dateType: 'year'}).then(res => {
this.$refs.nhtjBar.setData({
tooltip: {
trigger: 'axis',
axisPointer: {type: 'shadow'}
},
legend: {
show: false
},
grid: {
left: '5%',
right: '5%',
bottom: '0',
top: '35',
containLabel: true
},
yAxis: {
type: 'value',
name: '单位(kwh)',
max: function (value) {
return value.max * 1.2;
},
nameTextStyle: {color: '#ccc'},
axisLabel: {color: '#ccc'},
axisLine: {
show: true
},
splitLine: {
show: true,
lineStyle: {
color: 'rgba(255, 255,255, 0.3)',
type: 'dashed'
}
}
},
xAxis: {
type: 'category',
data: res.data.reportList.map(v => v.date),
axisLabel: {color: '#ccc'},
splitLine: {
show: true,
lineStyle: {
color: 'rgba(255, 255,255, 0.3)',
type: 'dashed'
}
}
},
series: [
{
name: '红',
type: 'bar',
label: {
show: false,
},
barWidth: '30px',
data: res.data.reportList.map(v => v.expend),
itemStyle: {
color: new echarts.graphic.LinearGradient(
0, 0, 0, 1, //
[
{offset: 0, color: '#1FA7F4'}, //
{offset: 1, color: '#3351B4'} //
]
)
}
},
{
type: 'line',
label: {
show: false,
},
barWidth: '30px',
data: res.data.reportList.map(v => v.expend),
itemStyle: {color: '#fff'},
areaStyle: {
color: new echarts.graphic.LinearGradient(
0, 0, 0, 1, // x0,y0 x1,y1 ()
[
{offset: 0, color: 'rgba(255,255,255,0.53)'}, // #fff8
{offset: 1, color: 'rgba(255,255,255,0)'} // #fff0
]
)
}
},
]
})
})
realTimeAlarm().then(res => {
this.realTimeAlarmData = JSON.parse(JSON.stringify(res.data.alarmDataList))
this.scrollKey2 = new Date().getTime()
})
},
methods: {
}
}
</script>
<style scoped lang="less">
.scrollTable {
color: rgb(185, 186, 192);
margin: auto 0px;
padding: 4px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
display: inline-block;
width: 25%;
}
.table1 {
position: absolute;
top: 44%;
left: 28%;
width: 44%;
height: 21%;
overflow: hidden;
}
.h1 {
background-size: 100% 100%;
background-repeat: no-repeat;
background-image: url('~@/assets/board/T1bg.png')
}
.T1 {
background-size: 100% 100%;
background-repeat: no-repeat;
background-image: url('~@/assets/board/h1bg.png');
margin-top: 2px;
}
.table2 {
position: absolute;
top: 74%;
left: 75%;
width: 22%;
height: 21%;
overflow: hidden;
}
.h2 {
background-size: 100% 100%;
background-repeat: no-repeat;
background-image: url('~@/assets/board/T1-1bg.png')
}
.T2 {
background-size: 100% 100%;
background-repeat: no-repeat;
background-image: url('~@/assets/board/h1-1bg.png');
margin-top: 2px;
}
.bg {
width: 100%;
height: 100%;
@ -93,6 +691,7 @@ export default {
font-weight: 600;
letter-spacing: 0.5vw;
transform: translateX(-50%);
}
.subTitle {
@ -151,6 +750,218 @@ export default {
background-size: 100% 100%;
background-image: url('~@/assets/board/向上箭头.png');
}
.img1 {
display: inline-block;
vertical-align: middle;
width: 1vw;
height: 1vw;
background-repeat: no-repeat;
background-size: 100% 100%;
background-image: url('~@/assets/board/向下箭头.png');
}
}
}
.jfpgtjBar {
position: absolute;
top: 39%;
left: 75%;
width: 22%;
height: 27%;
}
.nhtjBar {
position: absolute;
top: 74%;
left: 27.5%;
width: 45%;
height: 20.5%;
}
.jfpgtjPie {
position: absolute;
top: 17%;
left: 75%;
width: 12.5%;
height: 20%;
}
.ydfxtjPie {
position: absolute;
top: 74%;
left: 3%;
width: 22%;
height: 20%;
}
.jfpgtjInfo {
position: absolute;
top: 17%;
left: 87.5%;
width: 8.5%;
height: 20%;
color: #fff;
display: flex;
flex-direction: column;
justify-content: space-between;
.market {
display: inline-block;
vertical-align: middle;
width: 0.8vw;
height: 0.8vw;
margin-right: 0.8vw;
background-color: #28A7FC;
border-radius: 50%;
}
.text {
display: inline-block;
font-size: 0.8vw;
width: calc(100% - 1.6vw - 2.5vw);
text-align: left;
}
.num {
display: inline-block;
font-size: 0.8vw;
width: 2.5vw;
text-align: left;
}
}
.icon {
position: absolute;
width: 2vw;
height: 2vw;
background-color: #fff1;
.img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 60%;
height: 60%;
background-repeat: no-repeat;
background-size: 100% 100%;
}
}
.icon1 {
top: 46%;
left: 4%;
.img {
background-image: url('~@/assets/board/jz1.png');
}
}
.icon2 {
top: 50%;
left: 4%;
.img {
background-image: url('~@/assets/board/jz2.png');
}
}
.icon3 {
top: 54%;
left: 4%;
.img {
background-image: url('~@/assets/board/jz3.png');
}
}
.icon4 {
top: 58%;
left: 4%;
.img {
background-image: url('~@/assets/board/jz4.png');
}
}
.icon5 {
top: 62%;
left: 4%;
.img {
background-image: url('~@/assets/board/jz5.png');
}
}
.ph {
position: absolute;
width: 19vw;
height: 2vw;
background: linear-gradient(to right, rgba(255, 255, 255, 0.067), rgba(255, 255, 255, 0));
.text {
position: absolute;
font-size: 0.8vw;
color: #fff;
top: 70%;
transform: translateY(-100%);
left: 1vw;
}
.num {
position: absolute;
font-size: 0.8vw;
text-align: right;
color: #fff;
top: 70%;
transform: translateY(-100%);
right: 1vw;
}
.schedule {
position: absolute;
top: 80%;
left: 1vw;
width: calc(100% - 2vw);
height: 3px;
background-color: #fff2;
.schedule1 {
position: absolute;
top: 0;
left: 0;
height: 100%;
background: linear-gradient(to right, #249CE3, #2FE1E0);
}
}
}
.ph1 {
top: 46%;
left: 6.5%;
}
.ph2 {
top: 50%;
left: 6.5%;
}
.ph3 {
top: 54%;
left: 6.5%;
}
.ph4 {
top: 58%;
left: 6.5%;
}
.ph5 {
top: 62%;
left: 6.5%;
}
</style>

@ -35,7 +35,7 @@ module.exports = {
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
target: `http://localhost:8010`,
target: `http://192.168.100.100:8010`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''

Loading…
Cancel
Save