1216 lines
43 KiB
Vue
1216 lines
43 KiB
Vue
<template>
|
||
<div class="energyManage">
|
||
<el-row :gutter="20">
|
||
<el-col :md="24" :lg="{ span: 14, push: 5 }">
|
||
<el-tabs v-model="activeName" @tab-change="handleClick">
|
||
<el-tab-pane label="风机列表" name="list">
|
||
<div class="centerContainer">
|
||
<div class="airBlowerList">
|
||
<!-- <div class="title">风机列表</div> -->
|
||
<el-table class="table" :data="tableData">
|
||
<el-table-column
|
||
v-for="item in tableColumn"
|
||
:key="item.prop"
|
||
:prop="item.prop"
|
||
:label="item.label"
|
||
align="center"
|
||
:width="item.width"
|
||
>
|
||
<template #default="scope">
|
||
<div v-if="item.prop === 'processedoperationmode'">
|
||
<el-tag v-if="scope.row.locked === 1" color="rgba(254,55,49,0.20)" style="color: #fe3731"
|
||
>已锁定</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 20"
|
||
color="rgba(0,100,170,0.20)"
|
||
style="color: #0064aa"
|
||
>并网</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 10"
|
||
color="rgba(0,160,150,0.20)"
|
||
style="color: #00a096"
|
||
>维护</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 8"
|
||
color="rgba(255,126,0,0.20)"
|
||
style="color: #ff7e00"
|
||
>限功率运行</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 0"
|
||
color="rgba(153,153,153,0.20)"
|
||
style="color: #666666"
|
||
>离线</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 16"
|
||
color="rgba(6,180,41,0.20)"
|
||
style="color: #06b429"
|
||
>启动</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 6"
|
||
color="rgba(254,55,49,0.20)"
|
||
style="color: #fe3731"
|
||
>正常停机</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 1"
|
||
color="rgba(254,55,49,0.20)"
|
||
style="color: #fe3731"
|
||
>外部因素导致停机</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 2"
|
||
color="rgba(254,55,49,0.20)"
|
||
style="color: #fe3731"
|
||
>停机</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 11"
|
||
color="rgba(255,182,0,0.20)"
|
||
style="color: #ffb600"
|
||
>待机</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 1110"
|
||
color="rgba(153,153,153,0.20)"
|
||
style="color: #666666"
|
||
>解缆状态</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 1111"
|
||
color="rgba(254,55,49,0.20)"
|
||
style="color: #fe3731"
|
||
>电网故障停机</el-tag
|
||
>
|
||
<el-tag
|
||
v-if="scope.row.processedoperationmode === 33"
|
||
color="rgba(153, 153, 153, 0.2)"
|
||
style="color: #999999"
|
||
>通讯中断</el-tag
|
||
>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="风机曲线" name="chart">
|
||
<div class="centerContainer">
|
||
<div class="chartPart">
|
||
<div class="title">
|
||
<span> 自动更新 </span>
|
||
<el-switch v-model="autoUpdateChartSwitch" @change="changeUpdateChart"></el-switch>
|
||
</div>
|
||
<div class="lineChart">
|
||
<div class="chart" ref="chartRef"></div>
|
||
</div>
|
||
<div class="info">
|
||
<div class="infoItem" v-for="item in infoList" :key="item.label">
|
||
<div class="label">{{ item.label }}</div>
|
||
<div class="val">{{ item.value }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</el-col>
|
||
<el-col :md="12" :lg="{ span: 5, pull: 14 }">
|
||
<div class="leftContainer">
|
||
<div class="overview">
|
||
<div class="title">AGC总览</div>
|
||
<div class="content">
|
||
<div class="rect">
|
||
<div class="left">
|
||
<div class="img img1"></div>
|
||
<div class="imgName">全场有功</div>
|
||
</div>
|
||
<div class="right">
|
||
<div class="num">{{ realDataList.windfarmactivepower }}</div>
|
||
<div class="unit">MW</div>
|
||
</div>
|
||
</div>
|
||
<div class="rect">
|
||
<div class="left">
|
||
<div class="img img2"></div>
|
||
<div class="imgName">AGC目标值</div>
|
||
</div>
|
||
<div class="right">
|
||
<div class="num">{{ realDataListSetValue.AgcTarget }}</div>
|
||
<div class="unit">MW</div>
|
||
</div>
|
||
</div>
|
||
<div class="check">
|
||
<div class="left">
|
||
<div class="name">AGC投入/退出</div>
|
||
<div class="status">
|
||
<div class="smallDot" :class="realDataList.operationstatusagc ? 'successColor' : 'defaultColor'"></div>
|
||
<div class="smallDot" :class="realDataList.operationstatusagc ? 'defaultColor' : 'errorColor'"></div>
|
||
</div>
|
||
</div>
|
||
<div class="right">
|
||
<el-switch v-model="agcOnoffSwitch"></el-switch>
|
||
</div>
|
||
</div>
|
||
<div class="checkInput">
|
||
<div class="top">
|
||
<div class="left">AGC远程/就地</div>
|
||
<div class="right">
|
||
<el-switch v-model="agcLocalRemoteSwitch"></el-switch>
|
||
</div>
|
||
</div>
|
||
<div class="bottom">
|
||
<el-input v-model="agcTargetValue" placeholder="本地目标值设置MW 请输入">
|
||
<template #suffix>
|
||
<div class="saveBtn"></div>
|
||
</template>
|
||
</el-input>
|
||
</div>
|
||
</div>
|
||
<div class="check">
|
||
<div class="left">AGC增闭锁</div>
|
||
<div class="right">
|
||
<div class="dot" :class="realDataList.activepowerincdisabled ? 'successColor' : 'errorColor'"></div>
|
||
</div>
|
||
</div>
|
||
<div class="check">
|
||
<div class="left">AGC减闭锁</div>
|
||
<div class="right">
|
||
<div class="dot" :class="realDataList.activepowerdecdisabled ? 'successColor' : 'errorColor'"></div>
|
||
</div>
|
||
</div>
|
||
<div class="check">
|
||
<div class="left">AGC可增有功</div>
|
||
<div class="right">
|
||
<div class="num">{{ realDataList.activepowerinccapacity }}</div>
|
||
<div class="unit">MW</div>
|
||
</div>
|
||
</div>
|
||
<div class="check">
|
||
<div class="left">AGC可减有功</div>
|
||
<div class="right">
|
||
<div class="num">{{ realDataList.activepowerdeccapacity }}</div>
|
||
<div class="unit">MW</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="record">
|
||
<div class="title">AGC记录</div>
|
||
<div class="recordList">
|
||
<el-scrollbar>
|
||
<div class="recordItem" v-for="item in agcRecordList" :key="item">{{ item }}</div>
|
||
</el-scrollbar>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :md="12" :lg="5">
|
||
<div class="rightContainer">
|
||
<div class="overview">
|
||
<div class="title">AVC总览</div>
|
||
<div class="content">
|
||
<div class="rect">
|
||
<div class="left">
|
||
<div class="img img1"></div>
|
||
<div class="imgName">全场无功</div>
|
||
</div>
|
||
<div class="right">
|
||
<div class="num">{{ realDataList.windfarmreactivepower }}</div>
|
||
<div class="unit">MVar</div>
|
||
</div>
|
||
</div>
|
||
<div class="rect">
|
||
<div class="left">
|
||
<div class="img img2"></div>
|
||
<div class="imgName">AVC目标值</div>
|
||
</div>
|
||
<div class="right">
|
||
<div class="num">{{ realDataListSetValue.AvcTarget }}</div>
|
||
<div class="unit">MVar</div>
|
||
</div>
|
||
</div>
|
||
<div class="check">
|
||
<div class="left">
|
||
<div class="name">AVC投入/退出</div>
|
||
<div class="status">
|
||
<div class="smallDot" :class="realDataList.operationstatusavc ? 'successColor' : 'defaultColor'"></div>
|
||
<div class="smallDot" :class="realDataList.operationstatusavc ? 'defaultColor' : 'errorColor'"></div>
|
||
</div>
|
||
</div>
|
||
<div class="right">
|
||
<el-switch v-model="avcOnoffSwitch"></el-switch>
|
||
</div>
|
||
</div>
|
||
<div class="checkInput">
|
||
<div class="top">
|
||
<div class="left">AVC远程/就地</div>
|
||
<div class="right">
|
||
<el-switch v-model="avcLocalRemoteSwitch"></el-switch>
|
||
</div>
|
||
</div>
|
||
<div class="bottom">
|
||
<el-input v-model="avcTargetValue" placeholder="本地目标值设置MW 请输入">
|
||
<template #suffix>
|
||
<div class="saveBtn"></div>
|
||
</template>
|
||
</el-input>
|
||
</div>
|
||
</div>
|
||
<div class="check">
|
||
<div class="left">AVC增闭锁</div>
|
||
<div class="right">
|
||
<div class="dot" :class="realDataList.reactivepowerincdisabled ? 'successColor' : 'errorColor'"></div>
|
||
</div>
|
||
</div>
|
||
<div class="check">
|
||
<div class="left">AVC减闭锁</div>
|
||
<div class="right">
|
||
<div class="dot" :class="realDataList.reactivepowerdecdisabled ? 'successColor' : 'errorColor'"></div>
|
||
</div>
|
||
</div>
|
||
<div class="check">
|
||
<div class="left">AVC可增无功</div>
|
||
<div class="right">
|
||
<div class="num">{{ realDataList.reactivepowerinccapacity }}</div>
|
||
<div class="unit">MVar</div>
|
||
</div>
|
||
</div>
|
||
<div class="check">
|
||
<div class="left">AVC可减无功</div>
|
||
<div class="right">
|
||
<div class="num">{{ realDataList.reactivepowerdeccapacity }}</div>
|
||
<div class="unit">MVar</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="record">
|
||
<div class="title">AVC记录</div>
|
||
<div class="recordList">
|
||
<el-scrollbar>
|
||
<div class="recordItem" v-for="item in avcRecordList" :key="item">{{ item }}</div>
|
||
</el-scrollbar>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted, onUnmounted, nextTick, reactive } from 'vue'
|
||
import { getAirBlowerListReq } from '/@/api/backend/airBlower/request'
|
||
import { getRealValueListReq, getRealValueRangeReq } from '/@/api/backend/deviceModel/request'
|
||
import { getRealTimeState, getCutDecimalsValue, getEnumToValue } from '/@/views/backend/equipment/airBlower/utils'
|
||
import { ElMessage, dayjs } from 'element-plus'
|
||
import { debounce } from 'lodash-es'
|
||
import * as echarts from 'echarts'
|
||
|
||
const agcOnoffSwitch = ref(true)
|
||
const agcOnOff = ref(true)
|
||
const agcLocalRemoteSwitch = ref(false)
|
||
const agcTargetValue = ref()
|
||
const agcAdd = ref(true)
|
||
const agcSub = ref(false)
|
||
const agcRecordList = ref([
|
||
'2023-12-26 22:07:25一次调频指令变更为:156212kW',
|
||
'2023-12-25 22:07:25进入一次调频模式',
|
||
'2023-12-24 22:07:25进入一次调频模式',
|
||
'2023-12-23 22:07:25进入一次调频模式',
|
||
'2023-12-22 22:07:25一次调频指令变更为:156212kW',
|
||
])
|
||
|
||
const avcOnoffSwitch = ref(true)
|
||
const avcOnOff = ref(false)
|
||
const avcLocalRemoteSwitch = ref(false)
|
||
const avcTargetValue = ref()
|
||
const avcAdd = ref(true)
|
||
const avcSub = ref(false)
|
||
const avcRecordList = ref([
|
||
'2023-12-26 22:07:25一次调频指令变更为:156212kW',
|
||
'2023-12-25 22:07:25进入一次调频模式',
|
||
'2023-12-24 22:07:25进入一次调频模式',
|
||
'2023-12-23 22:07:25进入一次调频模式',
|
||
'2023-12-22 22:07:25一次调频指令变更为:156212kW',
|
||
])
|
||
|
||
const tableColumn = [
|
||
{
|
||
label: '编号',
|
||
prop: 'name',
|
||
width: '',
|
||
},
|
||
{
|
||
label: '风速m/s',
|
||
prop: 'iwindspeed',
|
||
width: '',
|
||
},
|
||
{
|
||
label: '转速rmp',
|
||
prop: 'igenspeed',
|
||
width: '',
|
||
},
|
||
{
|
||
label: '有功kW',
|
||
prop: 'igenpower',
|
||
width: '',
|
||
},
|
||
{
|
||
label: '有功给定kW',
|
||
prop: 'iactivepowersetpointvalue',
|
||
width: '',
|
||
},
|
||
{
|
||
label: '无功kVar',
|
||
prop: 'ireactivepower',
|
||
width: '',
|
||
},
|
||
{
|
||
label: '无功给定kVar',
|
||
prop: 'ireactivepowersetpointvalue',
|
||
width: '',
|
||
},
|
||
{
|
||
label: '状态',
|
||
prop: 'processedoperationmode',
|
||
width: '',
|
||
},
|
||
]
|
||
|
||
const curDevice = ref('1846101273013739522')
|
||
const tableData = ref<any[]>([])
|
||
|
||
const createTableReqParams = (airblowerList: { irn: string; name: string }[]) => {
|
||
const curTableKey = tableColumn.map((item) => item.prop)
|
||
const airBlowerIds: any = []
|
||
const airBlowerInfo: any = {}
|
||
const params = airblowerList.map((item: any) => {
|
||
airBlowerInfo[item.irn] = {
|
||
name: item.name,
|
||
model: item.model,
|
||
belongLine: item.belongLine,
|
||
modelId: item.modelId,
|
||
deviceCode: item.deviceCode,
|
||
}
|
||
airBlowerIds.push(item.irn)
|
||
return {
|
||
deviceId: item.irn,
|
||
attributes: [...curTableKey, 'processedoperationmode', 'iyplevel', 'gridlostdetected', 'ibplevel'],
|
||
}
|
||
})
|
||
return { params, airBlowerInfo, airBlowerIds }
|
||
}
|
||
|
||
const getAirBlowerList = () => {
|
||
let airBlowerInfoObj: any = {}
|
||
let airBlowerIdList: any[] = []
|
||
getAirBlowerListReq()
|
||
.then((res) => {
|
||
if (res.success) {
|
||
return createTableReqParams(res.data)
|
||
} else {
|
||
throw '获取风机列表失败'
|
||
}
|
||
})
|
||
.then((data) => {
|
||
airBlowerInfoObj = data.airBlowerInfo
|
||
airBlowerIdList = data.airBlowerIds
|
||
return getRealValueListReq(data!.params)
|
||
})
|
||
.then((res) => {
|
||
if (res.success) {
|
||
const data = airBlowerIdList.map((id) => {
|
||
const realData: any = {}
|
||
Object.keys(res.data[id]).forEach((key) => {
|
||
const cutVal = getCutDecimalsValue(res.data[id][key])
|
||
realData[key] = getEnumToValue(key, cutVal)
|
||
})
|
||
const state = getRealTimeState(res.data[id])
|
||
return {
|
||
irn: id,
|
||
name: airBlowerInfoObj[id].name,
|
||
model: airBlowerInfoObj[id].model,
|
||
iotModelId: airBlowerInfoObj[id].modelId,
|
||
belongLine: airBlowerInfoObj[id].belongLine,
|
||
deviceCode: airBlowerInfoObj[id].deviceCode,
|
||
...realData,
|
||
processedoperationmode: state,
|
||
}
|
||
})
|
||
tableData.value = data
|
||
} else {
|
||
throw '获取风机列表失败'
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
ElMessage.error(err)
|
||
})
|
||
}
|
||
let timer: any = null
|
||
const autoUpdateAirBlower = () => {
|
||
getAirBlowerList()
|
||
if (!timer) {
|
||
timer = setInterval(() => {
|
||
getAirBlowerList()
|
||
}, 2000)
|
||
}
|
||
}
|
||
|
||
const autoUpdateChartSwitch = ref(false)
|
||
const changeUpdateChart = (val: boolean) => {
|
||
if (val) {
|
||
autoUpdateChart()
|
||
} else {
|
||
clearInterval(autoUpdateChartTimer)
|
||
autoUpdateChartTimer = null
|
||
}
|
||
}
|
||
|
||
const chartRef = ref()
|
||
let chartInstance: any = null
|
||
|
||
let realDataXAxis: any[] = [
|
||
'2024-12-06 16:53:37',
|
||
'2024-12-06 16:53:38',
|
||
'2024-12-06 16:53:39',
|
||
'2024-12-06 16:53:40',
|
||
'2024-12-06 16:53:41',
|
||
'2024-12-06 16:53:42',
|
||
'2024-12-06 16:53:43',
|
||
'2024-12-06 16:53:44',
|
||
'2024-12-06 16:53:45',
|
||
'2024-12-06 16:53:46',
|
||
'2024-12-06 16:53:47',
|
||
'2024-12-06 16:53:48',
|
||
]
|
||
let realDataSeries: any[] = [
|
||
{
|
||
id: 'iwindspeed',
|
||
name: '风速',
|
||
type: 'line',
|
||
barWidth: 20,
|
||
itemStyle: {
|
||
color: '#096676',
|
||
barBorderRadius: 2,
|
||
},
|
||
smooth: true,
|
||
symbol: 'none',
|
||
data: [24.68, 24.68, 25.3, 24.09, 24.51, 24.51, 24.46, 23.87, 23.97, 23.87, 23.87, 24.61],
|
||
},
|
||
{
|
||
id: 'ireactivepowersetpointvalue',
|
||
name: '给定无功功率',
|
||
type: 'line',
|
||
barWidth: 20,
|
||
itemStyle: {
|
||
color: '#8236aa',
|
||
barBorderRadius: 2,
|
||
},
|
||
smooth: true,
|
||
symbol: 'none',
|
||
data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||
},
|
||
{
|
||
id: 'ireactivepower',
|
||
name: '无功功率',
|
||
type: 'line',
|
||
barWidth: 20,
|
||
itemStyle: {
|
||
color: '#687068',
|
||
barBorderRadius: 2,
|
||
},
|
||
smooth: true,
|
||
symbol: 'none',
|
||
data: [50.6, 50.6, 50.6, 50.6, 50.6, 50.6, 50.6, 50.6, 50.6, 50.6, 50.6, 50.6],
|
||
},
|
||
{
|
||
id: 'iactivepowersetpointvalue',
|
||
name: '给定有功功率',
|
||
type: 'line',
|
||
barWidth: 20,
|
||
itemStyle: {
|
||
color: '#164342',
|
||
barBorderRadius: 2,
|
||
},
|
||
smooth: true,
|
||
symbol: 'none',
|
||
data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||
},
|
||
{
|
||
id: 'igenpower',
|
||
name: '有功功率',
|
||
type: 'line',
|
||
barWidth: 20,
|
||
itemStyle: {
|
||
color: '#532457',
|
||
barBorderRadius: 2,
|
||
},
|
||
smooth: true,
|
||
symbol: 'none',
|
||
data: [1518.9, 1518.9, 1518.8, 1518.7, 1518.7, 1518.7, 1518.7, 1518.8, 1518.59, 1518.59, 1518.59, 1518.59],
|
||
},
|
||
{
|
||
id: 'igenspeed',
|
||
name: '转速',
|
||
type: 'line',
|
||
barWidth: 20,
|
||
itemStyle: {
|
||
color: '#945810',
|
||
barBorderRadius: 2,
|
||
},
|
||
smooth: true,
|
||
symbol: 'none',
|
||
data: [1808.3, 1808.3, 1808.3, 1808.3, 1808.3, 1808.3, 1808.2, 1808.2, 1808.2, 1808, 1808, 1807.8],
|
||
},
|
||
{
|
||
id: 'iwinddirection',
|
||
name: '风向',
|
||
type: 'line',
|
||
barWidth: 20,
|
||
itemStyle: {
|
||
color: '#195658',
|
||
barBorderRadius: 2,
|
||
},
|
||
smooth: true,
|
||
symbol: 'none',
|
||
data: [0.8, 0.8, 6.9, -6.1, 0.6, 0.6, -0.5, -6.6, -5.41, -4.21, -4.21, 2.5],
|
||
},
|
||
]
|
||
const getRandomDarkColor = () => {
|
||
let r = Math.floor(Math.random() * 200) // 限制在0到127之间,以生成较深的颜色
|
||
let g = Math.floor(Math.random() * 200)
|
||
let b = Math.floor(Math.random() * 200)
|
||
|
||
let color = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`
|
||
return color
|
||
}
|
||
|
||
const createChartData = (data: { [k: string]: number }, time: string) => {
|
||
if (realDataXAxis.length > 300) {
|
||
realDataXAxis.shift()
|
||
realDataSeries.forEach((item: any) => {
|
||
item.data.shift()
|
||
})
|
||
}
|
||
const attrCode = Object.keys(data)
|
||
realDataXAxis.push(time)
|
||
|
||
const seriesData = attrCode.map((item) => {
|
||
const curVal = getCutDecimalsValue(data[item], 2)
|
||
|
||
if (realDataSeries.length) {
|
||
const seriesItem = realDataSeries.find((series) => series.id === item)
|
||
seriesItem.data.push(curVal)
|
||
return seriesItem
|
||
} else {
|
||
const color = getRandomDarkColor()
|
||
return {
|
||
id: item,
|
||
name: chartProps[item],
|
||
type: 'line',
|
||
barWidth: 20,
|
||
itemStyle: {
|
||
color: color,
|
||
barBorderRadius: 2,
|
||
},
|
||
smooth: true,
|
||
symbol: 'none',
|
||
data: [curVal],
|
||
}
|
||
}
|
||
})
|
||
realDataSeries = seriesData
|
||
createChart()
|
||
}
|
||
const createChart = () => {
|
||
const chart = chartInstance ?? echarts.init(chartRef.value)
|
||
let option = null
|
||
if (chartInstance && realDataXAxis.length > 1) {
|
||
option = {
|
||
xAxis: {
|
||
data: realDataXAxis,
|
||
},
|
||
series: realDataSeries,
|
||
}
|
||
} else {
|
||
const legendData = Object.values(chartProps).map((item) => {
|
||
return {
|
||
name: item,
|
||
}
|
||
})
|
||
option = {
|
||
grid: {
|
||
top: 50,
|
||
right: 23,
|
||
bottom: 50,
|
||
left: 18,
|
||
containLabel: true,
|
||
},
|
||
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',
|
||
interval: 'auto',
|
||
formatter: function (value: any) {
|
||
return value.slice(11)
|
||
},
|
||
//rotate: 45
|
||
},
|
||
splitLine: {
|
||
//分割线配置
|
||
show: false,
|
||
lineStyle: {
|
||
color: '#999999',
|
||
},
|
||
},
|
||
data: realDataXAxis,
|
||
},
|
||
yAxis: [
|
||
{
|
||
type: 'value',
|
||
name: '',
|
||
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',
|
||
},
|
||
},
|
||
},
|
||
],
|
||
dataZoom: [
|
||
{
|
||
type: 'inside',
|
||
start: 0,
|
||
end: 100,
|
||
},
|
||
{
|
||
start: 0,
|
||
end: 100,
|
||
},
|
||
],
|
||
legend: {
|
||
data: legendData,
|
||
textStyle: {
|
||
color: '#73767a',
|
||
},
|
||
icon: 'roundRect',
|
||
},
|
||
series: realDataSeries,
|
||
}
|
||
}
|
||
chart.setOption(option, { replaceMerge: 'series' })
|
||
chartInstance = chart
|
||
}
|
||
|
||
const chartProps: any = {
|
||
iwindspeed: '风速',
|
||
iwinddirection: '风向',
|
||
igenspeed: '转速',
|
||
igenpower: '有功功率',
|
||
iactivepowersetpointvalue: '给定有功功率',
|
||
ireactivepower: '无功功率',
|
||
ireactivepowersetpointvalue: '给定无功功率',
|
||
}
|
||
const getRangeValueList = () => {
|
||
const params = {
|
||
startTime: dayjs().startOf('day').valueOf(),
|
||
endTime: dayjs().valueOf(),
|
||
devices: [
|
||
{
|
||
deviceId: tableData.value[1].irn,
|
||
attributes: Object.keys(chartProps),
|
||
},
|
||
],
|
||
interval: '1m',
|
||
}
|
||
getRealValueRangeReq(params).then((res) => {
|
||
if (res.data) {
|
||
}
|
||
})
|
||
}
|
||
const getRealDataForChart = () => {
|
||
const params = [
|
||
{
|
||
deviceId: tableData.value[0].irn,
|
||
attributes: Object.keys(chartProps),
|
||
},
|
||
]
|
||
getRealValueListReq(params).then((res) => {
|
||
if (res.data) {
|
||
const time = dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||
createChartData(res.data?.[tableData.value[0].irn], time)
|
||
}
|
||
})
|
||
}
|
||
let autoUpdateChartTimer: any = null
|
||
|
||
const autoUpdateChart = () => {
|
||
if (!autoUpdateChartTimer) {
|
||
autoUpdateChartTimer = setInterval(() => {
|
||
getRealDataForChart()
|
||
}, 1000)
|
||
}
|
||
}
|
||
|
||
const infoList = ref([
|
||
{
|
||
label: '风机总功率kW',
|
||
value: 157646,
|
||
},
|
||
{
|
||
label: '当前有功指令kW',
|
||
value: 156212,
|
||
},
|
||
{
|
||
label: '电网有功指令kW',
|
||
value: 120000,
|
||
},
|
||
{
|
||
label: '调频有功指令kW',
|
||
value: 156212,
|
||
},
|
||
{
|
||
label: '电网电压kV',
|
||
value: 200,
|
||
},
|
||
{
|
||
label: '风机总无功kVar',
|
||
value: 1596.4,
|
||
},
|
||
{
|
||
label: '当前无功指令kW',
|
||
value: 0.0,
|
||
},
|
||
{
|
||
label: '线路损耗功率kW',
|
||
value: 3259,
|
||
},
|
||
{
|
||
label: '电网出口功率kW',
|
||
value: 156679,
|
||
},
|
||
{
|
||
label: '电网频率Hz',
|
||
value: 0.0,
|
||
},
|
||
])
|
||
|
||
const activeName = ref('list')
|
||
const handleClick = (tabName: any) => {
|
||
if (tabName === 'chart') {
|
||
console.log(tabName)
|
||
|
||
stopAutoUpdate()
|
||
nextTick(() => {
|
||
createChart()
|
||
})
|
||
} else if (tabName === 'list') {
|
||
autoUpdateAirBlower()
|
||
}
|
||
}
|
||
|
||
const containerHeight = ref('900px')
|
||
const getContainerHeight = () => {
|
||
const container = document.querySelector('.leftContainer')
|
||
if (container) {
|
||
const { height } = container.getBoundingClientRect()
|
||
containerHeight.value = `${height}px`
|
||
}
|
||
}
|
||
const resizeChart = debounce(() => {
|
||
chartInstance?.resize()
|
||
}, 500)
|
||
const resizeFn = () => {
|
||
resizeChart()
|
||
getContainerHeight()
|
||
}
|
||
const stopAutoUpdate = () => {
|
||
timer && clearInterval(timer)
|
||
timer = null
|
||
|
||
}
|
||
|
||
const realDataListSetValue = reactive({
|
||
AgcTarget: '0',
|
||
AvcTarget: '0',
|
||
})
|
||
const realDataList = reactive({
|
||
windfarmactivepower: '',
|
||
operationstatusagc: '',
|
||
RemoteCtrlStatusAgc: '',
|
||
activepowerincdisabled: '',
|
||
activepowerdecdisabled: '',
|
||
activepowerinccapacity: '',
|
||
activepowerdeccapacity: '',
|
||
windfarmreactivepower: '',
|
||
operationstatusavc: '',
|
||
RemoteCtrlStatusAvc: '',
|
||
reactivepowerincdisabled: '',
|
||
reactivepowerdecdisabled: '',
|
||
reactivepowerinccapacity: '',
|
||
reactivepowerdeccapacity: '',
|
||
})
|
||
const getRealDataForList = () => {
|
||
const deviceId = '1881630608594132993'
|
||
const attrs = Object.keys(realDataList) as (keyof typeof realDataList)[]
|
||
const params = [
|
||
{
|
||
deviceId,
|
||
attributes: attrs,
|
||
},
|
||
]
|
||
getRealValueListReq(params).then((res) => {
|
||
if (res.data) {
|
||
console.log(res.data, 'res.data')
|
||
const data = res.data[deviceId]
|
||
attrs.forEach((item) => {
|
||
realDataList[item] = data?.[item] ?? '-'
|
||
})
|
||
}
|
||
})
|
||
}
|
||
let listTimer: any = null
|
||
const autoUpdateList = () => {
|
||
if (!listTimer) {
|
||
getRealDataForList()
|
||
listTimer = setInterval(() => {
|
||
getRealDataForList()
|
||
}, 2000)
|
||
}
|
||
}
|
||
const clearListTimer = () => {
|
||
listTimer && clearInterval(listTimer)
|
||
listTimer = null
|
||
}
|
||
onMounted(() => {
|
||
getContainerHeight()
|
||
autoUpdateList()
|
||
autoUpdateAirBlower()
|
||
window.addEventListener('resize', resizeFn)
|
||
})
|
||
onUnmounted(() => {
|
||
window.removeEventListener('resize', resizeFn)
|
||
stopAutoUpdate()
|
||
clearListTimer()
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@mixin defaultBoxStyle {
|
||
display: flex;
|
||
align-self: center;
|
||
justify-content: space-between;
|
||
padding: 0 10px;
|
||
width: 100%;
|
||
}
|
||
@mixin borderstyle {
|
||
border-radius: 10px;
|
||
border: 1px solid #e1edf6;
|
||
}
|
||
@mixin overViewRight {
|
||
.right {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 100%;
|
||
.num {
|
||
font-size: 20px;
|
||
line-height: 20px;
|
||
color: #333333;
|
||
letter-spacing: 0;
|
||
font-weight: 700;
|
||
}
|
||
.unit {
|
||
margin-left: 5px;
|
||
height: 10%;
|
||
font-size: 14px;
|
||
color: #4e5969;
|
||
letter-spacing: 0;
|
||
line-height: 14px;
|
||
font-weight: 700;
|
||
}
|
||
.dot {
|
||
height: 45%;
|
||
aspect-ratio: 1/1;
|
||
border-radius: 50%;
|
||
}
|
||
}
|
||
}
|
||
@mixin overview {
|
||
.overview {
|
||
padding: 20px;
|
||
width: 100%;
|
||
background-color: #fff;
|
||
border-radius: 6px;
|
||
.title {
|
||
height: 28px;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #4e5969;
|
||
}
|
||
.content {
|
||
width: 100%;
|
||
height: calc(100% - 20px);
|
||
.rect {
|
||
@include defaultBoxStyle;
|
||
margin: 10px 0;
|
||
aspect-ratio: 310/70;
|
||
background-color: #f0f6ff;
|
||
@include borderstyle;
|
||
.left {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 100%;
|
||
.img {
|
||
height: 51%;
|
||
aspect-ratio: 1/1;
|
||
background-repeat: no-repeat;
|
||
background-size: contain;
|
||
}
|
||
.imgName {
|
||
margin-left: 5px;
|
||
font-size: 14px;
|
||
color: #4e5969;
|
||
letter-spacing: 0;
|
||
font-weight: 400;
|
||
}
|
||
.img1 {
|
||
background-image: url('/@/assets/energyManage/power.png');
|
||
}
|
||
.img2 {
|
||
background-image: url('/@/assets/energyManage/AGC.png');
|
||
}
|
||
}
|
||
@include overViewRight;
|
||
}
|
||
.check {
|
||
@include defaultBoxStyle;
|
||
margin-bottom: 10px;
|
||
aspect-ratio: 310/50;
|
||
@include borderstyle;
|
||
.left {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 100%;
|
||
color: #4e5969;
|
||
.status {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 100%;
|
||
.smallDot {
|
||
margin-left: 5px;
|
||
height: 25%;
|
||
aspect-ratio: 1/1;
|
||
border-radius: 50%;
|
||
}
|
||
}
|
||
}
|
||
@include overViewRight;
|
||
}
|
||
.checkInput {
|
||
padding: 0 10px;
|
||
margin-bottom: 10px;
|
||
width: 100%;
|
||
aspect-ratio: 310/88;
|
||
color: #4e5969;
|
||
@include borderstyle;
|
||
.top {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 5px;
|
||
margin-bottom: 10px;
|
||
height: 30%;
|
||
}
|
||
.bottom {
|
||
.el-input {
|
||
background-color: #f0f6ff;
|
||
:deep(.el-input__wrapper) {
|
||
.el-input__inner {
|
||
width: 92%;
|
||
flex-grow: 0;
|
||
}
|
||
}
|
||
}
|
||
.saveBtn {
|
||
margin: 0;
|
||
height: 60%;
|
||
aspect-ratio: 1/1;
|
||
background-image: url('/@/assets/energyManage/defaultSave.png');
|
||
background-size: contain;
|
||
background-repeat: no-repeat;
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
@mixin record {
|
||
.record {
|
||
margin: 20px 0;
|
||
padding: 20px;
|
||
width: 100%;
|
||
aspect-ratio: 350/270;
|
||
background-color: #fff;
|
||
border-radius: 6px;
|
||
color: #4e5969;
|
||
.title {
|
||
height: 30px;
|
||
font-size: 18px;
|
||
color: #4e5969;
|
||
font-weight: 600;
|
||
}
|
||
.recordList {
|
||
width: 100%;
|
||
height: calc(100% - 30px);
|
||
.recordItem {
|
||
width: 100%;
|
||
min-height: 32px;
|
||
line-height: 150%;
|
||
word-break: break-all;
|
||
background-color: #f0f6ff;
|
||
&:nth-child(2n) {
|
||
background-color: #fff;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.energyManage {
|
||
width: 100%;
|
||
// height: 100%;
|
||
min-height: 100%;
|
||
background-color: #f2f3f5;
|
||
.el-row {
|
||
width: 100%;
|
||
height: 100%;
|
||
.el-col {
|
||
width: 100%;
|
||
height: 100%;
|
||
.successColor {
|
||
background-color: #06b429;
|
||
}
|
||
.errorColor {
|
||
background-color: #fe3731;
|
||
}
|
||
.defaultColor {
|
||
background-color: #e1e6ed;
|
||
}
|
||
.leftContainer {
|
||
@include overview;
|
||
@include record;
|
||
}
|
||
.centerContainer {
|
||
height: v-bind('containerHeight');
|
||
color: #4e5969;
|
||
.airBlowerList {
|
||
padding: 20px;
|
||
width: 100%;
|
||
height: calc(100% - 10px - 40px);
|
||
background-color: #fff;
|
||
border-radius: 6px;
|
||
// .title {
|
||
// height: 38px;
|
||
// font-size: 18px;
|
||
// font-weight: 600;
|
||
// }
|
||
.table {
|
||
height: 100%;
|
||
}
|
||
}
|
||
.chartPart {
|
||
padding: 20px;
|
||
width: 100%;
|
||
height: calc(100% - 10px - 40px);
|
||
background-color: #fff;
|
||
border-radius: 6px;
|
||
.title {
|
||
width: 100%;
|
||
height: 32px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
.lineChart {
|
||
width: 100%;
|
||
height: calc(100% - 102px);
|
||
.chart {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
.info {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
padding: 10px;
|
||
width: 100%;
|
||
height: 70px;
|
||
background-color: #f0f6ff;
|
||
.infoItem {
|
||
display: flex;
|
||
align-items: center;
|
||
width: 20%;
|
||
height: 50%;
|
||
font-size: 12px;
|
||
color: #333333;
|
||
font-weight: 400;
|
||
.label {
|
||
margin-left: 10%;
|
||
}
|
||
.val {
|
||
margin-left: 10px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.rightContainer {
|
||
@include overview;
|
||
@include record;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|