南瑞意见修改:前端页面和后端接口

This commit is contained in:
lixiaoyuan
2026-01-05 16:13:13 +08:00
parent 97e4b182de
commit e278ae1003
51 changed files with 2812 additions and 958 deletions

View File

@@ -9,9 +9,9 @@
>{{ 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>
<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">
@@ -133,11 +133,11 @@ export default {
msgModal: false,
systems: [
{
name: '储能系统',
name: '储能设备',
titles: [
{ v: '运行模式', key: 'workmode' },
{ v: '储能EMU状态', key: 'emu' },
{ v: '充电桩状态', key: 'cdz' }
// { v: '运行模式', key: 'workmode' },
// { v: '储能EMU状态', key: 'emu' },
// { v: '充电桩状态', key: 'cdz' }
//{ v: '总有功功率(台区)', key: 'power', sufix: 'kW' }
],
power: 60,
@@ -145,33 +145,33 @@ export default {
systemType: 1
},
{
name: '充电系统',
name: '充电设备',
power: 60,
num: 62,
systemType: 2,
titles: [
//{ v: '总功率', key: 'power', sufix: 'kW' },
{ v: '数量', key: 'num' }
// { v: '数量', key: 'num' }
]
},
{
name: '光伏系统',
name: '光伏设备',
power: 60,
num: 62,
systemType: 3,
titles: [
//{ v: '总功率', key: 'power', sufix: 'kW' },
{ v: '数量', key: 'num' }
// { v: '数量', key: 'num' }
]
},
{
name: '安防系统',
name: '辅助设备',
power: 60,
num: 62,
systemType: 4,
titles: [
// { v: '总功率', key: 'power', sufix: 'kW' },
{ v: '数量', key: 'num' }
// { v: '数量', key: 'num' }
]
}
// {
@@ -203,7 +203,7 @@ export default {
},
{
value: 5,
label: '自定时段'
label: '运营支撑'
}
],
deviceGroup: [],
@@ -224,7 +224,7 @@ export default {
{ value: '2', label: '配网增容' },
{ value: '3', label: '应急供电' },
{ value: '4', label: '并网保电' },
{ value: '5', label: '自定时段' }
{ value: '5', label: '运营支撑' }
],
options: { label: 'label', value: 'value' }
}
@@ -509,7 +509,6 @@ export default {
chooseStation(system) {
console.log(system, 'system')
this.systemType = system.systemType
}
}
@@ -551,40 +550,43 @@ export default {
display: flex;
justify-content: space-between;
.stations {
display: grid;
display: flex;
flex-direction: column;
//grid-template-rows: repeat(auto-fit, minmax(140px, 4fr));
border-radius: 12px;
background: $bg2-color;
padding-top: 15px;
grid-template-rows: repeat(auto-fit, minmax(140px, 4fr));
.station-item {
flex: 1;
// flex: 1;
margin: 0 15px 15px 15px;
border-radius: 12px;
border-radius: 10px;
width: 180px;
height: 50px;
display: flex;
flex-direction: column;
color: #fff;
padding: 10px 10px;
// 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;
}
// .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;

View File

@@ -1,7 +1,7 @@
<template>
<div class="Home">
<div class="content-left">
<div v-for="item in leftList" :key="item.componentId" :class="`grid-item ${item.class}`">
<div v-for="item in listLeft" :key="item.componentId" :class="`grid-item ${item.class}`">
<div class="tool">
<div class="title">
<i class="iconfont icon-hebing linear-text"></i>
@@ -9,30 +9,32 @@
</div>
</div>
<component
:is="item.componentId"
:info-key="item.infoKey"
:device-info="deviceInfo[item.infoKey]"
:total="item.infoKey === 'onLineTotal' ? deviceInfo.onLine : deviceInfo.allTotal"
></component>
<component :is="item.componentId" :prop-key="item.infoKey" :prop-option="compInfo[item.infoKey]"
:prop-data="compData[item.infoKey]">
</component>
</div>
</div>
<div class="tianditu">
<Map @changeStation="getCurrentStation"></Map>
</div>
<div class="content-right">
<div v-for="item in rightList" :key="item.componentId" :class="`grid-item ${item.class}`">
<div style="width:100%; height:32px;">
<span style="width:120px">场站选择</span>
<a-select v-model:value="selectStationId" style="margin-left: 10px; width: 200px;" @change="getStationChange">
<a-select-option v-for="station in stationList" :value="station['station_id']">{{ station.name }}
</a-select-option>
</a-select>
</div>
<div v-for="item in listRight" :key="item.componentId" :class="`grid-item ${item.class}`">
<div class="tool">
<div class="title">
<i class="iconfont icon-hebing linear-text"></i>
<span class="linear-text">{{ item.title }}</span>
</div>
</div>
<component
:is="item.componentId"
:device-info="deviceInfo[item.infoKey]"
:total="item.infoKey === 'onLineTotal' ? deviceInfo.onLine : deviceInfo.allTotal"
></component>
<component :is="item.componentId" :prop-key="item.infoKey" :prop-option="compInfo[item.infoKey]"
:prop-data="compDataStation[item.infoKey]">
</component>
</div>
</div>
</div>
@@ -40,12 +42,14 @@
<script>
import onLine from '@/components/Home/onLine.vue'
import TotalStation from '@/components/Home/TotalStation.vue'
import Operational from '@/components/Home/Operational.vue'
import Energy from '@/components/Home/Energy.vue'
import Charge from '@/components/Home/Charge.vue'
import Pv from '@/components/Home/Pv.vue'
import Alarm from '@/components/Home/Alarm.vue'
import Map from '@/components/Home/Map.vue'
import MyChartBar from '@/components/Home/MyChartBar.vue'
import { getReq, postReq } from '@/request/api'
import { getRunDays, getDateDaysAgo } from '@/utils/dealWithData'
import { markRaw } from 'vue'
@@ -54,13 +58,70 @@ export default {
components: { Map },
data() {
return {
refreshInterval:null,
refreshInterval: null,
showFlag: false,
stationId: null,
deviceInfo: {},
compData: {
total: {},
energy: [],
charge: [],
pv: [],
stateGrid: [],
},
compDataStation: {
totalStation: {},
energy: [],
charge: [],
pv: [],
stateGrid: [],
},
compInfo: {
total: {},
energy: {
yAxis: ['电量(kWh)'],
series: [
{ name: '日充电电量', key: 'storage_elect_in', color: '#22E4FF' },
{ name: '日放电电量', key: 'storage_elect_out', color: '#0E68E4' },
]
},
pv: {
yAxis: ['电量(kWh)'],
series: [
{ name: '日发电电量', key: 'solar_elect_gen', color: '#22E4FF' },
]
},
charge: {
yAxis: ['电量(kWh)'],
series: [
{ name: '日充电电量', key: 'charge_elect', color: '#22E4FF' },
]
},
stateGrid: {
yAxis: ['电量(kWh)'],
series: [
{ name: '日上网电量', key: 'solar_elect_grid', color: '#22E4FF' },
]
}
},
listLeft: [
{ title: '系统运行统计', class: 'online-status', componentId: markRaw(onLine), infoKey: 'total' },
{ title: '储能设备', class: 'energy-status', componentId: markRaw(MyChartBar), infoKey: 'energy' },
{ title: '充电设备', class: 'charge-analysis', componentId: markRaw(MyChartBar), infoKey: 'charge' },
{ title: '光伏设备', class: 'work-order', componentId: markRaw(MyChartBar), infoKey: 'pv' },
{ title: '电网侧', class: 'work-order', componentId: markRaw(MyChartBar), infoKey: 'stateGrid' },
],
listRight: [
{ title: '场站运行统计', class: 'online-status', componentId: markRaw(TotalStation), infoKey: 'totalStation' },
{ title: '储能设备', class: 'energy-status', componentId: markRaw(MyChartBar), infoKey: 'energy' },
{ title: '充电设备', class: 'charge-analysis', componentId: markRaw(MyChartBar), infoKey: 'charge' },
{ title: '光伏设备', class: 'work-order', componentId: markRaw(MyChartBar), infoKey: 'pv' },
{ title: '电网侧', class: 'work-order', componentId: markRaw(MyChartBar), infoKey: 'stateGrid' },
],
stationList: [],
selectStationId: '',
list: [
{
title: '运行状态',
title: '系统运行统计',
class: 'online-status',
componentId: markRaw(onLine),
infoKey: 'onLineTotal'
@@ -83,7 +144,6 @@ export default {
componentId: markRaw(Charge),
infoKey: 'charge'
},
{
title: '光伏设备',
class: 'work-order',
@@ -104,93 +164,96 @@ export default {
},
computed: {
leftList() {
return this.list.filter((_, index) => index % 2 === 0).slice(0, 3) // 左列取前3个偶数索引
return {} //this.list.filter((_, index) => index % 2 === 0).slice(0, 3) // 左列取前3个偶数索引
},
rightList() {
return this.list.filter((_, index) => index % 2 !== 0).slice(0, 3) // 右列取前3个奇数索引
return {} //this.list.filter((_, index) => index % 2 !== 0).slice(0, 3) // 右列取前3个奇数索引
}
},
beforeUnmount() {
if(this.refreshInterval){
if (this.refreshInterval) {
clearInterval(this.refreshInterval)
this.refreshInterval=null
this.refreshInterval = null
}
},
async mounted() {
await this.getStationList()
await this.loadAllData()
this.refreshInterval=setInterval(async()=>{
this.refreshInterval = setInterval(async () => {
await this.loadAllData()
},30000) //30s刷新一次
}, 30000) //30s刷新一次
},
methods: {
async loadAllData(){
async loadAllData() {
await Promise.all([
this.getOnLineList(),
this.getStatTotalList(),
this.getOperTotalList(),
this.getStatDayList(1),
this.getStatDayList(2),
this.getStatDayList(3)
this.queryStatTotal(),
this.queryStatTotal(this.selectStationId),
// this.getOperTotalList(),
this.getStatDayList(),
this.getStatDayList(this.selectStationId),
// this.getStatDayList(1),
// this.getStatDayList(2),
// this.getStatDayList(3)
]).then((r) => {
if (
this.deviceInfo.energy &&
this.deviceInfo.charge &&
this.deviceInfo.pv
) {
const newArr = this.mergedArray(
this.deviceInfo.energy,
this.deviceInfo.charge,
this.deviceInfo.pv
)
this.deviceInfo.alarm = newArr
}
// if (
// this.compData.energy &&
// this.compData.charge &&
// this.compData.pv
// ) {
// const newArr = this.mergedArray(
// this.compData.energy,
// this.compData.charge,
// this.compData.pv
// )
// this.compData.alarm = newArr
// }
})
},
getCurrentStation(e) {
this.stationId = e
},
// 查询系统统计信息
async getOnLineList() {
// 查询系统的场站列表
async getStationList() {
try {
// token: 用户TOKEN
const res = await getReq('/queryStatSystem')
const res = await getReq('/queryStationList', { page: 0, page_size: 100 })
if (res.errcode === 0) {
this.deviceInfo.onLine = JSON.parse(JSON.stringify(res.data))
this.deviceInfo.onLine.runDays = getRunDays(res.data.launch_date)
// 设置场站下拉列表,默认选中第一项
this.stationList = res.data
this.selectStationId = this.stationList[0]['station_id']
} else {
throw res
}
} catch (error) {
this.deviceInfo.onLine = {}
this.stationList = []
this.selectStationId = ''
}
},
// 查询系统累计统计信息
async getStatTotalList() {
// 查询系统累计统计信息, 不传参数 curStationId 时查询所有场站数据
async queryStatTotal(curStationId) {
try {
// token: 用户TOKEN
// date:日期
// station_id:场站ID为0或不传查询所有场站总计
// category:类别1:储能设备,2:充电设备,3:光伏设备,为0或不传查询所有类别总计
const query = {
date: getDateDaysAgo(0),
station_id: this.stationId,
category: 0
}
const res = await getReq('/queryStatTotal', query)
const res = await getReq('/queryStatTotal', { station_id: curStationId })
if (res.errcode === 0) {
this.deviceInfo.allTotal = res.data
const { income_charge: incomeCharge, income_elect: incomeElect } =
this.deviceInfo.allTotal
this.deviceInfo.allTotal.incomeTotal = +incomeCharge + +incomeElect
// this.compData.total = res.data
// const { income_charge: incomeCharge, income_elect: incomeElect } =
// this.compData.total
// this.compData.total.incomeTotal = +incomeCharge + +incomeElect
// this.compData.total.runDays = getRunDays(res.data.launch_date)
if (curStationId === undefined) {
this.compData.total = res.data
this.compData.total.runDays = getRunDays(res.data.launch_date)
} else {
this.compDataStation.totalStation = res.data
this.compDataStation.totalStation.runDays = getRunDays(res.data.launch_date)
}
} else {
throw res
}
} catch (error) {
this.deviceInfo.allTotal = {}
this.deviceInfo.allTotal.incomeTotal = 0
this.compData.total = {}
this.compData.total.incomeTotal = 0
}
},
// 运行分析 联调
@@ -198,35 +261,56 @@ export default {
try {
const res = await getReq('/queryStatStation', {})
if (res.errcode === 0) {
this.deviceInfo.operationTotal = res.data
this.compData.operationTotal = res.data
} else {
throw res
}
} catch (error) {
this.deviceInfo.operationTotal = [ ]
this.compData.operationTotal = []
}
},
// 示例获取7天前的日期
// 查询场站日统计信息
async getStatDayList(category) {
// 查询近7日的统计信息
async getStatDayList(curStationId) {
try {
if (curStationId === '') {
return
}
// station_id: 场站ID
// category: 类别: 1储能设备,2:充电设备,3:光伏设备
// start_date开始日期格式yyyy-mm-dd
// end_date结束日期格式yyyy-mm-dd
const query = {
station_id: this.stationId,
category,
let query = {
//station_id: this.stationId,
// category,
start_date: getDateDaysAgo(7 - 1),
end_date: getDateDaysAgo(0)
}
const arr = { 1: 'energy', 2: 'charge', 3: 'pv' }
if (curStationId) {
query.station_id = curStationId
}
//const arr = { 1: 'energy', 2: 'charge', 3: 'pv' }
const res = await getReq('/queryStatDayList', query)
if (res.errcode === 0) {
this.list.forEach((item) => {
this.deviceInfo[arr[category]] = res.data
})
if (curStationId) {
this.compDataStation.energy
= this.compDataStation.charge
= this.compDataStation.pv
= this.compDataStation.stateGrid
= res.data
} else {
this.compData.energy
= this.compData.pv
= this.compData.stateGrid
= this.compData.charge
= res.data
}
// this.list.forEach((item) => {
// //this.compData[arr[category]] = res.data
// alert(JSON.stringify(item))
// return
// })
} else {
throw res
}
@@ -251,6 +335,10 @@ export default {
})
})
return newArr
},
getStationChange() {
this.queryStatTotal(this.selectStationId),
this.getStatDayList(this.selectStationId)
}
}
}
@@ -278,13 +366,13 @@ export default {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
justify-content: flex-start;
}
.grid-item {
box-sizing: border-box;
width: 100%;
height: calc(33% - 10px);
height: calc(25% - 10px);
z-index: 20;
.tool {
@@ -295,7 +383,7 @@ export default {
display: flex;
justify-content: space-between;
align-items: center;
height: 45px;
height: 32px;
.linear-text {
background: linear-gradient(180deg, rgba(255, 255, 255, 1) 0%, rgba(40, 235, 231, 1) 100%);
@@ -335,12 +423,10 @@ export default {
margin-left: 20px;
font-size: 16px;
font-weight: 700;
background: linear-gradient(
90deg,
rgba(0, 186, 173, 0.15) 0%,
rgba(61, 254, 250, 0.15) 49.2%,
rgba(61, 254, 250, 0) 100%
);
background: linear-gradient(90deg,
rgba(0, 186, 173, 0.15) 0%,
rgba(61, 254, 250, 0.15) 49.2%,
rgba(61, 254, 250, 0) 100%);
-webkit-background-clip: text;
display: inline-block;
background-clip: text;

View File

@@ -2,30 +2,38 @@
<div class="device">
<searchBox :btn-option-list="btnOptionList" @operateForm="operateForm"></searchBox>
<div class="content-table">
<ComTable
:columns="columns"
:table-data="tableData"
@handlePagesizeChange="handlePagesizeChange"
ref="comTable"
:table-option="tableOption"
:page-option="pageOption"
>
<template #type="record">
<span>{{ getType(record.type) }}</span>
</template>
<template #isEnable="record">
<span
><a-tag :color="record.is_open == 1 ? 'green' : 'red'">{{
record.is_open == 1 ? '启用' : '禁用'
}}</a-tag></span
>
</template>
<div style="display: flex; height: calc(100% - 40px); ">
<div style="display: block; min-width:200px; width: 200px; height: 100%; overflow-y: auto; background-color: #08365c; border-radius: 5px; padding-top: 10px;">
<a-tree v-model:expandedKeys="expandedKeys" v-model:selectedKeys="selectedKeys"
v-model:checkedKeys="checkedKeys" :tree-data="stationTreeData" @select="onSelectStation">
</a-tree>
</div>
<template #action="record">
<OperateCom :record="record" :operate-list="operateList" @operateForm="operateForm" />
</template>
</ComTable>
<div class="content-table">
<ComTable
:columns="columns"
:table-data="tableData"
@handlePagesizeChange="handlePagesizeChange"
ref="comTable"
:table-option="tableOption"
:page-option="pageOption"
>
<template #type="record">
<span>{{ getType(record.type) }}</span>
</template>
<template #isEnable="record">
<span
><a-tag :color="record.is_open == 1 ? 'green' : 'red'">{{
record.is_open == 1 ? '启用' : '禁用'
}}</a-tag></span
>
</template>
<template #action="record">
<OperateCom :record="record" :operate-list="operateList" @operateForm="operateForm" />
</template>
</ComTable>
</div>
</div>
<a-modal v-model:open="formModal" width="750px" style="top: 80px" :footer="null">
<!-- action:edit add -->
@@ -75,7 +83,9 @@ export default {
paramsDate: {},
tableData: [],
tableOption: {},
stations: [] //场站列表
stations: [], //场站列表
stationTreeData: [],
selectedStationId: undefined
}
},
computed: {},
@@ -93,8 +103,8 @@ export default {
mounted() {
this.operateList = this.$getBtns(['查看', '修改', '删除'])
this.btnOptionList = this.$getBtns(['新增'])
this.getList()
this.getStations()
//this.getList()
},
methods: {
@@ -103,8 +113,11 @@ export default {
this.stations = []
try {
const res = await getReq('/queryStationList', { page: 0, page_size: 10000 })
this.stations = res.data
this.stationTreeData = []
res.data.forEach((item)=>{
this.stationTreeData.push( { title: item.name, key: item.station_id})
})
} catch (error) {
this.stations = []
}
@@ -122,6 +135,7 @@ export default {
const { page, pageSize } = this.pageOption
const params = {
station_id: this.selectedStationId,
...this.paramsDate,
page_size: pageSize,
page
@@ -216,6 +230,10 @@ export default {
this.pageOption.pageSize = pageOption.pageSize
this.pageOption.page = pageOption.page
this.getList()
},
onSelectStation(selectedKey, ) {
this.selectedStationId = selectedKey
this.getList()
}
}
}
@@ -229,6 +247,26 @@ export default {
.content-table {
width: 100%;
height: calc(100% - 52px);
margin-left: 20px;
}
}
:deep(.ant-tree-list) {
background-color: #08365c;
font-size: 16px;
color: white;
}
:deep(.ant-tree-treenode ) {
width: 100%;
height: 30px;
}
:deep(.ant-tree-node-content-wrapper) {
width: calc(100% - 34px);
height: 30px;
}
:deep(.ant-tree-node-selected) {
background-color: #27a188 !important;
border-radius: 5px !important;
}
</style>