1.一周充放曲线改为了时间聚合柱状图。

2.PCS最高温度修复bug展示多PCS设备的数据
3.PCS的状态根据状态枚举映射配置的内容显示
4.BMS的总览,增加工作状态、与PCS通讯、与EMS通讯的配置,及关联展示
5.增加批量导入单体电池点位的功能
6.修复计算点可能会出现id与code在一个池子内的问题,再次计算后数据正常
7.计算点增加小数位限制的功能,实时计算与7天历史接口都已经按照配置的小数位进行限制
8.统计报表中的功率曲线改为了按照分钟显示
9.功率曲线出现断点的问题是因为数据计算太密集了导致的,增加了前端连线不断的显示
10.PCS和电池堆的曲线与配置增加了关联设备显示
11.点位映射中的电池温度,增加了多设备
12.收益报表增加升序排列,合并当月所有合计
13.增加业务报表备注功能,可以根据业务设计开发,目前电表报表与收益报表均有备注列可以修改
This commit is contained in:
xiaoyang
2026-04-12 15:18:00 +08:00
parent fd860597de
commit 20df411925
17 changed files with 1272 additions and 460 deletions

View File

@ -33,9 +33,13 @@
class="quick-filter-input"
placeholder="按字段名/展示名/设备名筛选"
/>
<div class="filter-actions">
<div class="filter-actions" :class="{ 'single-battery-actions-visible': showSingleBatteryImportActions }">
<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" :disabled="!siteId || singleBatteryImportLoading" @click="downloadSingleBatteryTemplate">下载单体模板</el-button>
<el-button size="small" type="success" :disabled="!siteId || singleBatteryImportLoading" @click="openSingleBatteryImportDialog">
{{ singleBatteryImportLoading ? '导入中...' : '导入单体电池' }}
</el-button>
<input
ref="configInput"
type="file"
@ -43,6 +47,13 @@
style="display: none"
@change="handleConfigFileChange"
/>
<input
ref="singleBatteryImportInput"
type="file"
accept=".xlsx,.xls"
style="display: none"
@change="handleSingleBatteryImportFileChange"
/>
</div>
</div>
@ -459,17 +470,56 @@
@current-change="handlePointSelectorPageChange"
/>
</el-dialog>
<el-dialog
title="单体电池导入结果"
:visible.sync="singleBatteryImportResultVisible"
width="980px"
append-to-body
class="ems-dialog"
>
<el-alert
:title="singleBatteryImportResult.message || '导入完成'"
:type="singleBatteryImportResult.committed ? 'success' : 'warning'"
:closable="false"
style="margin-bottom: 16px;"
/>
<el-descriptions :column="3" border size="small">
<el-descriptions-item label="总行数">{{ singleBatteryImportResult.totalRows || 0 }}</el-descriptions-item>
<el-descriptions-item label="成功行数">{{ singleBatteryImportResult.successRows || 0 }}</el-descriptions-item>
<el-descriptions-item label="失败行数">{{ singleBatteryImportResult.failureRows || 0 }}</el-descriptions-item>
<el-descriptions-item label="新增单体">{{ singleBatteryImportResult.insertedBatteryCount || 0 }}</el-descriptions-item>
<el-descriptions-item label="更新单体">{{ singleBatteryImportResult.updatedBatteryCount || 0 }}</el-descriptions-item>
<el-descriptions-item label="是否已提交">{{ singleBatteryImportResult.committed ? '是' : '否' }}</el-descriptions-item>
<el-descriptions-item label="新增映射">{{ singleBatteryImportResult.insertedMappingCount || 0 }}</el-descriptions-item>
<el-descriptions-item label="更新映射">{{ singleBatteryImportResult.updatedMappingCount || 0 }}</el-descriptions-item>
</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>
</div>
</template>
<script>
import {
downloadSingleBatteryMonitorImportTemplate,
getPointMatchList,
getSingleMonitorProjectPointMapping,
getSingleMonitorWorkStatusEnumMappings,
importSingleBatteryMonitorMappings,
saveSingleMonitorProjectPointMapping,
saveSingleMonitorWorkStatusEnumMappings
} from '@/api/ems/site'
import { saveAs } from 'file-saver'
import { blobValidate } from '@/utils/ems'
export default {
name: 'MonitorPointMapping',
@ -519,6 +569,20 @@ export default {
suppressAutoSave: false,
isSaving: false,
saveStatusText: '自动保存已开启',
singleBatteryImportLoading: false,
singleBatteryImportResultVisible: false,
singleBatteryImportResult: {
committed: false,
totalRows: 0,
successRows: 0,
failureRows: 0,
insertedBatteryCount: 0,
updatedBatteryCount: 0,
insertedMappingCount: 0,
updatedMappingCount: 0,
message: '',
failureDetails: []
},
lastSavedPointSignature: '',
lastSavedEnumSignature: '',
workStatusEnumMappings: []
@ -561,6 +625,19 @@ export default {
this.schedulePointSelectorSearch()
}
},
computed: {
showSingleBatteryImportActions() {
const currentTopMenu = this.topMenuList.find(item => item.code === this.activeTopMenu)
if (!currentTopMenu) {
return false
}
if (currentTopMenu.code === 'SBJK_DTDC') {
return true
}
const activeChildCode = this.activeChildMap[this.activeTopMenu] || ''
return activeChildCode === 'SBJK_DTDC'
}
},
methods: {
async initMenuStructure() {
if (!this.siteId) {
@ -716,6 +793,83 @@ export default {
this.$refs.configInput.click()
}
},
openSingleBatteryImportDialog() {
if (!this.siteId || !this.$refs.singleBatteryImportInput) {
return
}
this.$refs.singleBatteryImportInput.value = ''
this.$refs.singleBatteryImportInput.click()
},
async downloadSingleBatteryTemplate() {
if (!this.siteId) {
this.$message.warning('璇峰厛閫夋嫨绔欑偣')
return
}
try {
const data = await downloadSingleBatteryMonitorImportTemplate(this.siteId)
if (!blobValidate(data)) {
const text = await data.text()
const json = JSON.parse(text)
this.$message.error(json.msg || '模板下载失败')
return
}
saveAs(data, `单体电池导入模板_${this.siteId}.xlsx`)
} catch (error) {
this.$message.error('模板下载失败,请稍后重试')
}
},
handleSingleBatteryImportFileChange(event) {
const file = event && event.target && event.target.files && event.target.files[0]
if (!file) {
return
}
const isExcel = /\.(xlsx|xls)$/i.test(file.name || '')
if (!isExcel) {
this.$message.error('仅支持导入 Excel 文件')
return
}
this.importSingleBatteryFile(file)
},
async importSingleBatteryFile(file) {
if (!this.siteId) {
this.$message.warning('璇峰厛閫夋嫨绔欑偣')
return
}
const formData = new FormData()
formData.append('siteId', this.siteId)
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) {
const source = result || {}
return {
committed: !!source.committed,
totalRows: Number(source.totalRows || 0),
successRows: Number(source.successRows || 0),
failureRows: Number(source.failureRows || 0),
insertedBatteryCount: Number(source.insertedBatteryCount || 0),
updatedBatteryCount: Number(source.updatedBatteryCount || 0),
insertedMappingCount: Number(source.insertedMappingCount || 0),
updatedMappingCount: Number(source.updatedMappingCount || 0),
message: source.message || '',
failureDetails: Array.isArray(source.failureDetails) ? source.failureDetails : []
}
},
handleConfigFileChange(event) {
const file = event && event.target && event.target.files && event.target.files[0]
if (!file) {
@ -1570,6 +1724,18 @@ export default {
gap: 8px;
}
.filter-actions:not(.single-battery-actions-visible) > .el-button:nth-of-type(3),
.filter-actions:not(.single-battery-actions-visible) > .el-button:nth-of-type(4) {
display: none;
}
.import-result-title {
margin-bottom: 10px;
font-size: 14px;
font-weight: 600;
color: #303133;
}
.quick-wrapper {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));