设备运行概览

main
liutao 1 day ago
parent 2872f52d57
commit 84101b7866

@ -52,6 +52,7 @@ const Echart = EchartChart
const props = defineProps<{
hourlyStatus: HourlyStatusItem[]
summary: StatusSummaryItem[]
summaryTotalHours?: number
}>()
const { t } = useI18n()
@ -137,11 +138,11 @@ const pieOption = computed<EChartsOption>(() => ({
tooltip: { trigger: 'item', formatter: '{b}: {c}%' },
graphic: [
{
type: 'text',
left: 'center',
top: '42%',
style: {
text: `${t('DataCollection.RunOverview.totalTimeLabel')}\n24.00 h`,
type: 'text',
left: 'center',
top: '42%',
style: {
text: `${t('DataCollection.RunOverview.totalTimeLabel')}\n${(props.summaryTotalHours ?? 0).toFixed(2)} h`,
textAlign: 'center',
fill: '#101828',
fontSize: 14,

@ -69,6 +69,7 @@ export interface RunOverviewData {
metrics: RunOverviewMetric[]
hourlyStatus: HourlyStatusItem[]
summary: StatusSummaryItem[]
summaryTotalHours: number
timelineRows: DeviceTimelineRow[]
totalDevices: number
}

@ -27,7 +27,11 @@
<OverviewMetricCards :metrics="overviewData.metrics" />
<StatusDistributionChart :hourly-status="overviewData.hourlyStatus" :summary="overviewData.summary" />
<StatusDistributionChart
:hourly-status="overviewData.hourlyStatus"
:summary="overviewData.summary"
:summary-total-hours="overviewData.summaryTotalHours"
/>
<OperationTimelineChart
:rows="overviewData.timelineRows"
@ -45,13 +49,14 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import { useFullscreen } from '@vueuse/core'
import { DeviceOperationOverviewApi } from '@/api/iot/deviceOperationOverview'
import { OrganizationApi } from '@/api/mes/organization'
import { handleTree } from '@/utils/tree'
import OverviewFilterBar from './components/OverviewFilterBar.vue'
import OverviewMetricCards from './components/OverviewMetricCards.vue'
import OperationTimelineChart from './components/OperationTimelineChart.vue'
import StatusDistributionChart from './components/StatusDistributionChart.vue'
import { buildDefaultQueryParams, buildRunOverviewData } from './mock'
import { buildDefaultQueryParams, buildRunOverviewTimelineData } from './mock'
import type {
OrganizationFilterItem,
OrganizationTreeOption,
@ -144,10 +149,36 @@ const collectDeviceOptionsByGroup = (groupId?: string) => {
}
const deviceOptions = computed(() => collectDeviceOptionsByGroup(queryParams.value.groupId))
const overviewData = ref<RunOverviewData>(buildRunOverviewData(queryParams.value, deviceOptions.value))
const overviewData = ref<RunOverviewData>({
metrics: [],
hourlyStatus: [],
summary: [],
summaryTotalHours: 0,
timelineRows: [],
totalDevices: 0
})
const currentDeviceIds = computed(() =>
queryParams.value.deviceId.length > 0 ? queryParams.value.deviceId : deviceOptions.value.map((item) => item.value)
)
const refreshData = () => {
overviewData.value = buildRunOverviewData(queryParams.value, deviceOptions.value)
const buildOverviewRequestParams = () => ({
ids: currentDeviceIds.value.join(','),
startTime: queryParams.value.timeRange[0],
endTime: queryParams.value.timeRange[1]
})
const refreshData = async () => {
const response = await DeviceOperationOverviewApi.getRunOverview(buildOverviewRequestParams())
const timelineData = buildRunOverviewTimelineData(queryParams.value, deviceOptions.value)
overviewData.value = {
metrics: response?.metrics || [],
hourlyStatus: response?.hourlyStatus || [],
summary: response?.summary || [],
summaryTotalHours: response?.summaryTotalHours || 0,
timelineRows: timelineData.timelineRows,
totalDevices: timelineData.totalDevices
}
const maxPage = Math.max(1, Math.ceil(overviewData.value.totalDevices / pageSize.value))
if (pageNo.value > maxPage) pageNo.value = maxPage
}
@ -156,22 +187,22 @@ const handleQuickRangeChange = (key: QuickRangeKey) => {
queryParams.value = {
...queryParams.value,
quickRange: key,
timeRange: key === 'custom' ? queryParams.value.timeRange : createDateRangeByQuickKey(key)
timeRange: createDateRangeByQuickKey(key)
}
pageNo.value = 1
refreshData()
void refreshData()
}
const handleQuery = () => {
pageNo.value = 1
refreshData()
void refreshData()
}
const resetQuery = () => {
queryParams.value = buildDefaultQueryParams()
pageNo.value = 1
pageSize.value = 10
refreshData()
void refreshData()
}
const handleExport = () => {
@ -220,7 +251,7 @@ watch(
onMounted(async () => {
await getOrganizationOptions()
refreshData()
await refreshData()
})
</script>

@ -1,13 +1,5 @@
import dayjs from 'dayjs'
import type {
DeviceTimelineRow,
HourlyStatusItem,
OverviewOption,
RunOverviewData,
RunOverviewMetric,
RunOverviewQueryParams,
RunStatus
} from './components/types'
import type { DeviceTimelineRow, OverviewOption, RunOverviewData, RunOverviewQueryParams, RunStatus } from './components/types'
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'
@ -81,93 +73,13 @@ const toTimelineRows = (deviceOptions: OverviewOption[]): DeviceTimelineRow[] =>
segments: shiftTimeline(BASE_SEGMENTS, TIMELINE_OFFSETS[index % TIMELINE_OFFSETS.length])
}))
const toHourlyStatus = (summaryFactor: number): HourlyStatusItem[] =>
Array.from({ length: 24 }, (_, hour) => {
const runningBase = 74 + Math.sin((hour / 24) * Math.PI * 3) * 14 + summaryFactor * 2
const standbyBase = 10 + Math.cos((hour / 24) * Math.PI * 4) * 7
const faultBase = 2 + Math.max(0, Math.sin((hour - 5) / 2.2)) * 5
const running = Math.max(48, Math.min(88, Number(runningBase.toFixed(2))))
const standby = Math.max(4, Math.min(26, Number(standbyBase.toFixed(2))))
const fault = Math.max(1, Math.min(8, Number(faultBase.toFixed(2))))
const offline = Number(Math.max(4, 100 - running - standby - fault).toFixed(2))
return {
hour: `${String(hour).padStart(2, '0')}:00`,
running,
standby,
fault,
offline
}
})
const createMetrics = (summaryFactor: number): RunOverviewMetric[] => [
{
key: 'utilizationRate',
icon: 'ep:pie-chart',
value: Number((75.42 + summaryFactor * 1.2).toFixed(2)),
unit: '%',
change: 4.32
},
{
key: 'powerOnRate',
icon: 'ep:video-play',
value: Number((90.12 + summaryFactor * 0.7).toFixed(2)),
unit: '%',
change: 2.15
},
{
key: 'faultRate',
icon: 'ep:warning',
value: Number(Math.max(1.5, 3.21 - summaryFactor * 0.35).toFixed(2)),
unit: '%',
change: -1.03
},
{
key: 'standbyRate',
icon: 'ep:timer',
value: Number(Math.max(4, 6.67 - summaryFactor * 0.25).toFixed(2)),
unit: '%',
change: -1.14
}
]
const createSummary = (summaryFactor: number) => {
const running = Number((75.42 + summaryFactor * 1.2).toFixed(2))
const standby = Number(Math.max(4, 6.67 - summaryFactor * 0.25).toFixed(2))
const fault = Number(Math.max(1.5, 3.21 - summaryFactor * 0.35).toFixed(2))
const offline = Number((100 - running - standby - fault).toFixed(2))
return [
{ status: 'running' as const, percent: running, hours: Number(((24 * running) / 100).toFixed(2)) },
{ status: 'standby' as const, percent: standby, hours: Number(((24 * standby) / 100).toFixed(2)) },
{ status: 'fault' as const, percent: fault, hours: Number(((24 * fault) / 100).toFixed(2)) },
{ status: 'offline' as const, percent: offline, hours: Number(((24 * offline) / 100).toFixed(2)) }
]
}
export const buildRunOverviewData = (
export const buildRunOverviewTimelineData = (
query: RunOverviewQueryParams,
deviceOptions: OverviewOption[] = FALLBACK_DEVICE_OPTIONS
): RunOverviewData => {
const summaryFactor =
query.quickRange === 'today'
? 0
: query.quickRange === 'yesterday'
? -0.8
: query.quickRange === 'last7Days'
? 1.1
: query.quickRange === 'last30Days'
? 0.45
: 0.15
): Pick<RunOverviewData, 'timelineRows' | 'totalDevices'> => {
const rows = toTimelineRows(deviceOptions.length > 0 ? deviceOptions : FALLBACK_DEVICE_OPTIONS)
const filteredRows = query.deviceId.length > 0 ? rows.filter((row) => query.deviceId.includes(row.id)) : rows
return {
metrics: createMetrics(summaryFactor),
hourlyStatus: toHourlyStatus(summaryFactor),
summary: createSummary(summaryFactor),
timelineRows: filteredRows,
totalDevices: filteredRows.length
}

Loading…
Cancel
Save