From 075dc313ab14730045564581ee9494cf76928818 Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Tue, 7 Jul 2026 15:37:39 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E4=BF=9D=E9=9A=9C?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/iot/device/index.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/views/iot/device/index.vue b/src/views/iot/device/index.vue index ef94b014..8c9eba62 100644 --- a/src/views/iot/device/index.vue +++ b/src/views/iot/device/index.vue @@ -973,8 +973,8 @@ const deviceStatisticsItems = computed(() => [ { key: 'total', label: '总数', value: deviceStatistics.total }, { key: 'running', label: '运行数', value: deviceStatistics.running }, { key: 'standby', label: '待机数', value: deviceStatistics.standby }, - /* { key: 'fault', label: '故障数', value: deviceStatistics.fault },*/ - { key: 'alarm', label: '报警数', value: deviceStatistics.alarm }, + { key: 'fault', label: '报警数', value: deviceStatistics.fault }, + /*{ key: 'alarm', label: '报警数', value: deviceStatistics.alarm },*/ { key: 'offline', label: '离线数', value: deviceStatistics.offline } ]) @@ -982,8 +982,8 @@ const normalizeDeviceOperatingStatus = (value: string | number | undefined): Dev const text = String(value ?? '').trim().toLowerCase() if (['运行', '运行中', 'running', 'run', '1'].includes(text)) return 'running' if (['待机', '待机中', 'standby', 'idle', '2'].includes(text)) return 'standby' - /*if (['故障', '故障中', 'fault', 'error', '3'].includes(text)) return 'fault'*/ - if (['报警', '报警中', 'alarm', 'warning', '4', '5'].includes(text)) return 'alarm' + if (['故障', '故障中', 'fault', 'error', '3'].includes(text)) return 'fault' +/* if (['报警', '报警中', 'alarm', 'warning', '4', '5'].includes(text)) return 'alarm'*/ return 'offline' } From 420b16bd1600bf8475827563c94ca6fb2892ef6b Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Tue, 7 Jul 2026 17:24:32 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=88=B6=E5=93=81?= =?UTF-8?q?=E8=BD=A6=E9=97=B4=E8=AE=BE=E5=A4=87=E8=BF=90=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/en.ts | 2 +- src/locales/zh-CN.ts | 2 +- .../dashboard1/dashboard3/index.vue | 200 ++++++++++++++---- 3 files changed, 161 insertions(+), 43 deletions(-) diff --git a/src/locales/en.ts b/src/locales/en.ts index 4c6a0706..b2698a54 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -409,7 +409,7 @@ export default { runningTime: 'Running Time (Today)', standbyTime: 'Idle Time (Today)', alarmTime: 'Alarm Time (Today)', - efficiency: 'Efficiency (Today)' + efficiency: 'Activation (Today)' }, empty: 'No device operation data' }, diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 73434e53..5b427f38 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -409,7 +409,7 @@ export default { runningTime: '运行时间(今日)', standbyTime: '空闲时间(今日)', alarmTime: '报警时间(今日)', - efficiency: '运行效率(今日)' + efficiency: '稼动率(今日)' }, empty: '暂无设备运行数据' }, diff --git a/src/views/report/dashboardPage/dashboard1/dashboard3/index.vue b/src/views/report/dashboardPage/dashboard1/dashboard3/index.vue index d9dceb79..a3bdc7ba 100644 --- a/src/views/report/dashboardPage/dashboard1/dashboard3/index.vue +++ b/src/views/report/dashboardPage/dashboard1/dashboard3/index.vue @@ -119,7 +119,12 @@ import { useFullscreen } from '@vueuse/core' import * as echarts from 'echarts' import type { EChartsOption } from 'echarts' import { DeviceOperationOverviewApi } from '@/api/iot/deviceOperationOverview' -import type { DeviceOperationOverviewRespVO, DeviceOperationOverviewTimelineRowVO } from '@/api/iot/deviceOperationOverview' +import type { + DeviceOperationOverviewHourlyStatusVO, + DeviceOperationOverviewRespVO, + DeviceOperationOverviewTimelineRowVO +} from '@/api/iot/deviceOperationOverview' +import { DeviceApi, type DeviceVO } from '@/api/iot/device' import { OrganizationApi } from '@/api/mes/organization' defineOptions({ name: 'IotReportDashboard3' }) @@ -128,6 +133,14 @@ const { t } = useI18n('Dashboard3') type RunStatus = 'running' | 'standby' | 'fault' | 'offline' +interface DeviceStatusStatistics { + total: number + running: number + standby: number + fault: number + offline: number +} + interface OrganizationFilterItem { id: string parentId?: string | null @@ -159,6 +172,15 @@ const overviewData = ref({ timelineRows: [], totalDevices: 0 }) +const last24HourlyStatus = ref([]) +const deviceStatusStatistics = ref({ + total: 0, + running: 0, + standby: 0, + fault: 0, + offline: 0 +}) +const deviceOperatingStatusMap = ref>({}) const weekTrend = ref<{ date: string; rate: number }[]>([]) const detailPage = ref(1) const detailPageSize = 8 @@ -178,18 +200,6 @@ const statusText = computed>(() => ({ offline: t('Status.offline') })) -const metricValueMap = computed(() => { - return overviewData.value.metrics.reduce>((acc, item) => { - acc[item.key] = Number(item.value) || 0 - return acc - }, {}) -}) - -const getMetricValue = (keys: string[]) => { - const foundKey = keys.find((key) => metricValueMap.value[key] !== undefined) - return foundKey ? metricValueMap.value[foundKey] : undefined -} - const calcDeviceRate = (count: number, total: number) => { if (!total) return 0 return Number(((count / total) * 100).toFixed(1)) @@ -248,7 +258,7 @@ const statusCards = computed(() => [ const detailRows = computed(() => { return overviewData.value.timelineRows.map((row) => { const minutes = calcMinutes(row) - const status = getCurrentStatus(row) + const status = deviceOperatingStatusMap.value[String(row.id)] || getCurrentStatus(row) return { id: String(row.id), name: row.name || '-', @@ -263,22 +273,14 @@ const detailRows = computed(() => { }) const totalDetailPages = computed(() => Math.max(1, Math.ceil(detailRows.value.length / detailPageSize))) -const runningDeviceCount = computed( - () => getMetricValue(['running', 'run', 'runningDevices']) ?? getRowsByStatus('running').length -) -const standbyDeviceCount = computed( - () => getMetricValue(['standby', 'idle', 'standbyDevices']) ?? getRowsByStatus('standby').length -) -const faultDeviceCount = computed( - () => getMetricValue(['fault', 'alarm', 'faultDevices']) ?? getRowsByStatus('fault').length -) -const offlineDeviceCount = computed( - () => getMetricValue(['offline', 'offlineDevices']) ?? getRowsByStatus('offline').length -) +const runningDeviceCount = computed(() => deviceStatusStatistics.value.running) +const standbyDeviceCount = computed(() => deviceStatusStatistics.value.standby) +const faultDeviceCount = computed(() => deviceStatusStatistics.value.fault) +const offlineDeviceCount = computed(() => deviceStatusStatistics.value.offline) const onlineDeviceCount = computed( () => runningDeviceCount.value + standbyDeviceCount.value + faultDeviceCount.value ) -const totalDeviceCount = computed(() => onlineDeviceCount.value + offlineDeviceCount.value) +const totalDeviceCount = computed(() => deviceStatusStatistics.value.total) const pagedRows = computed(() => { const start = (detailPage.value - 1) * detailPageSize return detailRows.value.slice(start, start + detailPageSize) @@ -290,15 +292,22 @@ const alarmDevices = computed(() => { .sort((a, b) => b.alarmMinutes - a.alarmMinutes) .slice(0, 2) }) -const hourlyRates = computed(() => - overviewData.value.hourlyStatus.map((item) => { - const total = item.running + item.standby + item.fault + item.offline - return { - hour: item.hour, - rate: total > 0 ? Number(((item.running / total) * 100).toFixed(1)) : 0 - } - }) -) +const hourlyRates = computed(() => { + const rateMap = last24HourlyStatus.value.reduce>((acc, item) => { + const rate = getHourlyRunningRate(item) + getHourlyKeyCandidates(item.hour).forEach((key) => { + acc[key] = rate + }) + return acc + }, {}) + + return getLast24HourBuckets().map((time) => ({ + hour: time.format('MM-DD HH:00'), + rate: getHourlyKeyCandidates(time.format('YYYY-MM-DD HH:00:00')) + .map((key) => rateMap[key]) + .find((rate) => rate !== undefined) ?? 0 + })) +}) const todayAverageRate = computed(() => average(hourlyRates.value.map((item) => item.rate))) const sevenDayAverageRate = computed(() => average(weekTrend.value.map((item) => item.rate))) @@ -319,7 +328,92 @@ const average = (values: number[]) => { return Number((valid.reduce((sum, item) => sum + item, 0) / valid.length).toFixed(1)) } -const getRowsByStatus = (status: RunStatus) => detailRows.value.filter((row) => row.status === status) +const getHourlyRunningRate = (item: DeviceOperationOverviewHourlyStatusVO) => { + const total = item.running + item.standby + item.fault + item.offline + return total > 0 ? Number(((item.running / total) * 100).toFixed(1)) : 0 +} + +const getHourlyKeyCandidates = (hour: string) => { + const text = String(hour || '').trim() + const candidates = new Set() + if (text) candidates.add(text) + + const parsed = dayjs(text) + if (parsed.isValid()) { + candidates.add(parsed.format('YYYY-MM-DD HH:00:00')) + candidates.add(parsed.format('YYYY-MM-DD HH:00')) + candidates.add(parsed.format('MM-DD HH:00')) + candidates.add(parsed.format('HH:00')) + } + + const hourMatch = text.match(/(\d{1,2}):00/) + if (hourMatch) { + candidates.add(`${hourMatch[1].padStart(2, '0')}:00`) + } + + return Array.from(candidates) +} + +const normalizeDeviceOperatingStatus = (value: string | number | undefined): RunStatus => { + const text = String(value ?? '').trim().toLowerCase() + if (['运行', '运行中', 'running', 'run', '1'].includes(text)) return 'running' + if (['待机', '待机中', 'standby', 'idle', '2'].includes(text)) return 'standby' + if (['故障', '故障中', 'fault', 'error', '3'].includes(text)) return 'fault' + return 'offline' +} + +const setDeviceStatusStatistics = (rows: DeviceVO[] = []) => { + const nextStats: DeviceStatusStatistics = { + total: rows.length, + running: 0, + standby: 0, + fault: 0, + offline: 0 + } + const nextStatusMap: Record = {} + rows.forEach((row) => { + const status = normalizeDeviceOperatingStatus(row.operatingStatus) + nextStats[status] += 1 + if (row.id != null) { + nextStatusMap[String(row.id)] = status + } + }) + deviceStatusStatistics.value = nextStats + deviceOperatingStatusMap.value = nextStatusMap +} + +const getDevicePageRows = (data: any): DeviceVO[] => { + return Array.isArray(data) ? data : Array.isArray(data?.list) ? data.list : [] +} + +const refreshDeviceStatusStatistics = async () => { + const ids = getDeviceIds().join(',') + const baseParams = { + ...(ids ? { ids } : {}) + } + + try { + const firstPage = await DeviceApi.getDevicePage({ + ...baseParams, + pageNo: 1, + pageSize: 1 + }) + const firstRows = getDevicePageRows(firstPage) + const total = Number((firstPage as any)?.total) || firstRows.length + const pageSize = Math.max(total, firstRows.length, 1) + const data = pageSize === firstRows.length + ? firstPage + : await DeviceApi.getDevicePage({ + ...baseParams, + pageNo: 1, + pageSize + }) + + setDeviceStatusStatistics(getDevicePageRows(data)) + } catch { + setDeviceStatusStatistics([]) + } +} const getCurrentStatus = (row: DeviceOperationOverviewTimelineRowVO): RunStatus => { const currentHour = dayjs().hour() + dayjs().minute() / 60 @@ -387,6 +481,24 @@ const buildParams = (startTime: string, endTime: string, pageSize = 1000) => ({ timelinePageSize: pageSize }) +const getDayRange = (date = dayjs()) => ({ + start: date.startOf('day').format('YYYY-MM-DD HH:mm:ss'), + end: date.add(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss') +}) + +const getLast24HoursRange = () => { + const end = dayjs().startOf('hour') + return { + start: end.subtract(24, 'hour').format('YYYY-MM-DD HH:mm:ss'), + end: end.format('YYYY-MM-DD HH:mm:ss') + } +} + +const getLast24HourBuckets = () => { + const end = dayjs().startOf('hour') + return Array.from({ length: 24 }, (_, index) => end.subtract(23 - index, 'hour')) +} + const loadOrganizationList = async () => { const data = await OrganizationApi.getOrganizationList() organizationList.value = Array.isArray(data) @@ -400,8 +512,7 @@ const loadOrganizationList = async () => { } const refreshTodayData = async () => { - const start = dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss') - const end = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss') + const { start, end } = getDayRange() const response = await DeviceOperationOverviewApi.getRunOverview(buildParams(start, end)) overviewData.value = { metrics: response?.metrics || [], @@ -414,11 +525,18 @@ const refreshTodayData = async () => { lastUpdateTime.value = dayjs().format('YYYY/M/D HH:mm:ss') } +const refreshLast24HourTrend = async () => { + const { start, end } = getLast24HoursRange() + const response = await DeviceOperationOverviewApi.getRunOverview(buildParams(start, end)) + last24HourlyStatus.value = response?.hourlyStatus || [] +} + const refreshWeekTrend = async () => { const requests = Array.from({ length: 7 }).map((_, index) => { const date = dayjs().subtract(6 - index, 'day') + const { start, end } = getDayRange(date) return DeviceOperationOverviewApi.getRunOverview( - buildParams(date.startOf('day').format('YYYY-MM-DD HH:mm:ss'), date.endOf('day').format('YYYY-MM-DD HH:mm:ss'), 1) + buildParams(start, end, 1) ).then((res) => { const running = res?.summary?.find((item) => item.status === 'running')?.percent || 0 return { @@ -431,7 +549,7 @@ const refreshWeekTrend = async () => { } const refreshData = async () => { - await refreshTodayData() + await Promise.all([refreshTodayData(), refreshDeviceStatusStatistics(), refreshLast24HourTrend()]) await refreshWeekTrend() renderCharts() } From 79d86d6476512442c1ce571140ad33d3290d826a Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Wed, 8 Jul 2026 14:07:35 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=B3=A8=E9=87=8A=E6=8E=89=E5=AF=BC?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mes/energyOverview/index.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/mes/energyOverview/index.vue b/src/views/mes/energyOverview/index.vue index f525cba0..1e6ccb29 100644 --- a/src/views/mes/energyOverview/index.vue +++ b/src/views/mes/energyOverview/index.vue @@ -51,10 +51,10 @@ {{ t('common.reset') }} - +