From a6e72ddf82e727b3fc675e5bd8e1e6c8be6bb867 Mon Sep 17 00:00:00 2001 From: zch Date: Wed, 8 Apr 2026 14:22:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8C=AF=E5=8A=A8=E6=8A=A5=E8=A1=A8):=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=8C=AF=E5=8A=A8=E6=8A=A5=E8=A1=A8=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增振动报表功能模块,包含以下组件和页面: - 设备树组件(VibrationBoardDeviceTree.vue) - 筛选组件(VibrationBoardFilter.vue) - 共享工具函数(vibrationBoardShared.ts) - 查询状态管理(useVibrationBoardQueryState.ts) - 性能优化文档(performance-optimization.md) - 六个报表页面(overview, trend, comparison, quality, distribution, anomaly) - API接口文件(vibrationBoard.ts) 实现振动数据的多维度展示和分析功能,包括概览、趋势、对比、质量、分布和异常检测等报表页面 --- src/api/ems/report/vibrationBoard.ts | 131 +++++++++++ .../report/vibrationBoard/advanced/index.vue | 166 ++++++++++++++ .../report/vibrationBoard/anomaly/index.vue | 203 +++++++++++++++++ .../vibrationBoard/comparison/index.vue | 137 +++++++++++ .../components/VibrationBoardDeviceTree.vue | 32 +++ .../components/VibrationBoardFilter.vue | 72 ++++++ .../components/useVibrationBoardQueryState.ts | 131 +++++++++++ .../components/vibrationBoardShared.ts | 17 ++ .../vibrationBoard/distribution/index.vue | 175 ++++++++++++++ .../report/vibrationBoard/overview/index.vue | 213 ++++++++++++++++++ .../report/vibrationBoard/quality/index.vue | 118 ++++++++++ .../ems/report/vibrationBoard/trend/index.vue | 115 ++++++++++ 12 files changed, 1510 insertions(+) create mode 100644 src/api/ems/report/vibrationBoard.ts create mode 100644 src/views/ems/report/vibrationBoard/advanced/index.vue create mode 100644 src/views/ems/report/vibrationBoard/anomaly/index.vue create mode 100644 src/views/ems/report/vibrationBoard/comparison/index.vue create mode 100644 src/views/ems/report/vibrationBoard/components/VibrationBoardDeviceTree.vue create mode 100644 src/views/ems/report/vibrationBoard/components/VibrationBoardFilter.vue create mode 100644 src/views/ems/report/vibrationBoard/components/useVibrationBoardQueryState.ts create mode 100644 src/views/ems/report/vibrationBoard/components/vibrationBoardShared.ts create mode 100644 src/views/ems/report/vibrationBoard/distribution/index.vue create mode 100644 src/views/ems/report/vibrationBoard/overview/index.vue create mode 100644 src/views/ems/report/vibrationBoard/quality/index.vue create mode 100644 src/views/ems/report/vibrationBoard/trend/index.vue diff --git a/src/api/ems/report/vibrationBoard.ts b/src/api/ems/report/vibrationBoard.ts new file mode 100644 index 0000000..f292e02 --- /dev/null +++ b/src/api/ems/report/vibrationBoard.ts @@ -0,0 +1,131 @@ +import request from '@/utils/request'; +import type { EmsActionResponse, VibrationBoardQuery } from '../types'; + +export interface VibrationDistributionPageVO { + metricField?: string; + metricLabel?: string; + unit?: string; + intervalBuckets?: Array<{ label?: string; count?: number | string }>; + histogramBuckets?: Array<{ startValue?: number | string; endValue?: number | string; count?: number | string }>; + calendarHeatmap?: Array<{ statDate?: string; avgValue?: number | string }>; + hourlyHeatmap?: Array<{ statDate?: string; statHour?: number | string; avgValue?: number | string }>; +} + +export interface VibrationOverviewPageVO { + metricField?: string; + metricLabel?: string; + unit?: string; + sampleCount?: number | string; + deviceCount?: number | string; + coverageRate?: number | string; + metricCards?: Array<{ field?: string; label?: string; unit?: string; latest?: number | string; avg?: number | string; max?: number | string }>; + gaugeItems?: Array<{ name?: string; value?: number | string; maxValue?: number | string; unit?: string }>; + primaryMetricStats?: { latest?: number | string; min?: number | string; avg?: number | string; max?: number | string }; + deviceRanks?: Array<{ + monitorId?: string; + monitorName?: string; + avg?: number | string; + latest?: number | string; + max?: number | string; + sampleCount?: number | string; + }>; +} + +export interface VibrationTrendPageVO { + metricField?: string; + metricLabel?: string; + unit?: string; + multiDevice?: boolean; + series?: Array<{ name?: string; field?: string; unit?: string; points?: Array<{ time?: string; value?: number | string }> }>; + hourlyItems?: Array<{ hour?: string; avgValue?: number | string }>; +} + +export interface VibrationComparisonPageVO { + metricField?: string; + metricLabel?: string; + unit?: string; + rankItems?: Array<{ monitorId?: string; monitorName?: string; avg?: number | string; latest?: number | string }>; + scatterItems?: Array<{ monitorId?: string; monitorName?: string; avg?: number | string; max?: number | string; sampleCount?: number | string }>; +} + +export interface VibrationQualityPageVO { + sampleCount?: number | string; + deviceCount?: number | string; + coverageRate?: number | string; + metricQualityItems?: Array<{ field?: string; label?: string; unit?: string; validRate?: number | string; validCount?: number | string }>; +} + +export interface VibrationAnomalyPageVO { + metricField?: string; + metricLabel?: string; + unit?: string; + highThreshold?: number | string; + warningThreshold?: number | string; + rapidRiseThreshold?: number | string; + stddevThreshold?: number | string; + minContinuousSamples?: number | string; + highEventCount?: number | string; + continuousEventCount?: number | string; + rapidRiseEventCount?: number | string; + jitterEventCount?: number | string; + highEvents?: Array<{ monitorId?: string; monitorName?: string; value?: number | string; recodeTime?: string }>; + continuousEvents?: Array<{ + monitorId?: string; + monitorName?: string; + startTime?: string; + endTime?: string; + maxValue?: number | string; + sampleCount?: number | string; + }>; + rapidRiseEvents?: Array<{ monitorId?: string; monitorName?: string; diff?: number | string; recodeTime?: string }>; + jitterEvents?: Array<{ monitorId?: string; monitorName?: string; hourBucket?: string; stddev?: number | string; sampleCount?: number | string }>; +} + +export interface VibrationAdvancedPageVO { + metricField?: string; + metricLabel?: string; + unit?: string; + lowBandUpper?: number | string; + focusBandUpper?: number | string; + sankeyNodes?: Array<{ name?: string }>; + sankeyLinks?: Array<{ source?: string; target?: string; value?: number | string }>; + treemapItems?: Array<{ name?: string; value?: number | string; levelTag?: string }>; + parallelAxes?: Array<{ dim?: number | string; name?: string; max?: number | string }>; + parallelSeries?: Array<{ monitorId?: string; monitorName?: string; values?: Array }>; +} + +function getPageData(url: string, query?: VibrationBoardQuery): Promise> { + return request({ + url, + method: 'get', + params: query + }); +} + +export function getVibrationOverviewData(query?: VibrationBoardQuery): Promise> { + return getPageData('/ems/report/vibrationBoard/overview', query); +} + +export function getVibrationTrendData(query?: VibrationBoardQuery): Promise> { + return getPageData('/ems/report/vibrationBoard/trend', query); +} + +export function getVibrationComparisonData(query?: VibrationBoardQuery): Promise> { + return getPageData('/ems/report/vibrationBoard/comparison', query); +} + +export function getVibrationQualityData(query?: VibrationBoardQuery): Promise> { + return getPageData('/ems/report/vibrationBoard/quality', query); +} + +export function getVibrationDistributionData(query?: VibrationBoardQuery): Promise> { + return getPageData('/ems/report/vibrationBoard/distribution', query); +} + +export function getVibrationAnomalyData(query?: VibrationBoardQuery): Promise> { + return getPageData('/ems/report/vibrationBoard/anomaly', query); +} + +export function getVibrationAdvancedData(query?: VibrationBoardQuery): Promise> { + return getPageData('/ems/report/vibrationBoard/advanced', query); +} diff --git a/src/views/ems/report/vibrationBoard/advanced/index.vue b/src/views/ems/report/vibrationBoard/advanced/index.vue new file mode 100644 index 0000000..e2b07c7 --- /dev/null +++ b/src/views/ems/report/vibrationBoard/advanced/index.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/src/views/ems/report/vibrationBoard/anomaly/index.vue b/src/views/ems/report/vibrationBoard/anomaly/index.vue new file mode 100644 index 0000000..59746f2 --- /dev/null +++ b/src/views/ems/report/vibrationBoard/anomaly/index.vue @@ -0,0 +1,203 @@ + + + + + diff --git a/src/views/ems/report/vibrationBoard/comparison/index.vue b/src/views/ems/report/vibrationBoard/comparison/index.vue new file mode 100644 index 0000000..8365c89 --- /dev/null +++ b/src/views/ems/report/vibrationBoard/comparison/index.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/src/views/ems/report/vibrationBoard/components/VibrationBoardDeviceTree.vue b/src/views/ems/report/vibrationBoard/components/VibrationBoardDeviceTree.vue new file mode 100644 index 0000000..e5d1b1c --- /dev/null +++ b/src/views/ems/report/vibrationBoard/components/VibrationBoardDeviceTree.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/src/views/ems/report/vibrationBoard/components/VibrationBoardFilter.vue b/src/views/ems/report/vibrationBoard/components/VibrationBoardFilter.vue new file mode 100644 index 0000000..8810399 --- /dev/null +++ b/src/views/ems/report/vibrationBoard/components/VibrationBoardFilter.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/src/views/ems/report/vibrationBoard/components/useVibrationBoardQueryState.ts b/src/views/ems/report/vibrationBoard/components/useVibrationBoardQueryState.ts new file mode 100644 index 0000000..bbd750f --- /dev/null +++ b/src/views/ems/report/vibrationBoard/components/useVibrationBoardQueryState.ts @@ -0,0 +1,131 @@ +import { getMonitorInfoTree } from '@/api/ems/base/baseMonitorInfo'; +import type { VibrationBoardQuery } from '@/api/ems/types'; +import { createDefaultTimeRange } from './vibrationBoardShared'; + +export function useVibrationBoardQueryState(defaultMetric = 'vibrationSpeed') { + const state = reactive({ + loading: false, + treeLoading: false, + monitorTreeOptions: [] as any[], + treeProps: { label: 'label', children: 'children' }, + deviceDisplayMap: {} as Record, + daterangeRecordTime: createDefaultTimeRange() as string[], + queryForm: { + compareScope: 'all', + monitorId: '', + monitorIds: [] as string[], + selectionLabel: '全部振动设备', + samplingInterval: 5, + vibrationParam: defaultMetric, + highThreshold: undefined as number | undefined, + warningThreshold: undefined as number | undefined, + minContinuousSamples: undefined as number | undefined, + rapidRiseThreshold: undefined as number | undefined, + stddevThreshold: undefined as number | undefined + } + }); + + const { loading, treeLoading, monitorTreeOptions, treeProps, deviceDisplayMap, daterangeRecordTime, queryForm } = toRefs(state); + const deviceCount = computed(() => queryForm.value.monitorIds?.length || (queryForm.value.monitorId ? 1 : 0)); + const hasMultiDevice = computed(() => deviceCount.value > 1 || ['group', 'all'].includes(queryForm.value.compareScope)); + + const buildDeviceDisplayMap = (nodes: any[]): Record => + (nodes || []).reduce( + (acc, item) => { + if (item.code && !item.children?.length) acc[item.code] = item.label; + if (item.children?.length) Object.assign(acc, buildDeviceDisplayMap(item.children)); + return acc; + }, + {} as Record + ); + + const getLeafNodes = (nodes: any[]): any[] => + (nodes || []).flatMap((item) => (item.children?.length ? getLeafNodes(item.children) : item.code ? [item] : [])); + + const buildSelection = (nodes: any[], selectionLabel: string, compareScope?: string) => { + const monitorIds = nodes.map((item) => item.code); + return { + compareScope: compareScope || (monitorIds.length > 1 ? 'group' : 'single'), + monitorId: monitorIds.length === 1 ? monitorIds[0] : '', + monitorIds, + selectionLabel + }; + }; + + const applySelection = (selection: any) => { + Object.assign(queryForm.value, selection); + }; + + const setTimeRange = (value: string[]) => { + daterangeRecordTime.value = value; + }; + + const setSamplingInterval = (value: number) => { + queryForm.value.samplingInterval = value; + }; + + const setVibrationParam = (value: string) => { + queryForm.value.vibrationParam = value; + }; + + const loadTree = async () => { + treeLoading.value = true; + try { + const response = await getMonitorInfoTree({ monitorType: 10 }); + monitorTreeOptions.value = response.data || []; + deviceDisplayMap.value = buildDeviceDisplayMap(monitorTreeOptions.value); + if (!queryForm.value.monitorIds.length && !queryForm.value.monitorId) { + applySelection(buildSelection(getLeafNodes(monitorTreeOptions.value), '全部振动设备', 'all')); + } + } finally { + treeLoading.value = false; + } + }; + + const handleTreeNodeClick = (data: any) => { + if (!data) return; + if (data.children?.length) { + applySelection(buildSelection(getLeafNodes(data.children), `${data.label}设备组`, 'group')); + return; + } + applySelection(buildSelection([data], data.label, 'single')); + }; + + const buildQuery = (): VibrationBoardQuery => ({ + samplingInterval: queryForm.value.samplingInterval, + vibrationParam: queryForm.value.vibrationParam, + beginRecordTime: daterangeRecordTime.value[0], + endRecordTime: daterangeRecordTime.value[1], + highThreshold: queryForm.value.highThreshold, + warningThreshold: queryForm.value.warningThreshold, + minContinuousSamples: queryForm.value.minContinuousSamples, + rapidRiseThreshold: queryForm.value.rapidRiseThreshold, + stddevThreshold: queryForm.value.stddevThreshold, + monitorId: queryForm.value.monitorIds?.length > 1 ? undefined : queryForm.value.monitorIds?.[0] || queryForm.value.monitorId, + monitorIds: queryForm.value.monitorIds?.length > 1 ? queryForm.value.monitorIds : undefined, + params: { + beginRecordTime: daterangeRecordTime.value[0], + endRecordTime: daterangeRecordTime.value[1] + } + }); + + const getMonitorDisplayName = (monitorId?: string) => (monitorId ? deviceDisplayMap.value[monitorId] || monitorId : '--'); + + return { + loading, + treeLoading, + monitorTreeOptions, + treeProps, + daterangeRecordTime, + queryForm, + deviceCount, + hasMultiDevice, + loadTree, + handleTreeNodeClick, + setTimeRange, + setSamplingInterval, + setVibrationParam, + buildQuery, + getMonitorDisplayName + }; +} diff --git a/src/views/ems/report/vibrationBoard/components/vibrationBoardShared.ts b/src/views/ems/report/vibrationBoard/components/vibrationBoardShared.ts new file mode 100644 index 0000000..ec5ced7 --- /dev/null +++ b/src/views/ems/report/vibrationBoard/components/vibrationBoardShared.ts @@ -0,0 +1,17 @@ +export const vibrationMetricOptions = [ + { field: 'vibrationSpeed', label: '振动速度', unit: 'mm/s', color: '#f59e0b' }, + { field: 'vibrationDisplacement', label: '振动位移', unit: 'um', color: '#5b8ff9' }, + { field: 'vibrationAcceleration', label: '振动加速度', unit: 'g', color: '#36cfc9' }, + { field: 'vibrationTemp', label: '振动温度', unit: '℃', color: '#f56c6c' } +] as const; + +export const formatDateTime = (date: Date) => { + const p = (n: number) => n.toString().padStart(2, '0'); + return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(date.getMinutes())}:${p(date.getSeconds())}`; +}; + +export const createDefaultTimeRange = () => { + const end = new Date(); + const start = new Date(end.getTime() - 24 * 3600 * 1000); + return [formatDateTime(start), formatDateTime(end)]; +}; diff --git a/src/views/ems/report/vibrationBoard/distribution/index.vue b/src/views/ems/report/vibrationBoard/distribution/index.vue new file mode 100644 index 0000000..2355dbc --- /dev/null +++ b/src/views/ems/report/vibrationBoard/distribution/index.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/src/views/ems/report/vibrationBoard/overview/index.vue b/src/views/ems/report/vibrationBoard/overview/index.vue new file mode 100644 index 0000000..839d21c --- /dev/null +++ b/src/views/ems/report/vibrationBoard/overview/index.vue @@ -0,0 +1,213 @@ + + + + + diff --git a/src/views/ems/report/vibrationBoard/quality/index.vue b/src/views/ems/report/vibrationBoard/quality/index.vue new file mode 100644 index 0000000..f5787cc --- /dev/null +++ b/src/views/ems/report/vibrationBoard/quality/index.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/src/views/ems/report/vibrationBoard/trend/index.vue b/src/views/ems/report/vibrationBoard/trend/index.vue new file mode 100644 index 0000000..f49dac8 --- /dev/null +++ b/src/views/ems/report/vibrationBoard/trend/index.vue @@ -0,0 +1,115 @@ + + + + +