Files
energy_storage/web/src/views/monitor.vue
2026-01-09 17:17:09 +08:00

612 lines
16 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>
<div class="monitor">
<div class="search">
<div class="left">
<div class="search-item">
<span>场站切换</span>
<a-select v-model:value="selectStationId" style="width: 300px" @change="getStationChange">
<a-select-option v-for="station in stations" :value="station['station_id']"
>{{ station.name }}
</a-select-option>
</a-select>
<a-button type="primary" @click="handleMessage" style="margin-left: 20px">运行参数查看</a-button>
<a-button type="primary" @click="openDispatchModeModal" style="margin-left: 20px">运行模式设置</a-button>
<a-button type="primary" @click="openDispatchParamModal" style="margin-left: 20px">运行参数设置</a-button>
</div>
</div>
<!-- <div class="right">
<div class="search-item">
<span>运行模式</span>
<a-select v-model:value="workMode" style="width: 220px">
<a-select-option :value="item.value" v-for="item in workModes">{{
item.label
}}</a-select-option>
</a-select>
</div>
<div class="search-item">
<a-button type="primary" @click="openDispatchModeModal">模式设置</a-button>
</div>
<div class="search-item">
<a-button type="primary" @click="openDispatchParamModal">参数设置</a-button>
</div>
</div> -->
</div>
<div class="content">
<div class="stations">
<div
class="station-item"
v-for="system in systems"
:key="system.name"
@click="chooseStation(system)"
:class="systemType == system.systemType ? 'active' : ''"
>
<span class="name">{{ system.name }}</span>
<span class="des" v-for="title in system.titles" :key="title.v"
>{{ title.v }}{{ system[title.key] }}{{ title.sufix }}
</span>
</div>
</div>
<div class="container">
<videos
v-if="systemType == 4"
:station-id="selectStationId"
@updateGatewayData="updateGatewayData"
/>
<device
v-else
:station-id="selectStationId"
:system-type="systemType"
@updateGatewayData="updateGatewayData"
/>
</div>
</div>
<a-modal
v-model:open="msgModal"
width="700px"
style="top: 80px"
:footer="null"
:destroy-on-close="true"
>
<div style="color: #fff; padding: 20px" v-html="message"></div>
</a-modal>
<a-modal
v-model:open="dispatchModeModal"
width="900px"
style="top: 80px"
:footer="null"
:destroy-on-close="true"
>
<DetailInfo
:items="detailInfoMode"
:rule-form="ruleForm"
ref="detailInfo"
>
</DetailInfo>
<div style="display: flex; justify-content: center; gap: 20px">
<a-button @click="handlebackMode">取消</a-button>
<a-button @click="handleConfirmMode" type="primary" >确认下发</a-button>
</div>
</a-modal>
<a-modal
v-model:open="dispatchModal"
width="900px"
style="top: 80px"
:footer="null"
:destroy-on-close="true"
>
<DetailInfo
:items="detailInfo"
:rule-form="ruleForm"
:form-rules="formRules"
ref="detailInfo"
>
</DetailInfo>
<div style="display: flex; justify-content: center; gap: 20px">
<a-button @click="handleback">取消</a-button>
<a-button @click="handleConfirm" type="primary" >确认下发</a-button>
</div>
</a-modal>
</div>
</template>
<script>
import device from '@/components/monitor/device.vue'
import videos from '@/components/monitor/videos.vue'
import { postReq, getReq } from '@/request/api'
export default {
name: 'MonitorView',
components: {
device,
videos
},
data() {
return {
// currentKey: '储能系统',
dispatchModal: false,
dispatchModeModal: false,
systemType: 1,
value: [],
stations: [],
selectStationId: '',
message: '',
msgModal: false,
systems: [
{
name: '储能设备',
titles: [
// { v: '运行模式', key: 'workmode' },
// { v: '储能EMU状态', key: 'emu' },
// { v: '充电桩状态', key: 'cdz' }
//{ v: '总有功功率(台区)', key: 'power', sufix: 'kW' }
],
power: 60,
num: 62,
systemType: 1
},
{
name: '充电设备',
power: 60,
num: 62,
systemType: 2,
titles: [
//{ v: '总功率', key: 'power', sufix: 'kW' },
// { v: '数量', key: 'num' }
]
},
{
name: '光伏设备',
power: 60,
num: 62,
systemType: 3,
titles: [
//{ v: '总功率', key: 'power', sufix: 'kW' },
// { v: '数量', key: 'num' }
]
},
{
name: '辅助设备',
power: 60,
num: 62,
systemType: 4,
titles: [
// { v: '总功率', key: 'power', sufix: 'kW' },
// { v: '数量', key: 'num' }
]
}
// {
// name: "储能系统4",
// }
],
workMode: '',
workModes: [
// 0手动1峰谷套利2配网增容3应急供电4并网保电5运营支撑
{
value: 0,
label: '手动'
},
{
value: 1,
label: '峰谷套利'
},
{
value: 2,
label: '配网增容'
},
{
value: 3,
label: '应急供电'
},
{
value: 4,
label: '并网保电'
},
{
value: 5,
label: '运营支撑'
}
],
deviceGroup: [],
detailInfoMode: {
title: '预制舱模式设置',
icon: 'icon-xinxi',
list: [
{
label: '预制舱运行模式',
value: '0',
key: 'work_mode',
type: 'select',
// addonAfter: '%',
// inputType:'number'
list: [
{ value: '0', label: '手动' },
{ value: '1', label: '峰谷套利' },
{ value: '2', label: '配网增容' },
{ value: '3', label: '应急供电' },
{ value: '4', label: '并网保电' },
{ value: '5', label: '运营支撑' }
],
options: { label: 'label', value: 'value' }
}
],
ruleForm: {}
},
detailInfo: {
title: '预制舱参数设置',
icon: 'icon-xinxi',
list: [
{
label: '储能放电下限值(0-99)',
value: '',
key: 'soc_min',
type: 'input',
addonAfter: '%',
inputType:'number'
},
{
label: '储能充电上限值(1-100)',
value: '',
key: 'soc_max',
type: 'input',
addonAfter: '%',
inputType:'number'
},
{
label: '台区变压器容量(160-1600)',
value: undefined,
key: 'capacity',
type: 'input',
addonAfter: 'kVA',
inputType:'number'
},
{
label: '安全输入功率(0-400)',
value: '',
key: 'power_safe',
type: 'input',
addonAfter: 'kW',
inputType:'number'
},
{
label: '储能最大放电功率(0-150)',
value: '',
key: 'power_discharge',
type: 'input',
addonAfter: 'kW',
inputType:'number'
},
{
label: '储能最大充电功率(0-150)',
value: '',
key: 'power_charge',
type: 'input',
addonAfter: 'kW',
inputType:'number'
},
{
label: '防逆流回差(10-300)',
value: '',
key: 'backflow',
type: 'input',
addonAfter: 'kW',
inputType:'number'
},
{
label: '防过载回差(10-300)',
value: '',
key: 'overload',
type: 'input',
addonAfter: 'kW',
inputType:'number'
}
],
ruleForm: {}
},
formRules: {
soc_min: [
{
trigger: 'blur',
required: true,
message: '请输入储能放电下限值'
},
{ pattern: /^([0-9]|[1-9][0-9])$/, message: '请输入0-99 的整数' }
],
soc_max: [
{
trigger: 'blur',
required: true,
message: '请输入储能充电上限值'
},
{ pattern: /^(100|[1-9][0-9]?)$/, message: '请输入1-100的整数' }
],
capacity: [
{
trigger: 'blur',
required: true,
message: '请输入台区变压器容量'
},
{ pattern: /^(1600|1[6-9][0-9]|[2-9][0-9]{2}|1[0-5][0-9]{2})$/, message: '请输入160-1600的整数' }
],
power_safe: [
{
trigger: 'blur',
required: true,
message: '请输入安全输入功率'
},
{
pattern: /^(400|[1-3][0-9]{2}|[1-9][0-9]|[0-9])$/, message: '请输入0-400的整数',
}
],
power_discharge: [
{
trigger: 'blur',
required: true,
message: '请输入储能最大放电功率'
},
{ pattern: /^(150|1[0-4][0-9]|[1-9][0-9]|[0-9])$/, message: '请输入0-150的整数' }
],
power_charge: [
{
trigger: 'blur',
required: true,
message: '请输入储能最大充电功率'
},
{ pattern: /^(150|1[0-4][0-9]|[1-9][0-9]|[0-9])$/, message: '请输入0-150的整数' }
],
backflow: [
{
trigger: 'blur',
required: true,
message: '请输入防逆流回差'
},
{
pattern: /^(300|[1-2][0-9]{2}|[1-9][0-9])$/, message: '请输入10-300的整数' }
],
overload: [
{
trigger: 'blur',
required: true,
message: '请输入防过载回差'
},
{ pattern: /^(300|[1-2][0-9]{2}|[1-9][0-9])$/, message: '请输入10-300的整数' }
]
},
ruleForm:{},
gatewayData1:{}
}
},
mounted() {
this.getStations()
},
methods: {
handlebackMode() {
this.dispatchModeModal = false
},
async handleConfirmMode() {
const res = await this.$refs.detailInfo.confirm()
if (!res) return
if (this.ruleForm.work_mode) {
this.handleSubmitMode()
}
},
async handleSubmitMode() {
try {
const res = await postReq('/updateStation', {
station_id: this.selectStationId,
work_mode: this.ruleForm.work_mode,
...this.ruleForm
})
if (res.errcode == 0) {
this.$message.success('下发成功')
this.dispatchModeModal=false
}
} catch (error) {
this.$message.error('下发失败')
this.dispatchModeModal=false
}
},
handleback() {
this.dispatchModal = false
},
async handleConfirm() {
const res = await this.$refs.detailInfo.confirm()
if (!res) return
this.handleSubmit()
},
async handleSubmit() {
try {
const res = await postReq('/updateGatewayParams', {
station_id: this.selectStationId,
work_mode: this.workMode,
...this.ruleForm
})
if (res.errcode == 0) {
this.$message.success('下发成功')
this.dispatchModal=false
}
} catch (error) {
this.$message.error('下发失败')
this.dispatchModal=false
}
},
updateGatewayData(gatewayData) {
this.systems[0].workmode = gatewayData.workmode
this.systems[0].emu = gatewayData.emu
this.systems[0].cdz = gatewayData.cdz
this.message = gatewayData.msg
},
handleMessage() {
this.msgModal = true
},
//下发
// handleDispatch
async openDispatchModeModal() {
this.dispatchModeModal = true;
},
async openDispatchParamModal() {
try {
const res = await getReq('/queryDevicByCategory', {
station_id: this.selectStationId,
category: 0
})
this.ruleForm=Object.keys(res.gateway).reduce((acc, key) => {
const value = res.gateway[key];
acc[key] = typeof value === 'number' ? String(value) : value;
return acc;
}, {});
this.dispatchModal = true
} catch (error) {
this.ruleForm = {}
}
},
//场站切换
getStationChange(val) {
console.log(val)
this.getStationAttr()
},
//查询场站列表
async getStations() {
try {
const res = await getReq('/queryStationList', { page: 0, page_size: 10000 })
this.stations = res.data
this.selectStationId = this.stations[0]['station_id']
this.getStationAttr()
} catch (error) {
this.stations = []
this.selectStationId = ''
this.$message.error(error.message)
}
},
// 查询场站的参数
async getStationAttr() {
try {
const res = await getReq('/queryStationOverview', { station_id: this.selectStationId })
res.data.device_group.forEach((Element, index) => {
this.systems[index].num = Element.count
this.systems[index].power = Element.power
// workmode 和 msg 数据通过 queryDevicByCategory 接口返回
//this.systems[index].workmode = Element.workmode
// if (Element.category == 1) {
// this.message = Element.msg
// }
})
this.workMode = res.data.work_mode
} catch (error) {
this.deviceGroup = []
this.workMode = ''
// this.$message.error(error.errmsg)
}
},
chooseStation(system) {
console.log(system, 'system')
this.systemType = system.systemType
}
}
}
</script>
<style scoped lang="scss">
@import url(@/style/color.scss);
.monitor {
// width: 100%;
// height: calc(100% - 40px);
height: 100%;
padding: 20px;
background: $bg1-color;
border-radius: 15px;
.search {
display: flex;
justify-content: space-between;
.search-item {
span {
margin-right: 20px;
}
color: #fff;
margin-left: 30px;
&:first-child {
margin-left: 0;
}
}
.left,
.right {
display: flex;
}
}
.content {
width: 100%;
height: calc(100% - 32px - 20px);
margin-top: 20px;
display: flex;
justify-content: space-between;
.stations {
display: flex;
flex-direction: column;
//grid-template-rows: repeat(auto-fit, minmax(140px, 4fr));
border-radius: 12px;
background: $bg2-color;
padding-top: 15px;
.station-item {
// flex: 1;
margin: 0 15px 15px 15px;
border-radius: 10px;
width: 180px;
height: 50px;
display: flex;
flex-direction: column;
// color: #fff;
// padding: 10px 10px;
cursor: pointer;
&:hover {
background: $table-bg;
}
.name {
padding-left: 20px;
font-size: 20px;
font-weight: 700;
line-height: 50px;
}
// .des {
// font-size: 14px;
// font-weight: 600;
// line-height: 30px;
// display: inline-block;
// max-width: 160px;
// white-space: nowrap;
// overflow: hidden;
// text-overflow: ellipsis;
// }
}
.active {
background: $bg3-color;
}
}
.container {
width: calc(100% - 200px);
display: flex;
justify-content: space-between;
}
}
}
:deep(.ant-input-group-addon) {
color: #fff;
border: 1px solid $border-color;
}
:deep(.ant-form-item-label) {
color: #fff;
font-size: 16px !important;
width: 180px !important;
}
</style>