This commit is contained in:
2026-02-13 21:46:12 +08:00
parent 7fdb6e2ad3
commit 50c72d6989
25 changed files with 1402 additions and 805 deletions

View File

@ -50,4 +50,3 @@ export default {
flex: 1;
}
</style>

View File

@ -0,0 +1,137 @@
<template>
<div class="ems-dashboard-editor-container" v-loading="loading">
<el-card shadow="always" class="common-card-container">
<div slot="header" class="clearfix">
<span class="card-title">运行参数配置</span>
<span class="site-tag">站点{{ siteId || '-' }}</span>
</div>
<el-form ref="form" :model="form" :rules="rules" label-width="180px" style="max-width: 760px;">
<el-form-item label="SOC下限(%)" prop="socDown">
<el-input-number v-model="form.socDown" :min="0" :max="100" :step="1" :precision="2" controls-position="right" style="width: 220px;" />
</el-form-item>
<el-form-item label="SOC上限(%)" prop="socUp">
<el-input-number v-model="form.socUp" :min="0" :max="100" :step="1" :precision="2" controls-position="right" style="width: 220px;" />
</el-form-item>
<el-form-item label="防逆流阈值(kW)" prop="antiReverseThreshold">
<el-input-number v-model="form.antiReverseThreshold" :min="0" :step="1" :precision="2" controls-position="right" style="width: 220px;" />
</el-form-item>
<el-form-item label="防逆流阈值上浮比例(%)" prop="antiReverseRangePercent">
<el-input-number v-model="form.antiReverseRangePercent" :min="0" :step="1" :precision="2" controls-position="right" style="width: 220px;" />
</el-form-item>
<el-form-item label="防逆流恢复上限(kW)" prop="antiReverseUp">
<el-input-number v-model="form.antiReverseUp" :min="0" :step="1" :precision="2" controls-position="right" style="width: 220px;" />
</el-form-item>
<el-form-item label="防逆流降功率比例(%)" prop="antiReversePowerDownPercent">
<el-input-number v-model="form.antiReversePowerDownPercent" :min="0" :max="100" :step="1" :precision="2" controls-position="right" style="width: 220px;" />
</el-form-item>
<el-form-item label="防逆流硬停阈值(kW)" prop="antiReverseHardStopThreshold">
<el-input-number v-model="form.antiReverseHardStopThreshold" :min="0" :step="1" :precision="2" controls-position="right" style="width: 220px;" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="saveLoading" @click="handleSave">保存</el-button>
<el-button @click="init">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
import getQuerySiteId from '@/mixins/ems/getQuerySiteId'
import { getStrategyRuntimeConfig, saveStrategyRuntimeConfig } from '@/api/ems/dzjk'
const emptyForm = () => ({
siteId: '',
socDown: 0,
socUp: 100,
antiReverseThreshold: 30,
antiReverseRangePercent: 20,
antiReverseUp: 100,
antiReversePowerDownPercent: 10,
antiReverseHardStopThreshold: 20
})
export default {
name: 'DzjkClpzRuntimeParam',
mixins: [getQuerySiteId],
data() {
return {
loading: false,
saveLoading: false,
form: emptyForm(),
rules: {
socDown: [
{ required: true, message: '请输入SOC下限', trigger: 'change' }
],
socUp: [
{ required: true, message: '请输入SOC上限', trigger: 'change' }
],
antiReverseThreshold: [
{ required: true, message: '请输入防逆流阈值', trigger: 'change' }
],
antiReverseRangePercent: [
{ required: true, message: '请输入防逆流阈值上浮比例', trigger: 'change' }
],
antiReverseUp: [
{ required: true, message: '请输入防逆流恢复上限', trigger: 'change' }
],
antiReversePowerDownPercent: [
{ required: true, message: '请输入防逆流降功率比例', trigger: 'change' }
],
antiReverseHardStopThreshold: [
{ required: true, message: '请输入防逆流硬停阈值', trigger: 'change' }
]
}
}
},
methods: {
init() {
if (!this.siteId) {
this.form = emptyForm()
return
}
this.loading = true
getStrategyRuntimeConfig(this.siteId).then(response => {
const data = response?.data || {}
this.form = {
...emptyForm(),
...data,
siteId: this.siteId
}
}).finally(() => {
this.loading = false
})
},
handleSave() {
if (!this.siteId) {
this.$message.error('缺少站点ID')
return
}
this.$refs.form.validate(valid => {
if (!valid) return
if (Number(this.form.socDown) > Number(this.form.socUp)) {
this.$message.error('SOC下限不能大于SOC上限')
return
}
this.saveLoading = true
saveStrategyRuntimeConfig({ ...this.form, siteId: this.siteId }).then(response => {
if (response?.code === 200) {
this.$message.success('保存成功')
this.init()
}
}).finally(() => {
this.saveLoading = false
})
})
}
}
}
</script>
<style scoped lang="scss">
.site-tag {
float: right;
color: #909399;
font-size: 13px;
}
</style>

