diff --git a/src/locales/en.ts b/src/locales/en.ts
index 7adb0e2c..1da7ebe7 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 2c33267a..c71062e8 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/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'
}
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') }}
-
+
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()
}