From 50c72d69898bccf25d37f71025335932f4fe105c Mon Sep 17 00:00:00 2001 From: dashixiong Date: Fri, 13 Feb 2026 21:46:12 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ems/dzjk.js | 17 + src/components/Ems/ZdBaseInfo/index.vue | 21 +- src/components/Ems/ZdSelect/index.vue | 65 ++- src/layout/components/Navbar.vue | 47 +- src/permission.js | 23 + src/router/ems.js | 23 +- src/router/index.js | 2 +- src/views/ems/dzjk/clpz/index.vue | 1 - .../ems/dzjk/clpz/runtimeParam/index.vue | 137 +++++ src/views/ems/dzjk/clpz/xftg/AddTemplate.vue | 201 +++++-- src/views/ems/dzjk/clpz/xftg/TempTable.vue | 6 +- src/views/ems/dzjk/home/ActiveChart.vue | 77 ++- src/views/ems/dzjk/home/WeekChart.vue | 77 ++- src/views/ems/dzjk/home/index.vue | 114 +++- src/views/ems/dzjk/index.vue | 19 +- src/views/ems/home/index.vue | 45 +- src/views/ems/site/mqtt/index.vue | 39 +- src/views/ems/site/pointConfig/index.vue | 519 ++++++++++-------- src/views/ems/site/powerTariff/index.vue | 42 +- .../site/sbbh/{AddDevice.vue => AddPlan.vue} | 75 +-- src/views/ems/site/sbbh/index.vue | 83 +-- src/views/ems/site/sblb/AddDevice.vue | 297 +++++----- src/views/ems/site/sblb/index.vue | 125 +---- .../ems/site/zdlb/MonitorPointMapping.vue | 140 ++++- src/views/ems/site/zdlb/index.vue | 12 +- 25 files changed, 1402 insertions(+), 805 deletions(-) create mode 100644 src/views/ems/dzjk/clpz/runtimeParam/index.vue rename src/views/ems/site/sbbh/{AddDevice.vue => AddPlan.vue} (92%) diff --git a/src/api/ems/dzjk.js b/src/api/ems/dzjk.js index 79b619f..41b52a6 100644 --- a/src/api/ems/dzjk.js +++ b/src/api/ems/dzjk.js @@ -330,6 +330,23 @@ export function configStrategy(data) { }) } +// 获取策略运行参数配置(按站点) +export function getStrategyRuntimeConfig(siteId) { + return request({ + url: `/system/strategyRuntimeConfig/getBySiteId?siteId=${siteId}`, + method: 'get' + }) +} + +// 保存策略运行参数配置(按站点) +export function saveStrategyRuntimeConfig(data) { + return request({ + url: `/system/strategyRuntimeConfig/save`, + method: 'post', + data + }) +} + //http://localhost:8089/strategy/temp/getTempNameList?strategyId=1&siteId=021_FXX_01 //获取策略下的所有模板列表 export function getTempNameList({siteId, strategyId}) { diff --git a/src/components/Ems/ZdBaseInfo/index.vue b/src/components/Ems/ZdBaseInfo/index.vue index 8028c33..6ccb1ec 100644 --- a/src/components/Ems/ZdBaseInfo/index.vue +++ b/src/components/Ems/ZdBaseInfo/index.vue @@ -4,7 +4,10 @@
{{ item.title }}
-
{{item.num | formatNumber}}
+
+ + {{item.num | formatNumber}} +
@@ -18,30 +21,35 @@ export default { title:'站点总数(座)', num:'', color:'#FFBD00', - attr:'siteNum' + attr:'siteNum', + loading: true },{ title:'装机功率(MW)', num:'', color:'#3C81FF', - attr:'installPower' + attr:'installPower', + loading: true },{ title:'装机容量(MW)', num:'', color:'#5AC7C0', - attr:'installCapacity' + attr:'installCapacity', + loading: true },{ title:'总充电量(KWh)', num:'', color:'#A696FF', - attr:'totalChargedCap' + attr:'totalChargedCap', + loading: true },{ title:'总放电量(KWh)', num:'', color:'#A696FF', - attr:'totalDischargedCap' + attr:'totalDischargedCap', + loading: true }] } @@ -50,6 +58,7 @@ export default { setData(res = {}){ this.data.forEach((item)=>{ item.num =res[item.attr] + item.loading = false }) } }, diff --git a/src/components/Ems/ZdSelect/index.vue b/src/components/Ems/ZdSelect/index.vue index bfe869f..8dbc6c7 100644 --- a/src/components/Ems/ZdSelect/index.vue +++ b/src/components/Ems/ZdSelect/index.vue @@ -2,8 +2,16 @@ + + diff --git a/src/views/ems/dzjk/clpz/xftg/AddTemplate.vue b/src/views/ems/dzjk/clpz/xftg/AddTemplate.vue index c8f8dd1..9387e5e 100644 --- a/src/views/ems/dzjk/clpz/xftg/AddTemplate.vue +++ b/src/views/ems/dzjk/clpz/xftg/AddTemplate.vue @@ -9,14 +9,6 @@ - - - - - - - - 新增 @@ -64,6 +56,12 @@ + + + + + + + + + + + + + + + { 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; } - \ No newline at end of file + diff --git a/src/views/ems/dzjk/clpz/xftg/TempTable.vue b/src/views/ems/dzjk/clpz/xftg/TempTable.vue index f79cf9b..3ad3043 100644 --- a/src/views/ems/dzjk/clpz/xftg/TempTable.vue +++ b/src/views/ems/dzjk/clpz/xftg/TempTable.vue @@ -39,14 +39,14 @@ prop="sdcDown" label="SOC下限"> { - 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 { } - diff --git a/src/views/ems/dzjk/home/WeekChart.vue b/src/views/ems/dzjk/home/WeekChart.vue index 592be06..9176d5a 100644 --- a/src/views/ems/dzjk/home/WeekChart.vue +++ b/src/views/ems/dzjk/home/WeekChart.vue @@ -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 + })) }) } } diff --git a/src/views/ems/dzjk/home/index.vue b/src/views/ems/dzjk/home/index.vue index b2f9b1b..fae1a3e 100644 --- a/src/views/ems/dzjk/home/index.vue +++ b/src/views/ems/dzjk/home/index.vue @@ -1,5 +1,5 @@