View File

@ -9,14 +9,6 @@
<el-form-item label="soc限制" prop="sdcLimit" required>
<el-switch :active-value="1" :inactive-value="0" v-model="formData.sdcLimit"></el-switch>
</el-form-item>
<!-- <template v-if="formData.sdcLimit === 1">-->
<el-form-item label="soc下限" prop="sdcDown">
<el-input v-model="formData.sdcDown" placeholder="请输入" clearable :style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label="soc上限" prop="sdcUp">
<el-input v-model="formData.sdcUp" placeholder="请输入" clearable :style="{width: '100%'}"></el-input>
</el-form-item>
<!-- </template>-->
</el-form>
<el-button type="primary" size="mini" @click="addTime">新增</el-button>
<!-- 新增时间段表单-->
@ -64,6 +56,12 @@
<el-input v-model="formInline.chargeDischargePower" placeholder="请输入"
:style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label="soc下限" prop="sdcDown">
<el-input v-model="formInline.sdcDown" placeholder="请输入" clearable :style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label="soc上限" prop="sdcUp">
<el-input v-model="formInline.sdcUp" placeholder="请输入" clearable :style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label="充电状态" prop="chargeStatus">
<el-select v-model="formInline.chargeStatus" placeholder="请选择" :style="{width: '100%'}">
<el-option v-for="(value,key) in chargeStatusOptions" :key="key+'chargeStatusOptions'" :label="value"
@ -84,20 +82,100 @@
<el-table-column
prop="startTime"
label="开始时间">
<template slot-scope="scope">
<el-time-select
v-if="mode === 'edit'"
v-model="scope.row.startTime"
placeholder="开始时间"
:picker-options="{
start: '00:00',
step: '00:01',
end: '23:59'
}"
:style="{width: '100%'}"
/>
<span v-else>{{ scope.row.startTime || '-' }}</span>
</template>
</el-table-column>
<el-table-column
prop="endTime"
label="结束时间">
<template slot-scope="scope">
<el-time-select
v-if="mode === 'edit'"
v-model="scope.row.endTime"
placeholder="结束时间"
:picker-options="{
start: '00:00',
step: '00:01',
end: '23:59',
minTime: scope.row.startTime
}"
:style="{width: '100%'}"
/>
<span v-else>{{ scope.row.endTime || '-' }}</span>
</template>
</el-table-column>
<el-table-column
prop="chargeDischargePower"
label="充放功率kW">
<template slot-scope="scope">
<el-input
v-if="mode === 'edit'"
v-model.trim="scope.row.chargeDischargePower"
placeholder="请输入"
clearable
:style="{width: '100%'}"
/>
<span v-else>{{ scope.row.chargeDischargePower || '-' }}</span>
</template>
</el-table-column>
<el-table-column
prop="sdcDown"
label="SOC下限">
<template slot-scope="scope">
<el-input
v-if="mode === 'edit'"
v-model.trim="scope.row.sdcDown"
placeholder="请输入"
clearable
:style="{width: '100%'}"
/>
<span v-else>{{ scope.row.sdcDown === null || scope.row.sdcDown === undefined || scope.row.sdcDown === '' ? '-' : scope.row.sdcDown + '%' }}</span>
</template>
</el-table-column>
<el-table-column
prop="sdcUp"
label="SOC上限">
<template slot-scope="scope">
<el-input
v-if="mode === 'edit'"
v-model.trim="scope.row.sdcUp"
placeholder="请输入"
clearable
:style="{width: '100%'}"
/>
<span v-else>{{ scope.row.sdcUp === null || scope.row.sdcUp === undefined || scope.row.sdcUp === '' ? '-' : scope.row.sdcUp + '%' }}</span>
</template>
</el-table-column>
<el-table-column
prop="chargeStatus"
label="充电状态">
<template slot-scope="scope">
{{ chargeStatusOptions[scope.row.chargeStatus] }}
<el-select
v-if="mode === 'edit'"
v-model="scope.row.chargeStatus"
placeholder="请选择"
:style="{width: '100%'}"
>
<el-option
v-for="(value,key) in chargeStatusOptions"
:key="key+'chargeStatusEditOptions'"
:label="value"
:value="key"
/>
</el-select>
<span v-else>{{ chargeStatusOptions[scope.row.chargeStatus] }}</span>
</template>
</el-table-column>
<el-table-column
@ -138,8 +216,6 @@ export default {
formData: {
templateName: '',
sdcLimit: false,
sdcDown: '',
sdcUp: '',
},
rules: {
templateName: [{
@ -147,19 +223,13 @@ export default {
message: '请输入',
trigger: 'blur'
}],
sdcDown: [
{required: true, message: '请输入', trigger: 'blur'},
{pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数'}
],
sdcUp: [
{required: true, message: '请输入', trigger: 'blur'},
{pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数'}
],
},
showAddTime: false,
formInline: {
timeRange: range,
chargeDischargePower: '',
sdcDown: '',
sdcUp: '',
chargeStatus: ''
},
formInlineRule: {
@ -175,6 +245,14 @@ export default {
},
{pattern: /^-?\d*\.?\d*$/, message: '请输入合法数字或小数'}
],
sdcDown: [
{required: true, message: '请输入', trigger: 'blur'},
{pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数'}
],
sdcUp: [
{required: true, message: '请输入', trigger: 'blur'},
{pattern: /^(0|[1-9]\d*)(\.\d+)?$/, message: '请输入合法数字或小数'}
],
chargeStatus: [{
required: true,
message: '请选择充放状态',
@ -198,11 +276,9 @@ export default {
this.formData = {
templateName: '',
sdcLimit: false,
sdcDown: '',
sdcUp: '',
}
this.formInline = {
timeRange: this.secondRange, chargeDischargePower: '', chargeStatus: ''
timeRange: this.secondRange, chargeDischargePower: '', sdcDown: '', sdcUp: '', chargeStatus: ''
}//startTime: '', endTime: '',
this.showAddTime = false
this.tableData = []
@ -216,14 +292,12 @@ export default {
getStrategyTempDetail(this.editTempId).then(response => {
const data = JSON.parse(JSON.stringify(response?.data || []));
if (data.length > 0) {
const {templateName, sdcLimit, sdcDown, sdcUp} = JSON.parse(JSON.stringify(data[0]));
const {templateName, sdcLimit} = JSON.parse(JSON.stringify(data[0]));
this.formData.templateName = templateName
this.formData.sdcLimit = sdcLimit
this.formData.sdcDown = sdcDown
this.formData.sdcUp = sdcUp
}
if (data.length === 1) {
const {startTime, endTime} = data;
const {startTime, endTime} = data[0];
if (!startTime || !endTime) {
this.tableData = []
} else {
@ -242,15 +316,15 @@ export default {
cancelAddTime() {
this.$refs.addTimeForm.resetFields()
this.showAddTime = false
this.formInline = {timeRange: this.secondRange, chargeDischargePower: '', chargeStatus: ''}//startTime: '', endTime: '',
this.formInline = {timeRange: this.secondRange, chargeDischargePower: '', sdcDown: '', sdcUp: '', chargeStatus: ''}//startTime: '', endTime: '',
},
saveTime() {
//表单校验校验成功添加到tableData里
this.$refs.addTimeForm.validate(valid => {
if (!valid) return
const {timeRange: [startTime, endTime], chargeDischargePower, chargeStatus} = this.formInline
const {timeRange: [startTime, endTime], chargeDischargePower, sdcDown, sdcUp, chargeStatus} = this.formInline
this.tableData.push({startTime, endTime, chargeDischargePower, chargeStatus})
this.tableData.push({startTime, endTime, chargeDischargePower, sdcDown, sdcUp, chargeStatus})
this.$nextTick(() => {
this.cancelAddTime()
})
@ -262,9 +336,14 @@ export default {
saveDialog() {
this.$refs.addTempForm.validate(valid => {
if (!valid) return
const {templateName, sdcLimit, sdcDown, sdcUp} = this.formData
const {templateName, sdcLimit} = this.formData
const {siteId, updateStrategyId} = this.$home
const {tableData} = this
const tableData = this.tableData.map(item => ({
...item,
sdcDown: this.normalizeSocValue(item.sdcDown),
sdcUp: this.normalizeSocValue(item.sdcUp)
}))
if (!this.validateTableData(tableData)) return
if (this.mode === 'edit') {
editStrategyTemp({
siteId,
@ -272,8 +351,6 @@ export default {
templateId: this.editTempId,
templateName,
sdcLimit,
sdcDown,
sdcUp,
timeConfigList: tableData
}).then(response => {
if (response?.code === 200) {
@ -288,8 +365,6 @@ export default {
strategyId: updateStrategyId,
templateName,
sdcLimit,
sdcDown,
sdcUp,
timeConfigList: tableData
}).then(response => {
if (response?.code === 200) {
@ -300,14 +375,64 @@ export default {
}
})
},
normalizeSocValue(value) {
if (value === null || value === undefined) return null
const normalized = String(value).replace('%', '').trim()
return normalized === '' ? null : normalized
},
toMinutes(timeValue) {
if (!timeValue || String(timeValue).indexOf(':') < 0) return -1
const [h, m] = String(timeValue).split(':')
const hour = Number(h), minute = Number(m)
if (!Number.isInteger(hour) || !Number.isInteger(minute)) return -1
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) return -1
return hour * 60 + minute
},
validateTableData(list = []) {
const numberPattern = /^-?\d+(\.\d+)?$/
const socPattern = /^(0|[1-9]\d*)(\.\d+)?$/
for (let i = 0; i < list.length; i++) {
const row = list[i]
const rowNo = i + 1
if (!row.startTime || !row.endTime) {
this.$message.error(`${rowNo}行:开始时间和结束时间不能为空`)
return false
}
const startMinute = this.toMinutes(row.startTime)
const endMinute = this.toMinutes(row.endTime)
if (startMinute < 0 || endMinute < 0 || startMinute >= endMinute) {
this.$message.error(`${rowNo}行:时间范围不合法`)
return false
}
if (!numberPattern.test(String(row.chargeDischargePower ?? '').trim())) {
this.$message.error(`${rowNo}行:充放功率格式不正确`)
return false
}
if (!socPattern.test(String(row.sdcDown ?? '').trim())) {
this.$message.error(`${rowNo}SOC下限格式不正确`)
return false
}
if (!socPattern.test(String(row.sdcUp ?? '').trim())) {
this.$message.error(`${rowNo}SOC上限格式不正确`)
return false
}
if (Number(row.sdcDown) > Number(row.sdcUp)) {
this.$message.error(`${rowNo}SOC下限不能大于SOC上限`)
return false
}
if (row.chargeStatus === undefined || row.chargeStatus === null || row.chargeStatus === '') {
this.$message.error(`${rowNo}行:请选择充电状态`)
return false
}
}
return true
},
closeDialog() {
// 清空所有数据
this.$refs.addTempForm.resetFields()
this.formData = {
templateName: '',
sdcLimit: 0,
sdcDown: '',
sdcUp: '',
}
this.tableData = []
this.cancelAddTime()
@ -322,4 +447,4 @@ export default {
max-height: 90vh;
overflow-y: auto;
}
</style>
</style>

View File

@ -39,14 +39,14 @@
prop="sdcDown"
label="SOC下限">
<template slot-scope="scope">
{{scope.row.sdcDown ? scope.row. sdcDown + '%' : '-'}}
{{scope.row.sdcDown === null || scope.row.sdcDown === undefined || scope.row.sdcDown === '' ? '-' : scope.row.sdcDown + '%'}}
</template>
</el-table-column>
<el-table-column
prop="sdcUp"
label="SOC上限">
<template slot-scope="scope">
{{scope.row.sdcUp ? scope.row.sdcUp + '%' : '-'}}
{{scope.row.sdcUp === null || scope.row.sdcUp === undefined || scope.row.sdcUp === '' ? '-' : scope.row.sdcUp + '%'}}
</template>
</el-table-column>
<el-table-column
@ -92,7 +92,7 @@ export default {
activeBtn:'',
tempList:[],
tableData:[],
mixinPrototype:['templateName','sdcLimit','sdcDown','sdcUp']
mixinPrototype:['templateName','sdcLimit']
}
},
computed:{

View File

@ -12,7 +12,8 @@
import * as echarts from 'echarts'
import resize from '@/mixins/ems/resize'
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
import {getPointData} from '@/api/ems/dzjk'
import {getProjectDisplayData} from '@/api/ems/dzjk'
import {getPointConfigCurve} from '@/api/ems/site'
import intervalUpdate from "@/mixins/ems/intervalUpdate";
export default {
@ -48,8 +49,34 @@ export default {
getGVQXData() {
this.showLoading()
const {siteId, timeRange} = this
getPointData({siteId, startDate: timeRange[0], endDate: timeRange[1]}).then(response => {
this.setOption(response?.data || [])
getProjectDisplayData(siteId).then(response => {
const displayData = response?.data || []
const sectionRows = displayData.filter(item =>
item && item.sectionName === '当日功率曲线' && item.useFixedDisplay !== 1 && item.dataPoint
)
const tasks = sectionRows.map(row => {
const pointId = String(row.dataPoint || '').trim()
if (!pointId) return Promise.resolve(null)
return getPointConfigCurve({
siteId,
pointId,
pointType: 'data',
rangeType: 'custom',
startTime: this.normalizeDateTime(timeRange[0], false),
endTime: this.normalizeDateTime(timeRange[1], true)
}).then(curveResponse => {
const list = curveResponse?.data || []
return {
name: row.fieldName || row.fieldCode || pointId,
data: list
.map(item => [this.parseToTimestamp(item.dataTime), Number(item.pointValue)])
.filter(item => item[0] && !Number.isNaN(item[1]))
}
}).catch(() => null)
})
return Promise.all(tasks)
}).then(series => {
this.setOption((series || []).filter(Boolean))
}).finally(() => this.hideLoading())
},
init(siteId) {
@ -70,12 +97,18 @@ export default {
hideLoading() {
this.chart && this.chart.hideLoading()
},
setOption(data) {
const source = [['日期', '电网功率', '负载功率', '储能功率', '光伏功率', 'soc平均值', 'soh平均值', '电池平均温度平均值']]
console.log('source.slice(1)', source[0].slice(1))
this.chart && data.forEach((item) => {
source.push([item.statisDate, item.gridPower, item.loadPower, item.storagePower, item.pvPower, item.avgSoc, item.avgSoh, item.avgTemp])
})
normalizeDateTime(value, endOfDay) {
const raw = String(value || '').trim()
if (!raw) return ''
if (raw.includes(' ')) return raw
return `${raw} ${endOfDay ? '23:59:59' : '00:00:00'}`
},
parseToTimestamp(value) {
if (!value) return null
const t = new Date(value).getTime()
return Number.isNaN(t) ? null : t
},
setOption(seriesData = []) {
this.chart.setOption({
grid: {
containLabel: true
@ -86,35 +119,28 @@ export default {
},
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
axisPointer: { type: 'cross' }
},
textStyle: {
color: "#333333",
},
xAxis: {
type: 'category',
type: 'time',
},
yAxis: [
{
type: 'value',
},
{
type: 'value',
},
],
dataset: {source},
series: source[0].slice(1).map((item, index) => {
yAxis: [{
type: 'value',
}],
series: seriesData.map((item) => {
return {
type: 'line',//index === 5 ? 'bar' : 'line',
name: item.name,
type: 'line',
showSymbol: false,
symbolSize: 2,
smooth: true,
areaStyle: {
opacity: 0.5,
},
yAxisIndex: index <= 4 ? 0 : 1
data: item.data
}
})
})
@ -124,4 +150,3 @@ export default {
}
</script>

View File

@ -12,7 +12,8 @@
import * as echarts from 'echarts'
import resize from '@/mixins/ems/resize'
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
import {getSevenChargeData} from '@/api/ems/dzjk'
import {getProjectDisplayData} from '@/api/ems/dzjk'
import {getPointConfigCurve} from '@/api/ems/site'
export default {
mixins: [resize],
@ -45,8 +46,34 @@ export default {
getWeekKData() {
this.showLoading()
const {siteId, timeRange} = this
getSevenChargeData({siteId, startDate: timeRange[0], endDate: timeRange[1]}).then(response => {
this.setOption(response?.data || [])
getProjectDisplayData(siteId).then(response => {
const displayData = response?.data || []
const sectionRows = displayData.filter(item =>
item && item.sectionName === '一周充放曲线' && item.useFixedDisplay !== 1 && item.dataPoint
)
const tasks = sectionRows.map(row => {
const pointId = String(row.dataPoint || '').trim()
if (!pointId) return Promise.resolve(null)
return getPointConfigCurve({
siteId,
pointId,
pointType: 'data',
rangeType: 'custom',
startTime: this.normalizeDateTime(timeRange[0], false),
endTime: this.normalizeDateTime(timeRange[1], true)
}).then(curveResponse => {
const list = curveResponse?.data || []
return {
name: row.fieldName || row.fieldCode || pointId,
data: list
.map(item => [this.parseToTimestamp(item.dataTime), Number(item.pointValue)])
.filter(item => item[0] && !Number.isNaN(item[1]))
}
}).catch(() => null)
})
return Promise.all(tasks)
}).then(series => {
this.setOption((series || []).filter(Boolean))
}).finally(() => this.hideLoading())
},
init(siteId) {
@ -65,18 +92,23 @@ export default {
hideLoading() {
this.chart && this.chart.hideLoading()
},
setOption(data, unit) {
const source = [['日期', '充电量', '放电量']]
data.forEach(item => {
source.push([item.ammeterDate, item.chargedCap, item.disChargedCap])
})
normalizeDateTime(value, endOfDay) {
const raw = String(value || '').trim()
if (!raw) return ''
if (raw.includes(' ')) return raw
return `${raw} ${endOfDay ? '23:59:59' : '00:00:00'}`
},
parseToTimestamp(value) {
if (!value) return null
const t = new Date(value).getTime()
return Number.isNaN(t) ? null : t
},
setOption(seriesData = []) {
this.chart && this.chart.setOption({
color: ['#4472c4', '#70ad47'],//所有充放电颜色保持统一
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
axisPointer: { type: 'cross' }
},
grid: {
containLabel: true
@ -86,9 +118,7 @@ export default {
bottom: '15',
},
xAxis: {
type: 'category',
name: unit,
nameLocation: 'center'
type: 'time'
},
yAxis: [{
type: 'value',
@ -100,19 +130,12 @@ export default {
onZero: false
}
}],
dataset: {
source
},
series: [
{
yAxisIndex: 0,
type: 'bar',
},
{
yAxisIndex: 0,
type: 'bar',
},
]
series: seriesData.map(item => ({
name: item.name,
yAxisIndex: 0,
type: 'bar',
data: item.data
}))
})
}
}

View File

@ -1,5 +1,5 @@
<template>
<div v-loading="loading">
<div>
<el-row style="background: #fff" class="row-container" :gutter="15">
<el-col :xs="24" :sm="24" :lg="5">
<!-- 站点信息-->
@ -21,13 +21,19 @@
<div class="title">
<i class="el-icon-location"></i>
</div>
<div class="value">{{ info.siteAddress }}</div>
<div class="value">
<i v-if="isBaseInfoLoading" class="el-icon-loading"></i>
<span v-else>{{ info.siteAddress || '-' }}</span>
</div>
</div>
<div class="site-info">
<div class="title">
<i class="el-icon-date"></i>
</div>
<div class="value">{{ info.runningTime || '-' }}</div>
<div class="value">
<i v-if="isBaseInfoLoading" class="el-icon-loading"></i>
<span v-else>{{ info.runningTime || '-' }}</span>
</div>
</div>
<!-- 装机功率容量 -->
<el-row :gutter="10" style="margin-top:20px;">
@ -38,7 +44,8 @@
<div class="sjgl-wrapper">
<div class="sjgl-title">装机功率(MW)</div>
<div class="sjgl-value">
{{ info.installPower | formatNumber }}
<i v-if="isBaseInfoLoading" class="el-icon-loading"></i>
<span v-else>{{ info.installPower | formatNumber }}</span>
</div>
</div>
</el-col>
@ -49,7 +56,8 @@
<div class="sjgl-wrapper">
<div class="sjgl-title">装机容量(MW)</div>
<div class="sjgl-value">
{{ info.installCapacity | formatNumber }}
<i v-if="isBaseInfoLoading" class="el-icon-loading"></i>
<span v-else>{{ info.installCapacity | formatNumber }}</span>
</div>
</div>
</el-col>
@ -67,7 +75,10 @@
<span class="card-title">总累计运行数据</span>
<div class="total-count">
<span class="title">总收入</span>
<span class="value">{{ runningInfo.totalRevenue | formatNumber }}</span>
<span class="value">
<i v-if="isRunningInfoLoading" class="el-icon-loading"></i>
<span v-else>{{ totalRevenueDisplayValue | formatNumber }}</span>
</span>
<span class="unit"></span>
</div>
</div>
@ -77,14 +88,15 @@
<el-row :gutter="10">
<el-col
:span="6"
v-for="(item, index) in sjglData"
v-for="(item, index) in runningDataCards"
:key="index + 'sjglData'"
class="sjgl-col"
>
<div class="sjgl-wrapper">
<div class="sjgl-title">{{ item.title }}</div>
<div class="sjgl-value" :style="{color:item.color}">
{{ runningInfo[item.attr] | formatNumber }}
<i v-if="item.loading" class="el-icon-loading"></i>
<span v-else>{{ item.value | formatNumber }}</span>
</div>
</div>
</el-col>
@ -104,7 +116,7 @@
<script>
import {getSingleSiteBaseInfo} from "@/api/ems/zddt";
import {getDzjkHomeView} from "@/api/ems/dzjk";
import {getDzjkHomeView, getProjectDisplayData} from "@/api/ems/dzjk";
import WeekChart from "./WeekChart.vue";
import ActiveChart from "./ActiveChart.vue";
import AlarmTable from "./AlarmTable.vue";
@ -119,7 +131,9 @@ export default {
data() {
return {
loading: false,
sjglData: [
baseInfoLoading: false,
runningInfoLoading: false,
fallbackSjglData: [
{
title: "今日充电量kWh",
attr: "dayChargedCap",
@ -163,38 +177,106 @@ export default {
],
info: {}, //基本信息
runningInfo: {}, //总累计运行数据+报警表格
runningDisplayData: [], //单站监控项目配置展示数据
};
},
computed: {
isBaseInfoLoading() {
const state = this.$data || {};
return !!(state.baseInfoLoading || state.loading);
},
isRunningInfoLoading() {
const state = this.$data || {};
return !!(state.runningInfoLoading || state.loading);
},
tableData() {
return this.runningInfo?.siteMonitorHomeAlarmVo || [];
},
totalRunningSectionData() {
return (this.runningDisplayData || []).filter(item => item.sectionName === "总累计运行数据");
},
totalRevenueDisplayItem() {
const sectionData = this.totalRunningSectionData || [];
const byFieldCode = sectionData.find(item => item.fieldCode === "totalRevenue");
if (byFieldCode) {
return byFieldCode;
}
return sectionData.find(item => item.fieldName === "总收入");
},
totalRevenueDisplayValue() {
return this.totalRevenueDisplayItem ? this.totalRevenueDisplayItem.fieldValue : this.runningInfo.totalRevenue;
},
runningDataCards() {
const sectionData = this.totalRunningSectionData || [];
if (sectionData.length > 0) {
const revenueFieldCode = this.totalRevenueDisplayItem ? this.totalRevenueDisplayItem.fieldCode : "";
return sectionData
.filter(item => item.fieldCode !== revenueFieldCode)
.map((item, index) => ({
title: item.fieldName,
value: item.fieldValue,
color: this.getCardColor(index),
loading: this.isRunningInfoLoading,
}));
}
return this.fallbackSjglData.map(item => ({
title: item.title,
value: this.runningInfo[item.attr],
color: item.color,
loading: this.isRunningInfoLoading,
}));
},
},
methods: {
setBaseInfoLoading(loading) {
if (Object.prototype.hasOwnProperty.call(this.$data, "baseInfoLoading")) {
this.baseInfoLoading = loading;
return;
}
this.$set(this.$data, "baseInfoLoading", loading);
},
setRunningInfoLoading(loading) {
if (Object.prototype.hasOwnProperty.call(this.$data, "runningInfoLoading")) {
this.runningInfoLoading = loading;
return;
}
this.$set(this.$data, "runningInfoLoading", loading);
},
getCardColor(index) {
const colors = ['#4472c4', '#70ad47', '#4472c4', '#f67438', '#4472c4', '#70ad47', '#70ad47', '#f67438'];
return colors[index % colors.length];
},
toAlarm() {
this.$router.push({path: "/dzjk/gzgj", query: this.$route.query});
},
getBaseInfo() {
this.setBaseInfoLoading(true);
return getSingleSiteBaseInfo(this.siteId).then((response) => {
this.info = response?.data || {};
}).finally(() => {
this.setBaseInfoLoading(false);
});
},
getRunningInfo() {
return getDzjkHomeView(this.siteId).then((response) => {
this.runningInfo = response?.data || {};
this.setRunningInfoLoading(true);
return Promise.all([
getDzjkHomeView(this.siteId),
getProjectDisplayData(this.siteId),
]).then(([homeResponse, displayResponse]) => {
this.runningInfo = homeResponse?.data || {};
this.runningDisplayData = displayResponse?.data || [];
}).finally(() => {
this.setRunningInfoLoading(false);
});
},
init() {
this.loading = true;
// 功率曲线
this.$refs.activeChart.init(this.siteId);
// 一周冲放曲线
this.$refs.weekChart.init(this.siteId);
// 静态信息 this.getBaseInfo()
// 总累计运行数据+故障告警 this.getRunningInfo()
Promise.all([this.getBaseInfo(), this.getRunningInfo()]).finally(() => {
this.loading = false;
});
Promise.all([this.getBaseInfo(), this.getRunningInfo()]);
// 一分钟循环一次总累计运行数据
this.updateInterval(this.getRunningInfo);
},

View File

@ -2,7 +2,6 @@
<template>
<div class="ems-dashboard-editor-container">
<zd-select :get-list-by-store="true" :default-site-id="$route.query.siteId" @submitSite="submitSite"/>
<el-menu
class="ems-second-menu"
:default-active="$route.meta.activeSecondMenuName"
@ -28,11 +27,8 @@
<script>
import { dzjk } from '@/router/ems'
const childrenRoute = dzjk[0].children[0].children//获取到单站监控下面的字路由
console.log('childrenRoute',childrenRoute)
import ZdSelect from '@/components/Ems/ZdSelect/index.vue'
import {mapState} from "vuex";
export default {
components:{ZdSelect},
data(){
return {
childrenRoute,
@ -44,18 +40,6 @@ export default {
dzjkAlarmLighting:state=>state.ems.dzjkAlarmLighting
})
},
methods:{
submitSite(id){
if(id !== this.$route.query.siteId){
// console.log('单站监控选择了其他的站点id=',id,'并更新页面地址参数')
this.$router.push({query:{...this.$route.query,siteId:id}})
}else{
// console.log('单站监控选择了相同的其他的站点id=',id,'页面地址不发生改变')
}
//获取告警列表数据
this.$store.dispatch('getSiteAlarmNum',id)
}
},
beforeRouteLeave(to,from, next){
//从单站监控下面的所有子页面跳出时会触发
// 清空store中的zdList 保障下次进入到单站监控会重新调用接口获取数据
@ -66,6 +50,9 @@ export default {
</script>
<style scoped lang="scss">
.ems-dashboard-editor-container{
padding-top: 12px;
}
.dzjk-ems-content-container{
margin-top:0;
min-height: 60vh;