Files
app-govern/pages/device/APF/comp/basic.vue
2026-06-29 11:01:44 +08:00

318 lines
10 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: '有效值(A)', field: 'Apf_RmsI_Sys(A)' },
{ label: '畸变率(%)', field: 'Apf_ThdA_Sys(%)' },
],
},
{
title: '电网电压',
dataKey: '电网电压',
metrics: [
{ label: '电压(V)', field: 'Apf_PhV_Sys(V)' },
{ label: '频率(Hz)', field: 'Apf_Freq(Hz)' },
{ label: '畸变率(%)', field: 'Apf_ThdU_Sys(%)' },
],
},
{
title: '负载电流',
dataKey: '负载电流',
metrics: [
{ label: '有效值(A)', field: 'Apf_RmsI_Load(A)' },
{ label: '畸变率(%)', field: 'Apf_ThdA_Load(%)' },
],
},
{
title: '补偿电流',
dataKey: '补偿电流',
metrics: [
{ label: '有效值(A)', field: 'Apf_RmsI_TolOut(A)' },
{ label: '负载率(%)', field: 'load_Rate' },
],
},
]
export default {
props: {
basicData: {
type: Array,
default: () => [],
},
},
data() {
return {
sections: SECTIONS,
phaseColors: [
{ name: 'A相', color: '#F1B22E' },
{ name: 'B相', color: '#2BA471' },
{ name: 'C相', color: '#D54941' },
],
renderData: {
电网电流: [],
电网电压: [],
负载电流: [],
补偿电流: [],
未知: [],
},
}
},
computed: {
isDataReady() {
return (
this.renderData.电网电流.length > 0 &&
this.renderData.电网电压.length > 0 &&
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] = item.statisticalData
} else {
this.renderData[key].push({
phase: item.phase,
[item.statisticalName]: item.statisticalData,
})
}
})
},
deep: true,
immediate: true,
},
},
methods: {
getDataKey(statisticalName) {
switch (statisticalName) {
case 'Apf_RmsI_Sys(A)':
case 'Apf_ThdA_Sys(%)':
return '电网电流'
case 'Apf_PhV_Sys(V)':
case 'Apf_Freq(Hz)':
case 'Apf_ThdU_Sys(%)':
return '电网电压'
case 'Apf_RmsI_Load(A)':
case 'Apf_ThdA_Load(%)':
return '负载电流'
case 'Apf_RmsI_TolOut(A)':
case 'load_Rate':
return '补偿电流'
default:
return '未知'
}
},
getSectionCards(section) {
const list = this.renderData[section.dataKey] || []
return section.metrics.map((metric) => ({
name: metric.label,
A: this.getPhaseValue(list, metric.field, 'A'),
B: this.getPhaseValue(list, metric.field, 'B'),
C: this.getPhaseValue(list, metric.field, 'C'),
}))
},
getPhaseValue(list, field, phase) {
const item = list.find(
(row) => String(row.phase || '').toUpperCase() === phase,
)
return this.formatValue(item[field])
},
formatValue(value) {
if (value == 3.1415926) return '-'
if (value === undefined || value === null || value === '') return '-'
const num = Number(value)
if (Number.isNaN(num)) return value
return num > 0 ? num.toFixed(2) : num
},
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: 28rpx;
font-weight: 700;
}
.phase-divider {
width: 1px;
background: #d2d2d2;
margin: 8rpx 0;
}
}
</style>