From 83a827ba5b53aa0ccb0c3c50c96c0bff35435b4c Mon Sep 17 00:00:00 2001 From: guanj Date: Wed, 8 Jul 2026 11:14:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E4=BB=B6=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/echarts/rmsboxi.vue | 2533 +++++++++-------- src/components/echarts/shushiboxi.vue | 34 +- src/components/echarts/waveForm.vue | 17 +- .../tree/details/monitoringPointDetails.vue | 114 +- src/utils/fileDownLoad.ts | 15 +- src/utils/waveCache.ts | 14 +- src/utils/waveDisplayHelper.ts | 59 + src/utils/waveRawStore.ts | 75 + src/utils/waveWorkerPool.ts | 6 +- .../terminal/userLedger/components/detail.vue | 23 +- .../business/terminal/userLedger/index.vue | 67 +- 11 files changed, 1568 insertions(+), 1389 deletions(-) create mode 100644 src/utils/waveDisplayHelper.ts create mode 100644 src/utils/waveRawStore.ts diff --git a/src/components/echarts/rmsboxi.vue b/src/components/echarts/rmsboxi.vue index 8e9cd872..ed250ed4 100644 --- a/src/components/echarts/rmsboxi.vue +++ b/src/components/echarts/rmsboxi.vue @@ -1,1264 +1,1269 @@ - - - + + + diff --git a/src/components/echarts/shushiboxi.vue b/src/components/echarts/shushiboxi.vue index 0a4402b7..e4485a98 100644 --- a/src/components/echarts/shushiboxi.vue +++ b/src/components/echarts/shushiboxi.vue @@ -18,6 +18,8 @@ import { calcShuYAxisRange, formatAxisLabel } from '@/utils/chartAxisHelper' import url from '@/assets/img/point.png' import { buildWaveCacheKey, getWaveCache, setWaveCache } from '@/utils/waveCache' import { getShuWorker, buildWorkerPayload } from '@/utils/waveWorkerPool' +import { compactShuWaveResult } from '@/utils/waveDisplayHelper' +import { getWaveRawList } from '@/utils/waveRawStore' let waveRequestId = 0 const pendingCacheKeys = new Map() @@ -75,7 +77,6 @@ const eventValue = ref('') const persistTime = ref('') const lineName = ref('') const subName = ref('') -const waveDatas = ref([]) const ptpass = ref('') const waveHeight = ref() const rmsHeight = ref() @@ -156,12 +157,13 @@ watch( ) const applyWorkerResult = (data: any) => { - titles.value = data.titles - iphasic.value = data.iphasic - time.value = data.time - type.value = data.type - severity.value = data.severity - initWave(data.waveDatas, data.time, data.type, data.severity, isOpen.value) + const compacted = compactShuWaveResult(data, props.value) + titles.value = compacted.titles + iphasic.value = compacted.iphasic + time.value = compacted.time + type.value = compacted.type + severity.value = compacted.severity + initWave(compacted.waveDatas, compacted.time, compacted.type, compacted.severity, isOpen.value) loading.value = false } @@ -179,7 +181,7 @@ onMounted(() => { } const cacheKey = pendingCacheKeys.get(data.requestId) if (cacheKey) { - setWaveCache(cacheKey, data) + setWaveCache(cacheKey, compactShuWaveResult(data, props.value)) pendingCacheKeys.delete(data.requestId) } applyWorkerResult(data) @@ -240,7 +242,10 @@ const waveData = (instantF: any, instantS: any, shunshiF: any, shunshiS: any, ti // 在组件中修改initWaves函数 const initWaves = () => { - if (!props.wp?.listWaveData?.length) { + const hasData = + props.wp?.listWaveData?.length || + getWaveRawList('shu', props.wp?.waveStoreKey)?.length + if (!hasData) { initWave(null, null, null, null, null) loading.value = false return @@ -480,8 +485,8 @@ const initWave = ( type: 'value', name: '时间\n(ms)', boundaryGap: false, - min: props.wp?.listWaveData[0][0] || 0, - max: props.wp?.listWaveData[props.wp?.listWaveData.length - 1][0] + 1 || 1, + min: props.wp?.waveTimeRange?.min ?? props.wp?.listWaveData?.[0]?.[0] ?? 0, + max: props.wp?.waveTimeRange?.max ?? ((props.wp?.listWaveData?.[props.wp?.listWaveData?.length - 1]?.[0] ?? 0) + 1), title: { text: 'ms', textStyle: { @@ -789,8 +794,8 @@ const drawPics = ( type: 'value', name: '时间\n(ms)', boundaryGap: false, - min: props.wp.listWaveData[0][0], - max: props.wp.listWaveData[props.wp.listWaveData.length - 1][0] + 1, + min: props.wp?.waveTimeRange?.min ?? props.wp?.listWaveData?.[0]?.[0] ?? 0, + max: props.wp?.waveTimeRange?.max ?? ((props.wp?.listWaveData?.[props.wp?.listWaveData?.length - 1]?.[0] ?? 0) + 1), title: { text: 'ms', textStyle: { @@ -944,7 +949,6 @@ const drawPics = ( } const backbxlb = () => { - waveDatas.value = [] const charts = [ myChartess.value, myChartess1.value, @@ -996,4 +1000,6 @@ const getMinOpen = (temp: number, tempA: number, tempB: number): number => { temp = temp < tempB ? temp : tempB return temp } + +defineExpose({ backbxlb }) diff --git a/src/components/echarts/waveForm.vue b/src/components/echarts/waveForm.vue index fb509276..91bf6132 100644 --- a/src/components/echarts/waveForm.vue +++ b/src/components/echarts/waveForm.vue @@ -55,10 +55,12 @@ import shushiboxi from '@/components/echarts/shushiboxi.vue' import rmsboxi from '@/components/echarts/rmsboxi.vue' import analytics from '@/components/echarts/analytics.vue' -import { ref, reactive } from 'vue' +import { ref, shallowRef } from 'vue' import { analysis } from '@/api/advance-boot/analyse' import { mainHeight } from '@/utils/layout' import { getMonitorEventAnalyseWave, downloadWaveFile } from '@/api/event-boot/transient' +import { buildWaveStoreKey, registerWaveRaw, buildSlimWp, releaseWaveRaw } from '@/utils/waveRawStore' +import { clearWaveCache } from '@/utils/waveCache' const emit = defineEmits(['backbxlb']) interface Props { // boxoList: any @@ -88,7 +90,7 @@ const shushiboxiRef = ref() const bxecharts = ref(mainHeight(145).height as any) const view2 = ref(true) const boxoList: any = ref(null) -const wp = ref(null) +const wp = shallowRef(null) const showBoxi = ref(true) const view3 = ref(false) const view4 = ref(false) @@ -102,7 +104,9 @@ const open = async (row: any) => { if (res != undefined) { boxoList.value = row boxoList.value.pt = res.data.pt - wp.value = res.data + const storeKey = buildWaveStoreKey(row) + registerWaveRaw(storeKey, res.data) + wp.value = buildSlimWp(res.data, storeKey) loading.value = false view4.value = true } @@ -125,6 +129,9 @@ const bxhandleClick = (tab: any) => { // console.log(tab, event); } const backbxlb = () => { + const storeKey = wp.value?.waveStoreKey + releaseWaveRaw(storeKey, 'all') + clearWaveCache() boxoList.value = null wp.value = null @@ -154,8 +161,8 @@ const AdvancedAnalytics = () => { view2.value = false } const changeView = () => { - if (shushiboxiRef.value) shushiboxiRef.value.backbxlb() - if (rmsboxiRef.value) rmsboxiRef.value.backbxlb() + shushiboxiRef.value?.backbxlb?.() + rmsboxiRef.value?.backbxlb?.() showBoxi.value = false setTimeout(() => { showBoxi.value = true diff --git a/src/components/tree/details/monitoringPointDetails.vue b/src/components/tree/details/monitoringPointDetails.vue index 7632f624..5e07f72b 100644 --- a/src/components/tree/details/monitoringPointDetails.vue +++ b/src/components/tree/details/monitoringPointDetails.vue @@ -3,167 +3,167 @@ - + {{ details.areaName }} - + {{ details.gdName }} - + {{ details.bdName }} - + {{ details.scale }} - + {{ details.manufacturer }} - + {{ details.devName }} - + {{ details.ip }} - + {{ details.lineName }} - + {{ details.id }} - + {{ details.comFlag }} - + {{ details.loadType }} - + {{ details.objName }} - + {{ details.ptType }} - + {{ details.pt }} - + {{ details.ct }} - + {{ details.standardCapacity }} - - {{ details.shortCapacity }} - - + {{ details.devCapacity }} - + + {{ details.shortCapacity }} + + {{ details.dealCapacity }} - - + + {{ limitValue.voltageDev }} - + {{ limitValue.uvoltageDev }} - + {{ limitValue.freqDev }} - + {{ limitValue.ubalance }} - + {{ limitValue.ineg }} - + {{ limitValue.flicker }} - + {{ limitValue.uaberrance }} - + {{ limitValue.oddHarm }} - + {{ limitValue.evenHarm }} - + {{ limitValue.iharm3 }} - + {{ limitValue.iharm4 }} - + {{ limitValue.iharm5 }} - + {{ limitValue.iharm6 }} - + {{ limitValue.iharm7 }} - + {{ limitValue.iharm8 }} - + {{ limitValue.iharm9 }} - + {{ limitValue.iharm10 }} - + {{ limitValue.iharm11 }} - + {{ limitValue.iharm12 }} - + {{ limitValue.iharm13 }} - + {{ limitValue.iharm14 }} - + {{ limitValue.iharm15 }} - + {{ limitValue.iharm16 }} - + {{ limitValue.iharm17 }} - + {{ limitValue.iharm18 }} - + {{ limitValue.iharm19 }} - + {{ limitValue.iharm20 }} - + {{ limitValue.iharm21 }} - + {{ limitValue.iharm22 }} - + {{ limitValue.iharm23 }} - + {{ limitValue.iharm24 }} - + {{ limitValue.iharm25 }} - + {{ limitValue.inUharm }} - + {{ limitValue.inUharm16 }} @@ -187,7 +187,7 @@ const open = (data: any) => { getLineDetailData(data.id).then(res => { details.value = res.data - + res.data.objName ? (title.value = res.data.objName + '_' + title.value) : '' }) getLineOverLimitData(data.id).then(res => { limitValue.value = res.data diff --git a/src/utils/fileDownLoad.ts b/src/utils/fileDownLoad.ts index fc0f51e1..15961e00 100644 --- a/src/utils/fileDownLoad.ts +++ b/src/utils/fileDownLoad.ts @@ -1,4 +1,5 @@ import { downloadFile } from '@/api/system-boot/file' +import { ElMessage } from 'element-plus' const sanitizeUrl = (url: string): string => { return url.replace(/\[/g, '(').replace(/\]/g, ')') @@ -7,7 +8,7 @@ const sanitizeUrl = (url: string): string => { // 下载文件 export const download = (urls: string) => { //console.log('下载', urls) - + ElMessage.info('下载中...') downloadFile({ filePath: urls }) .then((res: any) => { // 1. 确定文件MIME类型(优化:用更简洁的方式) @@ -26,9 +27,9 @@ export const download = (urls: string) => { } return mimeMap[ext] || '' } - + const blob = new Blob([res], { type: getFileType(urls) }) - + // 2. 提取文件名并保留原生后缀(核心修复点) const fileName = urls.split('/').at(-1) || '下载文件' // 先提取URL最后一段(文件名) @@ -43,8 +44,12 @@ export const download = (urls: string) => { // 4. 清理资源(优化) link.remove() window.URL.revokeObjectURL(url) // 释放blob URL + setTimeout(() => { + ElMessage.success('下载成功') + }, 1000) }) .catch(err => { + ElMessage.warning('下载失败:', err) console.error('下载失败:', err) // 可添加错误提示(如Toast) }) @@ -61,7 +66,7 @@ export const previewFile = async (urls: any) => { //console.log('预览', urls) let url = '' await downloadFile({ filePath: decodeURI(urls) }) - .then((res: any) => { + .then((res: any) => { // 1. 确定文件MIME类型(优化:用更简洁的方式) const getFileType = (url: string) => { const ext = url.split('.').pop()?.toLowerCase() || '' @@ -77,7 +82,7 @@ export const previewFile = async (urls: any) => { jpg: 'image/jpg' } return mimeMap[ext] || '' - } + } const blob = new Blob([res], { type: getFileType(decodeURI(urls)) }) // 3. 创建下载链接 url = window.URL.createObjectURL(blob) diff --git a/src/utils/waveCache.ts b/src/utils/waveCache.ts index d114d78f..38c2d345 100644 --- a/src/utils/waveCache.ts +++ b/src/utils/waveCache.ts @@ -1,4 +1,6 @@ -const MAX_CACHE_SIZE = 30 +import { getWaveRawList } from './waveRawStore' + +const MAX_CACHE_SIZE = 6 const cache = new Map() @@ -17,8 +19,10 @@ export function buildWaveCacheKey( boxoList: Record ): string { if (!wp) return '' - const waveFp = - type === 'shu' ? dataFingerprint(wp.listWaveData) : dataFingerprint(wp.listRmsData) + const rawList = wp.waveStoreKey + ? getWaveRawList(type, wp.waveStoreKey) + : ((type === 'shu' ? wp.listWaveData : wp.listRmsData) as unknown[][] | undefined) + const waveFp = dataFingerprint(rawList) const boxoFp = boxoList?.startTime ?? boxoList?.lineName ?? boxoList?.equipmentName ?? '' return `${type}|${wp.time}|${wp.waveType}|${wp.iphasic}|${value}|${isOpen}|${waveFp}|${boxoFp}` } @@ -40,3 +44,7 @@ export function setWaveCache(key: string, value: unknown): void { if (oldest !== undefined) cache.delete(oldest) } } + +export function clearWaveCache(): void { + cache.clear() +} diff --git a/src/utils/waveDisplayHelper.ts b/src/utils/waveDisplayHelper.ts new file mode 100644 index 00000000..88b6068b --- /dev/null +++ b/src/utils/waveDisplayHelper.ts @@ -0,0 +1,59 @@ +const EMPTY_SERIES: number[][] = [] + +function ds(data: number[][] | undefined): number[][] { + if (!data?.length) return EMPTY_SERIES + return data +} + +/** Worker 完整计算后,剔除当前未使用的值类型,降低内存占用 */ +export function compactShuWaveResult(data: any, value: number) { + const waveDatas = data.waveDatas.map((wd: any) => { + if (value === 1) { + return { + ...wd, + shunshiF: { + shunshiFA: ds(wd.shunshiF.shunshiFA), + shunshiFB: ds(wd.shunshiF.shunshiFB), + shunshiFC: ds(wd.shunshiF.shunshiFC) + }, + shunshiS: { shunshiSA: EMPTY_SERIES, shunshiSB: EMPTY_SERIES, shunshiSC: EMPTY_SERIES } + } + } + return { + ...wd, + shunshiF: { shunshiFA: EMPTY_SERIES, shunshiFB: EMPTY_SERIES, shunshiFC: EMPTY_SERIES }, + shunshiS: { + shunshiSA: ds(wd.shunshiS.shunshiSA), + shunshiSB: ds(wd.shunshiS.shunshiSB), + shunshiSC: ds(wd.shunshiS.shunshiSC) + } + } + }) + return { ...data, waveDatas } +} + +export function compactRmsWaveResult(data: any, value: number) { + const waveDatas = data.waveDatas.map((wd: any) => { + if (value === 1) { + return { + ...wd, + RMSFWave: { + rmsFA: ds(wd.RMSFWave.rmsFA), + rmsFB: ds(wd.RMSFWave.rmsFB), + rmsFC: ds(wd.RMSFWave.rmsFC) + }, + RMSSWave: { rmsSA: EMPTY_SERIES, rmsSB: EMPTY_SERIES, rmsSC: EMPTY_SERIES } + } + } + return { + ...wd, + RMSFWave: { rmsFA: EMPTY_SERIES, rmsFB: EMPTY_SERIES, rmsFC: EMPTY_SERIES }, + RMSSWave: { + rmsSA: ds(wd.RMSSWave.rmsSA), + rmsSB: ds(wd.RMSSWave.rmsSB), + rmsSC: ds(wd.RMSSWave.rmsSC) + } + } + }) + return { ...data, waveDatas } +} diff --git a/src/utils/waveRawStore.ts b/src/utils/waveRawStore.ts new file mode 100644 index 00000000..84ad013e --- /dev/null +++ b/src/utils/waveRawStore.ts @@ -0,0 +1,75 @@ +/** 原始波形数据存放于 Vue 响应式体系之外,避免大数组触发深度代理与重复持有 */ + +interface WaveRawEntry { + listWaveData?: unknown[][] + listRmsData?: unknown[][] + waveTimeRange?: { min: number; max: number } +} + +const rawStore = new Map() + +function calcTimeRange(data: unknown[][] | undefined): { min: number; max: number } | undefined { + if (!data?.length) return undefined + const first = data[0] as number[] + const last = data[data.length - 1] as number[] + return { min: first[0], max: last[0] + 1 } +} + +export function buildWaveStoreKey(boxoList: Record): string { + const id = boxoList?.eventId ?? boxoList?.id ?? '' + return `${id}|${boxoList?.startTime ?? ''}` +} + +export function registerWaveRaw(storeKey: string, wp: Record): void { + if (!storeKey || !wp) return + const entry: WaveRawEntry = rawStore.get(storeKey) ?? {} + + if (wp.listWaveData?.length) { + entry.listWaveData = wp.listWaveData + entry.waveTimeRange = calcTimeRange(wp.listWaveData) ?? entry.waveTimeRange + } + if (wp.listRmsData?.length) { + entry.listRmsData = wp.listRmsData + entry.waveTimeRange = calcTimeRange(wp.listRmsData) ?? entry.waveTimeRange + } + + rawStore.set(storeKey, entry) +} + +export function getWaveRawList(type: 'shu' | 'rms', storeKey: string | undefined): unknown[][] | undefined { + if (!storeKey) return undefined + const entry = rawStore.get(storeKey) + return type === 'shu' ? entry?.listWaveData : entry?.listRmsData +} + +export function getWaveTimeRange(storeKey: string | undefined): { min: number; max: number } | undefined { + if (!storeKey) return undefined + return rawStore.get(storeKey)?.waveTimeRange +} + +export function releaseWaveRaw(storeKey: string | undefined, type: 'shu' | 'rms' | 'all' = 'all'): void { + if (!storeKey) return + if (type === 'all') { + rawStore.delete(storeKey) + return + } + const entry = rawStore.get(storeKey) + if (!entry) return + if (type === 'shu') delete entry.listWaveData + if (type === 'rms') delete entry.listRmsData + if (!entry.listWaveData && !entry.listRmsData) { + rawStore.delete(storeKey) + } else { + rawStore.set(storeKey, entry) + } +} + +/** 构建不含大数组的 wp 对象,供子组件 props 使用 */ +export function buildSlimWp(wp: Record, storeKey: string): Record { + const { listWaveData: _w, listRmsData: _r, ...rest } = wp + return { + ...rest, + waveStoreKey: storeKey, + waveTimeRange: getWaveTimeRange(storeKey) ?? calcTimeRange(_w) ?? calcTimeRange(_r) + } +} diff --git a/src/utils/waveWorkerPool.ts b/src/utils/waveWorkerPool.ts index 0f1dd327..bf0b2a77 100644 --- a/src/utils/waveWorkerPool.ts +++ b/src/utils/waveWorkerPool.ts @@ -1,4 +1,5 @@ import { toRaw } from 'vue' +import { getWaveRawList } from './waveRawStore' type WorkerMessageHandler = (data: any) => void @@ -53,10 +54,11 @@ export function buildWorkerPayload( waveType: plainWp.waveType, yzd: plainWp.yzd } + const storeKey = plainWp.waveStoreKey as string | undefined if (type === 'shu') { - wpPayload.listWaveData = plainWp.listWaveData + wpPayload.listWaveData = getWaveRawList('shu', storeKey) ?? plainWp.listWaveData } else { - wpPayload.listRmsData = plainWp.listRmsData + wpPayload.listRmsData = getWaveRawList('rms', storeKey) ?? plainWp.listRmsData } const plainBoxo: Record = {} diff --git a/src/views/pqs/business/terminal/userLedger/components/detail.vue b/src/views/pqs/business/terminal/userLedger/components/detail.vue index fdbae524..4100f74d 100644 --- a/src/views/pqs/business/terminal/userLedger/components/detail.vue +++ b/src/views/pqs/business/terminal/userLedger/components/detail.vue @@ -212,12 +212,21 @@ })?.name }} - - {{ - energyQualityIndexList.find(item => { - return item.id == proviteData.energyQualityIndex - })?.name - }} + + + + + + {{ detailData.checkUrl?.replace(/^.*?\/(.*?)\//, '') || '' }} + + + + + + + + {{ detailData.assessUrl?.replace(/^.*?\/(.*?)\//, '') || '' }} + @@ -233,7 +242,7 @@ import { getDictTreeById } from '@/api/system-boot/dictTree' import { useDictData } from '@/stores/dictData' import { getFileNameAndFilePath } from '@/api/system-boot/file' import { Link, View } from '@element-plus/icons-vue' -import PreviewFile from '@/components/PreviewFile/index.vue' +import { download } from '@/utils/fileDownLoad' // import { addOrUpdateFile, getFileById } from '@/api/supervision-boot/interfere/index' defineOptions({ name: 'BpmUserReportDetail' }) diff --git a/src/views/pqs/business/terminal/userLedger/index.vue b/src/views/pqs/business/terminal/userLedger/index.vue index 79805816..a42890c6 100644 --- a/src/views/pqs/business/terminal/userLedger/index.vue +++ b/src/views/pqs/business/terminal/userLedger/index.vue @@ -1,6 +1,6 @@