map/ui/dasadmin/src/views/backend/WindBlower/index.vue
2024-12-20 17:16:57 +08:00

2004 lines
69 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="windBlower" ref="windBlower">
<el-row>
<el-col :md="24" :lg="6">
<div class="cardContentLeft">
<!--实时预览-->
<div class="overview">
<div class="cardLabel">
{{ '名称:' + route.query.name + '&nbsp;&nbsp;&nbsp;&nbsp;' + '型号:' + route.query.model }}
</div>
<div class="overviewDataSection" ref="listContainer">
<div class="overviewDataSectionItem">
<span class="realLeft">机组运行状态</span>
<span class="reafRight">{{ realTimeDataState }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">风速</span>
<span class="reafRight">{{ overviewData.iwindspeed }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">风向</span>
<span class="reafRight">{{ overviewData.iwinddirection }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">发电机转速</span>
<span class="reafRight">{{ overviewData.igenspeed }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">有功功率</span>
<span class="reafRight">{{ overviewData.igenpower }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">给定有功功率</span>
<span class="reafRight">{{ overviewData.iactivepowersetpointvalue }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">无功功率</span>
<span class="reafRight">{{ overviewData.ireactivepower }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">给定无功功率</span>
<span class="reafRight">{{ overviewData.ireactivepowersetpointvalue }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">限电原因</span>
<span class="reafRight">{{ overviewData.powerlimitsource }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">机舱位置</span>
<span class="reafRight">{{ overviewData.ivanedirection }}</span>
</div>
<div class="overviewDataSectionItem">
<span class="realLeft">日发电量</span>
<span class="reafRight">{{ overviewData.ikwhthisday }}</span>
</div>
</div>
<div class="overviewDataBtn">
<el-icon color="#0064AA" @click="() => (overviewSlotData.visible = true)"><DArrowRight /></el-icon>
</div>
</div>
<!--温度-->
<div class="temperatureList">
<div class="chartPart-item" @click="openTemperature">
<div class="chartParm" ref="temperatureChartRef1"></div>
</div>
<div class="chartPart-item" @click="openTemperature">
<div class="chartParm" ref="temperatureChartRef2"></div>
</div>
<div class="chartPart-item" @click="openTemperature">
<div class="chartParm" ref="temperatureChartRef3"></div>
</div>
</div>
</div>
</el-col>
<el-col :md="24" :lg="12">
<div class="cardContentCenter">
<!--风机控制-->
<div class="controlBackgroundImg" ref="backgroundImgRef">
<div class="switchWindBlower">
<el-tooltip :content="beforeAirBlower.name">
<el-icon color="#fff" size="30" @click="switchAirblower(0)"><ArrowLeftBold /></el-icon>
</el-tooltip>
<el-tooltip :content="afterAirBlower.name">
<el-icon color="#fff" size="30" @click="switchAirblower(1)"><ArrowRightBold /></el-icon>
</el-tooltip>
</div>
<div class="control-type">
<el-tag v-if="realTimeData.locked === 1" class="control-tag control-tag-left" type="primary">已锁定</el-tag>
<el-tag class="control-tag" type="primary">{{ realTimeDataState }}</el-tag>
</div>
<div class="btnLeft">
<el-button
@click="sendCommand('setTurbineFastStart')"
v-if="realTimeData.processedoperationmode !== 16"
class="control-btn"
type="primary"
>启动</el-button
>
<el-button @click="sendCommand('setTurbineStop')" v-else class="control-btn" type="primary">停机</el-button>
<el-button @click="sendCommand('setTurbineResetStatusCode')" class="control-btn" type="primary">复位</el-button>
<el-button @click="sendManualCommand(1)" v-if="realTimeData.locked !== 1" class="control-btn" type="primary"
>锁定</el-button
>
<el-button @click="sendManualCommand(0)" v-else class="control-btn" type="primary">解锁</el-button>
</div>
<el-tooltip :content="subSystem[0]">
<div @click="openSubSystem(0)" class="dot index-1"></div>
</el-tooltip>
<el-tooltip :content="subSystem[1]">
<div @click="openSubSystem(1)" class="dot index-2"></div>
</el-tooltip>
<el-tooltip :content="subSystem[2]">
<div @click="openSubSystem(2)" class="dot index-3"></div>
</el-tooltip>
<el-tooltip :content="subSystem[3]">
<div @click="openSubSystem(3)" class="dot index-4"></div>
</el-tooltip>
<el-tooltip :content="subSystem[4]">
<div @click="openSubSystem(4)" class="dot index-5"></div>
</el-tooltip>
<el-tooltip :content="subSystem[5]">
<div @click="openSubSystem(5)" class="dot index-6"></div>
</el-tooltip>
<el-tooltip :content="subSystem[6]">
<div @click="openSubSystem(6)" class="dot index-7"></div>
</el-tooltip>
<el-tooltip :content="subSystem[7]">
<div @click="openSubSystem(7)" class="dot index-8"></div>
</el-tooltip>
<el-tooltip :content="subSystem[8]">
<div @click="openSubSystem(8)" class="dot index-9"></div>
</el-tooltip>
<el-tooltip :content="subSystem[9]">
<div @click="openSubSystem(9)" class="dot index-10"></div>
</el-tooltip>
<el-tooltip :content="subSystem[10]">
<div @click="openSubSystem(10)" class="dot index-11"></div>
</el-tooltip>
<el-tooltip :content="subSystem[11]">
<div @click="openSubSystem(11)" class="dot index-12"></div>
</el-tooltip>
</div>
<!-- 功率趋势 -->
<div class="chartPart">
<div class="power">
<div class="chartBox">
<div class="power-chart" ref="powerChartRef"></div>
</div>
</div>
<div class="chartPart-item item_bar">
<div class="frequencyChart" ref="frequencyChartRef"></div>
</div>
</div>
</div>
</el-col>
<el-col :md="24" :lg="6" style="background: #f5f5f5">
<div class="cardContentRight">
<!--发电量概况-->
<div class="summarize">
<div class="cardLabel">发电量概况</div>
<div class="summarize-panel-list">
<div class="summarize-panel-row">
<div class="summarize-panel">
<div class="summarize-panel-pic">
<img src="~assets/dashboard/fdl1.png" alt="" />
</div>
<div class="summarize-panel-base">
<div>
<span class="content-number">{{ realTimeDataForSingle.ikwhthisday }}</span>
</div>
<div>{{ realTimeDataForSingleUnit.ikwhthisday }}</div>
<div>日发电量</div>
</div>
</div>
<div class="summarize-panel">
<div class="summarize-panel-pic">
<img src="~assets/dashboard/fdl2.png" alt="" />
</div>
<div class="summarize-panel-base">
<div>
<span class="content-number">{{ realTimeDataForSingle.monthprodenergy }}</span>
</div>
<div>{{ realTimeDataForSingleUnit.monthprodenergy }}</div>
<div>月发电量</div>
</div>
</div>
</div>
<div class="summarize-panel-row">
<div class="summarize-panel">
<div class="summarize-panel-pic">
<img src="~assets/dashboard/fdl3.png" alt="" />
</div>
<div class="summarize-panel-base">
<div>
<span class="content-number">{{ realTimeDataForSingle.yearprodenergy }}</span>
</div>
<div>{{ realTimeDataForSingleUnit.yearprodenergy }}</div>
<div>年发电量</div>
</div>
</div>
<div class="summarize-panel">
<div class="summarize-panel-pic">
<img src="~assets/dashboard/fdl4.png" alt="" />
</div>
<div class="summarize-panel-base">
<div>
<span class="content-number">{{ realTimeDataForSingle.ikwhoverall }}</span>
</div>
<div>{{ realTimeDataForSingleUnit.ikwhoverall }}</div>
<div>总发电量</div>
</div>
</div>
</div>
</div>
</div>
<!--实时告警-->
<div class="alarm" style="margin-bottom: 0">
<div class="cardLabel">实时告警</div>
<el-table
:data="tableData"
class="tablePart"
height="calc(100% - 18px)"
ref="myTable"
@mouseover.native="clearScroll"
@mouseleave.native="createScroll"
>
<el-table-column fixed prop="time" label="时间" />
<el-table-column prop="alertcontent" label="告警信息" />
</el-table>
</div>
</div>
</el-col>
</el-row>
<el-dialog v-model="visible" :title="subSystemName" width="1020" :before-close="handleClose">
<div class="dialogContent">
<el-scrollbar>
<div class="PitchPart">
<!-- <el-row > -->
<!-- <el-col :span="12"> -->
<div class="Pitchitem" v-for="item in subSystemDataList" :key="item.name">
<span class="PitchitemLeft">{{ item.name }}</span>
<span class="PitchitemRight">{{ item.value }}</span>
</div>
<!-- </el-col> -->
<!-- </el-row> -->
</div>
</el-scrollbar>
</div>
</el-dialog>
<el-dialog v-model="overviewSlotData.visible" title="实时预览">
<template #header>
<div class="overviewSlot">
<span style="font-size: 20px">实时预览</span>
<div class="radioForOverviewType">
<el-radio-group v-model="overviewSlotData.type">
<el-radio value="138">模拟量</el-radio>
<el-radio value="199">计算量</el-radio>
<el-radio value="140">状态量</el-radio>
</el-radio-group>
</div>
</div>
</template>
<Overview v-bind="overviewSlotData"></Overview>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { nextTick, onActivated, onMounted, reactive, ref, computed, watch, onBeforeMount, onUnmounted, VNode, VNodeRef } from 'vue'
import * as echarts from 'echarts'
import { useEventListener } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import { DArrowRight, ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue'
import { getRealValueListReq, getRealValueRangeReq } from '/@/api/backend/deviceModel/request'
import { getModelAttributeListReq } from '/@/api/backend/deviceModel/request'
import { getAirBlowerListReq } from '/@/api/backend/airBlower/request'
import { useRoute, useRouter } from 'vue-router'
import Overview from './overview.vue'
import { TableInstance } from 'element-plus'
import { dayjs, ElMessage, ElMessageBox } from 'element-plus'
import { getRealTimeState, getCutDecimalsValue, malFunctionKeys } from '/@/views/backend/equipment/airBlower/utils'
import { sendCommandReq, sendManualCommandReq } from '/@/api/backend/control/request'
import { getAlarmListReq } from '/@/api/backend/alarms/request'
import { queryfaultCodeDict } from '/@/api/backend/theoreticalpowerCurve/request'
import { useEnumStore } from '/@/stores/enums'
import { adminBaseRoutePath } from '/@/router/static/adminBase'
const enumStore = useEnumStore()
const route = useRoute()
const router = useRouter()
const { t } = useI18n()
const windBlower = ref()
const backgroundImgRef = ref()
const computedHeight = reactive({
powerHeight: '305px',
centerHeight: '1100px',
alarmHeight: '360px',
backgroundImgHeight: '435px',
})
const sizeChange = () => {
// const rect = windBlower.value?.getBoundingClientRect()
// if (!rect) return
// computedHeight.powerHeight = rect.height - 626 + 'px'
// computedHeight.alarmHeight = rect.height - 570 + 'px'
// computedHeight.centerHeight = rect.height - 20 + 'px'
const bgiRect = backgroundImgRef.value?.getBoundingClientRect()
console.log(bgiRect)
if (!bgiRect) return
computedHeight.backgroundImgHeight = bgiRect.height + 'px'
console.log(computedHeight.backgroundImgHeight)
}
let timer: any = null
let myTable = ref<TableInstance>()
const overviewData = reactive({
// processedoperationmode: '-',
iwindspeed: '-',
iwinddirection: '-',
igenspeed: '-',
igenpower: '-',
iactivepowersetpointvalue: '-',
ireactivepower: '-',
ireactivepowersetpointvalue: '-',
powerlimitsource: '-',
ivanedirection: '-',
ikwhthisday: '-',
})
const realTimeDataForSingle = ref<any>({
ikwhthisday: '-',
ikwhoverall: '-',
igenpower: '-',
igenspeed: '-',
ipitchangle: '',
iwindspeed: '-',
iwinddirection: '-',
monthprodenergy: '-',
yearprodenergy: '-',
})
const realTimeDataForSingleUnit = reactive({
ikwhthisday: 'kWh',
ikwhoverall: 'kWh',
igenpower: 'kW',
igenspeed: 'rpm',
ipitchangle: '°',
iwindspeed: 'm/s',
iwinddirection: '°',
monthprodenergy: 'kWh',
yearprodenergy: 'kWh',
})
const state: {
charts: { powerChart: any; temperatureChart1: any; temperatureChart2: any; temperatureChart3: any; frequencyChart: any }
remark: string
workingTimeFormat: string
pauseWork: boolean
} = reactive({
charts: {
powerChart: null,
temperatureChart1: null,
temperatureChart2: null,
temperatureChart3: null,
frequencyChart: null,
// trendChart: null,
},
remark: 'dashboard.Loading',
workingTimeFormat: '',
pauseWork: false,
})
const powerChartRef = ref<VNodeRef>()
const powerChartData: { time: any; values: any } = {
time: {},
values: {},
}
const initpowerChart = () => {
const powerChart = state.charts.powerChart ?? echarts.init(powerChartRef.value as unknown as HTMLElement)
const option = {
title: {
show: true,
text: '功率趋势',
textStyle: {
color: '#4E5969',
fontSize: 14,
fontFamily: 'PingFangSC-Semibold',
},
padding: 10,
},
grid: {
top: 70,
right: 23,
bottom: 10,
left: 18,
containLabel: true,
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'line',
},
show: true,
},
xAxis: {
type: 'category',
axisLine: {
show: true,
lineStyle: {
color: '#dadada',
width: 1,
type: 'solid',
},
},
axisLabel: {
//x轴文字的配置
show: true,
color: '#4E5969',
interval: 'auto',
//rotate: 45
},
splitLine: {
//分割线配置
show: false,
lineStyle: {
color: '#999999',
},
},
data: powerChartData.time.iTheoreticalPower,
},
yAxis: [
{
type: 'value',
name: '功率MW',
nameTextStyle: {
color: '#4E5969',
},
axisLine: {
show: false,
lineStyle: {
color: '#dadada',
width: 0,
type: 'solid',
},
},
axisLabel: {
//x轴文字的配置
show: true,
color: '#4E5969',
},
axisTick: { show: false },
splitLine: {
interval: 50,
lineStyle: {
type: 'dashed',
color: '#dadada',
},
},
},
{
type: 'value',
name: '风速m/s',
nameTextStyle: {
color: '#4E5969',
},
axisLine: {
show: false,
lineStyle: {
color: '#dadada',
width: 0,
type: 'solid',
},
},
axisLabel: {
//x轴文字的配置
show: true,
color: '#4E5969',
},
axisTick: { show: false },
splitLine: {
interval: 50,
lineStyle: {
type: 'dashed',
color: '#dadada',
},
},
},
],
legend: {
data: ['有功功率', '理论有功功率', '风速'],
textStyle: {
color: '#73767a',
},
top: 20,
},
series: [
{
name: '有功功率',
type: 'line',
barWidth: 20,
itemStyle: {
color: '#00A7EB',
barBorderRadius: 2,
},
smooth: 0.6,
symbol: 'none',
data: powerChartData.values?.iGenPower ?? [],
},
{
name: '理论有功功率',
type: 'line',
barWidth: 20,
itemStyle: {
color: '#62bd25',
barBorderRadius: 2,
},
smooth: 0.6,
symbol: 'none',
data: powerChartData.values?.iTheoreticalPower ?? [],
},
{
name: '风速',
type: 'line',
yAxisIndex: 1,
itemStyle: {
color: '#FF7E00',
barBorderRadius: 2,
},
smooth: 0.6,
symbol: 'none',
/*areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(255,126,0,0.8)'
}
])
},*/
data: powerChartData.values?.iWindSpeed ?? [],
},
],
}
powerChart.setOption(option)
state.charts.powerChart = powerChart
}
// const TrendDataForDay: {
// currentPeriod: {
// time: string[]
// value: number[]
// }
// samePeriod: {
// time: string[]
// value: number[]
// }
// } = {
// currentPeriod: {
// time: [],
// value: [],
// },
// samePeriod: {
// time: [],
// value: [],
// },
// }
// const TrendDataForMonth = [
// {
// currentPeriod: 26.3,
// samePeriod: 53.5,
// generationTime: '2024-10-01',
// },
// {
// currentPeriod: 36.3,
// samePeriod: 53.5,
// generationTime: '2024-10-02',
// },
// {
// currentPeriod: 46.3,
// samePeriod: 53.5,
// generationTime: '2024-10-03',
// },
// ]
// const trendChartRef = ref<VNodeRef>()
// const trendChartType = ref<'day' | 'month'>('day')
// const tabhandleClick = () => {
// state.charts.trendChart.clear()
// nextTick(() => {
// initTrendChart(trendChartType.value)
// })
// }
// const initTrendChart = (type: 'day' | 'month') => {
// const currentPeriod: number[] = type === 'day' ? TrendDataForDay.currentPeriod.value : TrendDataForMonth.map((item) => item.currentPeriod)
// const samePeriod: number[] = type === 'day' ? TrendDataForDay.samePeriod.value : TrendDataForMonth.map((item) => item.samePeriod)
// const xAxisdata: string[] = type === 'day' ? TrendDataForDay.currentPeriod.time : TrendDataForMonth.map((item) => item.generationTime)
// const trendChart = state.charts?.trendChart ?? echarts.init(trendChartRef.value as unknown as HTMLElement)
// const legend = type === 'day' ? ['当天', '环比'] : ['本月', '环比']
// const option = {
// grid: {
// top: 30,
// right: 10,
// bottom: 20,
// left: 70,
// borderColor: '#dadada',
// },
// tooltip: {
// trigger: 'axis',
// axisPointer: {
// type: 'shadow',
// },
// },
// xAxis: {
// type: 'category',
// axisLine: {
// show: true,
// lineStyle: {
// color: '#dadada',
// width: 1,
// type: 'solid',
// },
// },
// axisLabel: {
// //x轴文字的配置
// show: true,
// color: '#4E5969',
// },
// splitLine: {
// //分割线配置
// show: false,
// lineStyle: {
// color: '#999999',
// },
// },
// data: xAxisdata,
// },
// yAxis: [
// {
// type: 'value',
// name: 'kWh',
// nameTextStyle: {
// color: '#4E5969',
// },
// axisLine: {
// show: false,
// lineStyle: {
// color: '#dadada',
// width: 0,
// type: 'solid',
// },
// },
// axisLabel: {
// //x轴文字的配置
// show: true,
// color: '#4E5969',
// },
// axisTick: { show: false },
// splitLine: {
// interval: 50,
// lineStyle: {
// type: 'dashed',
// color: '#dadada',
// },
// },
// },
// ],
// legend: {
// data: legend,
// textStyle: {
// color: '#73767a',
// },
// },
// series: [
// {
// name: legend[0],
// data: currentPeriod,
// type: 'bar',
// smooth: true,
// itemStyle: {
// color: '#0277B3',
// },
// },
// {
// name: legend[1],
// data: samePeriod,
// type: 'bar',
// smooth: true,
// itemStyle: {
// color: '#00A096',
// },
// },
// ],
// }
// trendChart.setOption(option)
// state.charts.trendChart = trendChart
// }
const temperatureChartsData: { name: string; value: string | number }[] = [
{
name: '环境温度(°C)',
value: 0,
},
{
name: '舱内温度(°C)',
value: 0,
},
{
name: '塔基柜温度(°C)',
value: 0,
},
]
const temperatureChartRef1 = ref<VNodeRef>()
const temperatureChartRef2 = ref<VNodeRef>()
const temperatureChartRef3 = ref<VNodeRef>()
const initTemperatureChart = () => {
const temperatureChart1 = state.charts.temperatureChart1 ?? echarts.init(temperatureChartRef1.value as unknown as HTMLElement)
const temperatureChart2 = state.charts.temperatureChart2 ?? echarts.init(temperatureChartRef2.value as unknown as HTMLElement)
const temperatureChart3 = state.charts.temperatureChart3 ?? echarts.init(temperatureChartRef3.value as unknown as HTMLElement)
const options = []
for (let i = 0; i < temperatureChartsData.length; i++) {
const option = {
grid: {
top: 30,
right: 5,
bottom: 0,
left: -100,
containLabel: true,
},
xAxis: {
// type: 'category',
type: 'value',
show: false,
},
yAxis: {
// type: 'value',
type: 'category',
show: false,
},
series: [
{
name: temperatureChartsData[i].name,
data: [temperatureChartsData[i].value],
type: 'bar',
label: {
show: true,
align: 'left',
// verticalAlign:'top',
formatter: `{b|{a}} {a|{c}}`,
rich: {
a: {
color: '#333333',
fontSize: 22,
},
b: {
color: '#4E5969',
fontSize: 14,
},
},
width: 100,
height: 22,
position: 'insideLeft',
offset: [85, -22],
},
itemStyle: {
color: '#048bd2',
borderRadius: [0, 4, 4, 0],
},
barWidth: 20,
},
{
show: true,
type: 'bar',
barGap: '-100%',
barWidth: 20, // 统计条宽度
itemStyle: {
normal: {
color: '#048bd2',
opacity: 0.2,
borderRadius: 4,
},
},
z: 1,
data: [100],
},
],
aria: {
enabled: true,
decal: {
show: true,
decals: {
symbol: 'rect',
symbolSize: 3,
dashArrayX: 3,
rotation: 45,
color: 'rgba(0, 0, 0, 0.1)',
},
},
},
}
options.push(option)
}
temperatureChart1.setOption(options[0])
state.charts.temperatureChart1 = temperatureChart1
temperatureChart2.setOption(options[1])
state.charts.temperatureChart2 = temperatureChart2
temperatureChart3.setOption(options[2])
state.charts.temperatureChart3 = temperatureChart3
}
var frequencyData: {
name: string[]
value: number[]
} = {
name: [
'0~22.5',
'22.5~45',
'45~67.5',
'67.5~90',
'90~112.5',
'112.5~135',
'135~157.5',
'157.5~180',
'180~202.5',
'202.5~225',
'225~247.5',
'247.5~270',
'270~292.5',
'292.5~315',
'315~337.5',
'337.5~360',
],
value: [],
}
const frequencyChartRef = ref()
const initFrequencyChart = () => {
const frequencyChart = state.charts.frequencyChart ?? echarts.init(frequencyChartRef.value as unknown as HTMLElement)
// const seriesdata: any = frequencyData.map((item) => item.value)
const option = {
title: [
{
text: '风频图',
textStyle: {
color: '#4E5969',
fontSize: 14,
fontFamily: 'PingFangSC-Semibold',
},
padding: 10,
},
],
polar: {
radius: [0, '70%'],
},
radiusAxis: {
axisLine: {
show: true,
lineStyle: {
color: '#dadada',
},
},
axisLabel: {
show: true,
color: 'rgba(0,0,0,0.45)',
fontSize: 10,
},
axisTick: {
show: true,
lineStyle: {
color: '#dadada',
},
},
max: 100,
},
angleAxis: {
min: 0,
interval: 22.5,
startAngle: 90,
type: 'category',
axisLine: {
show: true,
lineStyle: {
color: '#dadada',
},
},
axisLabel: {
show: true,
color: 'rgba(0,0,0,0.45)',
fontSize: 10,
boundaryGap: false,
formatter: function (value: string) {
const reg = /.*(?=\~)/
return value.match(reg)![0]
},
},
axisTick: {
show: false,
lineStyle: {
color: '#dadada',
},
},
data: frequencyData.name,
//startAngle: 75
},
tooltip: {},
series: {
type: 'bar',
data: frequencyData.value,
coordinateSystem: 'polar',
itemStyle: {
color: '#0277B3',
},
// label: {
// show: true,
// position: 'middle',
// formatter: '{b}: {c}',
// },
},
animation: false,
}
frequencyChart.setOption(option)
state.charts.frequencyChart = frequencyChart
}
const echartsResize = () => {
nextTick(() => {
const chartKeys = Object.keys(state.charts) as Array<keyof typeof state.charts>
chartKeys.forEach((key) => {
state.charts[key].resize()
})
})
}
onActivated(() => {
echartsResize()
})
const tableData = ref<{ time: string; alertcontent: string }[]>([])
const clearScroll = () => {
clearInterval(timer)
timer = null
}
const createScroll = () => {
clearScroll()
const table = myTable.value!.layout.table.refs
const tableWrapper = table.bodyWrapper.firstElementChild!.firstElementChild
timer = setInterval(() => {
tableWrapper!.scrollTop += 1
if (tableWrapper!.clientHeight + tableWrapper!.scrollTop == tableWrapper!.scrollHeight) {
tableWrapper!.scrollTop = 0
}
}, 30)
}
const realTimeData = ref<any>({
processedoperationmode: 1111,
locked: 0,
})
const realTimeDataState = computed(() => {
switch (realTimeData.value.processedoperationmode) {
case 20:
return '并网'
case 10:
return '维护'
case 8:
return '限功率运行'
case 0:
return '离线'
case 16:
return '启动'
case 6:
return '正常停机'
case 1:
return '紧急停机'
case 2:
return '停机'
case 11:
return '待机'
case 33:
return '通讯中断'
case 1110:
return '解缆状态'
case 1111:
return '电网故障停机'
case 1112:
return '安全链停机'
}
})
const getModelList = () => {
return new Promise((resolve) => {
getModelAttributeListReq({ iotModelId: route.query.iotModelId as string }).then((res) => {
resolve(res.rows)
})
})
}
const getRealTimeData = () => {
return new Promise((resolve) => {
getRealValueListReq([{ deviceId: route.query.irn as string }]).then((res) => {
resolve(res.data?.[route.query.irn as string])
})
})
}
const createRealTimeData = async () => {
try {
const modelList: any = await getModelList()
const realData: any = await getRealTimeData()
realTimeData.value.processedoperationmode = getRealTimeState(realData)
realTimeData.value.locked = realData.locked
temperatureChartsData[0].value = getCutDecimalsValue(realData.itempoutdoor_1sec)
temperatureChartsData[1].value = getCutDecimalsValue(realData.itempnacelle_1sec)
temperatureChartsData[2].value = getCutDecimalsValue(realData.itempcabinettowerbase_1sec)
const overviewDatakeys: any = Object.keys(overviewData)
const sigleDataKeys: any = Object.keys(realTimeDataForSingle.value)
const dataFor138And139: { name: string; value: string }[] = []
const dataFor140: { name: string; value: string }[] = []
const dataFor199: { name: string; value: string }[] = []
const realDataForSub: any = subSystem.map(() => ({ type138: [], type140: [], type199: [] }))
modelList.forEach((item: any) => {
const realVal = realData[item.attributeCode.toLowerCase()]
let val = getCutDecimalsValue(realVal)
if (enumStore.keys.includes(item.attributeCode)) {
val = enumStore.data[item.attributeCode][val]
}
if (malFunctionKeys.includes(item.attributeCode)) {
val = malFunctionEnums?.[val] ?? val
}
if (sigleDataKeys.includes(item.attributeCode.toLowerCase())) {
realTimeDataForSingle.value[item.attributeCode.toLowerCase()] = val === '-' ? val : val
realTimeDataForSingleUnit[item.attributeCode.toLowerCase() as keyof typeof realTimeDataForSingleUnit] = item.unit
}
if (overviewDatakeys.includes(item.attributeCode.toLowerCase())) {
if (enumStore.keys.includes(item.attributeCode)) {
overviewData[item.attributeCode.toLowerCase() as keyof typeof overviewData] = val as string
} else {
overviewData[item.attributeCode.toLowerCase() as keyof typeof overviewData] = val === '-' ? val : val + item.unit
}
}
const showData = {
name: item.attributeName,
value: val === '-' ? val : val + (item?.unit ?? ''),
}
if (item.attributeType === 138 || item.attributeType === 139) {
dataFor138And139.push(showData)
} else if (item.attributeType === 140) {
dataFor140.push(showData)
} else if (item.attributeType === 199) {
dataFor199.push(showData)
}
if (subSystem.includes(item.subSystem)) {
const index = subSystem.indexOf(item.subSystem)
if (index >= 0) {
if (item.attributeType === 138 || item.attributeType === 139) {
realDataForSub[index].type138.push(showData)
} else if (item.attributeType === 140) {
const copyData = {
name: item.attributeCode + ' ' + item.attributeName,
value: showData.value,
}
realDataForSub[index].type140.push(copyData)
} else if (item.attributeType === 199) {
realDataForSub[index].type199.push(showData)
}
}
}
})
overviewSlotData.type138 = dataFor138And139
overviewSlotData.type140 = dataFor140
overviewSlotData.type199 = dataFor199
const realDataForSubSystem = realDataForSub.map((item: any) => {
return [...item.type138, ...item.type140, ...item.type199]
})
realTimeForSubSystem.value = realDataForSubSystem
} catch (err) {
console.log(err)
}
}
const subSystemName = ref('')
const visible = ref(false)
const handleClose = (done: () => void) => {
visible.value = false
}
const realTimeForSubSystem = ref<any>([])
const curSubSystem = ref(0)
const subSystemDataList = computed(() => {
return realTimeForSubSystem.value[curSubSystem.value]
})
const subSystem = ['变桨1', '变桨2', '变桨3', '传动链齿轮箱', '发电机', '机舱', '控制系统', '轮毂', '偏航系统', '气象', '塔基', '箱变']
const openSubSystem = (type: number) => {
subSystemName.value = subSystem[type]
curSubSystem.value = type
visible.value = true
}
const overviewSlotData = reactive<{ visible: boolean; type: '138' | '140' | '199'; type138: any[]; type140: any[]; type199: any[] }>({
visible: false,
type: '138',
type138: [],
type140: [],
type199: [],
})
let autoUpdateForSecondTimer: any = null
let autoUpdateTimerForMinuteTimer: any = null
let autoUpdateTimerForHourTimer: any = null
const autoUpdate = () => {
createRealTimeData()
nextTick(() => {
initTemperatureChart()
})
if (!autoUpdateForSecondTimer) {
autoUpdateForSecondTimer = setInterval(() => {
createRealTimeData()
initTemperatureChart()
}, 2000)
}
if (!autoUpdateTimerForMinuteTimer) {
autoUpdateTimerForMinuteTimer = setInterval(() => {
getAllChartData(['power', 'frequency'])
}, 60000)
}
// if (!autoUpdateTimerForHourTimer) {
// autoUpdateTimerForHourTimer = setInterval(() => {
// getAllChartData(['trend'])
// }, 60000 * 30)
// }
}
const getChartData = <T extends string = any>(params: {
startTime: number
endTime: number
attr: T[]
interval: string
}): Promise<{ times: { [K in T]: string[] }; val: { [K in T]: number[] } }> => {
return new Promise((resolve) => {
const data = {
startTime: params.startTime,
endTime: params.endTime,
devices: [
{
deviceId: route.query.irn as string,
attributes: params.attr,
},
],
interval: params.interval,
}
getRealValueRangeReq(data).then((res) => {
if (res.success) {
const data = res.data[route.query.irn as string]
if (!data) return
const rangeKeys = Object.keys(data)
const times: any = {}
const val: any = {}
rangeKeys.forEach((key) => {
times[key] = []
val[key] = []
data[key].times.forEach((item: number) => {
times[key].push(dayjs(item).format('HH:mm'))
})
data[key].values.forEach((item: number) => {
val[key].push(item)
})
})
resolve({ times, val })
}
})
})
}
const getThisDayChartData = () => {
return new Promise((resolve) => {
getChartData<'iGenPower' | 'iTheoreticalPower' | 'iWindSpeed'>({
startTime: new Date(new Date().toLocaleDateString()).getTime(),
endTime: Date.now(),
attr: ['iGenPower', 'iTheoreticalPower', 'iWindSpeed'],
interval: '5m',
}).then(({ times, val }) => {
powerChartData.time = { iGenPower: times.iGenPower, iTheoreticalPower: times.iTheoreticalPower, iWindSpeed: times.iWindSpeed }
powerChartData.values = { iGenPower: val.iGenPower, iTheoreticalPower: val.iTheoreticalPower, iWindSpeed: val.iWindSpeed }
resolve(true)
})
})
}
// const getLastMonthThisDayChartData = () => {
// return new Promise((resolve) => {
// const start = dayjs().subtract(1, 'month').startOf('day').valueOf()
// const end = dayjs().subtract(1, 'month').valueOf()
// getChartData<'iKWhThisDay'>({
// startTime: start,
// endTime: end,
// attr: ['iKWhThisDay'],
// interval: '1h',
// }).then(({ times, val }) => {
// TrendDataForDay.samePeriod.time = times.iKWhThisDay
// TrendDataForDay.samePeriod.value = val.iKWhThisDay
// resolve(true)
// })
// })
// }
// const getThisDayChartForHourData = () => {
// return new Promise((resolve) => {
// getChartData<'iKWhThisDay'>({
// startTime: new Date(new Date().toLocaleDateString()).getTime(),
// endTime: Date.now(),
// attr: ['iKWhThisDay'],
// interval: '1h',
// }).then(({ times, val }) => {
// TrendDataForDay.currentPeriod.time = times.iKWhThisDay
// TrendDataForDay.currentPeriod.value = val.iKWhThisDay
// resolve(true)
// })
// })
// }
const getThisDayChartDataForMinute = () => {
return new Promise((resolve) => {
getChartData<'iWindDirection'>({
startTime: new Date(new Date().toLocaleDateString()).getTime(),
endTime: Date.now(),
attr: ['iWindDirection'],
interval: '1m',
}).then(({ val }) => {
const len = val.iWindDirection.length
const result: number[] = new Array(16).fill(0)
val.iWindDirection.forEach((item: number) => {
item = item < 0 ? 360 + item : item > 360 ? 360 : item
const divisor = Math.ceil(item / 22.5) - 1
result[divisor] += 1
})
const percent = result.map((item) => Math.floor((item / len) * 100000) / 1000)
frequencyData.value = percent
resolve(true)
})
})
}
const getAllChartData = (type: ('power' | 'trend' | 'frequency')[] = ['power', 'trend', 'frequency']) => {
if (type.includes('power')) {
getThisDayChartData().then(() => {
nextTick(() => {
initpowerChart()
})
})
}
// if (type.includes('trend')) {
// const trendDataForLastYear = getLastMonthThisDayChartData()
// const trendDataForThisDay = getThisDayChartForHourData()
// Promise.all([trendDataForLastYear, trendDataForThisDay]).then(() => {
// initTrendChart(trendChartType.value)
// })
// }
if (type.includes('frequency')) {
getThisDayChartDataForMinute().then((res) => {
nextTick(() => {
initFrequencyChart()
})
})
}
}
const sendCommand = (type: 'setTurbineFastStart' | 'setTurbineStop' | 'setTurbineResetStatusCode') => {
const sendTypeEnum = {
setTurbineFastStart: '风机快速启动指令',
setTurbineStop: '风机停机指令',
setTurbineResetStatusCode: '风机复位故障代码指令',
}
ElMessageBox.confirm('确认发送' + sendTypeEnum[type] + '吗?', '', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
const serviceName = sendTypeEnum[type]
const optDesc = serviceName + ',设定值为1'
sendCommandReq({
deviceId: route.query.irn as string,
serviceCode: type,
serviceName,
optDesc,
opValue: 1,
}).then((res) => {
if (res.code == 200) {
ElMessage.success('指令发送成功')
} else {
ElMessage.error('指令发送失败')
}
})
})
}
const sendManualCommand = (type: 1 | 0) => {
const serviceName = type === 0 ? '风机解锁' : '风机锁定'
ElMessageBox.confirm('确认' + serviceName + '吗?', '', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
sendManualCommandReq({
deviceId: route.query.irn as string,
serviceCode: 'Locked',
serviceName,
optDesc: serviceName + ',设定值为:' + type,
opValue: type,
}).then((res) => {
if (res.code == 200) {
ElMessage.success('指令发送成功')
} else {
ElMessage.error('指令发送失败')
}
})
})
}
const getAlarmList = () => {
const start = dayjs().startOf('day').toDate().getTime()
const end = dayjs().endOf('day').toDate().getTime()
return getAlarmListReq({
startTime: start,
endTime: end,
deviceCode: [route.query.deviceCode],
}).then((res) => {
if (res.rows) {
tableData.value = res.rows.map((item: any) => {
return {
time: dayjs(item.eventTime).format('YYYY-MM-DD HH:mm:ss'),
alertcontent: item.eventText,
}
})
}
})
}
let malFunctionEnums: any = {}
const getMalfunctionEnums = () => {
const curWindBlower = airBlowerList.value.find((item) => item.irn === route.query.irn)
queryfaultCodeDict({ madeinfactory: curWindBlower!.madeinfactory, model: curWindBlower!.model }).then((res) => {
if (res.code == 200) {
const data: any = {}
res.data.forEach((item: any) => {
data[item.code] = item.description
})
malFunctionEnums = data
}
})
}
const airBlowerList = ref<
{
irn: string
model: string
name: string
deviceCode: string
iotModelId: string
madeinfactory: string
}[]
>([])
const getAirBlowerList = () => {
return getAirBlowerListReq().then((res) => {
if (res.success) {
airBlowerList.value = res.data.map((item) => {
return {
irn: item.irn,
model: item.model,
name: item.name,
deviceCode: item.deviceCode,
iotModelId: item.modelId,
madeinfactory: item.madeinfactory,
}
})
return
}
})
}
const beforeAirBlower = computed(() => {
const len = airBlowerList.value.length
if (len === 0) return { irn: '', model: '', name: '', deviceCode: '', iotModelId: '' }
const curIndex = airBlowerList.value.findIndex((item) => item.irn === route.query.irn)
if (curIndex === 0) return airBlowerList.value[len - 1]
return airBlowerList.value[curIndex - 1]
})
const afterAirBlower = computed(() => {
const len = airBlowerList.value.length
if (len === 0) return { irn: '', model: '', name: '', deviceCode: '', iotModelId: '' }
const curIndex = airBlowerList.value.findIndex((item) => item.irn === route.query.irn)
if (curIndex === len - 1) return airBlowerList.value[0]
return airBlowerList.value[curIndex + 1]
})
const switchAirBlowerQuery = ref()
const switchAirblower = (type: 0 | 1) => {
const data = type === 0 ? beforeAirBlower.value : afterAirBlower.value
const query = {
irn: data.irn,
iotModelId: data.iotModelId,
deviceCode: data.deviceCode,
model: data.model,
name: data.name,
}
switchAirBlowerQuery.value = query
router.push({
name: 'windTurbine',
query,
})
}
onMounted(() => {
window.addEventListener('resize', sizeChange)
sizeChange()
getAllChartData()
useEventListener(window, 'resize', echartsResize)
autoUpdate()
getAlarmList().then(() => {
createScroll()
})
getAirBlowerList().then(() => {
getMalfunctionEnums()
})
switchAirBlowerQuery.value = route.query
})
onUnmounted(() => {
window.removeEventListener('resize', sizeChange)
autoUpdateForSecondTimer && clearInterval(autoUpdateForSecondTimer)
autoUpdateTimerForHourTimer && clearInterval(autoUpdateTimerForHourTimer)
autoUpdateTimerForMinuteTimer && clearInterval(autoUpdateTimerForMinuteTimer)
const chartKeys = Object.keys(state.charts) as Array<keyof typeof state.charts>
chartKeys.forEach((key) => {
state.charts[key] && state.charts[key].dispose()
state.charts[key] = null
})
})
watch(
() => route.query.irn,
() => {
autoUpdateForSecondTimer && clearInterval(autoUpdateForSecondTimer)
autoUpdateForSecondTimer = null
autoUpdateTimerForHourTimer && clearInterval(autoUpdateTimerForHourTimer)
autoUpdateTimerForHourTimer = null
autoUpdateTimerForMinuteTimer && clearInterval(autoUpdateTimerForMinuteTimer)
autoUpdateTimerForMinuteTimer = null
const chartKeys = Object.keys(state.charts) as Array<keyof typeof state.charts>
chartKeys.forEach((key) => {
state.charts[key] && state.charts[key].dispose()
state.charts[key] = null
})
nextTick(() => {
autoUpdate()
getAlarmList()
getAllChartData()
})
}
)
const openTemperature = (row: any) => {
if (!router.hasRoute('windBlowerTemperature')) {
router.addRoute('admin', {
path: adminBaseRoutePath + '/windBlowerTemperature',
name: 'windBlowerTemperature',
component: () => import('/@/views/backend/WindBlower/temperature.vue'),
meta: {
title: 'windBlowerTemperature',
menuDesc: '温度管理',
addtab: true,
},
})
}
router.push({
name: 'windBlowerTemperature',
query: {
...switchAirBlowerQuery.value,
},
})
}
</script>
<style scoped lang="scss">
$marginNum: 10px;
$labelHeight: 24px;
@mixin cardDefaultStyle {
padding: 10px;
border-radius: 10px;
background-color: #fff;
}
@mixin cardlabel {
.cardLabel {
width: 100%;
height: $labelHeight;
font-size: 18px;
line-height: 18px;
font-weight: 600;
color: #4e5969;
}
}
.radioItem {
margin-top: -43px;
float: right;
}
.dialogContent {
width: 100%;
height: 500px;
// overflow-y: auto;
// overflow-x: hidden;
.PitchPart {
display: flex;
flex-wrap: wrap;
width: 100%;
.Pitchitem {
border: 1px solid #e1edf6;
display: flex;
justify-content: space-between;
width: 50%;
.PitchitemLeft {
display: flex;
align-items: center;
width: 325px;
min-height: 50px;
background: #f7f9fc;
padding-left: 20px;
font-size: 14px;
color: #4e5969;
letter-spacing: 0;
font-weight: 500;
word-break: break-all;
}
.PitchitemRight {
/*width: 180px;*/
height: 25px;
line-height: 50px;
padding-right: 20px;
font-size: 14px;
color: #4e5969;
letter-spacing: 0;
font-weight: 500;
}
}
}
}
.dialog-footer {
text-align: center;
.el-button {
width: 120px;
height: 40px;
}
}
.overviewSlot {
display: flex;
.radioForOverviewType {
margin-left: auto;
}
}
.windBlower {
width: 100%;
height: 100%;
background-color: #f2f3f5;
// overflow: hidden;
.el-row {
width: 100%;
height: 100%;
.el-col {
width: 100%;
height: 100%;
}
}
.cardContentLeft {
margin: 0 5px;
width: calc(100% - 10px);
height: 100%;
.overview {
width: 100%;
height: 60%;
min-height: 450px;
@include cardDefaultStyle;
@include cardlabel;
.overviewDataSection {
width: 100%;
height: calc(100% - 42px);
.overviewDataSectionItem {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
padding: 0 10px;
width: 100%;
height: calc(9% - 5px);
min-height: 20px;
font-size: 14px;
color: #333333;
background: #f0f6ff;
border-radius: 4px;
}
}
.overviewDataBtn {
display: flex;
justify-content: center;
align-items: end;
width: 100%;
height: 18px;
font-size: 18px;
.el-icon {
transform: rotate(90deg);
}
:hover {
cursor: pointer;
}
}
}
.temperatureList {
@include cardDefaultStyle;
@include cardlabel;
display: flex;
flex-direction: column;
justify-content: space-around;
margin: 10px 0;
width: 100%;
height: calc(40% - 20px);
.chartPart-item {
// margin: 5px 0;
width: 100%;
height: calc(33.3% - 10px);
background: #f0f6ff;
border-radius: 8px;
// text-align: center;
color: #4e5969;
.chartParm {
width: 100%;
height: 100%;
}
.frequencyChart {
width: 100%;
height: 100%;
}
}
}
// .power {
// @include cardDefaultStyle;
// @include cardlabel;
// margin: 10px 0;
// width: 100%;
// height: calc(40% - 20px);
// // min-height: 285px;
// // height: v-bind('computedHeight.powerHeight');
// .chartBox {
// width: 100%;
// height: calc(100% - $labelHeight);
// .power-chart {
// width: 100%;
// height: 100%;
// }
// }
// }
}
.cardContentCenter {
@include cardDefaultStyle;
// display: flex;
// flex-direction: column;
// justify-content: space-around;
margin: 0 5px 10px 5px;
width: calc(100% - 10px);
height: calc(100% - 10px);
// min-height: 900px;
.controlBackgroundImg {
position: relative;
width: 100%;
aspect-ratio: 43 / 24;
background: url('/@/assets/WindBlower/bg.png') no-repeat;
background-size: contain;
&:hover {
.switchWindBlower {
opacity: 1;
}
}
.switchWindBlower {
position: absolute;
left: 50%;
top: 5%;
transform: translateX(-50%);
width: 100px;
height: 40px;
// background-color: red;
opacity: 0;
display: flex;
justify-content: space-between;
transition: opacity 0.4s;
.el-icon:hover {
cursor: pointer;
color: rgba(255, 255, 255, 0.7);
}
}
.control-type {
width: 100%;
display: flex;
.control-tag {
background: #0064aa;
border-radius: 0 8px 0 0;
min-width: 80px;
height: 40px;
color: #ffffff;
font-size: 20px;
border: none;
margin-left: auto;
}
.control-tag-left {
margin-left: 0;
border-radius: 8px 0 0 0;
background-color: #ff4949;
}
}
.btnLeft {
display: flex;
flex-direction: column;
align-items: flex-end;
position: absolute;
bottom: 0;
.el-button {
width: 80px;
height: 40px;
// background: rgb(0,100,170);
border-radius: 6px;
color: #ffffff;
border: none;
margin-bottom: 10px;
}
}
.dot {
position: absolute;
width: 15px;
height: 15px;
background-color: rgba(255, 255, 255, 0);
transition: all 0.5s ease;
background-position: center center;
background-size: contain;
background-repeat: no-repeat;
&:hover {
transform: scale(1.2);
cursor: pointer;
}
&:nth-of-type(1) {
top: 25%;
left: 50%;
}
}
.index-1 {
left: 24%;
top: 1%;
width: 12%;
height: 48%;
&:hover {
transform: scale(1.5);
background-image: url('/@/assets/WindBlower/9.png');
}
}
.index-2 {
left: 30%;
top: 83%;
width: 15%;
height: 16%;
&:hover {
transform: scale(1.8);
background-image: url('/@/assets/WindBlower/3.png');
}
}
.index-3 {
left: 12.5%;
top: 69%;
width: 4%;
height: 25%;
&:hover {
transform: scale(3);
background-image: url('/@/assets/WindBlower/4.png');
}
}
.index-4 {
left: 46%;
top: 49%;
width: 12%;
height: 15%;
&:hover {
transform: scale(1.8);
background-image: url('/@/assets/WindBlower/1.png');
}
}
.index-5 {
left: 66%;
top: 35%;
width: 8%;
height: 14%;
// transform: rotate(-51deg);
&:hover {
transform: scale(1.8);
background-image: url('/@/assets/WindBlower/7.png');
}
}
.index-6 {
left: 60%;
top: 63%;
width: 27%;
height: 5%;
transform: rotate(-15deg);
&:hover {
transform: scale(5);
background-image: url('/@/assets/WindBlower/5.png');
}
}
.index-7 {
left: 73%;
top: 48%;
width: 7%;
height: 11%;
transform: rotate(-13deg);
&:hover {
transform: scale(2.5);
background-image: url('/@/assets/WindBlower/6.png');
}
}
.index-8 {
left: 23%;
top: 53%;
width: 19%;
height: 26%;
&:hover {
background-image: url('/@/assets/WindBlower/2.png');
}
}
.index-9 {
left: 50%;
top: 66%;
width: 9%;
height: 13%;
&:hover {
transform: scale(2);
background-image: url('/@/assets/WindBlower/10.png');
}
}
.index-10 {
left: 67%;
top: 1%;
width: 12%;
height: 12%;
&:hover {
transform: scale(2.5);
background-image: url('/@/assets/WindBlower/8.png');
}
}
.index-11 {
right: 16%;
bottom: 3%;
width: 13%;
height: 23%;
}
.index-12 {
right: 2%;
bottom: 3%;
width: 13%;
height: 23%;
}
}
.chartPart {
margin: 10px 0;
width: 100%;
height: calc(100% - 10px - v-bind('computedHeight.backgroundImgHeight'));
// min-height: 220px;
display: flex;
justify-content: space-between;
&:hover {
cursor: pointer;
}
.power {
margin-bottom: 5px;
width: 60%;
height: 100%;
background-color: #f0f6ff;
border-radius: 8px;
.chartBox {
width: 100%;
height: 100%;
.power-chart {
width: 100%;
height: 100%;
}
}
}
.chartPart-item {
margin: 0 0 5px 5px;
// width: 25%;
height: 100%;
background: #f0f6ff;
border-radius: 8px;
color: #4e5969;
.frequencyChart {
width: 100%;
height: 100%;
}
}
.item_bar {
width: 40%;
}
}
}
.cardContentRight {
margin: 0 5px;
width: calc(100% - 10px);
height: 100%;
// min-height: 920px;
.summarize {
width: 100%;
height: 40%;
@include cardDefaultStyle;
@include cardlabel;
.summarize-panel-list {
width: 100%;
height: calc(100% - $labelHeight);
.summarize-panel-row {
display: flex;
width: 100%;
height: 50%;
min-height: 110px;
.summarize-panel {
margin: 5px;
width: calc(50% - 10px);
height: calc(100% - 10px);
background-color: #f0f6ff;
border-radius: 10px;
.summarize-panel-pic {
display: flex;
justify-content: center;
align-items: center;
min-height: 36px;
height: 40%;
img {
height: 80%;
aspect-ratio: 1/1;
}
}
.summarize-panel-base {
width: 100%;
height: 60%;
display: flex;
flex-direction: column;
justify-content: center;
div {
display: flex;
justify-content: center;
height: 30%;
font-size: 14px;
color: rgb(78, 89, 105);
.content-number {
color: #333333;
font-size: 16px;
font-weight: 600;
}
&:first-child {
height: 40%;
align-items: center;
}
}
}
}
}
}
}
.alarm {
margin: 10px 0;
width: 100%;
// min-height: 350px;
height: calc(60% - 20px);
@include cardDefaultStyle;
@include cardlabel;
}
}
}
</style>