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;