From 3745ef5bbca5181e0bfc6d8fb582bcd19155bb3a Mon Sep 17 00:00:00 2001
From: liutao <790864623@qq.com>
Date: Thu, 14 May 2026 15:30:38 +0800
Subject: [PATCH] =?UTF-8?q?=E8=83=BD=E6=BA=90=E6=A6=82=E8=A7=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/api/mes/energydevice/index.ts | 59 +++++
src/views/mes/energyOverview/index.vue | 349 ++++++++++---------------
2 files changed, 190 insertions(+), 218 deletions(-)
diff --git a/src/api/mes/energydevice/index.ts b/src/api/mes/energydevice/index.ts
index 0ea5247f..feccb85a 100644
--- a/src/api/mes/energydevice/index.ts
+++ b/src/api/mes/energydevice/index.ts
@@ -42,6 +42,61 @@ export interface EnergyDeviceDataRecordVO {
latestDataTime?: string
}
+export interface EnergyOverviewMetricVO {
+ key: string
+ label: string
+ value: string
+ unit?: string
+ subLabel?: string
+ subValue?: string
+ change?: string
+ down?: boolean
+}
+
+export interface EnergyOverviewTrendVO {
+ unit?: string
+ xAxis: string[]
+ data: string[]
+}
+
+export interface EnergyOverviewRegionItemVO {
+ name: string
+ value: string
+ percent: string
+}
+
+export interface EnergyOverviewRegionVO {
+ unit?: string
+ totalValue: string
+ items: EnergyOverviewRegionItemVO[]
+}
+
+export interface EnergyOverviewRankVO {
+ id: number
+ name: string
+ region: string
+ value: string
+}
+
+export interface EnergyOverviewDetailVO {
+ id: number
+ name: string
+ energyType: string
+ region: string
+ value: string
+ startTime: string
+ endTime: string
+}
+
+export interface EnergyOverviewRespVO {
+ metrics: EnergyOverviewMetricVO[]
+ trendChart: EnergyOverviewTrendVO
+ regionChart: EnergyOverviewRegionVO
+ rankList: EnergyOverviewRankVO[]
+ total: number
+ detailList: EnergyOverviewDetailVO[]
+}
+
// 能源设备 API
export const EnergyDeviceApi = {
// 查询能源设备分页
@@ -66,6 +121,10 @@ export const EnergyDeviceApi = {
return await request.get({ url: `/mes/energy-device/queryDataRecords`, params })
},
+ queryOverviewData: async (params: any) => {
+ return await request.get({ url: `/mes/energy-device/queryOverviewData`, params })
+ },
+
exportQueryDataRecords: async (params: any) => {
return await request.download({ url: `/mes/energy-device/record-export-excel`, params })
},
diff --git a/src/views/mes/energyOverview/index.vue b/src/views/mes/energyOverview/index.vue
index ea7d7c2f..a674c335 100644
--- a/src/views/mes/energyOverview/index.vue
+++ b/src/views/mes/energyOverview/index.vue
@@ -9,7 +9,13 @@
label-width="auto"
>
-
+
@@ -63,7 +69,7 @@
{{ item.label }}
{{ item.value }}
- {{ item.unit }}
+ {{ item.unit }}
{{ item.subLabel }}
@@ -85,14 +91,6 @@
能源用量趋势
-
-
-
-
-
-
-
-
@@ -104,14 +102,6 @@
区域能耗占比
-
-
-
-
-
-
-
-
@@ -176,6 +166,14 @@
import { EChartsOption } from 'echarts'
import { Echart } from '@/components/Echart'
import { EnergyTypeApi, EnergyTypeVO } from '@/api/mes/energytype'
+import {
+ EnergyDeviceApi,
+ EnergyOverviewDetailVO,
+ EnergyOverviewMetricVO,
+ EnergyOverviewRankVO,
+ EnergyOverviewRespVO
+} from '@/api/mes/energydevice'
+import {OrganizationApi, OrganizationVO} from "@/api/mes/organization";
defineOptions({ name: 'EnergyOverview' })
@@ -192,24 +190,12 @@ interface MetricCard {
subValue?: string
}
-interface DetailRow {
- id: number
- name: string
- energyType: string
- region: string
- value: string
- startTime: string
- endTime: string
-}
+type DetailRow = EnergyOverviewDetailVO
const message = useMessage()
const defaultTimeRange = ['2026-05-04 00:00:00', '2026-05-05 23:59:59']
-const orgOptions = [
- { label: '成型系统', value: 1 },
- { label: '蛋托线01', value: 2 },
- { label: '烘干系统', value: 3 }
-]
+const orgOptions = ref<{ label: string; value: string | number; raw?: OrganizationVO }[]>([])
const loading = ref(false)
const queryFormRef = ref()
@@ -224,55 +210,19 @@ const queryParams = reactive({
})
const metricCards = ref([])
-const rankList = ref([
- { name: '蛋托成型柜用电量', region: '成型系统', value: '2,139.09' },
- { name: '蛋托线真空柜用电量', region: '蛋托线01', value: '0' },
- { name: '蛋托干燥柜用电量', region: '烘干系统', value: '0' },
- { name: '蛋托线其它用电量', region: '蛋托线01', value: '0' },
- { name: '烘干系统其它用电量', region: '烘干系统', value: '0' }
-])
-const detailList = ref([])
+const rankList = ref([])
const pageDetailList = ref([])
const total = ref(0)
const hasEnergyTypes = computed(() => energyTypeOptions.value.length > 0)
const selectedEnergyType = computed(() =>
energyTypeOptions.value.find((item) => item.id === queryParams.energyTypeId)
)
-const selectedEnergyName = computed(() => selectedEnergyType.value?.name || '电')
const selectedEnergyUnit = computed(() => selectedEnergyType.value?.unit || 'kWh')
-const selectedUnitConsumptionUnit = computed(() => `${selectedEnergyUnit.value}/件`)
-
-const hours = [
- '00:00',
- '01:00',
- '02:00',
- '03:00',
- '04:00',
- '05:00',
- '06:00',
- '07:00',
- '08:00',
- '09:00',
- '10:00',
- '11:00',
- '12:00',
- '13:00',
- '14:00',
- '15:00',
- '16:00',
- '17:00',
- '18:00',
- '19:00',
- '20:00',
- '21:00',
- '22:00'
-]
-const trendData = [90, 80, 72, 75, 74, 85, 105, 116, 120, 112, 123, 127, 122, 146, 156.8, 147, 116, 94, 88, 102, 103, 116, 106]
-const regionData = [
- { name: '成型系统', value: 2139.09 },
- { name: '蛋托线01', value: 0 },
- { name: '烘干系统', value: 0 }
-]
+
+const trendXAxis = ref([])
+const trendSeries = ref([])
+const regionItems = ref<{ name: string; value: number; percent: string }[]>([])
+const regionTotal = ref('0')
const trendChartOptions = reactive({
color: ['#2f7df6'],
@@ -280,12 +230,12 @@ const trendChartOptions = reactive({
trigger: 'axis',
formatter: (params: any) => {
const item = params?.[0]
- return `${item.axisValue}
用电量:${item.data} kWh`
+ return `${item?.axisValue || ''}
用量:${item?.data || 0} ${selectedEnergyUnit.value}`
}
},
legend: {
top: 4,
- data: ['用电量 (kWh)']
+ data: ['用量']
},
grid: {
left: 24,
@@ -297,25 +247,23 @@ const trendChartOptions = reactive({
xAxis: {
type: 'category',
boundaryGap: false,
- name: '时间(小时)',
+ name: '时间',
nameLocation: 'middle',
nameGap: 28,
- data: hours,
+ data: trendXAxis.value,
axisLine: { lineStyle: { color: '#dcdfe6' } },
axisLabel: { color: '#606266' }
},
yAxis: {
type: 'value',
- name: 'kWh',
+ name: selectedEnergyUnit.value,
min: 0,
- max: 180,
- interval: 30,
axisLabel: { color: '#606266' },
splitLine: { lineStyle: { color: '#e8edf5' } }
},
series: [
{
- name: '用电量 (kWh)',
+ name: '用量',
type: 'line',
smooth: true,
symbol: 'circle',
@@ -334,21 +282,16 @@ const trendChartOptions = reactive({
]
}
},
- data: trendData,
- markPoint: {
- symbolSize: 48,
- label: { color: '#2f7df6', formatter: '{c}' },
- data: [{ type: 'max', name: '峰值' }]
- }
+ data: trendSeries.value
}
]
-}) as EChartsOption
+})
const regionPieOptions = reactive({
- color: ['#2f7df6', '#52c41a', '#faad14'],
+ color: ['#2f7df6', '#52c41a', '#faad14', '#13c2c2', '#fa8c16'],
tooltip: {
trigger: 'item',
- formatter: '{b}
{c} kWh ({d}%)'
+ formatter: `{b}
{c} ${selectedEnergyUnit.value} ({d}%)`
},
legend: {
orient: 'vertical',
@@ -357,10 +300,10 @@ const regionPieOptions = reactive({
itemWidth: 12,
itemHeight: 12,
formatter: (name: string) => {
- const item = regionData.find((row) => row.name === name)
+ const item = regionItems.value.find((row) => row.name === name)
const value = item?.value ?? 0
- const percent = value > 0 ? '100.00' : '0.00'
- return `${name} ${value.toLocaleString()} kWh (${percent}%)`
+ const percent = item?.percent ?? '0.00'
+ return `${name} ${value.toLocaleString()} ${selectedEnergyUnit.value} (${percent}%)`
}
},
series: [
@@ -373,7 +316,7 @@ const regionPieOptions = reactive({
label: {
show: true,
position: 'center',
- formatter: '总用电量\n{total|2,139.09 kWh}',
+ formatter: `总用量\n{total|${regionTotal.value} ${selectedEnergyUnit.value}}`,
color: '#303133',
rich: {
total: {
@@ -385,116 +328,93 @@ const regionPieOptions = reactive({
}
},
labelLine: { show: false },
- data: regionData
+ data: regionItems.value
}
]
-}) as EChartsOption
-
-const buildMetrics = () => {
- metricCards.value = [
- {
- key: 'total',
- label: '总用电量',
- value: '2,139.09',
- unit: selectedEnergyUnit.value,
- icon: 'ep:lightning',
- theme: 'blue',
- subLabel: '较昨日',
- change: '12.53%'
- },
- {
- key: 'today',
- label: '今日用电量',
- value: '860.32',
- unit: selectedEnergyUnit.value,
- icon: 'ep:calendar',
- theme: 'green',
- subLabel: '较昨日',
- change: '12.15%'
- },
- {
- key: 'peak',
- label: '峰值用电',
- value: '156.8',
- unit: selectedEnergyUnit.value,
- icon: 'ep:histogram',
- theme: 'purple',
- subLabel: '时间:',
- subValue: '2026-05-04 14:00 ~ 15:00'
- },
- {
- key: 'unit',
- label: '单位产量能耗',
- value: '0.38',
- unit: selectedUnitConsumptionUnit.value,
- icon: 'ep:orange',
- theme: 'orange',
- subLabel: '较昨日',
- change: '5.21%',
- down: true
- },
- {
- key: 'region',
- label: '最大耗能区域',
- value: '成型系统',
- unit: '',
- icon: 'ep:pie-chart',
- theme: 'cyan',
- subLabel: '占比:',
- subValue: '100.00%'
- }
- ]
-}
+})
-const buildDetails = () => {
- const timeRange = queryParams.timeRange?.length === 2 ? queryParams.timeRange : defaultTimeRange
- detailList.value = [
- {
- id: 1,
- name: '蛋托线真空柜用电量',
- energyType: selectedEnergyName.value,
- region: '蛋托线01',
- value: '0',
- startTime: timeRange[0],
- endTime: timeRange[1]
- },
- {
- id: 2,
- name: '蛋托成型柜用电量',
- energyType: selectedEnergyName.value,
- region: '成型系统',
- value: '2,139.09',
- startTime: timeRange[0],
- endTime: timeRange[1]
- },
- {
- id: 3,
- name: '蛋托干燥柜用电量',
- energyType: selectedEnergyName.value,
- region: '烘干系统',
- value: '0',
- startTime: timeRange[0],
- endTime: timeRange[1]
+const metricThemeMap: Record = {
+ total: { icon: 'ep:lightning', theme: 'blue' },
+ deviceCount: { icon: 'ep:cpu', theme: 'green' },
+ topDevice: { icon: 'ep:histogram', theme: 'purple' },
+ topRegion: { icon: 'ep:pie-chart', theme: 'cyan' },
+ range: { icon: 'ep:calendar', theme: 'orange' }
+}
+
+const normalizeMetricCards = (metrics: EnergyOverviewMetricVO[]) => {
+ metricCards.value = metrics.map((item) => ({
+ key: item.key,
+ label: item.label,
+ value: item.value,
+ unit: item.unit || '',
+ icon: metricThemeMap[item.key]?.icon || 'ep:data-line',
+ theme: metricThemeMap[item.key]?.theme || 'blue',
+ subLabel: item.subLabel || '',
+ subValue: item.subValue || '',
+ change: item.change,
+ down: item.down
+ }))
+}
+
+const updateCharts = (data: EnergyOverviewRespVO) => {
+ trendXAxis.value = data.trendChart?.xAxis || []
+ trendSeries.value = (data.trendChart?.data || []).map((item) => Number(item) || 0)
+ regionItems.value = (data.regionChart?.items || []).map((item) => ({
+ name: item.name,
+ value: Number(item.value) || 0,
+ percent: item.percent
+ }))
+ regionTotal.value = data.regionChart?.totalValue || '0'
+
+ trendChartOptions.xAxis = {
+ ...(trendChartOptions.xAxis as any),
+ data: trendXAxis.value
+ }
+ trendChartOptions.yAxis = {
+ ...(trendChartOptions.yAxis as any),
+ name: selectedEnergyUnit.value
+ }
+ if (Array.isArray(trendChartOptions.series) && trendChartOptions.series[0]) {
+ ;(trendChartOptions.series[0] as any).data = trendSeries.value
+ }
+ if (Array.isArray(regionPieOptions.series) && regionPieOptions.series[0]) {
+ ;(regionPieOptions.series[0] as any).data = regionItems.value
+ ;(regionPieOptions.series[0] as any).label = {
+ ...(regionPieOptions.series[0] as any).label,
+ formatter: `总用量\n{total|${regionTotal.value} ${selectedEnergyUnit.value}}`
}
- ]
- total.value = detailList.value.length
+ }
}
-const getList = () => {
+const getList = async () => {
if (!hasEnergyTypes.value) {
metricCards.value = []
- detailList.value = []
+ rankList.value = []
pageDetailList.value = []
total.value = 0
return
}
loading.value = true
- buildMetrics()
- buildDetails()
- const start = (queryParams.pageNo - 1) * queryParams.pageSize
- pageDetailList.value = detailList.value.slice(start, start + queryParams.pageSize)
- loading.value = false
+ try {
+ const timeRange = queryParams.timeRange?.length === 2 ? queryParams.timeRange : defaultTimeRange
+ const res = await EnergyDeviceApi.queryOverviewData({
+ orgId: queryParams.orgId,
+ energyTypeId: queryParams.energyTypeId,
+ startTime: timeRange[0],
+ endTime: timeRange[1],
+ pageNo: queryParams.pageNo,
+ pageSize: queryParams.pageSize
+ })
+ const data = ((res as any)?.data || res) as EnergyOverviewRespVO
+ normalizeMetricCards(data.metrics || [])
+ rankList.value = data.rankList || []
+ pageDetailList.value = data.detailList || []
+ total.value = data.total || 0
+ updateCharts(data)
+ } finally {
+ loading.value = false
+ }
}
const normalizeEnergyTypeList = (data: unknown): EnergyTypeVO[] => {
@@ -508,14 +428,7 @@ const normalizeEnergyTypeList = (data: unknown): EnergyTypeVO[] => {
return []
}
-const getDefaultEnergyType = (list: EnergyTypeVO[]) => {
- return (
- list.find((item) => item.name === '电') ||
- list.find((item) => item.name === '水') ||
- list.find((item) => item.name === '气') ||
- list[0]
- )
-}
+const getDefaultEnergyType = (list: EnergyTypeVO[]) => list[0]
const getEnergyTypes = async () => {
try {
@@ -531,10 +444,6 @@ const getEnergyTypes = async () => {
}
const handleQuery = () => {
- if (!hasEnergyTypes.value) {
- getList()
- return
- }
queryParams.pageNo = 1
getList()
}
@@ -547,11 +456,28 @@ const resetQuery = () => {
handleQuery()
}
-const handleExport = () => {
- message.info('示例页暂未接入导出接口')
+const handleExport = async () => {
+ const timeRange = queryParams.timeRange?.length === 2 ? queryParams.timeRange : defaultTimeRange
+ await EnergyDeviceApi.exportQueryDataRecords({
+ orgId: queryParams.orgId,
+ startTime: timeRange[0],
+ endTime: timeRange[1]
+ })
+ message.success('导出已发起')
+}
+
+const loadOrgOptions = async () => {
+ if (orgOptions.value.length) return
+ const data: any = await OrganizationApi.getOrganizationList({})
+ const rows = (Array.isArray(data) ? data : data?.list ?? data?.data ?? []) as OrganizationVO[]
+ orgOptions.value = rows
+ .filter((r) => r?.id !== undefined && r?.name)
+ .map((r) => ({ label: r.name, value: r.id, raw: r }))
}
+
onMounted(async () => {
+ await loadOrgOptions()
await getEnergyTypes()
if (hasEnergyTypes.value) {
getList()
@@ -705,19 +631,6 @@ onMounted(async () => {
color: var(--el-color-primary-light-3);
}
-.panel-actions {
- display: inline-flex;
- align-items: center;
- gap: 4px;
-
- .el-button {
- width: 30px;
- height: 30px;
- padding: 0;
- border: 1px solid var(--el-border-color-light);
- }
-}
-
.rank-index {
display: inline-flex;
width: 22px;