Files
smart_storage_app/pages/home-page/device-detail/components/jingke-pcs.vue
2025-07-01 16:59:10 +08:00

963 lines
23 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>
<view class="pcs-warp">
<view class="box">
<view class="topology-box">
<Section title="PCS拓扑图" />
<topoCanvas cId="canvas" :width="'100%'" :height="'100%'" :canvas-data="canvasData" />
</view>
</view>
<view class="box">
<Section title="总有功/总无功功率">
<view slot="right" style="flex: 1">
<view class="top-right-box">
<span class="time">时间粒度:</span>
<span @click="openTimeAction" class="value">{{sampleTime}}</span>
</view>
</view>
</Section>
<view class="chart-box">
<zero-loading v-if="chartLoading" position="absolute"></zero-loading>
<charts v-else id="pcsChart" :options="curve_option"></charts>
</view>
</view>
<view class="box" style="margin-bottom: 0;">
<Section title="设备数据" />
<zero-loading v-if="stationLoading" position="absolute"></zero-loading>
<view v-else class="group-box">
<view class="group-item border-right" @click="showHistory('交流有功', 'outputPower', 'pcs', 'kW', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/zjrl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.outputPower | kWFormat }}
</view>
<view class="item-title">交流有功({{panelData.outputPower | kwUnitFormat }})</view>
</view>
</view>
<view class="group-item border-right"
@click="showHistory('交流无功', 'reactivePowerPCS', 'pcs', 'kVar', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/zjrl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.reactivePowerPCS }}
</view>
<view class="item-title">交流无功(kVar)</view>
</view>
</view>
<view class="group-item" @click="showHistory('电网频率', 'grid', 'pcs', 'Hz', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/dqgl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.grid | isNull }}
</view>
<view class="item-title">电网频率(Hz)</view>
</view>
</view>
<view class="group-item border-right" @click="showHistory('UV电压', 'volA', 'pcs', 'V', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/zfdl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.volA | isNull }}
</view>
<view class="item-title">UV电压(V)</view>
</view>
</view>
<view class="group-item border-right" @click="showHistory('VW电压', 'volB', 'pcs', 'V', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/zfdl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.volB | isNull }}
</view>
<view class="item-title">VW电压(V)</view>
</view>
</view>
<view class="group-item" @click="showHistory('WU电压', 'volC', 'pcs', 'V', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/zfdl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.volC | isNull }}
</view>
<view class="item-title">WU电压(V)</view>
</view>
</view>
<view class="group-item border-right" @click="showHistory('U相电流', 'currentA', 'pcs', 'A', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/rfdl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.currentA | isNull }}
</view>
<view class="item-title">U相电流(A)</view>
</view>
</view>
<view class="group-item border-right" @click="showHistory('V相电流', 'currentB', 'pcs', 'A', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/rfdl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.currentB | isNull }}
</view>
<view class="item-title">V相电流(A)</view>
</view>
</view>
<view class="group-item" @click="showHistory('W相电流', 'currentC', 'pcs', 'A', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/rfdl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.currentC | isNull }}
</view>
<view class="item-title">W相电流(A)</view>
</view>
</view>
<view class="group-item border-right" @click="showHistory('直流功率', 'dcPower', 'pcs', 'kW', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/dqgl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.dcPower | kWFormat }}</view>
<view class="item-title">直流功率({{ panelData.dcPower | kwUnitFormat}})</view>
</view>
</view>
<view class="group-item border-right" @click="showHistory('直流电压', 'dcInputVol', 'pcs', 'V', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/zfdl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.dcInputVol | isNull }}
</view>
<view class="item-title">直流电压(V)</view>
</view>
</view>
<view class="group-item" @click="showHistory('直流电流', 'dcCurrent', 'pcs', 'A', '')">
<view class="history-icon">
<image src="/static/aidex/images/history-icon.png"></image>
</view>
<view class="item-icon">
<image src="/static/aidex/images/rfdl.png"></image>
</view>
<view class="item-con">
<view class="item-num">{{ panelData.dcCurrent | isNull }}
</view>
<view class="item-title">直流电流(A)</view>
</view>
</view>
</view>
</view>
<historyModal :is-show.sync="histroyShow" :title="chartTitle" :params="hisParams" />
<u-action-sheet :list="actionSheetList" v-model="timeShow" @click="actionSheetCallback" @close="closeTimeAction"
:mask-close-able="false">
</u-action-sheet>
</view>
</template>
<script>
import topoCanvas from '@/components/new-canvas/index.vue'
import historyModal from '@/components/history-modal/index.vue'
import Section from '@/components/section/index.vue'
import charts from "@/components/charts/index";
export default {
components: {
historyModal,
Section,
charts,
topoCanvas
},
data() {
return {
sampleTime: "1分钟",
curve_option: {},
panelData: {},
histroyShow: false,
chartTitle: null,
chartLoading: false,
stationLoading: false,
hisParams: {},
timeShow: false,
time: "1",
stationId: null,
srcId: null,
hnStationId: [417, 398, 416, 415, 405, 485],
actionSheetList: [{
text: "1分钟",
value: 1,
},
{
text: "5分钟",
value: 5,
},
{
text: "10分钟",
value: 10,
},
{
text: "15分钟",
value: 15,
},
{
text: "20分钟",
value: 20,
},
{
text: "30分钟",
value: 30,
},
],
canvasData: [],
textCanvasData: [
//0
{
type: "text",
coord: [
[190, 70]
],
font: [{
text: "交流断路器",
size: 12,
color: "#333333",
}, ],
},
//1
{
type: "text",
coord: [
[190, 200]
],
font: [{
text: "直流断路器",
size: 12,
color: "#333333",
}, ],
},
//2
{
type: "text",
coord: [
[200, 260]
],
font: [{
text: "SOC",
size: 12,
color: "#999",
},
{
text: "20%",
size: 12,
color: "#000",
left: 35
},
],
},
//3
{
type: "text",
coord: [
[35, 80]
],
font: [{
text: "运行状态:",
size: 14,
color: "#999999",
width: 50,
},
{
text: "",
size: 14,
color: "#000000",
left: 65
},
],
},
//4
{
type: "text",
coord: [
[3, 100]
],
font: [{
text: "远方/就地状态:",
size: 14,
color: "#999999",
width: 50,
},
{
text: "",
size: 14,
color: "#000000",
left: 98
},
],
},
//5
{
type: "text",
coord: [
[20, 120]
],
font: [{
text: "并离网状态:",
size: 14,
color: "#999999",
width: 50,
},
{
text: "",
size: 14,
color: "#000",
left: 80
},
],
},
],
imageCanvasData: [{
//AC/DC
type: "image",
url: "/static/topology/DC.png",
coord: [
[140, 110],
[60, 60],
],
},
{
//电池
type: "image",
url: "/static/topology/battary.png",
coord: [
[144, 240],
[50, 50],
],
},
],
lineCanvasData: [{
type: "line",
coord: [
[60, 30],
[280, 30],
],
color: "#19875c",
width: 2,
},
{
type: "line",
coord: [
[170, 30],
[170, 240],
],
color: "#19875c",
width: 2,
},
],
rectCanvasData: [{
type: "rect",
background: "#19875c",
coord: [
[160, 50],
[20, 30],
],
},
{
type: "rect",
background: "#19875c",
coord: [
[160, 180],
[20, 30],
],
},
]
}
},
computed: {
currentStation() {
return this.vuex_currentStation;
},
},
watch: {
currentStation: {
handler(val) {
this.stationId = val.id;
},
deep: true,
immediate: true,
},
},
methods: {
getData(srcId) {
this.srcId = srcId
this.getChart()
this.getStatus()
},
getChart() {
const self = this
self.chartLoading = true;
return new Promise((resolve, reject) => {
self.$u.api.deviceList
.GetPCSCurve({
stationId: this.stationId,
sampleTime: this.time,
srcId: this.srcId,
})
.then((res) => {
self.chartLoading = false;
self.initChargeChart(res.data);
// this.getStatus();
resolve(res);
})
.catch((err) => {
reject("错误");
})
.finally(() => {
self.chartLoading = false;
});
});
},
initChargeChart(val) {
const gonglv = [];
const soc = [];
const xAxis = [];
val.forEach((v) => {
xAxis.push(v.data);
gonglv.push(v.pcsRealTimeCurve);
soc.push(v.inCoreDataCurve);
});
this.curve_option = {
color: ["#009458", "#BFE49F"],
animationDuration: 500,
animationEasing: "cubicInOut",
tooltip: {
trigger: "axis",
axisPointer: {},
confine: true,
textStyle: {
textShadowBlur: 10, // 重点
textShadowColor: 'transparent', // 重点
},
position: function(point, params, dom, rect, size) {
// 鼠标坐标和提示框位置的参考坐标系是以外层div的左上角那一点为原点x轴向右y轴向下
// 提示框位置
var x = 0; // x坐标位置
var y = 0; // y坐标位置
// 当前鼠标位置
var pointX = point[0];
var pointY = point[1];
// 外层div大小
// var viewWidth = size.viewSize[0];
// var viewHeight = size.viewSize[1];
// 提示框大小
var boxWidth = size.contentSize[0];
var boxHeight = size.contentSize[1];
// boxWidth > pointX 说明鼠标左边放不下提示框
if (boxWidth > pointX) {
x = 5; // 自己定个x坐标值以防出屏
y -= 15; // 防止点被覆盖住,可根据情况自行调节
} else {
// 左边放的下
x = pointX - boxWidth - 15;
}
// boxHeight > pointY 说明鼠标上边放不下提示框
if (boxHeight + 20 > pointY) {
y = pointY + 15;
} else if (boxHeight > pointY) {
y = 5;
} else {
// 上边放得下
y += pointY - boxHeight;
}
return [x, y];
},
},
grid: {
top: "15%",
left: "5%",
right: "3%",
bottom: "10%",
containLabel: true,
},
xAxis: {
type: "category",
data: xAxis,
splitLine: {
show: false,
},
axisLabel: {
show: true,
color: "#A3A3A3",
formatter: function(params) {
let newParamsName = "";
const paramsNameNumber = params.length; // 文字总长度
const provideNumber = 11; //一行显示几个字
const rowNumber = Math.ceil(paramsNameNumber / provideNumber);
if (paramsNameNumber > provideNumber) {
for (let p = 0; p < rowNumber; p++) {
const start = p * provideNumber;
const end = start + provideNumber;
const tempStr =
p === rowNumber - 1 ?
params.substring(start, paramsNameNumber) :
params.substring(start, end) + "\n";
newParamsName += tempStr;
}
} else {
newParamsName = params;
}
return newParamsName;
},
},
},
yAxis: [{
type: "value",
name: "总有功功率(kW)",
axisLabel: {
color: "#A3A3A3",
},
nameTextStyle: {
color: "#A3A3A3",
},
min: function(value) {
return Math.floor(
(Math.abs(value.min) < value.max ?
-value.max * 1.05 :
value.min * 1.05
).toFixed(2)
);
},
max: function(value) {
return Math.ceil(
(Math.abs(value.min) < value.max ?
value.max * 1.05 :
-value.min * 1.05
).toFixed(2)
);
},
nameTextStyle: {
fontSize: 12,
padding: [0, 0, 0, 30],
},
},
{
type: "value",
name: "总无功功率(kVar)",
axisLabel: {
color: "#A3A3A3",
},
nameTextStyle: {
color: "#A3A3A3",
},
min: function(value) {
return Math.floor(
(Math.abs(value.min) < value.max ?
-value.max * 1.05 :
value.min * 1.05
).toFixed(2)
);
},
max: function(value) {
return Math.ceil(
(Math.abs(value.min) < value.max ?
value.max * 1.05 :
-value.min * 1.05
).toFixed(2)
);
},
nameTextStyle: {
fontSize: 12,
padding: [0, 0, 0, -40],
},
},
],
dataZoom: {
type: "inside",
},
series: [{
name: "总有功功率",
type: "line",
smooth: true,
symbol: "none",
areaStyle: {
opacity: 0,
},
// lineStyle: {
// color: '#00C8FF'
// },
data: gonglv,
},
{
name: "总无功功率",
type: "line",
smooth: true,
yAxisIndex: 1,
symbol: "none",
areaStyle: {
opacity: 0,
},
// lineStyle: {
// color: '#FBBB11'
// },
data: soc,
},
],
};
},
openTimeAction() {
this.timeShow = true;
},
getStatus() {
this.stationLoading = true
const self = this;
this.$u.api.deviceList
.GetNewValue({
stationId: this.stationId,
srcId: this.srcId,
colList: [
"outputPower", "reactivePowerPCS", "grid", "volA", "volB", "volC", "currentA",
"currentB", "currentC",
"dcPower", "dcInputVol", "dcCurrent", "workStatus", "remoteInPlace", "andOffGrid",
"stateCharging", "stateDischarging",
"deviceStateStand", "deviceStateFault", "deviceStateFull", "deviceStateEmpty",
"acBreaker", "dcBreaker", "eStop", "soc"
],
})
.then((res) => {
const battery = self.updateBattery(res.data.soc?.value ? res.data.soc.value : 0);
self.rectCanvasData.push(battery)
this.panelData = {
outputPower: res.data.outputPower?.value ? res.data.outputPower?.value : 0,
reactivePowerPCS: res.data.reactivePowerPCS?.value ? res.data.reactivePowerPCS?.value : 0,
grid: res.data.grid?.value ? res.data.grid?.value : 0,
volA: res.data.volA?.value ? res.data.volA?.value : 0,
volB: res.data.volB?.value ? res.data.volB?.value : 0,
volC: res.data.volC?.value ? res.data.volC?.value : 0,
currentA: res.data.currentA?.value ? res.data.currentA?.value: 0,
currentB: res.data.currentB?.value ? res.data.currentB?.value : 0,
currentC: res.data.currentC?.value ? res.data.currentC?.value: 0,
dcPower: res.data.dcPower?.value ? res.data.dcPower?.value: 0,
dcInputVol: res.data.dcInputVol?.value ? res.data.dcInputVol?.value:0,
dcCurrent: res.data.dcCurrent?.value ? res.data.dcCurrent?.value:0,
};
this.textCanvasData[2].font[1].text = res.data.soc?.value ? res.data.soc.value + "%" : 0 + "%";
this.textCanvasData[3].font[1].text = this.workStatus(res.data.workStatus?.value);
this.textCanvasData[4].font[1].text = this.GetAndOffGridStatus(res.data.andOffGrid?.value)
if (!res.data.acBreaker?.value) {
self.rectCanvasData[0].background = "rgba(25, 135, 92, 0.6y4)";
}
if (!res.data.dcBreaker?.value) {
self.rectCanvasData[1].background = "rgba(25, 135, 92, 0.6y4)";
}
this.canvasData = [...this.textCanvasData, ...this.imageCanvasData, ...this
.lineCanvasData, ...this.rectCanvasData
]
})
.finally((res) => {
this.stationLoading = false;
});
},
GetAndOffGridStatus(val) {
if (val === 1) {
return '离网'
} else {
return '并网'
}
},
updateBattery(value) {
//电池电量 value 为0~100
const batteryHeight = 0.35 * value;
const battery = {
type: "rect",
background: "#19875c",
coord: [
[156, 285 - batteryHeight],
[25.5, batteryHeight],
],
};
return battery;
},
getRunState(value, value2) {
if (value) {
return "待机";
} else if (value2) {
return "故障";
} else {
return "";
}
},
// 设备工作状态
workStatus(val) {
if (val === 0) {
return '停机'
} else if (val === 1) {
return '待机'
} else if (val === 2) {
return '故障'
} else if (val === 3) {
return '充电'
} else if (val === 4) {
return '放电'
} else if (val === 5) {
return '充电降额'
} else if (val === 6) {
return '放电降额'
}
},
// 充放电状态
getFlowDirection(val, flowDirection) {
if (flowDirection === 2) {
if (+val > +1) {
return '放电'
}
if (+val < -1) {
return '充电'
}
if (val < 1 || +val.abs < 1 || !val) {
return '静置'
}
} else {
if (+val > +1) {
return '充电'
}
if (+val < -1) {
return '放电'
}
if (val < 1 || +val.abs < 1 || !val) {
return '静置'
}
}
},
// 电池信息
updateBattery(value) {
//电池电量 value 为0~100
const batteryHeight = 0.35 * value;
const battery = {
type: "rect",
background: "#19875c",
coord: [
[156, 285 - batteryHeight],
[25.5, batteryHeight],
],
};
return battery;
},
// 历史曲线
showHistory(name, modelCol, modelType, unit) {
this.hisParams = {
modelCol: modelCol,
modelType: modelType,
unit: unit
}
this.chartTitle = name
this.histroyShow = true
},
actionSheetCallback(index) {
this.sampleTime = this.actionSheetList[index].text;
this.time = this.actionSheetList[index].value;
this.getChart()
},
closeTimeAction() {
this.timeShow = false;
},
}
}
</script>
<style lang="scss" scoped>
.pcs-warp {
background-color: #f5f5f5;
.box {
background: #ffffff;
border-radius: 16rpx;
padding: 20rpx;
width: 100%;
border: 16rpx;
box-shadow: 0px 4rpx 16rpx rgba(0, 0, 0, 0.08);
margin-bottom: 20rpx;
position: relative;
.topology-box {
width: 100%;
height: 640rpx;
position: relative;
}
.top-right-box {
display: flex;
flex-direction: row;
align-items: center;
flex: 1;
justify-content: flex-end;
.time {
padding-right: 10rpx;
display: inline-block;
height: 40rpx;
}
.value {
background-color: #f5f5f5;
border-radius: 8rpx;
padding: 10rpx 20rpx 10rpx 20rpx;
height: 40rpx;
}
.top-right-item {
padding: 5rpx 10rpx;
border: 1rpx solid #4c9ee6;
font-size: 24rpx;
color: #4c9ee6;
cursor: pointer;
margin-right: 10rpx;
border-radius: 8rpx;
&.active {
background: #4c9ee6;
color: #ffffff;
}
}
}
.chart-box {
width: 650rpx;
height: 460rpx;
margin-top: 20rpx;
position: relative;
}
.group-box {
width: 100%;
display: flex;
flex-wrap: wrap;
position: relative;
.group-item {
display: flex;
flex-direction: column;
align-items: center;
width: 199rpx;
justify-content: center;
background: #d7e9e548;
border-radius: 10rpx;
margin: 10rpx 10rpx;
position: relative;
padding: 10rpx 0;
.history-icon {
position: absolute;
right: 0rpx;
top: 0rpx;
}
image {
width: 40rpx;
height: 40rpx;
}
.item-con {
display: flex;
flex-direction: column;
align-items: center;
// margin-left: 15rpx;
width: 100%;
.item-title {
width: 100%;
font-size: 24rpx;
color: #2a2a2a;
margin-top: 10rpx;
text-align: center;
}
.item-num {
font-size: 36rpx;
color: #282828;
font-weight: bold;
max-width: 90%;
text-align: center;
}
.item-unit {
color: #cccccc;
font-size: 16rpx;
padding-left: 10rpx;
}
}
}
}
}
}
</style>