You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

774 lines
20 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view>
<uni-nav-bar left-icon="left" fixed="true" title="逆变器视图" @clickLeft="handlerBack"></uni-nav-bar>
<view class="container">
<view class="search-box">
<uni-section title="选择电站" type="line" >
<uni-data-picker placeholder="请选择电站"
popup-title=""
:localdata="stationData"
:clear-icon="false"
v-model="selectStations"
:map="{text:'stationName',value:'id', children: 'children'}"
@change="onchange"
>
</uni-data-picker>
<uni-data-picker placeholder="选择逆变器"
style="margin-top: 10upx;"
:localdata="secondList"
:clear-icon="false"
v-model="secondValueList"
:map="{text:'name',value:'value', children: 'addd'}"
@change="onchange1"
>
</uni-data-picker>
</uni-section>
<uni-section title="设备状态" type="line" >
<template v-slot:right>
<view class="flex">
<u-button @click="handleSetConfig" type="primary" size="mini" text="设置参数"></u-button>
</view>
</template>
<div class="report-status">
<div class="report-status-item">
<div class="flex flex-column flex-col-center">
<div style="margin-bottom: 10px;">
<!-- <div>状态:</div -->
<div class="flex">
<div class="item flex flex-center">
<i class="bg s1"></i>
<span class="text">正常</span>
</div>
<div class="item flex flex-center ">
<i class="bg s2"></i>
<span class="text">停机</span>
</div>
<div class="item flex flex-center ">
<i class="bg s3"></i>
<span class="text">异常</span>
</div>
<div class="item flex flex-center ">
<i class="bg s4"></i>
<span class="text">不在线</span>
</div>
</div>
</div>
<div class="flex flex-row-center">
关闭<u-switch v-model="value4" size="20"
inactiveColor="#ff0000"
activeColor="#00a403" style="margin: 0 10px;"></u-switch>开启
</div>
</div>
</div>
</div>
</uni-section>
</view>
<view class="content">
<view class='timer' @click="show = true">时间:{{ timer }}</view>
<view class="limit-timer" :class="infoData.powerLimitTime ? 'limit' : ''">{{ !infoData.powerLimitTime ? '未限制功率' : `限制功率: ${infoData.powerLimit.p}%` }}</view>
<echarts ref="echarts" class="uni-ec-canvas" :option="option" canvasId="echarts"></echarts>
<div class="report-item-bottom flex flex-column" v-show="type === 'first'">
<table border="2" :class="itemStateClass(infoData.state).clas">
<tr>
<td>直流电压V</td>
<td class="text-c">{{ infoData.dcVoltage }}</td>
</tr>
<tr>
<td>直流电流A</td>
<td class="text-c">{{ infoData.dcCurrent }}</td>
</tr>
<tr>
<tr>
<td>A相电压V</td>
<td class="text-c">{{ infoData.voltageA }}</td>
</tr>
<tr>
<tr>
<td>A相电流A</td>
<td class="text-c">{{ infoData.currentA }}</td>
</tr>
<tr>
<tr>
<td>B相电压V</td>
<td class="text-c">{{ infoData.voltageB }}</td>
</tr>
<tr>
<tr>
<td>B相电流A</td>
<td class="text-c">{{ infoData.currentB }}</td>
</tr>
<tr>
<tr>
<td>C相电压V</td>
<td class="text-c">{{ infoData.voltageC }}</td>
</tr>
<tr>
<tr>
<td>C相电流A</td>
<td class="text-c">{{ infoData.currentC }}</td>
</tr>
</table>
<table border="2" class="tb2" :class="itemStateClass(infoData.state).clas">
<tr>
<td>日发电量kWh</td>
<td class="text-c">{{ infoData.dayEnergy }}</td>
</tr>
<tr>
<td>总发电量kWh</td>
<td class="text-c">{{ infoData.totalEnergy }}</td>
</tr>
<tr>
<td>逆变器效率</td>
<td class="text-c">{{ infoData.inverterFrequency }}</td>
</tr>
<tr>
<td>并网频率</td>
<td class="text-c">{{ infoData.frequency }}</td>
</tr>
<tr>
<td>功率因数</td>
<td class="text-c">{{ infoData.powerFactor }}</td>
</tr>
<tr>
<td>散热器温度°C</td>
<td class="text-c">{{ infoData.temperature }}</td>
</tr>
<tr>
<td>额定输出功率kW</td>
<td class="text-c">{{ infoData.ratedOutputPower }}</td>
</tr>
<tr>
<td>无功功率kW</td>
<td class="text-c">{{ infoData.noHavePower }}</td>
</tr>
<tr>
<td>AB线电压V</td>
<td class="text-c">{{ infoData.voltageA }}</td>
</tr>
<tr>
<td>A相并网电流A</td>
<td class="text-c">{{ infoData.currentA }}</td>
</tr>
<tr>
<td>BC线电压V</td>
<td class="text-c">{{ infoData.voltageB }}</td>
</tr>
<tr>
<td>B相并网电流A</td>
<td class="text-c">{{ infoData.currentB }}</td>
</tr>
<tr>
<td>CA线电压V</td>
<td class="text-c">{{ infoData.voltageC }}</td>
</tr>
<tr>
<td>C相并网电流A</td>
<td class="text-c">{{ infoData.currentC }}</td>
</tr>
</table>
</div>
<div class="report-item-bottom flex flex-column " v-show="type === 'second'">
<table border="2" :class="itemStateClass(infoData.state).clas">
<tr v-for="(item, index) in infoData._itemList" :key="index">
<td>光伏组串{{index + 1 }}</td>
<td class="text-c">{{ item.value }}</td>
</tr>
</table>
<table border="2" class="tb2" :class="itemStateClass(infoData.state).clas">
<tr>
<td>机内空气温度</td>
<td class="text-c">{{ infoData.temperature }}</td>
</tr>
<tr>
<td>总有功功率</td>
<td class="text-c">{{ infoData.totalHavePower }}</td>
</tr>
<tr>
<td>总无功功率</td>
<td class="text-c">{{ infoData.noHavePower }}</td>
</tr>
<tr>
<td>电网频率</td>
<td class="text-c">{{ infoData.frequency }}</td>
</tr>
<tr>
<td>额定输出功率</td>
<td class="text-c">{{ infoData.ratedOutputPower }}</td>
</tr >
<tr>
<td>效率</td>
<td class="text-c">{{ infoData.inverterFrequency }}</td>
</tr>
<tr>
<td>今日发电</td>
<td class="text-c">{{ infoData.dayEnergy }}</td>
</tr>
<tr>
<td>累计发电</td>
<td class="text-c">{{ infoData.totalEnergy }}</td>
</tr>
<tr>
<td>总运行时长</td>
<td class="text-c">{{ infoData.totalRunTime }}</td>
</tr>
</table>
</div>
</view>
<u-datetime-picker
:show="show"
v-model="timeVal"
mode="date"
@confirm="handleConfirmTime"
@cancel="handleCanceTime"
></u-datetime-picker>
<u-modal :show="showConfig" @confirm="handleConfirmConfig" ref="uModal"
@cancel="handleCloseConfig"
:showCancelButton="true" :asyncClose="true">
<template class="slot-content">
<view style="width:100%">
<uni-section title="限制功率(%)(0-110)">
<template v-slot:right>
<uni-number-box v-model="numVal" :min="0" :step="0.1" :max="110" ></uni-number-box>
</template>
</uni-section>
</view>
</template>
</u-modal>
</view>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
import { scrollSmoothTo } from '@/common/util/index.js'
import echarts from '@/components/echarts-uniapp/echarts-uniapp.vue'
import '@/common/util/day.js'
import {
getBigScreenPowerPinLianSubarrayView,
getBigScreenPowerInverterInfo,
getBigScreenPowerInverterInfo2,
getBigScreenPowerInverterInfoMatrixView,
getBigScreenPowerInverterAGC,
getBigScreenPowerInverterMqttBySn
} from '@/api/modules/station.js'
import _mixin from './mixin.js'
import { AREA_SECOND_STATIONS, FIRST_STATIONS, UN_SHOW_STATIONS, END_NUM } from './config'
let myChart = null
let SECOND_VALUE = ''
let timer = null
export default {
components:{
echarts
},
mixins: [_mixin],
data() {
return {
value4: true,
show: false,
showConfig: false,
timeVal: Number(new Date()),
timer: new Date().format('YYYY-MM-DD'),
option: {},
secondValue: '',
secondSnNo: 0,
selectStations: [],
chartData: {},
type: 'first',
isChange: false,
secondList: [],
secondValueList: [],
infoData:{},
numVal: 0
}
},
mounted() {
this.init()
},
methods: {
handleCloseConfig() {
this.showConfig = false
},
handleConfirmConfig() {
const params = {
sn: this.secondSnNo,
powerLimit: this.numVal
}
if (timer && this.showConfig) return
clearTimeout(timer)
getBigScreenPowerInverterAGC(params).then(res => {
timer = setTimeout(() => {
this.handleSetConfig('second')
clearTimeout(timer)
timer = null
}, 3000)
})
},
handleSetConfig(type = 'init') {
if (!this.secondSnNo) return
const sn = this.secondSnNo
getBigScreenPowerInverterMqttBySn({ sn }).then(res => {
const data = res.data.data
if (!data) {
this.$refs.uToast.show({
message: '未配置系统参数',
type: 'default',
complete() {
}
})
return
}
this.showConfig = true
const mqtt = JSON.parse(data.mqttMessage)
this.numVal = mqtt.p
const that = this
if (type !== 'init') {
that.showConfig = false
this.$refs.uToast.show({
message: '配置成功',
type: 'default',
complete() {
}
})
}
})
},
handlerBack() {
this.$router.replace('/pages/station/item1')
},
onChangeTime(val) {
this.timer = val
this.timeVal = Number(new Date())
this.getChartDataList()
},
onchange(e) {
this.selectStations = e.detail.value.map(v => v.value)
uni.setStorageSync('_pro_selectStation', this.selectStations)
this.isChange = true
SECOND_VALUE = null
this.type = AREA_SECOND_STATIONS.includes(this.selectStations[1]) ? 'second' : FIRST_STATIONS.includes(this.selectStations[0]) ? 'first' : 'second'
this.getDataListInfo(this.type, 'change')
scrollSmoothTo()
},
onchange1(e) {
this.secondValueList = e.detail.value.map(v => v.value)
this.secondValue = this.secondValueList[0]
this.isChange = true
this.secondSnNo = this.secondList.filter(v => v.value === this.secondValue)[0].snNo
this.getDataInfo(this.secondValue)
this.onChangeTime(new Date().format('YYYY-MM-DD'))
scrollSmoothTo()
},
init() {
const hasLocalData = this.selectStations.length
const stationId = hasLocalData ? this.selectStations[0] : this.$route.query.id
const data = hasLocalData ? this.selectStations : [stationId, this.$route.query.sd]
this.selectStations = data
this.station = this.stationData.filter(v => {
return v.id === data[0]
})[0]
this.type = AREA_SECOND_STATIONS.includes(data[1]) ? 'second' : FIRST_STATIONS.includes(data[0]) ? 'first' : 'second'
uni.setStorageSync('_pro_selectStation', this.selectStations)
this.getDataListInfo(this.type)
},
getDataListInfo(type = 'first', type1 = 'base') {
const data = {
stationId: this.selectStations[0],
powerStationPartitionId: this.selectStations[1]
}
const METHOD = {
'first': getBigScreenPowerInverterInfo,
'second': getBigScreenPowerInverterInfo2
}
METHOD[type](data).then(res => {
this.handleFilterSecondArr(res.data.data, type, type1)
})
},
handleFilterSecondArr(arr, type, type1) {
if (type === 'second') {
const item = arr[0]
const maxNum = this._getMaxNum(item)
this.secondList = Array.from(Array(maxNum)).map((v, inde) => {
const index = inde + 1
return {
name: item[`equipmentName${index}`].split(' ')[1],
value: item[`inverterInfoId${index}`],
snNo: item[`sn${index}`]
}
})
} else {
this.secondList = arr.map(v => {
return {
name: v.equipmentName.split(' ')[1],
value: v.id,
snNo: v.sn
}
})
}
const seList = this.secondList.filter(v => v.value === this.$route.query.ud)
this.secondValue = SECOND_VALUE ? SECOND_VALUE : type1 !== 'base' ?
this.secondList[0].value : seList.length ? seList[0].value : this.secondList[0].value
this.secondSnNo = this.secondList.filter(v => v.value === this.secondValue)[0].snNo
this.secondValueList = [this.secondValue]
this.getDataInfo(this.secondValue)
this.getChartDataList()
},
getDataInfo(inverterInfoId) {
getBigScreenPowerInverterInfoMatrixView(
{
inverterInfoId
}
).then(res => {
const _data = res.data.data
const maxNum = this._getMaxNum(_data, 'electricQuantity')
const _itemList = Array.from(Array(maxNum)).map((v, inde) => {
const index = inde + 1
return {
value: _data[`electricQuantity${index}`],
}
})
_data._name = _data.equipmentName.split(' ')[1]
_data.powerLimit = JSON.parse( _data.powerLimit)
this.infoData = Object.assign({_itemList }, _data)
})
},
handleConfirmTime(e) {
this.timer = new Date(e.value).format('YYYY-MM-DD')
this.show = false
this.getChartDataList()
},
handleCanceTime() {
this.show = false
},
getChartDataList() {
const data = {
createTime: this.timer,
id: this.secondValue,
stationId: this.selectStations[0]
}
const type1 = '日发电量'
const type2 = '总有功功率'
const type3 = '总直流电流'
const types = [type1, type2, type3]
getBigScreenPowerPinLianSubarrayView(data).then(res => {
if (!res.data.data.length) {
this.setChartData()
return
}
const map = new Map()
res.data.data.forEach(v => {
types.forEach(cv => {
if (v.type.includes(cv)) {
if (map.has(cv)) {
const item = map.get(cv)
item[v.name] = v
map.set(cv, item)
} else {
map.set(cv, {[v.name]: v})
}
}
})
})
function resetList(list) {
if (!list) return []
return list.filter(v => {
const [hour, mint] = v.name.split(':')
const _hour = Number(hour)
const _mint = Number(mint)
if (_hour >= 5 && _mint > 0) {
return v
}
})
}
this.chartData = {
type1: {
name: type1,
list: resetList(Object.values(map.get(type1))) || []
},
type2: {
name: type2,
list: resetList(Object.values(map.get(type2))) || []
},
type3: {
name: type3,
list: resetList(Object.values(map.get(type3))) || []
}
}
this.setChartData(this.chartData)
})
},
setChartData(chartData = {}, type = '') {
console.log(chartData)
if (!Object.keys(chartData).length) {
this.option = option
return
}
const option = this.getChartOption(chartData)
this.option = option
},
_getMaxNum(item, text = 'dayEnergy') {
if (text !== 'dayEnergy') {
const eightNumArr = ['1052758023'] // 塞纳8个组串
const sixteenNumArr = ['30'] // 16个组串
return eightNumArr.includes(this.selectStations[0]) ? 8 :
sixteenNumArr.includes(this.selectStations[1]) ? 16 : 12
}
let maxNum = 1
for (let i = 1; i<= 50; i++) {
if (!item[text + i]) {
maxNum = i-1
break
}
}
return maxNum
},
getChartOption(chartData = {}) {
return {
legend: {
data: Object.values(chartData).map(v => v.name),
textStyle: {
color: '#f2f2f2'
}
},
tooltip: {
trigger: 'axis',
formatter(params) {
let html = ''
params.forEach(v => {
const type = v.seriesName.includes('日发电量') ? 'kWh' : v.seriesName.includes('有功功率') ? 'kW' : 'A'
html += `\n${v.marker} ${v.seriesName} ${v.value} ${type}`
})
return `\t${params[0].name}\t ${html}\t`
}
},
grid: {
top: '20%',
left: '3%',
right: '3%',
bottom: '10%',
// 包含文本
containLabel: true,
// 是否显示网格线
show: false,
// 边框颜色
borderColor: 'rgba(0, 240, 255, 0.3)',
},
xAxis: {
type: 'category',
// show: false,
data: chartData.type1.list.map(v => v.name),
axisLabel: {
color: "#f2f2f2"
}
},
yAxis: [
{
name: 'kW/A',
type: 'value',
nameTextStyle: {
color: "#f2f2f2"
},
axisLabel: {
color: "#f2f2f2"
},
axisLine: {
onZero: false,
show: true,
color: 'red'
},
// max: 10
},
{
name: 'kWh',
type: 'value',
nameTextStyle: {
color: "#f2f2f2"
},
axisLabel: {
color: "#f2f2f2"
},
axisLine: {
onZero: false,
show: true,
color: 'red'
},
}
],
series: [
{
data: chartData.type1.list,
type: 'line',
lineStyle: {
color: "#00ec03"
},
itemStyle: {
opacity: 0
},
color: "#00ec03",
name: chartData.type1.name,
yAxisIndex: 1,
},
{
data: chartData.type2.list,
type: 'line',
lineStyle: {
color: "#e000ec"
},
itemStyle: {
opacity: 0
},
color: "#e000ec",
name: chartData.type2.name,
},
{
data: chartData.type3.list,
type: 'line',
// lineStyle: {
// color: "#e000ec"
// },
itemStyle: {
opacity: 0
},
name: chartData.type3.name
}
]
}
},
}
}
</script>
<style lang="scss">
/deep/ .selected-list{
align-items: center;
}
.uni-ec-canvas{
width:100%;
height:550upx;
display:block;
}
.container {
padding: 20upx 0;
}
.content {
position: relative;
padding: 20upx;
.timer {
margin-bottom: 20upx;
}
.limit-timer {
position: absolute;
right: 20upx;
top: 20upx;
&.limit {
color: #fc6;
}
}
background: #000;
color: #f5f5f5;
}
.search-box {
position: sticky;
top: 90upx;
width: 100%;
z-index: 2;
}
.report-status {
color: #555;
transform: scale(0.85);
// margin-left: -110upx;
padding-bottom: 20upx;
.report-status-item {
margin-top: 4upx;
}
.item {
margin-right: 10upx;
.bg {
display: block;
width: 50upx;
height: 30upx;
margin-right: 2px;
border: 1px solid #ccc;
&.s1 {
background: #00a403;
}
&.s2 {
background: #ff0000;
}
&.s3 {
background: #fc6;
}
&.s4 {
background: #808080;
}
}
}
}
.report-item-bottom {
padding: 40upx;
table.tb2 {
margin-top: 20upx;
}
tr td {
width: 50%;
}
table {
&.z1 {
// color: #00d40e;
tr td{
&:nth-child(2) {
color: #00d40e;
}
}
}
&.z2 {
// color: #ff0000;
td{
&:nth-child(2) {
color: #ff0000;
}
}
}
&.z3 {
// color: #fc6;
tr td{
&:nth-child(2) {
color: #fc6;
}
}
}
&.z4 {
// color: #808080;
td{
&:nth-child(2) {
color: #808080;
}
}
}
}
}
</style>