1. 单体电池批量更改为一个批量生成的按钮

2. 运行曲线数据改用电表报表的数据
This commit is contained in:
xiaoyang
2026-04-15 22:44:21 +08:00
parent b73999bd23
commit 801d8eab1d
3 changed files with 262 additions and 242 deletions

View File

@ -1,6 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
//获取单个站点的信息 //鑾峰彇鍗曚釜绔欑偣鐨勪俊鎭?
export function getDzjkHomeView(siteId) { export function getDzjkHomeView(siteId) {
return request({ return request({
url: `/ems/siteMonitor/homeView?siteId=${siteId}`, url: `/ems/siteMonitor/homeView?siteId=${siteId}`,
@ -8,7 +8,7 @@ export function getDzjkHomeView(siteId) {
}) })
} }
//获取单个站点总累计运行数据(基于日表) //鑾峰彇鍗曚釜绔欑偣鎬荤疮璁¤繍琛屾暟鎹紙鍩轰簬鏃ヨ〃锛?
export function getDzjkHomeTotalView(siteId) { export function getDzjkHomeTotalView(siteId) {
return request({ return request({
url: `/ems/siteMonitor/homeTotalView?siteId=${siteId}`, url: `/ems/siteMonitor/homeTotalView?siteId=${siteId}`,
@ -16,7 +16,7 @@ export function getDzjkHomeTotalView(siteId) {
}) })
} }
// 单站监控项目点位配置(供单站监控功能查询) // 鍗曠珯鐩戞帶椤圭洰鐐逛綅閰嶇疆锛堜緵鍗曠珯鐩戞帶鍔熻兘鏌ヨ锛?
export function getProjectPointMapping(siteId) { export function getProjectPointMapping(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getProjectPointMapping?siteId=${siteId}`, url: `/ems/siteMonitor/getProjectPointMapping?siteId=${siteId}`,
@ -24,7 +24,7 @@ export function getProjectPointMapping(siteId) {
}) })
} }
// 单站监控项目展示数据(字段配置 + 最新值) // 鍗曠珯鐩戞帶椤圭洰灞曠ず鏁版嵁锛堝瓧娈甸厤缃?+ 鏈€鏂板€硷級
export function getProjectDisplayData(siteId) { export function getProjectDisplayData(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getProjectDisplayData?siteId=${siteId}`, url: `/ems/siteMonitor/getProjectDisplayData?siteId=${siteId}`,
@ -32,7 +32,7 @@ export function getProjectDisplayData(siteId) {
}) })
} }
// 单站监控项目展示数据写入(批量) // 鍗曠珯鐩戞帶椤圭洰灞曠ず鏁版嵁鍐欏叆锛堟壒閲忥級
export function saveProjectDisplayData(data) { export function saveProjectDisplayData(data) {
return request({ return request({
url: `/ems/siteMonitor/saveProjectDisplayData`, url: `/ems/siteMonitor/saveProjectDisplayData`,
@ -41,7 +41,7 @@ export function saveProjectDisplayData(data) {
}) })
} }
//站点首页 冲放曲线 //绔欑偣棣栭〉 鍐叉斁鏇茬嚎
export function getSevenChargeData({siteId, startDate, endDate}) { export function getSevenChargeData({siteId, startDate, endDate}) {
return request({ return request({
url: `/ems/siteMap/getSevenChargeData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`, url: `/ems/siteMap/getSevenChargeData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`,
@ -49,7 +49,7 @@ export function getSevenChargeData({siteId, startDate, endDate}) {
}) })
} }
// 获取站点包含的设备种类 用来判断单站监控设备监控的菜单栏展示 // 鑾峰彇绔欑偣鍖呭惈鐨勮澶囩绫?鐢ㄦ潵鍒ゆ柇鍗曠珯鐩戞帶璁惧鐩戞帶鐨勮彍鍗曟爮灞曠ず
export function getSiteAllDeviceCategory(siteId) { export function getSiteAllDeviceCategory(siteId) {
return request({ return request({
url: `/ems/siteConfig/getSiteAllDeviceCategory?siteId=${siteId}`, url: `/ems/siteConfig/getSiteAllDeviceCategory?siteId=${siteId}`,
@ -65,7 +65,7 @@ export function getEmsDataList(siteId) {
}) })
} }
//获取pcs、实时运行头部的设备信息 //鑾峰彇pcs銆佸疄鏃惰繍琛屽ご閮ㄧ殑璁惧淇℃伅
export function getRunningHeadInfo(siteId) { export function getRunningHeadInfo(siteId) {
return request({ return request({
url: `/ems/siteMonitor/runningHeadInfo?siteId=${siteId}`, url: `/ems/siteMonitor/runningHeadInfo?siteId=${siteId}`,
@ -73,7 +73,7 @@ export function getRunningHeadInfo(siteId) {
}) })
} }
//获取pcs列表 //鑾峰彇pcs鍒楄〃
export function getPcsDetailInfo(siteId) { export function getPcsDetailInfo(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getPcsDetailInfo?siteId=${siteId}`, url: `/ems/siteMonitor/getPcsDetailInfo?siteId=${siteId}`,
@ -81,7 +81,7 @@ export function getPcsDetailInfo(siteId) {
}) })
} }
//获取BMS总览数据 //鑾峰彇BMS鎬昏鏁版嵁
export function getBMSOverView(siteId) { export function getBMSOverView(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getBMSOverView?siteId=${siteId}`, url: `/ems/siteMonitor/getBMSOverView?siteId=${siteId}`,
@ -89,7 +89,7 @@ export function getBMSOverView(siteId) {
}) })
} }
//获取BMS电池簇总览数据 //鑾峰彇BMS鐢垫睜绨囨€昏鏁版嵁
export function getBMSBatteryCluster(siteId) { export function getBMSBatteryCluster(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getBMSBatteryCluster?siteId=${siteId}`, url: `/ems/siteMonitor/getBMSBatteryCluster?siteId=${siteId}`,
@ -97,7 +97,7 @@ export function getBMSBatteryCluster(siteId) {
}) })
} }
//获取单体电池 电池堆列表数据 //鑾峰彇鍗曚綋鐢垫睜 鐢垫睜鍫嗗垪琛ㄦ暟鎹?
export function getStackNameList(siteId) { export function getStackNameList(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getStackNameList?siteId=${siteId}`, url: `/ems/siteMonitor/getStackNameList?siteId=${siteId}`,
@ -105,7 +105,7 @@ export function getStackNameList(siteId) {
}) })
} }
//获取单体电池 电池簇列表数据 //鑾峰彇鍗曚綋鐢垫睜 鐢垫睜绨囧垪琛ㄦ暟鎹?
export function getClusterNameList({stackDeviceId, siteId}) { export function getClusterNameList({stackDeviceId, siteId}) {
return request({ return request({
url: `/ems/siteMonitor/getClusterNameList?stackDeviceId=${stackDeviceId}&siteId=${siteId}`, url: `/ems/siteMonitor/getClusterNameList?stackDeviceId=${stackDeviceId}&siteId=${siteId}`,
@ -113,7 +113,7 @@ export function getClusterNameList({stackDeviceId, siteId}) {
}) })
} }
//单体电池表格数据 //鍗曚綋鐢垫睜琛ㄦ牸鏁版嵁
export function getClusterDataInfoList({siteId, stackDeviceId, clusterDeviceId, batteryId, pageSize, pageNum}) { export function getClusterDataInfoList({siteId, stackDeviceId, clusterDeviceId, batteryId, pageSize, pageNum}) {
return request({ return request({
url: `/ems/siteMonitor/getClusterDataInfoList?clusterDeviceId=${clusterDeviceId}&siteId=${siteId}&stackDeviceId=${stackDeviceId}&batteryId=${batteryId}&pageSize=${pageSize}&pageNum=${pageNum}`, url: `/ems/siteMonitor/getClusterDataInfoList?clusterDeviceId=${clusterDeviceId}&siteId=${siteId}&stackDeviceId=${stackDeviceId}&batteryId=${batteryId}&pageSize=${pageSize}&pageNum=${pageNum}`,
@ -121,7 +121,7 @@ export function getClusterDataInfoList({siteId, stackDeviceId, clusterDeviceId,
}) })
} }
// 单体电池图表 // 鍗曚綋鐢垫睜鍥捐〃
//http://localhost:8089/ems/siteMonitor/getSingleBatteryData?clusterDeviceId=BMSC01&siteId=021_FXX_01&deviceId=001&startDate=2025-07-11&endDate=2025-07-18 //http://localhost:8089/ems/siteMonitor/getSingleBatteryData?clusterDeviceId=BMSC01&siteId=021_FXX_01&deviceId=001&startDate=2025-07-11&endDate=2025-07-18
export function getSingleBatteryData({siteId, deviceId, clusterDeviceId, startDate, endDate}) { export function getSingleBatteryData({siteId, deviceId, clusterDeviceId, startDate, endDate}) {
return request({ return request({
@ -130,7 +130,7 @@ export function getSingleBatteryData({siteId, deviceId, clusterDeviceId, startDa
}) })
} }
//获取液冷列表数据 //鑾峰彇娑插喎鍒楄〃鏁版嵁
export function getCoolingDataList(siteId) { export function getCoolingDataList(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getCoolingDataList?siteId=${siteId}`, url: `/ems/siteMonitor/getCoolingDataList?siteId=${siteId}`,
@ -138,7 +138,7 @@ export function getCoolingDataList(siteId) {
}) })
} }
//获取动环数据 //鑾峰彇鍔ㄧ幆鏁版嵁
export function getDhDataList(siteId) { export function getDhDataList(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getDhDataList?siteId=${siteId}`, url: `/ems/siteMonitor/getDhDataList?siteId=${siteId}`,
@ -146,7 +146,7 @@ export function getDhDataList(siteId) {
}) })
} }
//获取消防数据 //鑾峰彇娑堥槻鏁版嵁
export function getXfDataList(siteId) { export function getXfDataList(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getXfDataList?siteId=${siteId}`, url: `/ems/siteMonitor/getXfDataList?siteId=${siteId}`,
@ -155,7 +155,7 @@ export function getXfDataList(siteId) {
} }
//获取电表数据 //鑾峰彇鐢佃〃鏁版嵁
export function getAmmeterDataList(siteId) { export function getAmmeterDataList(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getAmmeterDataList?siteId=${siteId}`, url: `/ems/siteMonitor/getAmmeterDataList?siteId=${siteId}`,
@ -163,7 +163,7 @@ export function getAmmeterDataList(siteId) {
}) })
} }
// 故障告警 // 鏁呴殰鍛婅
export function getAlarmDetailList({ export function getAlarmDetailList({
status, status,
siteId, siteId,
@ -180,7 +180,7 @@ export function getAlarmDetailList({
}) })
} }
// 告警生成工单 // 鍛婅鐢熸垚宸ュ崟
export function createTicketNo(data) { export function createTicketNo(data) {
return request({ return request({
url: `/ems/siteAlarm/createTicketNo`, url: `/ems/siteAlarm/createTicketNo`,
@ -189,7 +189,7 @@ export function createTicketNo(data) {
}) })
} }
// 告警确认关闭 // 鍛婅纭鍏抽棴
export function closeAlarm(data) { export function closeAlarm(data) {
return request({ return request({
url: `/ems/siteAlarm/closeAlarm`, url: `/ems/siteAlarm/closeAlarm`,
@ -273,17 +273,17 @@ function normalizeDateInput(dateStr) {
function resolveElectricUnit(startDate, endDate) { function resolveElectricUnit(startDate, endDate) {
const start = new Date(`${normalizeDateInput(startDate)} 00:00:00`) const start = new Date(`${normalizeDateInput(startDate)} 00:00:00`)
const end = new Date(`${normalizeDateInput(endDate)} 00:00:00`) const end = new Date(`${normalizeDateInput(endDate)} 00:00:00`)
if (isNaN(start.getTime()) || isNaN(end.getTime())) return '' if (isNaN(start.getTime()) || isNaN(end.getTime())) return '\u65e5'
const diffDays = Math.floor((end.getTime() - start.getTime()) / (24 * 60 * 60 * 1000)) const diffDays = Math.floor((end.getTime() - start.getTime()) / (24 * 60 * 60 * 1000))
if (diffDays <= 0) return '' if (diffDays <= 0) return '\u65e5'
if (diffDays < 30) return '' if (diffDays < 30) return '\u65e5'
return '' return '\u6708'
} }
function formatByUnit(date, unit) { function formatByUnit(date, unit) {
const p = (n) => String(n).padStart(2, '0') const p = (n) => String(n).padStart(2, '0')
if (unit === '') return `${p(date.getHours())}:${p(date.getMinutes())}` if (unit === '\u65e5') return `${p(date.getHours())}:${p(date.getMinutes())}`
if (unit === '') return `${date.getFullYear()}-${p(date.getMonth() + 1)}` if (unit === '\u6708') return `${date.getFullYear()}-${p(date.getMonth() + 1)}`
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())}` return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())}`
} }
@ -394,6 +394,39 @@ function resolveAliasByField(aliasMap, fieldName) {
return aliasMap[withoutStat] || '' return aliasMap[withoutStat] || ''
} }
function toFixedNumber(value, digits = 2) {
const num = toNumber(value)
if (num == null) return null
return Number(num.toFixed(digits))
}
function sortDailyAmmeterRows(rows = []) {
return [...(rows || [])].sort((a, b) => {
const ta = new Date(`${a?.dataTime || ''} 00:00:00`).getTime()
const tb = new Date(`${b?.dataTime || ''} 00:00:00`).getTime()
return (isNaN(ta) ? 0 : ta) - (isNaN(tb) ? 0 : tb)
})
}
function queryAllAmmeterDailyRows({siteId, startTime, endTime, pageSize = 500, pageNum = 1, rows = []}) {
return getAmmeterData({siteId, startTime, endTime, pageSize, pageNum}).then((response) => {
const currentRows = Array.isArray(response?.rows) ? response.rows : []
const allRows = rows.concat(currentRows)
const total = Number(response?.total) || 0
if (allRows.length >= total || currentRows.length < pageSize) {
return allRows
}
return queryAllAmmeterDailyRows({
siteId,
startTime,
endTime,
pageSize,
pageNum: pageNum + 1,
rows: allRows,
})
})
}
function queryMenuPointCurves({siteId, menuCode, startDate, endDate, mappingFilter}) { function queryMenuPointCurves({siteId, menuCode, startDate, endDate, mappingFilter}) {
return getProjectPointMapping(siteId).then((mappingResp) => { return getProjectPointMapping(siteId).then((mappingResp) => {
const allMappings = Array.isArray(mappingResp?.data) ? mappingResp.data : [] const allMappings = Array.isArray(mappingResp?.data) ? mappingResp.data : []
@ -420,94 +453,48 @@ function queryMenuPointCurves({siteId, menuCode, startDate, endDate, mappingFilt
}) })
} }
// 电量指标 // 鐢甸噺鎸囨爣
export function getElectricData({siteId, startDate, endDate}) { export function getElectricData({siteId, startDate, endDate}) {
return getProjectPointMapping(siteId).then((mappingResp) => { return queryAllAmmeterDailyRows({
const allMappings = Array.isArray(mappingResp?.data) ? mappingResp.data : [] siteId,
const gltjMappings = allMappings.filter(item => item?.menuCode === 'TJBB_GLTJ') startTime: startDate,
endTime: endDate,
const chargedMap = findMappingByField(gltjMappings, ['chargedCap_stat', 'chargedCap']) }).then((rows) => {
const disChargedMap = findMappingByField(gltjMappings, ['disChargedCap_stat', 'disChargedCap']) const sortedRows = sortDailyAmmeterRows(rows)
const dailyEfficiencyMap = findMappingByField(gltjMappings, ['dailyEfficiency']) const sevenDayDisChargeStats = sortedRows.map((item) => {
const totalChargedMap = findMappingByField(gltjMappings, ['totalChargedCap_stat', 'totalChargedCap']) const chargedCap = toNumber(item?.activeTotalKwh)
const totalDisChargedMap = findMappingByField(gltjMappings, ['totalDisChargedCap_stat', 'totalDisChargedCap']) const disChargedCap = toNumber(item?.reActiveTotalKwh)
const totalEfficiencyMap = findMappingByField(gltjMappings, ['efficiency']) const rowEffect = toNumber(item?.effect)
const dailyEfficiency = rowEffect != null
const pointMap = { ? rowEffect
charged: getDataPointFromMapping(chargedMap), : (chargedCap > 0 && disChargedCap != null ? toFixedNumber((disChargedCap / chargedCap) * 100) : null)
disCharged: getDataPointFromMapping(disChargedMap),
dailyEfficiency: getDataPointFromMapping(dailyEfficiencyMap),
totalCharged: getDataPointFromMapping(totalChargedMap),
totalDisCharged: getDataPointFromMapping(totalDisChargedMap),
totalEfficiency: getDataPointFromMapping(totalEfficiencyMap),
}
const queryTasks = Object.keys(pointMap).map((key) => {
const pointId = pointMap[key]
return queryPointCurveByPointId({siteId, pointId, startDate, endDate})
.then(curve => ({key, curve}))
.catch(() => ({key, curve: []}))
})
return Promise.all(queryTasks).then((queryResult) => {
const curveMap = {}
queryResult.forEach(item => {
curveMap[item.key] = item.curve || []
})
const unit = resolveElectricUnit(startDate, endDate)
const chargedSeries = aggregateCurveByUnit(curveMap.charged, unit)
const disChargedSeries = aggregateCurveByUnit(curveMap.disCharged, unit)
const efficiencySeries = aggregateCurveByUnit(curveMap.dailyEfficiency, unit)
const labels = Array.from(new Set([
...chargedSeries.keys(),
...disChargedSeries.keys(),
...efficiencySeries.keys(),
])).sort()
const sevenDayDisChargeStats = labels.map((label) => {
const chargedCap = chargedSeries.get(label)
const disChargedCap = disChargedSeries.get(label)
let dailyEfficiency = efficiencySeries.get(label)
if (dailyEfficiency == null && chargedCap != null && chargedCap !== 0 && disChargedCap != null) {
dailyEfficiency = Number(((disChargedCap / chargedCap) * 100).toFixed(2))
}
return { return {
ammeterDate: label, ammeterDate: item?.dataTime || '',
chargedCap: chargedCap == null ? '' : chargedCap, chargedCap: chargedCap == null ? '' : chargedCap,
disChargedCap: disChargedCap == null ? '' : disChargedCap, disChargedCap: disChargedCap == null ? '' : disChargedCap,
dailyEfficiency: dailyEfficiency == null ? '' : dailyEfficiency, dailyEfficiency: dailyEfficiency == null ? '' : dailyEfficiency,
} }
}) })
const fallbackTotalCharged = sevenDayDisChargeStats.reduce((acc, item) => acc + (toNumber(item.chargedCap) || 0), 0) const totalChargedCap = toFixedNumber(sevenDayDisChargeStats.reduce((acc, item) => acc + (toNumber(item.chargedCap) || 0), 0))
const fallbackTotalDisCharged = sevenDayDisChargeStats.reduce((acc, item) => acc + (toNumber(item.disChargedCap) || 0), 0) const totalDisChargedCap = toFixedNumber(sevenDayDisChargeStats.reduce((acc, item) => acc + (toNumber(item.disChargedCap) || 0), 0))
const efficiency = totalChargedCap > 0
const totalChargedCap = getLatestCurveValue(curveMap.totalCharged) ? toFixedNumber((totalDisChargedCap / totalChargedCap) * 100)
const totalDisChargedCap = getLatestCurveValue(curveMap.totalDisCharged) : 0
const totalEfficiency = getLatestCurveValue(curveMap.totalEfficiency)
const resultTotalCharged = totalChargedCap == null ? fallbackTotalCharged : totalChargedCap
const resultTotalDisCharged = totalDisChargedCap == null ? fallbackTotalDisCharged : totalDisChargedCap
const resultEfficiency = totalEfficiency == null
? (resultTotalCharged > 0 ? Number(((resultTotalDisCharged / resultTotalCharged) * 100).toFixed(2)) : 0)
: totalEfficiency
return { return {
data: { data: {
totalChargedCap: resultTotalCharged, totalChargedCap: totalChargedCap == null ? 0 : totalChargedCap,
totalDisChargedCap: resultTotalDisCharged, totalDisChargedCap: totalDisChargedCap == null ? 0 : totalDisChargedCap,
efficiency: resultEfficiency, efficiency: efficiency == null ? 0 : efficiency,
unit, unit: '\u65e5',
sevenDayDisChargeStats, sevenDayDisChargeStats,
} }
} }
}) })
})
} }
//获取pcs列表 //鑾峰彇pcs鍒楄〃
export function getPcsNameList(siteId) { export function getPcsNameList(siteId) {
return request({ return request({
url: `/ems/siteMonitor/getPcsNameList?siteId=${siteId}`, url: `/ems/siteMonitor/getPcsNameList?siteId=${siteId}`,
@ -515,7 +502,7 @@ export function getPcsNameList(siteId) {
}) })
} }
// pcs曲线 // pcs鏇茬嚎
export function getPCSData({siteId, startTime, endTime}) { export function getPCSData({siteId, startTime, endTime}) {
const kind = resolveRangeKind(startTime, endTime) const kind = resolveRangeKind(startTime, endTime)
const aliasMap = { const aliasMap = {
@ -560,7 +547,7 @@ export function getPCSData({siteId, startTime, endTime}) {
}) })
} }
// 电池堆曲线 // 鐢垫睜鍫嗘洸绾?
export function getStackData({siteId, startTime, endTime}) { export function getStackData({siteId, startTime, endTime}) {
const kind = resolveRangeKind(startTime, endTime) const kind = resolveRangeKind(startTime, endTime)
const aliasMap = { const aliasMap = {
@ -604,7 +591,7 @@ export function getStackData({siteId, startTime, endTime}) {
}) })
} }
// 电池温度 // 鐢垫睜娓╁害
export function getClusterData({siteId, stackId, clusterId, dateTime, pageNum, pageSize}) { export function getClusterData({siteId, stackId, clusterId, dateTime, pageNum, pageSize}) {
const startDate = dateTime || normalizeDateInput('') const startDate = dateTime || normalizeDateInput('')
const endDate = dateTime || normalizeDateInput('') const endDate = dateTime || normalizeDateInput('')
@ -660,8 +647,8 @@ export function getClusterData({siteId, stackId, clusterId, dateTime, pageNum, p
} }
// 实时运行 // 瀹炴椂杩愯
//储能 //鍌ㄨ兘
export function storagePower(siteId, startTime, endTime) { export function storagePower(siteId, startTime, endTime) {
return request({ return request({
url: `/ems/siteMonitor/runningGraph/storagePower?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`, url: `/ems/siteMonitor/runningGraph/storagePower?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`,
@ -669,7 +656,7 @@ export function storagePower(siteId, startTime, endTime) {
}) })
} }
//poc温度 //poc娓╁害
export function pcsMaxTemp(siteId, startTime, endTime) { export function pcsMaxTemp(siteId, startTime, endTime) {
return request({ return request({
url: `/ems/siteMonitor/runningGraph/pcsMaxTemp?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`, url: `/ems/siteMonitor/runningGraph/pcsMaxTemp?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`,
@ -677,7 +664,7 @@ export function pcsMaxTemp(siteId, startTime, endTime) {
}) })
} }
// 电池平均soc // 鐢垫睜骞冲潎soc
export function batteryAveSoc(siteId, startTime, endTime) { export function batteryAveSoc(siteId, startTime, endTime) {
return request({ return request({
url: `/ems/siteMonitor/runningGraph/batteryAveSoc?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`, url: `/ems/siteMonitor/runningGraph/batteryAveSoc?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`,
@ -685,7 +672,7 @@ export function batteryAveSoc(siteId, startTime, endTime) {
}) })
} }
// 电池平均温度 // 鐢垫睜骞冲潎娓╁害
export function batteryAveTemp(siteId, startTime, endTime) { export function batteryAveTemp(siteId, startTime, endTime) {
return request({ return request({
url: `/ems/siteMonitor/runningGraph/batteryAveTemp?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`, url: `/ems/siteMonitor/runningGraph/batteryAveTemp?siteId=${siteId}&startDate=${startTime}&endDate=${endTime}`,
@ -693,7 +680,7 @@ export function batteryAveTemp(siteId, startTime, endTime) {
}) })
} }
// 功率曲线 // 鍔熺巼鏇茬嚎
export function getPowerData({siteId, startDate, endDate}) { export function getPowerData({siteId, startDate, endDate}) {
const kind = resolveRangeKind(startDate, endDate) const kind = resolveRangeKind(startDate, endDate)
const aliasMap = { const aliasMap = {
@ -729,7 +716,7 @@ export function getPowerData({siteId, startDate, endDate}) {
}) })
} }
//电表列表 //鐢佃〃鍒楄〃
export function getLoadNameList(siteId) { export function getLoadNameList(siteId) {
return request({ return request({
url: `/ems/statsReport/getLoadNameList?siteId=${siteId}`, url: `/ems/statsReport/getLoadNameList?siteId=${siteId}`,
@ -737,7 +724,7 @@ export function getLoadNameList(siteId) {
}) })
} }
// 电表报表 // 鐢佃〃鎶ヨ〃
export function getAmmeterData({siteId, startTime, endTime, pageSize, pageNum}) { export function getAmmeterData({siteId, startTime, endTime, pageSize, pageNum}) {
return request({ return request({
url: `/ems/statsReport/getAmmeterDataFromDaily`, url: `/ems/statsReport/getAmmeterDataFromDaily`,
@ -752,7 +739,7 @@ export function getAmmeterData({siteId, startTime, endTime, pageSize, pageNum})
}) })
} }
// 电价报表 // 鐢典环鎶ヨ〃
export function getAmmeterRevenueData(data) { export function getAmmeterRevenueData(data) {
return request({ return request({
url: `/ems/statsReport/getAmmeterRevenueData`, url: `/ems/statsReport/getAmmeterRevenueData`,
@ -777,7 +764,7 @@ export function saveBizRemark(data) {
}) })
} }
//策略列表 //绛栫暐鍒楄〃
export function strategyRunningList(siteId) { export function strategyRunningList(siteId) {
return request({ return request({
url: `/system/strategyRunning/list?siteId=${siteId}`, url: `/system/strategyRunning/list?siteId=${siteId}`,
@ -785,7 +772,7 @@ export function strategyRunningList(siteId) {
}) })
} }
//停止策略 //鍋滄绛栫暐
export function stopStrategyRunning(id) { export function stopStrategyRunning(id) {
return request({ return request({
url: `/system/strategyRunning/stop?id=${id}`, url: `/system/strategyRunning/stop?id=${id}`,
@ -793,7 +780,7 @@ export function stopStrategyRunning(id) {
}) })
} }
// 获取所有主策略 // 鑾峰彇鎵€鏈変富绛栫暐
export function getMainStrategyList() { export function getMainStrategyList() {
return request({ return request({
url: `/system/strategyRunning/getMainStrategyList`, url: `/system/strategyRunning/getMainStrategyList`,
@ -801,7 +788,7 @@ export function getMainStrategyList() {
}) })
} }
//获取所有辅助策略 //鑾峰彇鎵€鏈夎緟鍔╃瓥鐣?
export function getAuxStrategyList() { export function getAuxStrategyList() {
return request({ return request({
url: `/system/strategyRunning/getAuxStrategyList`, url: `/system/strategyRunning/getAuxStrategyList`,
@ -809,7 +796,7 @@ export function getAuxStrategyList() {
}) })
} }
//配置策略 //閰嶇疆绛栫暐
export function configStrategy(data) { export function configStrategy(data) {
return request({ return request({
url: `/system/strategyRunning/configStrategy`, url: `/system/strategyRunning/configStrategy`,
@ -818,7 +805,7 @@ export function configStrategy(data) {
}) })
} }
// 获取策略运行参数配置(按站点) // 鑾峰彇绛栫暐杩愯鍙傛暟閰嶇疆锛堟寜绔欑偣锛?
export function getStrategyRuntimeConfig(siteId) { export function getStrategyRuntimeConfig(siteId) {
return request({ return request({
url: `/system/strategyRuntimeConfig/getBySiteId?siteId=${siteId}`, url: `/system/strategyRuntimeConfig/getBySiteId?siteId=${siteId}`,
@ -826,7 +813,7 @@ export function getStrategyRuntimeConfig(siteId) {
}) })
} }
// 保存策略运行参数配置(按站点) // 淇濆瓨绛栫暐杩愯鍙傛暟閰嶇疆锛堟寜绔欑偣锛?
export function saveStrategyRuntimeConfig(data) { export function saveStrategyRuntimeConfig(data) {
return request({ return request({
url: `/system/strategyRuntimeConfig/save`, url: `/system/strategyRuntimeConfig/save`,
@ -836,7 +823,7 @@ export function saveStrategyRuntimeConfig(data) {
} }
//http://localhost:8089/strategy/temp/getTempNameList?strategyId=1&siteId=021_FXX_01 //http://localhost:8089/strategy/temp/getTempNameList?strategyId=1&siteId=021_FXX_01
//获取策略下的所有模板列表 //鑾峰彇绛栫暐涓嬬殑鎵€鏈夋ā鏉垮垪琛?
export function getTempNameList({siteId, strategyId}) { export function getTempNameList({siteId, strategyId}) {
return request({ return request({
url: `/strategy/temp/getTempNameList?siteId=${siteId}&strategyId=${strategyId}`, url: `/strategy/temp/getTempNameList?siteId=${siteId}&strategyId=${strategyId}`,
@ -844,7 +831,7 @@ export function getTempNameList({siteId, strategyId}) {
}) })
} }
//获取模板详情 //鑾峰彇妯℃澘璇︽儏
///strategy/temp/list?templateId=1 ///strategy/temp/list?templateId=1
export function getStrategyTempDetail(templateId) { export function getStrategyTempDetail(templateId) {
return request({ return request({
@ -853,7 +840,7 @@ export function getStrategyTempDetail(templateId) {
}) })
} }
//新增模板 //鏂板妯℃澘
export function addStrategyTemp(data) { export function addStrategyTemp(data) {
return request({ return request({
url: `/strategy/temp`, url: `/strategy/temp`,
@ -886,7 +873,7 @@ export function timeConfigList({siteId, strategyId}) {
}) })
} }
//保存时间配置 //淇濆瓨鏃堕棿閰嶇疆
// http://localhost:8089/strategy/timeConfig // http://localhost:8089/strategy/timeConfig
export function setTimeConfigList(data) { export function setTimeConfigList(data) {
return request({ return request({
@ -896,7 +883,7 @@ export function setTimeConfigList(data) {
}) })
} }
// 策略曲线图 // 绛栫暐鏇茬嚎鍥?
//http://localhost:8089/strategy/curve/curveList?strategyId=1&siteId=021_FXX_01 //http://localhost:8089/strategy/curve/curveList?strategyId=1&siteId=021_FXX_01
export function curveList({siteId, strategyId}) { export function curveList({siteId, strategyId}) {
return request({ return request({
@ -905,7 +892,7 @@ export function curveList({siteId, strategyId}) {
}) })
} }
//单站监控 首页 当日功率曲线 //鍗曠珯鐩戞帶 棣栭〉 褰撴棩鍔熺巼鏇茬嚎
export function getPointData({siteId, startDate, endDate}) { export function getPointData({siteId, startDate, endDate}) {
return request({ return request({
url: `/ems/siteMonitor/getPointData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`, url: `/ems/siteMonitor/getPointData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`,

View File

@ -518,3 +518,11 @@ export function deleteMqtt(id) {
method: 'delete', method: 'delete',
}) })
} }
export function initializeSingleBatteryMonitorMappings(data) {
return request({
url: `/ems/siteConfig/initializeSingleBatteryMonitorMappings`,
method: 'post',
data
})
}

View File

@ -36,9 +36,8 @@
<div class="filter-actions" :class="{ 'single-battery-actions-visible': showSingleBatteryImportActions }"> <div class="filter-actions" :class="{ 'single-battery-actions-visible': showSingleBatteryImportActions }">
<el-button size="small" :disabled="!siteId" @click="openImportConfigDialog">导入配置</el-button> <el-button size="small" :disabled="!siteId" @click="openImportConfigDialog">导入配置</el-button>
<el-button size="small" type="primary" :disabled="!siteId" @click="exportConfig">导出配置</el-button> <el-button size="small" type="primary" :disabled="!siteId" @click="exportConfig">导出配置</el-button>
<el-button size="small" :disabled="!siteId || singleBatteryImportLoading" @click="downloadSingleBatteryTemplate">下载单体模板</el-button> <el-button v-if="showSingleBatteryImportActions" size="small" type="success" :disabled="!siteId || singleBatteryInitLoading" @click="openSingleBatteryInitDialog">
<el-button size="small" type="success" :disabled="!siteId || singleBatteryImportLoading" @click="openSingleBatteryImportDialog"> {{ singleBatteryInitLoading ? '初始化中...' : '初始化单体电池配置' }}
{{ singleBatteryImportLoading ? '导入中...' : '导入单体电池' }}
</el-button> </el-button>
<input <input
ref="configInput" ref="configInput"
@ -47,13 +46,6 @@
style="display: none" style="display: none"
@change="handleConfigFileChange" @change="handleConfigFileChange"
/> />
<input
ref="singleBatteryImportInput"
type="file"
accept=".xlsx,.xls"
style="display: none"
@change="handleSingleBatteryImportFileChange"
/>
</div> </div>
</div> </div>
@ -471,55 +463,73 @@
/> />
</el-dialog> </el-dialog>
<el-dialog <el-dialog
title="单体电池导入结果" title="初始化单体电池配置"
:visible.sync="singleBatteryImportResultVisible" :visible.sync="singleBatteryInitDialogVisible"
width="560px"
append-to-body
class="ems-dialog"
>
<el-form label-width="110px" size="small">
<el-form-item label="电池堆">
<el-select v-model="singleBatteryInitForm.stackDeviceId" placeholder="请选择电池堆" style="width: 100%" @change="handleSingleBatteryStackChange">
<el-option v-for="item in singleBatteryStackOptions" :key="item.id || item.deviceId || item" :label="item.name || item.deviceName || item.id || item.deviceId || item" :value="item.id || item.deviceId || item" />
</el-select>
</el-form-item>
<el-form-item label="电池簇">
<el-select v-model="singleBatteryInitForm.clusterDeviceId" placeholder="请选择电池簇" style="width: 100%">
<el-option v-for="item in singleBatteryClusterOptions" :key="item.id || item.deviceId || item" :label="item.name || item.deviceName || item.id || item.deviceId || item" :value="item.id || item.deviceId || item" />
</el-select>
</el-form-item>
<el-form-item label="目标数量">
<el-input-number v-model="singleBatteryInitForm.targetCount" :min="1" :precision="0" controls-position="right" style="width: 220px" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="singleBatteryInitDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="singleBatteryInitLoading" @click="submitSingleBatteryInit">确定初始化</el-button>
</div>
</el-dialog>
<el-dialog
title="初始化结果"
:visible.sync="singleBatteryInitResultVisible"
width="980px" width="980px"
append-to-body append-to-body
class="ems-dialog" class="ems-dialog"
> >
<el-alert <el-alert
:title="singleBatteryImportResult.message || '导入完成'" :title="singleBatteryInitResult.message || '初始化完成'"
:type="singleBatteryImportResult.committed ? 'success' : 'warning'" :type="singleBatteryInitResult.committed ? 'success' : 'warning'"
:closable="false" :closable="false"
style="margin-bottom: 16px;" style="margin-bottom: 16px;"
/> />
<el-descriptions :column="3" border size="small"> <el-descriptions :column="3" border size="small">
<el-descriptions-item label="总行数">{{ singleBatteryImportResult.totalRows || 0 }}</el-descriptions-item> <el-descriptions-item label="站点ID">{{ singleBatteryInitResult.siteId || '-' }}</el-descriptions-item>
<el-descriptions-item label="成功行数">{{ singleBatteryImportResult.successRows || 0 }}</el-descriptions-item> <el-descriptions-item label="初始化范围">{{ singleBatteryInitResult.scopeType === 'stack' ? '电池堆' : '电池簇' }}</el-descriptions-item>
<el-descriptions-item label="失败行数">{{ singleBatteryImportResult.failureRows || 0 }}</el-descriptions-item> <el-descriptions-item label="范围设备ID">{{ singleBatteryInitResult.scopeDeviceId || '-' }}</el-descriptions-item>
<el-descriptions-item label="新增单体">{{ singleBatteryImportResult.insertedBatteryCount || 0 }}</el-descriptions-item> <el-descriptions-item label="目标数量">{{ singleBatteryInitResult.targetCount || 0 }}</el-descriptions-item>
<el-descriptions-item label="更新单体">{{ singleBatteryImportResult.updatedBatteryCount || 0 }}</el-descriptions-item> <el-descriptions-item label="现有单体">{{ singleBatteryInitResult.existingBatteryCount || 0 }}</el-descriptions-item>
<el-descriptions-item label="是否已提交">{{ singleBatteryImportResult.committed ? '是' : '否' }}</el-descriptions-item> <el-descriptions-item label="已初始化单体">{{ singleBatteryInitResult.initializedBatteryCount || 0 }}</el-descriptions-item>
<el-descriptions-item label="新增映射">{{ singleBatteryImportResult.insertedMappingCount || 0 }}</el-descriptions-item> <el-descriptions-item label="新增单体">{{ singleBatteryInitResult.insertedBatteryCount || 0 }}</el-descriptions-item>
<el-descriptions-item label="更新映射">{{ singleBatteryImportResult.updatedMappingCount || 0 }}</el-descriptions-item> <el-descriptions-item label="命中点位">{{ singleBatteryInitResult.pointHitCount || 0 }}</el-descriptions-item>
<el-descriptions-item label="固定值回填">{{ singleBatteryInitResult.fixedValueFallbackCount || 0 }}</el-descriptions-item>
<el-descriptions-item label="新增映射">{{ singleBatteryInitResult.insertedMappingCount || 0 }}</el-descriptions-item>
<el-descriptions-item label="更新映射">{{ singleBatteryInitResult.updatedMappingCount || 0 }}</el-descriptions-item>
<el-descriptions-item label="是否成功">{{ singleBatteryInitResult.committed ? '是' : '否' }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<div v-if="(singleBatteryImportResult.failureDetails || []).length > 0" style="margin-top: 16px;">
<div class="import-result-title">失败明细</div>
<el-table class="common-table" :data="singleBatteryImportResult.failureDetails" stripe max-height="360">
<el-table-column prop="rowNum" label="Excel行号" width="100" />
<el-table-column prop="siteId" label="站点ID" width="140" />
<el-table-column prop="stackDeviceId" label="电池堆编号" width="140" />
<el-table-column prop="clusterDeviceId" label="电池簇编号" width="140" />
<el-table-column prop="batteryDeviceId" label="单体编号" width="120" />
<el-table-column prop="errorMessage" label="失败原因" min-width="260" />
</el-table>
</div>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { import {
downloadSingleBatteryMonitorImportTemplate,
getPointMatchList, getPointMatchList,
getSingleMonitorProjectPointMapping, getSingleMonitorProjectPointMapping,
getSingleMonitorWorkStatusEnumMappings, getSingleMonitorWorkStatusEnumMappings,
importSingleBatteryMonitorMappings, initializeSingleBatteryMonitorMappings,
saveSingleMonitorProjectPointMapping, saveSingleMonitorProjectPointMapping,
saveSingleMonitorWorkStatusEnumMappings saveSingleMonitorWorkStatusEnumMappings
} from '@/api/ems/site' } from '@/api/ems/site'
import { saveAs } from 'file-saver' import { getClusterNameList, getStackNameList } from '@/api/ems/dzjk'
import { blobValidate } from '@/utils/ems'
export default { export default {
name: 'MonitorPointMapping', name: 'MonitorPointMapping',
@ -569,19 +579,30 @@ export default {
suppressAutoSave: false, suppressAutoSave: false,
isSaving: false, isSaving: false,
saveStatusText: '自动保存已开启', saveStatusText: '自动保存已开启',
singleBatteryImportLoading: false, singleBatteryInitLoading: false,
singleBatteryImportResultVisible: false, singleBatteryInitDialogVisible: false,
singleBatteryImportResult: { singleBatteryInitResultVisible: false,
singleBatteryStackOptions: [],
singleBatteryClusterOptions: [],
singleBatteryInitForm: {
stackDeviceId: '',
clusterDeviceId: '',
targetCount: 1
},
singleBatteryInitResult: {
committed: false, committed: false,
totalRows: 0, siteId: '',
successRows: 0, scopeType: 'stack',
failureRows: 0, scopeDeviceId: '',
targetCount: 0,
existingBatteryCount: 0,
initializedBatteryCount: 0,
insertedBatteryCount: 0, insertedBatteryCount: 0,
updatedBatteryCount: 0, pointHitCount: 0,
fixedValueFallbackCount: 0,
insertedMappingCount: 0, insertedMappingCount: 0,
updatedMappingCount: 0, updatedMappingCount: 0,
message: '', message: ''
failureDetails: []
}, },
lastSavedPointSignature: '', lastSavedPointSignature: '',
lastSavedEnumSignature: '', lastSavedEnumSignature: '',
@ -793,81 +814,85 @@ export default {
this.$refs.configInput.click() this.$refs.configInput.click()
} }
}, },
openSingleBatteryImportDialog() { async openSingleBatteryInitDialog() {
if (!this.siteId || !this.$refs.singleBatteryImportInput) {
return
}
this.$refs.singleBatteryImportInput.value = ''
this.$refs.singleBatteryImportInput.click()
},
async downloadSingleBatteryTemplate() {
if (!this.siteId) { if (!this.siteId) {
this.$message.warning('璇峰厛閫夋嫨绔欑偣') this.$message.warning('请先选择站点')
return return
} }
try { this.singleBatteryInitDialogVisible = true
const data = await downloadSingleBatteryMonitorImportTemplate(this.siteId) this.singleBatteryInitForm.stackDeviceId = ''
if (!blobValidate(data)) { this.singleBatteryInitForm.clusterDeviceId = ''
const text = await data.text() this.singleBatteryInitForm.targetCount = 1
const json = JSON.parse(text) await this.loadSingleBatteryStackOptions()
this.$message.error(json.msg || '模板下载失败') this.singleBatteryClusterOptions = []
return
}
saveAs(data, `单体电池导入模板_${this.siteId}.xlsx`)
} catch (error) {
this.$message.error('模板下载失败,请稍后重试')
}
}, },
handleSingleBatteryImportFileChange(event) { async loadSingleBatteryStackOptions() {
const file = event && event.target && event.target.files && event.target.files[0] const response = await getStackNameList(this.siteId)
if (!file) { this.singleBatteryStackOptions = Array.isArray(response?.data) ? response.data : []
return
}
const isExcel = /\.(xlsx|xls)$/i.test(file.name || '')
if (!isExcel) {
this.$message.error('仅支持导入 Excel 文件')
return
}
this.importSingleBatteryFile(file)
}, },
async importSingleBatteryFile(file) { async handleSingleBatteryStackChange(stackDeviceId) {
if (!this.siteId) { this.singleBatteryInitForm.clusterDeviceId = ''
this.$message.warning('璇峰厛閫夋嫨绔欑偣') if (!stackDeviceId) {
this.singleBatteryClusterOptions = []
return return
} }
const formData = new FormData() const response = await getClusterNameList({ stackDeviceId, siteId: this.siteId })
formData.append('siteId', this.siteId) this.singleBatteryClusterOptions = Array.isArray(response?.data) ? response.data : []
formData.append('file', file)
this.singleBatteryImportLoading = true
try {
const response = await importSingleBatteryMonitorMappings(formData)
this.singleBatteryImportResult = this.normalizeSingleBatteryImportResult(response?.data || {})
this.singleBatteryImportResultVisible = true
if (this.singleBatteryImportResult.committed) {
await this.initMenuStructure()
this.$message.success('单体电池导入成功')
} else {
this.$message.warning(this.singleBatteryImportResult.message || '导入校验未通过')
}
} catch (error) {
this.$message.error('导入失败,请稍后重试')
} finally {
this.singleBatteryImportLoading = false
}
}, },
normalizeSingleBatteryImportResult(result) { normalizeSingleBatteryInitResult(result) {
const source = result || {} const source = result || {}
return { return {
committed: !!source.committed, committed: !!source.committed,
totalRows: Number(source.totalRows || 0), siteId: source.siteId || '',
successRows: Number(source.successRows || 0), scopeType: source.scopeType || 'stack',
failureRows: Number(source.failureRows || 0), scopeDeviceId: source.scopeDeviceId || '',
targetCount: Number(source.targetCount || 0),
existingBatteryCount: Number(source.existingBatteryCount || 0),
initializedBatteryCount: Number(source.initializedBatteryCount || 0),
insertedBatteryCount: Number(source.insertedBatteryCount || 0), insertedBatteryCount: Number(source.insertedBatteryCount || 0),
updatedBatteryCount: Number(source.updatedBatteryCount || 0), pointHitCount: Number(source.pointHitCount || 0),
fixedValueFallbackCount: Number(source.fixedValueFallbackCount || 0),
insertedMappingCount: Number(source.insertedMappingCount || 0), insertedMappingCount: Number(source.insertedMappingCount || 0),
updatedMappingCount: Number(source.updatedMappingCount || 0), updatedMappingCount: Number(source.updatedMappingCount || 0),
message: source.message || '', message: source.message || ''
failureDetails: Array.isArray(source.failureDetails) ? source.failureDetails : [] }
},
async submitSingleBatteryInit() {
if (!this.siteId) {
this.$message.warning('请先选择站点')
return
}
if (!this.singleBatteryInitForm.stackDeviceId) {
this.$message.warning('请选择电池堆')
return
}
if (!this.singleBatteryInitForm.clusterDeviceId) {
this.$message.warning('请选择电池簇')
return
}
const scopeType = 'cluster'
const scopeDeviceId = this.singleBatteryInitForm.clusterDeviceId
this.singleBatteryInitLoading = true
try {
const response = await initializeSingleBatteryMonitorMappings({
siteId: this.siteId,
scopeType,
scopeDeviceId,
targetCount: this.singleBatteryInitForm.targetCount
})
this.singleBatteryInitResult = this.normalizeSingleBatteryInitResult(response?.data || {})
this.singleBatteryInitDialogVisible = false
this.singleBatteryInitResultVisible = true
if (this.singleBatteryInitResult.committed) {
await this.initMenuStructure()
this.$message.success('初始化单体电池配置成功')
} else {
this.$message.warning(this.singleBatteryInitResult.message || '初始化未完成')
}
} catch (error) {
this.$message.error(error?.message || '初始化失败,请稍后重试')
} finally {
this.singleBatteryInitLoading = false
} }
}, },
handleConfigFileChange(event) { handleConfigFileChange(event) {