diff --git a/src/utils/echartMethod.ts b/src/utils/echartMethod.ts index 000a444..97a45dc 100644 --- a/src/utils/echartMethod.ts +++ b/src/utils/echartMethod.ts @@ -1,157 +1,296 @@ -const dataProcessing = (arr: any[]) => { - return arr - .filter(item => typeof item === 'number' || (typeof item === 'string' && !isNaN(parseFloat(item)))) - .map(item => (typeof item === 'number' ? item : parseFloat(item))) -} - -const calculateValue = (o:number,value: number, num: number, isMin: boolean) => { - if (value === 0) { - return 0 - }else if(value>0&& Math.abs(value)<1 && isMin==true){ - return 0 - }else if(value>-1&& value<0 && isMin==false){ - return 0 - } - let base - if (Math.abs(o) >= 100) { - base = 100 - } else if (Math.abs(o) >= 10) { - base = 10 - } else if (Math.abs(o) >= 1) { - base = 1 - } else { - base = 0.1 - } - let calculatedValue - if (isMin) { - if (value < 0) { - calculatedValue = value + num * value - } else { - calculatedValue = value - num * value - } - } else { - if (value < 0) { - calculatedValue = value - num * value - } else { - calculatedValue = value + num * value - } - } - if (base === 0.1) { - return parseFloat(calculatedValue.toFixed(1)) - } else if (isMin) { - return Math.floor(calculatedValue / base) * base - } else { - return Math.ceil(calculatedValue / base) * base - } -} - -// 处理y轴最大最小值 -export const yMethod = (arr: any) => { - let num = 0.2 - let numList = dataProcessing(arr) - let maxValue = 0 - let minValue = 0 - let max = 0 - let min = 0 - maxValue = Math.max(...numList) - minValue = Math.min(...numList) - const o=maxValue-minValue - min = calculateValue( o,minValue, num, true) - max = calculateValue(o,maxValue, num, false) - // if (-100 >= minValue) { - // min = Math.floor((minValue + num * minValue) / 100) * 100 - // } else if (-10 >= minValue && minValue > -100) { - // min = Math.floor((minValue + num * minValue) / 10) * 10 - // } else if (-1 >= minValue && minValue > -10) { - // min = Math.floor(minValue + num * minValue) - // } else if (0 > minValue && minValue > -1) { - // min = parseFloat((minValue + num * minValue).toFixed(1)) - // } else if (minValue == 0) { - // min = 0 - // } else if (0 < minValue && minValue < 1) { - // min = parseFloat((minValue - num * minValue).toFixed(1)) - // } else if (1 <= minValue && minValue < 10) { - // min = Math.floor(minValue - num * minValue) - // } else if (10 <= minValue && minValue < 100) { - // min = Math.floor((minValue - num * minValue) / 10) * 10 - // } else if (100 <= minValue) { - // min = Math.floor((minValue - num * minValue) / 100) * 100 - // } - - // if (-100 >= maxValue) { - // max = Math.ceil((maxValue - num * maxValue) / 100) * 100 - // } else if (-10 >= maxValue && maxValue > -100) { - // max = Math.ceil((maxValue - num * maxValue) / 10) * 10 - // } else if (-1 >= maxValue && maxValue > -10) { - // max = Math.ceil(maxValue - num * maxValue) - // } else if (0 > maxValue && maxValue > -1) { - // max = parseFloat((maxValue - num * maxValue).toFixed(1)) - // } else if (maxValue == 0) { - // max = 0 - // } else if (0 < maxValue && maxValue < 1) { - // max = parseFloat((maxValue + num * maxValue).toFixed(1)) - // } else if (1 <= maxValue && maxValue < 10) { - // max = Math.ceil(maxValue + num * maxValue) - // } else if (10 <= maxValue && maxValue < 100) { - // max = Math.ceil((maxValue + num * maxValue) / 10) * 10 - // } else if (100 <= maxValue) { - // max = Math.ceil((maxValue + num * maxValue) / 100) * 100 - // } - - // if (maxValue > 1000 || minValue < -1000) { - // max = Math.ceil(maxValue / 100) * 100 - // if (minValue == 0) { - // min = 0 - // } else { - // min = Math.floor(minValue / 100) * 100 - // } - // } else if (maxValue < 60 && minValue > 40) { - // max = 60 - // min = 40 - // } else if (maxValue == minValue && maxValue < 10 && minValue > 0) { - // max = Math.ceil(maxValue / 10) * 10 - // min = Math.floor(minValue / 10) * 10 - // } else if (maxValue == minValue && maxValue != 0 && minValue != 0) { - // max = Math.ceil(maxValue / 10 + 1) * 10 - // min = Math.floor(minValue / 10 - 1) * 10 - // } else { - // max = Math.ceil(maxValue / 10) * 10 - // min = Math.floor(minValue / 10) * 10 - // } - - // if (maxValue > 0 && maxValue < 1) { - // max = 1 - // } else if (max == 0 && minValue > -1 && minValue < 0) { - // min = -1 - // } - - return [min, max] -} - - -/** - * title['A相','B相',] - * data[[1,2],[3,4]] - */ -// 导出csv文件 -const convertToCSV = (title: object, data: any) => { - console.log('🚀 ~ convertToCSV ~ data:', data) - let csv = '' - // 添加列头 - csv += ',' + title.join(',') + '\n' - // 遍历数据并添加到CSV字符串中 - data?.map(item => { - csv += item.join(',') + '\n' - }) - return csv -} -export const exportCSV = (title: object, data: any, filename: string) => { - const csv = convertToCSV(title, data) - const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }) - const link = document.createElement('a') - link.href = URL.createObjectURL(blob) - link.download = filename - link.click() - // 释放URL对象 - URL.revokeObjectURL(link.href) -} +const dataProcessing = (arr: any[]) => { + return arr + .filter(item => typeof item === 'number' || (typeof item === 'string' && !isNaN(parseFloat(item)))) + .map(item => (typeof item === 'number' ? item : parseFloat(item))) +} + +const calculateValue = (o: number, value: number, num: number, isMin: boolean) => { + if (value === 0) { + return 0 + } else if (value > 0 && Math.abs(value) < 1 && isMin == true) { + return 0 + } else if (value > -1 && value < 0 && isMin == false) { + return 0 + } + let base + if (Math.abs(o) >= 100) { + base = 100 + } else if (Math.abs(o) >= 10) { + base = 10 + } else if (Math.abs(o) >= 1) { + base = 1 + } else { + base = 0.1 + } + let calculatedValue + if (isMin) { + if (value < 0) { + calculatedValue = value + num * value + } else { + calculatedValue = value - num * value + } + } else { + if (value < 0) { + calculatedValue = value - num * value + } else { + calculatedValue = value + num * value + } + } + if (base === 0.1) { + return parseFloat(calculatedValue.toFixed(1)) + } else if (isMin) { + return Math.floor(calculatedValue / base) * base + } else { + return Math.ceil(calculatedValue / base) * base + } +} + +// 处理y轴最大最小值 +export const yMethod = (arr: any) => { + let num = 0.2 + let numList = dataProcessing(arr) + let maxValue = 0 + let minValue = 0 + let max = 0 + let min = 0 + maxValue = Math.max(...numList) + minValue = Math.min(...numList) + const o = maxValue - minValue + min = calculateValue(o, minValue, num, true) + max = calculateValue(o, maxValue, num, false) + // if (-100 >= minValue) { + // min = Math.floor((minValue + num * minValue) / 100) * 100 + // } else if (-10 >= minValue && minValue > -100) { + // min = Math.floor((minValue + num * minValue) / 10) * 10 + // } else if (-1 >= minValue && minValue > -10) { + // min = Math.floor(minValue + num * minValue) + // } else if (0 > minValue && minValue > -1) { + // min = parseFloat((minValue + num * minValue).toFixed(1)) + // } else if (minValue == 0) { + // min = 0 + // } else if (0 < minValue && minValue < 1) { + // min = parseFloat((minValue - num * minValue).toFixed(1)) + // } else if (1 <= minValue && minValue < 10) { + // min = Math.floor(minValue - num * minValue) + // } else if (10 <= minValue && minValue < 100) { + // min = Math.floor((minValue - num * minValue) / 10) * 10 + // } else if (100 <= minValue) { + // min = Math.floor((minValue - num * minValue) / 100) * 100 + // } + + // if (-100 >= maxValue) { + // max = Math.ceil((maxValue - num * maxValue) / 100) * 100 + // } else if (-10 >= maxValue && maxValue > -100) { + // max = Math.ceil((maxValue - num * maxValue) / 10) * 10 + // } else if (-1 >= maxValue && maxValue > -10) { + // max = Math.ceil(maxValue - num * maxValue) + // } else if (0 > maxValue && maxValue > -1) { + // max = parseFloat((maxValue - num * maxValue).toFixed(1)) + // } else if (maxValue == 0) { + // max = 0 + // } else if (0 < maxValue && maxValue < 1) { + // max = parseFloat((maxValue + num * maxValue).toFixed(1)) + // } else if (1 <= maxValue && maxValue < 10) { + // max = Math.ceil(maxValue + num * maxValue) + // } else if (10 <= maxValue && maxValue < 100) { + // max = Math.ceil((maxValue + num * maxValue) / 10) * 10 + // } else if (100 <= maxValue) { + // max = Math.ceil((maxValue + num * maxValue) / 100) * 100 + // } + + // if (maxValue > 1000 || minValue < -1000) { + // max = Math.ceil(maxValue / 100) * 100 + // if (minValue == 0) { + // min = 0 + // } else { + // min = Math.floor(minValue / 100) * 100 + // } + // } else if (maxValue < 60 && minValue > 40) { + // max = 60 + // min = 40 + // } else if (maxValue == minValue && maxValue < 10 && minValue > 0) { + // max = Math.ceil(maxValue / 10) * 10 + // min = Math.floor(minValue / 10) * 10 + // } else if (maxValue == minValue && maxValue != 0 && minValue != 0) { + // max = Math.ceil(maxValue / 10 + 1) * 10 + // min = Math.floor(minValue / 10 - 1) * 10 + // } else { + // max = Math.ceil(maxValue / 10) * 10 + // min = Math.floor(minValue / 10) * 10 + // } + + // if (maxValue > 0 && maxValue < 1) { + // max = 1 + // } else if (max == 0 && minValue > -1 && minValue < 0) { + // min = -1 + // } + + return [min, max] +} + +/** + * title['A相','B相',] + * data[[1,2],[3,4]] + */ +// 导出csv文件 +const convertToCSV = (title: object, data: any) => { + let csv = '' + // 添加列头 + csv += ',' + title.join(',') + '\n' + // 遍历数据并添加到CSV字符串中 + data?.map(item => { + csv += '\u200B' + item.join(',') + '\n' + }) + return csv +} +export const exportCSV = (title: object, data: any, filename: string) => { + const csv = convertToCSV(title, data) + const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }) + const link = document.createElement('a') + link.href = URL.createObjectURL(blob) + link.download = filename + link.click() + // 释放URL对象 + URL.revokeObjectURL(link.href) +} + +/** + * 补全时间序列数据中缺失的条目 + * @param rawData 原始数据,格式为 [["时间字符串", "数值", "单位", "类型"], ...] + * @returns 补全后的数据,缺失条目数值为 null + */ +export const completeTimeSeries = (rawData: string[][]): (string | null)[][] => { + // 步骤1:校验原始数据并解析时间 + if (rawData.length < 2) { + console.warn('数据量不足2条,无法计算时间间隔,直接返回原始数据') + return rawData.map(item => [...item]) + } + + // 解析所有时间为Date对象,过滤无效时间并按时间排序 + const validData = rawData + .map(item => { + // 确保至少有时间和数值字段 + if (!item[0]) { + return { time: new Date(0), item, isValid: false } + } + const time = new Date(item[0]) + return { time, item, isValid: !isNaN(time.getTime()) } + }) + .filter(data => data.isValid) + .sort((a, b) => a.time.getTime() - b.time.getTime()) // 确保数据按时间排序 + .map(data => data.item) + + if (validData.length < 2) { + throw new Error('有效时间数据不足2条,无法继续处理') + } + + // 步骤2:计算时间间隔(分析前几条数据确定最可能的间隔) + const intervals: number[] = [] + // 分析前10条数据来确定间隔,避免单一间隔出错 + const analyzeCount = Math.min(10, validData.length - 1) + for (let i = 0; i < analyzeCount; i++) { + const currentTime = new Date(validData[i][0]!).getTime() + const nextTime = new Date(validData[i + 1][0]!).getTime() + const interval = nextTime - currentTime + if (interval > 0) { + intervals.push(interval) + } + } + + // 取最常见的间隔作为标准间隔 + const timeInterval = getMostFrequentValue(intervals) + if (timeInterval <= 0) { + throw new Error('无法确定有效的时间间隔') + } + + // 步骤3:生成完整的时间序列范围(从第一条到最后一条) + const startTime = new Date(validData[0][0]!).getTime() + const endTime = new Date(validData[validData.length - 1][0]!).getTime() + const completeTimes: Date[] = [] + + // 生成从 startTime 到 endTime 的所有间隔时间点 + for (let time = startTime; time <= endTime; time += timeInterval) { + completeTimes.push(new Date(time)) + } + + // 步骤4:将原始数据转为时间映射表,使用精确的时间字符串匹配 + const timeDataMap = new Map() + validData.forEach(item => { + // 使用原始时间字符串作为键,避免格式转换导致的匹配问题 + if (item[0]) { + timeDataMap.set(item[0], item) + } + }) + + // 提取模板数据(从第一条有效数据中提取单位和类型,处理可能的缺失) + const template = validData[0] + + // 步骤5:对比补全数据,缺失条目数值为 null + const completedData = completeTimes.map(time => { + // 保持与原始数据相同的时间格式 + const timeStr = formatTime(time) + const existingItem = timeDataMap.get(timeStr) + + if (existingItem) { + // 存在该时间,返回原始数据 + return [...existingItem] + } else { + // 缺失该时间,数值设为 null,其他字段沿用第一个有效数据的格式 + // 处理可能缺失的单位和类型字段 + const result: (string | null | undefined)[] = [timeStr, '/'] + // 仅在原始数据有单位字段时才添加 + if (template.length > 2) { + result.push(template[2]) + } + // 仅在原始数据有类型字段时才添加 + if (template.length > 3) { + result.push(template[3]) + } + return result + } + }) + + return completedData +} + +/** + * 格式化时间为 "YYYY-MM-DD HH:mm:ss" 格式 + * @param date 日期对象 + * @returns 格式化后的时间字符串 + */ +function formatTime(date: Date): string { + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + const seconds = String(date.getSeconds()).padStart(2, '0') + + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` +} + +/** + * 获取数组中出现频率最高的值 + * @param arr 数字数组 + * @returns 出现频率最高的值 + */ +function getMostFrequentValue(arr: number[]): number { + if (arr.length === 0) return 0 + + const frequencyMap = new Map() + arr.forEach(num => { + frequencyMap.set(num, (frequencyMap.get(num) || 0) + 1) + }) + + let maxFrequency = 0 + let mostFrequent = arr[0] + + frequencyMap.forEach((frequency, num) => { + if (frequency > maxFrequency) { + maxFrequency = frequency + mostFrequent = num + } + }) + + return mostFrequent +} diff --git a/src/views/govern/alarm/Abnormal.vue b/src/views/govern/alarm/Abnormal.vue index 39d8bb4..3dbc541 100644 --- a/src/views/govern/alarm/Abnormal.vue +++ b/src/views/govern/alarm/Abnormal.vue @@ -94,7 +94,15 @@ const tableStore = new TableStore({ }, { title: '设备名称', field: 'ndid', align: 'center' }, { title: '异常时间', field: 'evtTime', align: 'center', sortable: true }, - { title: '告警代码', field: 'code', align: 'center', sortable: true } + { + title: '告警代码', + field: 'code', + align: 'center', + sortable: true, + formatter: (row: any) => { + return row.cellValue ? '\u200B' + row.cellValue : '/' + } + } ] }) diff --git a/src/views/govern/alarm/Device.vue b/src/views/govern/alarm/Device.vue index 6c9667a..fc109bf 100644 --- a/src/views/govern/alarm/Device.vue +++ b/src/views/govern/alarm/Device.vue @@ -86,7 +86,7 @@ const tableStore = new TableStore({ { title: '设备名称', field: 'equipmentName', align: 'center' }, { title: '工程名称', field: 'engineeringName', align: 'center' }, { title: '项目名称', field: 'projectName', align: 'center' }, - { title: '发生时刻', field: 'startTime', align: 'center', minWidth: 110, sortable: true }, + { title: '发生时刻', field: 'startTime', align: 'center', width: 180, sortable: true }, { title: '模块信息', field: 'moduleNo', @@ -99,20 +99,21 @@ const tableStore = new TableStore({ title: '告警代码', field: 'code', align: 'center', - + width: 100, formatter: (row: any) => { - return row.cellValue ? row.cellValue : '/' + return row.cellValue ? '\u200B' + row.cellValue : '/' }, sortable: true }, { title: '事件描述', + minWidth: 220, field: 'showName' }, { title: '级别', field: 'level', - + width: 100, render: 'tag', custom: { 1: 'danger', @@ -133,7 +134,8 @@ const tableStore = new TableStore({ // } // } ], - beforeSearchFun: () => {} + beforeSearchFun: () => {}, + }) provide('tableStore', tableStore) diff --git a/src/views/govern/alarm/Front.vue b/src/views/govern/alarm/Front.vue index 23b9e33..8fb99a6 100644 --- a/src/views/govern/alarm/Front.vue +++ b/src/views/govern/alarm/Front.vue @@ -51,7 +51,7 @@ const tableStore = new TableStore({ align: 'center', formatter: (row: any) => { - return row.cellValue ? row.cellValue : '/' + return row.cellValue ? '\u200B' + row.cellValue : '/' }, sortable: true }, diff --git a/src/views/govern/analyze/APF/index.vue b/src/views/govern/analyze/APF/index.vue index cc52dbd..2e04e65 100644 --- a/src/views/govern/analyze/APF/index.vue +++ b/src/views/govern/analyze/APF/index.vue @@ -1,448 +1,469 @@ - - - - - - + + + + + + diff --git a/src/views/govern/device/control/tabs/trend.vue b/src/views/govern/device/control/tabs/trend.vue index 1c031cd..95c73ab 100644 --- a/src/views/govern/device/control/tabs/trend.vue +++ b/src/views/govern/device/control/tabs/trend.vue @@ -1,798 +1,861 @@ - - - - + + + + diff --git a/src/views/govern/device/planData/index.vue b/src/views/govern/device/planData/index.vue index 6c8c564..6b3cf74 100644 --- a/src/views/govern/device/planData/index.vue +++ b/src/views/govern/device/planData/index.vue @@ -1,1119 +1,1190 @@ - - - - - + + + + + diff --git a/src/views/govern/monitorRecall/steadyRecall.vue b/src/views/govern/monitorRecall/steadyRecall.vue index 9c58964..e883734 100644 --- a/src/views/govern/monitorRecall/steadyRecall.vue +++ b/src/views/govern/monitorRecall/steadyRecall.vue @@ -14,7 +14,6 @@