feat(ems): 添加产线信息显示和视图切换功能
refactor(dashboard): 移除过时的趋势分析组件 - 在仪表盘表格中添加产线信息显示 - 在设备管理页面增加卡片/表格视图切换功能 - 删除不再使用的趋势分析组件
This commit is contained in:
@@ -1,125 +1,696 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="4">
|
||||
<!-- <div style="padding: 10px;">
|
||||
<el-select v-model="energyType" placeholder="请选择能源类型" @change="refresh">
|
||||
<el-option v-for="item in energyTypeList" :key="item.energyTypeId" :label="item.name" :value="item.energyTypeId" />
|
||||
</el-select>
|
||||
</div> -->
|
||||
|
||||
<div class="ems-dashboard-container" v-loading="loading">
|
||||
<!-- 顶部筛选区域 -->
|
||||
<div class="filter-panel">
|
||||
<el-form :inline="true" :model="queryParams" class="filter-form">
|
||||
<el-form-item label="时间范围">
|
||||
<time-range-picker
|
||||
v-model="timeRangeParams"
|
||||
start-key="recordStartDate"
|
||||
end-key="recordEndDate"
|
||||
:default-start-time="defaultStartTime"
|
||||
:default-end-time="defaultEndTime"
|
||||
@quick-select="loadData"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-refresh" @click="loadData">刷新</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 区域选择,默认展开,最高高度为60%的屏幕尺寸 -->
|
||||
<el-tree @node-click="handleNodeClick" :data="locationList" :props="defaultProps" :default-expand-all="true" :style="{ height: 'calc(50vh - 50px)' }"></el-tree>
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stat-cards">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6" v-for="(stat, index) in statistics" :key="index">
|
||||
<div class="stat-card" :class="'stat-' + index">
|
||||
<div class="stat-icon">
|
||||
<i :class="stat.icon"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value">{{ stat.value }}</div>
|
||||
<div class="stat-label">{{ stat.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 设备列表 -->
|
||||
<!-- <ul v-if="locationId">
|
||||
<li></li>
|
||||
</ul>
|
||||
<div v-else>
|
||||
<el-empty description="请选择区域"></el-empty>
|
||||
</div> -->
|
||||
</el-col>
|
||||
<!-- 图表区域 -->
|
||||
<div class="charts-section">
|
||||
<el-row :gutter="20">
|
||||
<!-- 能源消耗趋势图 -->
|
||||
<el-col :span="16">
|
||||
<div class="chart-card">
|
||||
<div class="chart-header">
|
||||
<span class="chart-title">能源消耗趋势</span>
|
||||
</div>
|
||||
<div ref="trendChart" class="chart-container"></div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="20" v-if="showRight">
|
||||
<!-- <el-tabs v-model="activeTab" type="card">
|
||||
<el-tab-pane label="环比概况" name="1"></el-tab-pane>
|
||||
<el-tab-pane label="近期趋势" name="2"></el-tab-pane>
|
||||
<el-tab-pane label="同比分析" name="3"></el-tab-pane>
|
||||
<el-tab-pane label="环比分析" name="4"></el-tab-pane>
|
||||
</el-tabs> -->
|
||||
<el-tabs v-model="energyType" type="card" @tab-click="refresh">
|
||||
<el-tab-pane v-for="item in energyTypeList" :key="item.energyTypeId" :label="item.name" :name="item.energyTypeId"></el-tab-pane>
|
||||
</el-tabs>
|
||||
<!-- 能源类型占比 -->
|
||||
<el-col :span="8">
|
||||
<div class="chart-card">
|
||||
<div class="chart-header">
|
||||
<span class="chart-title">能源类型占比</span>
|
||||
</div>
|
||||
<div ref="energyTypeChart" class="chart-container"></div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<Overview ref="overview" :unit="energyUnit" :energyName="energyName" :energyType="energyType" :locationId="locationId" :deviceId="deviceId" />
|
||||
<RecentTrend ref="recentTrend" :unit="energyUnit" :energyName="energyName" :energyType="energyType" :locationId="locationId" :deviceId="deviceId" />
|
||||
<YearToYear ref="yearToYear" :unit="energyUnit" :energyName="energyName" :energyType="energyType" :locationId="locationId" :deviceId="deviceId" />
|
||||
<MonthToMonth ref="monthToMonth" :unit="energyUnit" :energyName="energyName" :energyType="energyType" :locationId="locationId" :deviceId="deviceId" />
|
||||
</el-col>
|
||||
<el-col :span="20" v-else>
|
||||
<el-empty description="请选择能源类型和区域"></el-empty>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" style="margin-top: 20px">
|
||||
<!-- 产线能耗对比 -->
|
||||
<el-col :span="12">
|
||||
<div class="chart-card">
|
||||
<div class="chart-header">
|
||||
<span class="chart-title">产线能耗对比</span>
|
||||
</div>
|
||||
<div ref="productionLineChart" class="chart-container"></div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 设备能耗排行 -->
|
||||
<el-col :span="12">
|
||||
<div class="chart-card">
|
||||
<div class="chart-header">
|
||||
<span class="chart-title">设备能耗排行 TOP 10</span>
|
||||
</div>
|
||||
<div ref="deviceRankingChart" class="chart-container"></div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 产线能源类型二维表 -->
|
||||
<el-row :gutter="20" style="margin-top: 20px">
|
||||
<el-col :span="24">
|
||||
<div class="chart-card">
|
||||
<div class="chart-header">
|
||||
<span class="chart-title">产线能源类型二维表</span>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<el-table :data="tableData" border style="width: 100%" :summary-method="getSummaries" show-summary>
|
||||
<el-table-column prop="lineName" label="产线"></el-table-column>
|
||||
<el-table-column v-for="type in energyTypeList" :key="type.energyTypeId"
|
||||
:prop="'type_' + type.energyTypeId" :label="type.name">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row['type_' + type.energyTypeId] ? Number(scope.row['type_' + type.energyTypeId]).toFixed(2) : 0 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="total" label="合计" fixed="right" width="150">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.total ? Number(scope.row.total).toFixed(2) : 0 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listEnergyType } from "@/api/ems/energyType";
|
||||
import { listLocation } from "@/api/ems/location";
|
||||
|
||||
import Overview from "./panels/Overview.vue";
|
||||
import RecentTrend from "./panels/RecentTrends.vue";
|
||||
import YearToYear from "./panels/YearOnYear.vue";
|
||||
import MonthToMonth from "./panels/MonthToMonth.vue";
|
||||
import { listEnergyType } from '@/api/ems/energyType'
|
||||
import { listMeter } from '@/api/ems/meter'
|
||||
import { listEnergyRecord } from '@/api/ems/energyRecord'
|
||||
import dayjs from 'dayjs'
|
||||
import * as echarts from 'echarts'
|
||||
import TimeRangePicker from '@/views/wms/report/components/timeRangePicker.vue'
|
||||
|
||||
export default {
|
||||
name: "Dashboard",
|
||||
name: 'EmsDashboard',
|
||||
dicts: ['sys_lines'],
|
||||
components: {
|
||||
Overview,
|
||||
RecentTrend,
|
||||
YearToYear,
|
||||
MonthToMonth
|
||||
TimeRangePicker
|
||||
},
|
||||
data() {
|
||||
const now = dayjs()
|
||||
return {
|
||||
energyTypeList: [],
|
||||
energyType: '',
|
||||
locationId: '',
|
||||
deviceId: '',
|
||||
locationList: [
|
||||
],
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
timeRangeParams: {
|
||||
recordStartDate: now.startOf('month').format('YYYY-MM-DD HH:mm:ss'),
|
||||
recordEndDate: now.endOf('month').format('YYYY-MM-DD HH:mm:ss')
|
||||
},
|
||||
activeTab: '1'
|
||||
defaultStartTime: now.startOf('month').format('YYYY-MM-DD HH:mm:ss'),
|
||||
defaultEndTime: now.endOf('month').format('YYYY-MM-DD HH:mm:ss'),
|
||||
queryParams: {
|
||||
energyTypeId: null,
|
||||
productionLines: []
|
||||
},
|
||||
energyTypeList: [],
|
||||
meterList: [],
|
||||
energyRecords: [],
|
||||
statistics: [],
|
||||
tableData: [],
|
||||
trendChartType: 'day',
|
||||
trendChart: null,
|
||||
energyTypeChart: null,
|
||||
productionLineChart: null,
|
||||
deviceRankingChart: null,
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getEnergyTypeList();
|
||||
this.getLocationList();
|
||||
mounted() {
|
||||
this.initCharts()
|
||||
this.loadBasicData()
|
||||
window.addEventListener('resize', this.handleResize)
|
||||
},
|
||||
computed: {
|
||||
showRight() {
|
||||
return this.energyType && (this.deviceId || this.locationId);
|
||||
},
|
||||
energyUnit() {
|
||||
return this.energyTypeList.find(item => item.energyTypeId === this.energyType)?.unit;
|
||||
},
|
||||
energyName() {
|
||||
return this.energyTypeList.find(item => item.energyTypeId === this.energyType)?.name;
|
||||
}
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.handleResize)
|
||||
if (this.trendChart) this.trendChart.dispose()
|
||||
if (this.energyTypeChart) this.energyTypeChart.dispose()
|
||||
if (this.productionLineChart) this.productionLineChart.dispose()
|
||||
if (this.deviceRankingChart) this.deviceRankingChart.dispose()
|
||||
},
|
||||
methods: {
|
||||
getEnergyTypeList() {
|
||||
listEnergyType({ pageNum: 1, pageSize: 9999 }).then(response => {
|
||||
this.energyTypeList = response.rows;
|
||||
this.energyType = this.energyTypeList[0]?.energyTypeId;
|
||||
});
|
||||
initCharts() {
|
||||
this.trendChart = echarts.init(this.$refs.trendChart)
|
||||
this.energyTypeChart = echarts.init(this.$refs.energyTypeChart)
|
||||
this.productionLineChart = echarts.init(this.$refs.productionLineChart)
|
||||
this.deviceRankingChart = echarts.init(this.$refs.deviceRankingChart)
|
||||
},
|
||||
getLocationList() {
|
||||
listLocation().then(response => {
|
||||
this.locationList = this.handleTree(response.data, "locationId", "parentId");
|
||||
});
|
||||
handleResize() {
|
||||
this.trendChart && this.trendChart.resize()
|
||||
this.energyTypeChart && this.energyTypeChart.resize()
|
||||
this.productionLineChart && this.productionLineChart.resize()
|
||||
this.deviceRankingChart && this.deviceRankingChart.resize()
|
||||
},
|
||||
handleNodeClick(data) {
|
||||
this.locationId = data.locationId;
|
||||
this.deviceId = undefined;
|
||||
this.refresh();
|
||||
async loadBasicData() {
|
||||
try {
|
||||
this.loading = true;
|
||||
const [energyTypeRes, meterRes] = await Promise.all([
|
||||
listEnergyType({ pageSize: 999 }),
|
||||
listMeter({ pageSize: 999, isTotalMeter: 0 })
|
||||
])
|
||||
this.energyTypeList = energyTypeRes.rows || []
|
||||
this.meterList = meterRes.rows || []
|
||||
await this.loadData()
|
||||
} catch (error) {
|
||||
console.error('加载基础数据失败', error)
|
||||
}
|
||||
},
|
||||
refresh() {
|
||||
if (this.$refs.overview) {
|
||||
this.$refs.overview.refresh();
|
||||
async loadData() {
|
||||
try {
|
||||
const query = {
|
||||
recordStartDate: this.timeRangeParams.recordStartDate,
|
||||
recordEndDate: this.timeRangeParams.recordEndDate
|
||||
}
|
||||
if (this.queryParams.energyTypeId) {
|
||||
query.energyTypeId = this.queryParams.energyTypeId
|
||||
}
|
||||
this.loading = true;
|
||||
const res = await listEnergyRecord(query)
|
||||
this.energyRecords = res.rows || []
|
||||
this.processData()
|
||||
} catch (error) {
|
||||
console.error('加载数据失败', error)
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
if (this.$refs.recentTrend) {
|
||||
this.$refs.recentTrend.refresh();
|
||||
},
|
||||
processData() {
|
||||
const validMeterIds = new Set(this.meterList.map(m => m.meterId))
|
||||
this.energyRecords = this.energyRecords.filter(record => validMeterIds.has(record.meterId))
|
||||
this.calculateStatistics()
|
||||
this.updateTrendChart()
|
||||
this.updateEnergyTypeChart()
|
||||
this.updateProductionLineChart()
|
||||
this.updateDeviceRankingChart()
|
||||
this.updateTableData()
|
||||
},
|
||||
calculateStatistics() {
|
||||
const totalConsumption = this.energyRecords.reduce((sum, record) => sum + (Number(record.consumption) || 0), 0)
|
||||
const avgDaily = this.getAvgDailyConsumption()
|
||||
const maxConsumption = this.getMaxConsumption()
|
||||
const deviceCount = new Set(this.energyRecords.map(r => r.meterId)).size
|
||||
|
||||
this.statistics = [
|
||||
{
|
||||
label: '总能耗',
|
||||
value: Number(totalConsumption).toFixed(2),
|
||||
icon: 'el-icon-data-line',
|
||||
trend: 12.5
|
||||
},
|
||||
{
|
||||
label: '日均能耗',
|
||||
value: Number(avgDaily).toFixed(2),
|
||||
icon: 'el-icon-data-analysis',
|
||||
trend: -3.2
|
||||
},
|
||||
{
|
||||
label: '最高能耗',
|
||||
value: Number(maxConsumption).toFixed(2),
|
||||
icon: 'el-icon-s-data',
|
||||
trend: 5.1
|
||||
},
|
||||
{
|
||||
label: '活跃设备',
|
||||
value: deviceCount,
|
||||
icon: 'el-icon-s-tools',
|
||||
trend: 8.3
|
||||
}
|
||||
]
|
||||
},
|
||||
getAvgDailyConsumption() {
|
||||
if (this.energyRecords.length === 0) return 0
|
||||
const dateMap = new Map()
|
||||
this.energyRecords.forEach(record => {
|
||||
const date = record.recordDate
|
||||
if (!dateMap.has(date)) {
|
||||
dateMap.set(date, 0)
|
||||
}
|
||||
dateMap.set(date, dateMap.get(date) + (Number(record.consumption) || 0))
|
||||
})
|
||||
const total = Array.from(dateMap.values()).reduce((sum, val) => sum + val, 0)
|
||||
return total / dateMap.size
|
||||
},
|
||||
getMaxConsumption() {
|
||||
if (this.energyRecords.length === 0) return 0
|
||||
return Math.max(...this.energyRecords.map(r => Number(r.consumption) || 0))
|
||||
},
|
||||
updateTrendChart() {
|
||||
const dateFormat = this.trendChartType === 'day' ? 'YYYY-MM-DD' :
|
||||
this.trendChartType === 'week' ? 'YYYY-WW' : 'YYYY-MM'
|
||||
|
||||
const groupedData = new Map()
|
||||
this.energyRecords.forEach(record => {
|
||||
const dateKey = dayjs(record.recordDate).format(dateFormat)
|
||||
const energyType = this.getEnergyTypeName(record.energyId)
|
||||
|
||||
if (!groupedData.has(dateKey)) {
|
||||
groupedData.set(dateKey, {})
|
||||
}
|
||||
if (!groupedData.get(dateKey)[energyType]) {
|
||||
groupedData.get(dateKey)[energyType] = 0
|
||||
}
|
||||
groupedData.get(dateKey)[energyType] += Number(record.consumption) || 0
|
||||
})
|
||||
|
||||
const dates = Array.from(groupedData.keys()).sort()
|
||||
const energyTypes = Array.from(new Set(this.energyRecords.map(r => this.getEnergyTypeName(r.energyId))))
|
||||
|
||||
const colors = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#00CED1']
|
||||
const series = energyTypes.map((type, index) => ({
|
||||
name: type,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: dates.map(date => groupedData.get(date)[type] || 0),
|
||||
itemStyle: { color: colors[index % colors.length] },
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: colors[index % colors.length] + '80' },
|
||||
{ offset: 1, color: colors[index % colors.length] + '10' }
|
||||
])
|
||||
}
|
||||
}))
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: energyTypes
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: dates
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '能耗'
|
||||
},
|
||||
series
|
||||
}
|
||||
if (this.$refs.yearToYear) {
|
||||
this.$refs.yearToYear.refresh();
|
||||
this.trendChart.setOption(option)
|
||||
},
|
||||
updateEnergyTypeChart() {
|
||||
const typeMap = new Map()
|
||||
this.energyRecords.forEach(record => {
|
||||
const typeName = this.getEnergyTypeName(record.energyId)
|
||||
typeMap.set(typeName, (typeMap.get(typeName) || 0) + (Number(record.consumption) || 0))
|
||||
})
|
||||
|
||||
const data = Array.from(typeMap.entries()).map(([name, value]) => ({ name, value }))
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 'center'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '能源类型',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data
|
||||
}
|
||||
]
|
||||
}
|
||||
if (this.$refs.monthToMonth) {
|
||||
this.$refs.monthToMonth.refresh();
|
||||
this.energyTypeChart.setOption(option)
|
||||
},
|
||||
updateProductionLineChart() {
|
||||
const lineMap = new Map()
|
||||
|
||||
this.energyRecords.forEach(record => {
|
||||
const meter = this.meterList.find(m => m.meterId === record.meterId)
|
||||
if (meter && meter.productionLine) {
|
||||
const lines = Array.isArray(meter.productionLine) ? meter.productionLine : [meter.productionLine]
|
||||
lines.forEach(lineId => {
|
||||
const lineName = this.getLineName(lineId)
|
||||
lineMap.set(lineName, (lineMap.get(lineName) || 0) + (Number(record.consumption) || 0))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const sortedLines = Array.from(lineMap.entries())
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
name: '能耗'
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: sortedLines.map(item => item[0])
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '能耗',
|
||||
type: 'bar',
|
||||
data: sortedLines.map(item => item[1]),
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: '#409EFF' },
|
||||
{ offset: 1, color: '#00CED1' }
|
||||
]),
|
||||
borderRadius: [0, 4, 4, 0]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
this.productionLineChart.setOption(option)
|
||||
},
|
||||
updateDeviceRankingChart() {
|
||||
const deviceMap = new Map()
|
||||
|
||||
this.energyRecords.forEach(record => {
|
||||
const meter = this.meterList.find(m => m.meterId === record.meterId)
|
||||
const deviceName = meter ? meter.meterCode : `设备${record.meterId}`
|
||||
deviceMap.set(deviceName, (deviceMap.get(deviceName) || 0) + (Number(record.consumption) || 0))
|
||||
})
|
||||
|
||||
const sortedDevices = Array.from(deviceMap.entries())
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, 10)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
name: '能耗'
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: sortedDevices.map(item => item[0]).reverse()
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '能耗',
|
||||
type: 'bar',
|
||||
data: sortedDevices.map(item => item[1]).reverse(),
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: '#67C23A' },
|
||||
{ offset: 1, color: '#95E67D' }
|
||||
]),
|
||||
borderRadius: [0, 4, 4, 0]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
this.deviceRankingChart.setOption(option)
|
||||
},
|
||||
getEnergyTypeName(energyTypeId) {
|
||||
const type = this.energyTypeList.find(t => t.energyTypeId === energyTypeId)
|
||||
return type ? type.name : '未知'
|
||||
},
|
||||
getLineName(lineId) {
|
||||
const line = this.dict.type.sys_lines?.find(l => l.value === lineId)
|
||||
return line ? line.label : lineId
|
||||
},
|
||||
updateTableData() {
|
||||
const lineEnergyMap = new Map()
|
||||
|
||||
this.energyRecords.forEach(record => {
|
||||
const meter = this.meterList.find(m => m.meterId === record.meterId)
|
||||
if (meter && meter.productionLine) {
|
||||
const lines = Array.isArray(meter.productionLine) ? meter.productionLine : [meter.productionLine]
|
||||
lines.forEach(lineId => {
|
||||
const lineName = this.getLineName(lineId)
|
||||
const energyTypeId = record.energyId
|
||||
|
||||
if (!lineEnergyMap.has(lineName)) {
|
||||
lineEnergyMap.set(lineName, {})
|
||||
}
|
||||
const lineData = lineEnergyMap.get(lineName)
|
||||
if (!lineData['type_' + energyTypeId]) {
|
||||
lineData['type_' + energyTypeId] = 0
|
||||
}
|
||||
lineData['type_' + energyTypeId] += Number(record.consumption) || 0
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
this.tableData = Array.from(lineEnergyMap.entries()).map(([lineName, data]) => {
|
||||
const row = { lineName, ...data }
|
||||
let total = 0
|
||||
this.energyTypeList.forEach(type => {
|
||||
const val = data['type_' + type.energyTypeId] || 0
|
||||
total += val
|
||||
})
|
||||
row.total = total
|
||||
return row
|
||||
})
|
||||
},
|
||||
getSummaries(param) {
|
||||
const { columns, data } = param
|
||||
const sums = []
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计'
|
||||
return
|
||||
}
|
||||
if (column.property === 'total') {
|
||||
const values = data.map(item => Number(item[column.property]) || 0)
|
||||
sums[index] = values.reduce((prev, curr) => prev + curr, 0).toFixed(2)
|
||||
} else if (column.property && column.property.startsWith('type_')) {
|
||||
const values = data.map(item => Number(item[column.property]) || 0)
|
||||
sums[index] = values.reduce((prev, curr) => prev + curr, 0).toFixed(2)
|
||||
} else {
|
||||
sums[index] = ''
|
||||
}
|
||||
})
|
||||
return sums
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ems-dashboard-container {
|
||||
padding: 20px;
|
||||
background: #f6f7fb;
|
||||
min-height: calc(100vh - 100px);
|
||||
|
||||
.filter-panel {
|
||||
background: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
|
||||
.filter-form {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-cards {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.stat-card {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
&.stat-0 {
|
||||
border-left: 4px solid #409EFF;
|
||||
}
|
||||
&.stat-1 {
|
||||
border-left: 4px solid #67C23A;
|
||||
}
|
||||
&.stat-2 {
|
||||
border-left: 4px solid #E6A23C;
|
||||
}
|
||||
&.stat-3 {
|
||||
border-left: 4px solid #F56C6C;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16px;
|
||||
font-size: 28px;
|
||||
|
||||
.stat-0 & {
|
||||
background: linear-gradient(135deg, #409EFF, #00CED1);
|
||||
color: #ffffff;
|
||||
}
|
||||
.stat-1 & {
|
||||
background: linear-gradient(135deg, #67C23A, #95E67D);
|
||||
color: #ffffff;
|
||||
}
|
||||
.stat-2 & {
|
||||
background: linear-gradient(135deg, #E6A23C, #F7D94C);
|
||||
color: #ffffff;
|
||||
}
|
||||
.stat-3 & {
|
||||
background: linear-gradient(135deg, #F56C6C, #FC9494);
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
flex: 1;
|
||||
|
||||
.stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-trend {
|
||||
font-size: 12px;
|
||||
color: #67C23A;
|
||||
|
||||
&:has(.el-icon-bottom) {
|
||||
color: #F56C6C;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.charts-section {
|
||||
.chart-card {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.chart-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user