Files
app-govern/pages/device/APF/comp/power.vue
2026-07-02 09:55:30 +08:00

304 lines
9.8 KiB
Vue

<template>
<view>
<uni-load-more status="loading" v-if="!isDataReady"></uni-load-more>
<view class="basic mt10" v-else>
<view class="params-wrap" v-for="(section, ind) in sections" :key="section.title">
<view class="section-title-row">
<view class="section-title">
<view class="grid-card-title-with-icon"></view>
<text>{{ section.title }}</text>
</view>
<view class="legend-row" v-if="ind == 0">
<view class="legend-item" v-for="phase in phaseColors" :key="phase.name">
<view class="legend-dot" :style="{ background: phase.color }" />
<text class="legend-text">{{ phase.name }}</text>
</view>
</view>
</view>
<view class="params-section">
<view v-for="(rowItems, rowIdx) in chunkedChildren(getSectionCards(section))" :key="rowIdx"
class="double-row">
<view v-for="(child, childIdx) in rowItems" :key="childIdx" class="param-group">
<view class="param-title">
<text>{{ child.name }}</text>
</view>
<view class="phase-vertical">
<view class="phase-item-vertical">
<text class="phase-value-vertical" :style="{ color: phaseColors[0].color }">{{
child.A
}}</text>
</view>
<view class="phase-divider" />
<view class="phase-item-vertical">
<text class="phase-value-vertical" :style="{ color: phaseColors[1].color }">{{
child.B
}}</text>
</view>
<view class="phase-divider" />
<view class="phase-item-vertical">
<text class="phase-value-vertical" :style="{ color: phaseColors[2].color }">{{
child.C
}}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const SECTIONS = [
{
title: '电网侧',
dataKey: '电网侧',
metrics: [
{ label: '有功功率(kW)', field: 'Apf_P_Sys(W)', divide: 1000 },
{ label: '无功功率(kVar)', field: 'Apf_Q_Sys(Var)', divide: 1000 },
{ label: '视在功率(kVA)', field: 'Apf_S_Sys(VA)', divide: 1000 },
{ label: '功率因数', field: 'Apf_PF_Sys(null)' },
],
},
{
title: '负载侧',
dataKey: '负载侧',
metrics: [
{ label: '有功功率(kW)', field: 'Apf_P_Load(W)', divide: 1000 },
{ label: '无功功率(kVar)', field: 'Apf_Q_Load(Var)', divide: 1000 },
{ label: '视在功率(kVA)', field: 'Apf_S_Load(VA)', divide: 1000 },
{ label: '功率因数', field: 'Apf_PF_Load(null)' },
],
},
]
export default {
props: {
basicData: {
type: Array,
default: () => [],
},
},
data() {
return {
sections: SECTIONS,
phaseColors: [
{ name: 'A相', color: '#DAA520' },
{ name: 'B相', color: '#2E8B57' },
{ name: 'C相', color: '#D54941' },
],
renderData: {
电网侧: [],
负载侧: [],
未知: [],
},
}
},
computed: {
isDataReady() {
return this.renderData.电网侧.length > 0 && this.renderData.负载侧.length > 0
},
},
watch: {
basicData: {
handler(newVal) {
this.renderData = {
电网侧: [],
负载侧: [],
未知: [],
}
; (newVal || []).forEach((item) => {
if (item.phase === 'avg') {
return
}
const key = this.getDataKey(item.statisticalName)
const index = this.renderData[key].findIndex((item2) => item2.phase === item.phase)
if (index > -1) {
this.renderData[key][index][item.statisticalName] = this.normalizeStatisticalData(
item.statisticalData,
)
} else {
this.renderData[key].push({
phase: item.phase,
[item.statisticalName]: this.normalizeStatisticalData(item.statisticalData),
})
}
})
},
deep: true,
immediate: true,
},
},
methods: {
getDataKey(statisticalName) {
switch (statisticalName) {
case 'Apf_P_Sys(W)':
case 'Apf_Q_Sys(Var)':
case 'Apf_S_Sys(VA)':
case 'Apf_PF_Sys(null)':
return '电网侧'
case 'Apf_P_Load(W)':
case 'Apf_Q_Load(Var)':
case 'Apf_S_Load(VA)':
case 'Apf_PF_Load(null)':
return '负载侧'
default:
return '未知'
}
},
getSectionCards(section) {
const list = this.renderData[section.dataKey] || []
return section.metrics.map((metric) => ({
name: metric.label,
A: this.getPhaseValue(list, metric, 'A'),
B: this.getPhaseValue(list, metric, 'B'),
C: this.getPhaseValue(list, metric, 'C'),
}))
},
getPhaseValue(list, metric, phase = 'A') {
const item = list.find((row) => String(row.phase || '').toUpperCase() === phase)
return item ? this.formatValue(item[metric.field], metric.divide) : '-'
},
normalizeStatisticalData(value) {
if (value == 3.1415926 || value === null || value === '') {
return '-'
}
return value
},
formatValue(value, divide) {
if (value == 3.1415926 || value === null || value === '' || value === '-') {
return '-'
}
const num = Number(value)
if (Number.isNaN(num)) return value
if (divide) {
// return (num / divide).toFixed(2)
return num != 0 ? (num / divide).toFixed(2) : num.toFixed(2)
}
return num.toFixed(2)
},
chunkedChildren(children) {
const result = []
for (let i = 0; i < children.length; i += 2) {
result.push(children.slice(i, i + 2))
}
return result
},
},
}
</script>
<style lang="scss" scoped>
.basic {
.params-wrap {
margin-bottom: 20rpx;
background-color: #fff;
border-radius: 10rpx;
overflow: hidden;
}
.section-title-row {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12rpx;
padding: 20rpx 20rpx 0;
}
.section-title {
display: flex;
align-items: center;
font-size: 30rpx;
font-weight: 600;
color: #333;
}
.grid-card-title-with-icon {
display: inline-flex;
align-items: center;
position: relative;
width: 8rpx;
height: 28rpx;
margin-right: 12rpx;
background: $uni-theme-color;
border-radius: 3rpx;
}
.legend-row {
display: flex;
gap: 20rpx;
align-items: center;
}
.legend-item {
display: flex;
align-items: center;
gap: 8rpx;
}
.legend-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
}
.legend-text {
font-size: 24rpx;
color: #666666;
}
.params-section {
padding: 16rpx 16rpx 20rpx;
}
.double-row {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16rpx;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
}
.param-group {
min-width: 0;
background: #f3f3f3;
border-radius: 16rpx;
padding: 16rpx 8rpx 12rpx;
}
.param-title {
font-size: 26rpx;
color: #666666;
margin-bottom: 8rpx;
padding-left: 4rpx;
}
.phase-vertical {
display: flex;
align-items: stretch;
}
.phase-item-vertical {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 4rpx;
}
.phase-value-vertical {
font-size: 26rpx;
font-weight: 700;
}
.phase-divider {
width: 1px;
background: #d2d2d2;
margin: 8rpx 0;
}
}
</style>