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

317 lines
9.9 KiB
Vue

<template>
<div style="width:100%" v-loading="loading">
<el-form :inline="true" class="select-container">
<el-form-item label="时间选择">
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
:clearable="false"
:picker-options="pickerOptions"
:default-value="defaultDateRange"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSearch" native-type="button">搜索</el-button>
</el-form-item>
<el-form-item>
<el-button @click="onReset" native-type="button">重置</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="exportTable" native-type="button">导出</el-button>
</el-form-item>
</el-form>
<el-table
class="common-table"
:data="tableData"
:summary-method="getSummaries"
show-summary
stripe
style="width: 100%; margin-top: 25px;"
>
<el-table-column label="汇总" min-width="100px" align="center">
<el-table-column prop="dataTime" label="日期" min-width="100px" align="center"></el-table-column>
<el-table-column prop="dayType" label="日期类型" min-width="100px" align="center"></el-table-column>
<el-table-column prop="weatherDesc" label="天气情况" min-width="180px" align="center"></el-table-column>
</el-table-column>
<el-table-column label="充电价格" align="center">
<el-table-column align="center" prop="activePeakPrice" label="尖"></el-table-column>
<el-table-column align="center" prop="activeHighPrice" label="峰"></el-table-column>
<el-table-column align="center" prop="activeFlatPrice" label="平"></el-table-column>
<el-table-column align="center" prop="activeValleyPrice" label="谷"></el-table-column>
<el-table-column align="center" prop="activeTotalPrice" label="总"></el-table-column>
</el-table-column>
<el-table-column label="放电价格" align="center">
<el-table-column align="center" prop="reActivePeakPrice" label="尖"></el-table-column>
<el-table-column align="center" prop="reActiveHighPrice" label="峰"></el-table-column>
<el-table-column align="center" prop="reActiveFlatPrice" label="平"></el-table-column>
<el-table-column align="center" prop="reActiveValleyPrice" label="谷"></el-table-column>
<el-table-column align="center" prop="reActiveTotalPrice" label="总"></el-table-column>
</el-table-column>
<el-table-column label="" align="center">
<el-table-column prop="actualRevenue" label="实际收益" align="center"></el-table-column>
</el-table-column>
<el-table-column label="备注" align="center" fixed="right" min-width="260">
<template slot-scope="scope">
<div class="remark-cell">
<span class="remark-text">{{ scope.row.remark || "-" }}</span>
<el-button type="text" @click="editRemark(scope.row)">编辑</el-button>
</div>
</template>
</el-table-column>
</el-table>
<el-pagination
v-show="tableData.length > 0"
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalSize"
style="margin-top: 15px; text-align: center"
>
</el-pagination>
</div>
</template>
<script>
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import { batchGetBizRemark, getAmmeterRevenueData, saveBizRemark } from "@/api/ems/dzjk";
import { formatDate } from "@/filters/ems";
const BIZ_TYPE = "stats_report";
const REPORT_KEY = "SYBB";
export default {
name: "DzjkTjbbSybb",
mixins: [getQuerySiteId],
data() {
return {
loading: false,
pickerOptions: {
disabledDate(time) {
return time.getTime() > Date.now();
},
},
defaultDateRange: [],
dateRange: [],
tableData: [],
summaryTotals: {},
pageSize: 10,
pageNum: 1,
totalSize: 0,
};
},
methods: {
buildRemarkKey(dataTime) {
return `${this.siteId}_${dataTime || ""}`;
},
loadRemarks(rows) {
if (!rows.length) return Promise.resolve({});
return batchGetBizRemark({
bizType: BIZ_TYPE,
bizKey1: REPORT_KEY,
bizKey2List: rows.map(row => this.buildRemarkKey(row.dataTime)),
}).then(response => response?.data || {});
},
applyRemarks(rows, remarkMap) {
rows.forEach(row => {
this.$set(row, "remark", remarkMap[this.buildRemarkKey(row.dataTime)] || "");
});
},
toScaledInt(value) {
const num = Number(value);
return Number.isFinite(num) ? Math.round(num * 1000) : 0;
},
sumRowsByProp(rows, prop) {
const total = (rows || []).reduce((sum, row) => sum + this.toScaledInt(row?.[prop]), 0);
return total / 1000;
},
formatSummaryNumber(value) {
const num = Number(value);
if (!Number.isFinite(num)) return "";
return num.toFixed(3).replace(/\.?0+$/, "");
},
buildSummaryTotals(rows) {
const numericProps = [
"activePeakPrice",
"activeHighPrice",
"activeFlatPrice",
"activeValleyPrice",
"activeTotalPrice",
"reActivePeakPrice",
"reActiveHighPrice",
"reActiveFlatPrice",
"reActiveValleyPrice",
"reActiveTotalPrice",
"actualRevenue",
];
return numericProps.reduce((result, prop) => {
result[prop] = this.sumRowsByProp(rows, prop);
return result;
}, {});
},
getSummaries({ columns, data }) {
return columns.map((column, index) => {
if (index === 0) return "合计";
const prop = column.property;
if (!prop) return "";
if (Object.prototype.hasOwnProperty.call(this.summaryTotals, prop)) {
return this.formatSummaryNumber(this.summaryTotals[prop]);
}
const hasNumericValue = (data || []).some(item => Number.isFinite(Number(item?.[prop])));
return hasNumericValue ? this.formatSummaryNumber(this.sumRowsByProp(data, prop)) : "";
});
},
exportTable() {
if (!this.dateRange?.length) return;
const [startTime, endTime] = this.dateRange;
this.download(
"ems/statsReport/exportAmmeterRevenueData",
{
siteId: this.siteId,
startTime,
endTime,
},
`收益报表_${startTime}-${endTime}.xlsx`
);
},
onSearch() {
this.pageNum = 1;
this.getData();
},
onReset() {
this.dateRange = this.defaultDateRange;
this.pageNum = 1;
this.getData();
},
handleSizeChange(val) {
this.pageSize = val;
this.$nextTick(() => {
this.getData();
});
},
handleCurrentChange(val) {
this.pageNum = val;
this.$nextTick(() => {
this.getData();
});
},
editRemark(row) {
this.$prompt("请输入备注", "编辑备注", {
inputValue: row.remark || "",
inputType: "textarea",
inputPlaceholder: "可输入该日报表备注",
confirmButtonText: "保存",
cancelButtonText: "取消",
})
.then(({ value }) => {
return saveBizRemark({
bizType: BIZ_TYPE,
bizKey1: REPORT_KEY,
bizKey2: this.buildRemarkKey(row.dataTime),
remark: value || "",
}).then(() => {
this.$set(row, "remark", value || "");
this.$message.success("备注保存成功");
});
})
.catch(() => {});
},
getData() {
this.loading = true;
const { siteId, pageNum, pageSize } = this;
const [startTime = "", endTime = ""] = this.dateRange || [];
getAmmeterRevenueData({ siteId, startTime, endTime, pageSize, pageNum })
.then(pageResponse => {
const rows = pageResponse?.rows || [];
const total = Number(pageResponse?.total || 0);
this.totalSize = total;
this.tableData = rows;
const tasks = [this.loadRemarks(rows)];
if (total > 0) {
tasks.push(
getAmmeterRevenueData({
siteId,
startTime,
endTime,
pageNum: 1,
pageSize: total,
})
);
}
return Promise.all(tasks);
})
.then(([remarkMap, allResponse]) => {
this.applyRemarks(this.tableData, remarkMap || {});
const allRows = allResponse?.rows || allResponse?.data || [];
this.summaryTotals = this.buildSummaryTotals(allRows);
})
.finally(() => {
this.loading = false;
});
},
init() {
this.dateRange = [];
this.tableData = [];
this.summaryTotals = {};
this.totalSize = 0;
this.pageSize = 10;
this.pageNum = 1;
const now = new Date();
const lastDay = now.getTime();
const firstDay = new Date(new Date().setDate(1)).getTime();
this.defaultDateRange = [formatDate(firstDay), formatDate(lastDay)];
this.dateRange = [formatDate(firstDay), formatDate(lastDay)];
this.getData();
},
},
};
</script>
<style scoped lang="scss">
::v-deep {
.common-table.el-table {
.el-table__header-wrapper th,
.common-table.el-table .el-table__fixed-header-wrapper th {
border-bottom: 1px solid #dfe6ec;
}
.el-table__footer-wrapper {
tbody td.el-table__cell {
color: #000;
font-weight: bolder;
}
}
}
}
.remark-cell {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.remark-text {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: left;
}
</style>