修改文件上传问题

This commit is contained in:
guanj
2026-07-08 11:14:32 +08:00
parent 40b1092718
commit 83a827ba5b
11 changed files with 1568 additions and 1389 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,8 @@ import { calcShuYAxisRange, formatAxisLabel } from '@/utils/chartAxisHelper'
import url from '@/assets/img/point.png' import url from '@/assets/img/point.png'
import { buildWaveCacheKey, getWaveCache, setWaveCache } from '@/utils/waveCache' import { buildWaveCacheKey, getWaveCache, setWaveCache } from '@/utils/waveCache'
import { getShuWorker, buildWorkerPayload } from '@/utils/waveWorkerPool' import { getShuWorker, buildWorkerPayload } from '@/utils/waveWorkerPool'
import { compactShuWaveResult } from '@/utils/waveDisplayHelper'
import { getWaveRawList } from '@/utils/waveRawStore'
let waveRequestId = 0 let waveRequestId = 0
const pendingCacheKeys = new Map<number, string>() const pendingCacheKeys = new Map<number, string>()
@@ -75,7 +77,6 @@ const eventValue = ref('')
const persistTime = ref('') const persistTime = ref('')
const lineName = ref('') const lineName = ref('')
const subName = ref('') const subName = ref('')
const waveDatas = ref<WaveData[]>([])
const ptpass = ref('') const ptpass = ref('')
const waveHeight = ref<number>() const waveHeight = ref<number>()
const rmsHeight = ref<number>() const rmsHeight = ref<number>()
@@ -156,12 +157,13 @@ watch(
) )
const applyWorkerResult = (data: any) => { const applyWorkerResult = (data: any) => {
titles.value = data.titles const compacted = compactShuWaveResult(data, props.value)
iphasic.value = data.iphasic titles.value = compacted.titles
time.value = data.time iphasic.value = compacted.iphasic
type.value = data.type time.value = compacted.time
severity.value = data.severity type.value = compacted.type
initWave(data.waveDatas, data.time, data.type, data.severity, isOpen.value) severity.value = compacted.severity
initWave(compacted.waveDatas, compacted.time, compacted.type, compacted.severity, isOpen.value)
loading.value = false loading.value = false
} }
@@ -179,7 +181,7 @@ onMounted(() => {
} }
const cacheKey = pendingCacheKeys.get(data.requestId) const cacheKey = pendingCacheKeys.get(data.requestId)
if (cacheKey) { if (cacheKey) {
setWaveCache(cacheKey, data) setWaveCache(cacheKey, compactShuWaveResult(data, props.value))
pendingCacheKeys.delete(data.requestId) pendingCacheKeys.delete(data.requestId)
} }
applyWorkerResult(data) applyWorkerResult(data)
@@ -240,7 +242,10 @@ const waveData = (instantF: any, instantS: any, shunshiF: any, shunshiS: any, ti
// 在组件中修改initWaves函数 // 在组件中修改initWaves函数
const 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) initWave(null, null, null, null, null)
loading.value = false loading.value = false
return return
@@ -480,8 +485,8 @@ const initWave = (
type: 'value', type: 'value',
name: '时间\n(ms)', name: '时间\n(ms)',
boundaryGap: false, boundaryGap: false,
min: props.wp?.listWaveData[0][0] || 0, min: props.wp?.waveTimeRange?.min ?? props.wp?.listWaveData?.[0]?.[0] ?? 0,
max: props.wp?.listWaveData[props.wp?.listWaveData.length - 1][0] + 1 || 1, max: props.wp?.waveTimeRange?.max ?? ((props.wp?.listWaveData?.[props.wp?.listWaveData?.length - 1]?.[0] ?? 0) + 1),
title: { title: {
text: 'ms', text: 'ms',
textStyle: { textStyle: {
@@ -789,8 +794,8 @@ const drawPics = (
type: 'value', type: 'value',
name: '时间\n(ms)', name: '时间\n(ms)',
boundaryGap: false, boundaryGap: false,
min: props.wp.listWaveData[0][0], min: props.wp?.waveTimeRange?.min ?? props.wp?.listWaveData?.[0]?.[0] ?? 0,
max: props.wp.listWaveData[props.wp.listWaveData.length - 1][0] + 1, max: props.wp?.waveTimeRange?.max ?? ((props.wp?.listWaveData?.[props.wp?.listWaveData?.length - 1]?.[0] ?? 0) + 1),
title: { title: {
text: 'ms', text: 'ms',
textStyle: { textStyle: {
@@ -944,7 +949,6 @@ const drawPics = (
} }
const backbxlb = () => { const backbxlb = () => {
waveDatas.value = []
const charts = [ const charts = [
myChartess.value, myChartess.value,
myChartess1.value, myChartess1.value,
@@ -996,4 +1000,6 @@ const getMinOpen = (temp: number, tempA: number, tempB: number): number => {
temp = temp < tempB ? temp : tempB temp = temp < tempB ? temp : tempB
return temp return temp
} }
defineExpose({ backbxlb })
</script> </script>

View File

@@ -55,10 +55,12 @@
import shushiboxi from '@/components/echarts/shushiboxi.vue' import shushiboxi from '@/components/echarts/shushiboxi.vue'
import rmsboxi from '@/components/echarts/rmsboxi.vue' import rmsboxi from '@/components/echarts/rmsboxi.vue'
import analytics from '@/components/echarts/analytics.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 { analysis } from '@/api/advance-boot/analyse'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { getMonitorEventAnalyseWave, downloadWaveFile } from '@/api/event-boot/transient' 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']) const emit = defineEmits(['backbxlb'])
interface Props { interface Props {
// boxoList: any // boxoList: any
@@ -88,7 +90,7 @@ const shushiboxiRef = ref()
const bxecharts = ref(mainHeight(145).height as any) const bxecharts = ref(mainHeight(145).height as any)
const view2 = ref(true) const view2 = ref(true)
const boxoList: any = ref(null) const boxoList: any = ref(null)
const wp = ref(null) const wp = shallowRef<any>(null)
const showBoxi = ref(true) const showBoxi = ref(true)
const view3 = ref(false) const view3 = ref(false)
const view4 = ref(false) const view4 = ref(false)
@@ -102,7 +104,9 @@ const open = async (row: any) => {
if (res != undefined) { if (res != undefined) {
boxoList.value = row boxoList.value = row
boxoList.value.pt = res.data.pt 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 loading.value = false
view4.value = true view4.value = true
} }
@@ -125,6 +129,9 @@ const bxhandleClick = (tab: any) => {
// console.log(tab, event); // console.log(tab, event);
} }
const backbxlb = () => { const backbxlb = () => {
const storeKey = wp.value?.waveStoreKey
releaseWaveRaw(storeKey, 'all')
clearWaveCache()
boxoList.value = null boxoList.value = null
wp.value = null wp.value = null
@@ -154,8 +161,8 @@ const AdvancedAnalytics = () => {
view2.value = false view2.value = false
} }
const changeView = () => { const changeView = () => {
if (shushiboxiRef.value) shushiboxiRef.value.backbxlb() shushiboxiRef.value?.backbxlb?.()
if (rmsboxiRef.value) rmsboxiRef.value.backbxlb() rmsboxiRef.value?.backbxlb?.()
showBoxi.value = false showBoxi.value = false
setTimeout(() => { setTimeout(() => {
showBoxi.value = true showBoxi.value = true

View File

@@ -3,167 +3,167 @@
<el-tabs type="border-card" v-model="activeName" @tab-click="handleClick"> <el-tabs type="border-card" v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="监测点详情" name="tab1"> <el-tab-pane label="监测点详情" name="tab1">
<el-descriptions :column="3" border label-width="150px"> <el-descriptions :column="3" border label-width="150px">
<el-descriptions-item label="项目工程"> <el-descriptions-item label="项目工程">
{{ details.areaName }} {{ details.areaName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="项目单位"> <el-descriptions-item label="项目单位">
{{ details.gdName }} {{ details.gdName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="所属变电站"> <el-descriptions-item label="所属变电站">
{{ details.bdName }} {{ details.bdName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="电压等级"> <el-descriptions-item label="电压等级">
{{ details.scale }} {{ details.scale }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="终端厂家"> <el-descriptions-item label="终端厂家">
{{ details.manufacturer }} {{ details.manufacturer }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="终端名称"> <el-descriptions-item label="终端名称">
{{ details.devName }} {{ details.devName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="网络参数"> <el-descriptions-item label="网络参数">
{{ details.ip }} {{ details.ip }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="监测点名称"> <el-descriptions-item label="监测点名称">
{{ details.lineName }} {{ details.lineName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="监测点序号"> <el-descriptions-item label="监测点序号">
{{ details.id }} {{ details.id }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="通讯状态"> <el-descriptions-item label="通讯状态">
{{ details.comFlag }} {{ details.comFlag }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="干扰源类型"> <el-descriptions-item label="干扰源类型">
{{ details.loadType }} {{ details.loadType }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="监测点对象名称"> <el-descriptions-item label="监测点对象名称">
{{ details.objName }} {{ details.objName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="接线方式"> <el-descriptions-item label="接线方式">
{{ details.ptType }} {{ details.ptType }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="PT变比"> <el-descriptions-item label="PT变比">
{{ details.pt }} {{ details.pt }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="CT变比"> <el-descriptions-item label="CT变比">
{{ details.ct }} {{ details.ct }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="基准容量(MVA)"> <el-descriptions-item label="基准容量(MVA)">
{{ details.standardCapacity }} {{ details.standardCapacity }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="最小短路容量(MVA)"> <el-descriptions-item label="供电设备容量(MVA)">
{{ details.shortCapacity }}
</el-descriptions-item>
<el-descriptions-item label="供电设备容量(MVA)">
{{ details.devCapacity }} {{ details.devCapacity }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="用户协议容量(MVA)"> <el-descriptions-item label="最小短路容量(MW)">
{{ details.shortCapacity }}
</el-descriptions-item>
<el-descriptions-item label="用户协议容量(MW)">
{{ details.dealCapacity }} {{ details.dealCapacity }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="监测点限值" name="tab2"> <el-tab-pane label="监测点限值" name="tab2">
<el-descriptions :column="3" border label-width="250px"> <el-descriptions :column="3" border label-width="250px">
<el-descriptions-item label="电压偏差上限值(%)"> <el-descriptions-item label="电压偏差上限值(%)">
{{ limitValue.voltageDev }} {{ limitValue.voltageDev }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="电压偏差下限值(%)"> <el-descriptions-item label="电压偏差下限值(%)">
{{ limitValue.uvoltageDev }} {{ limitValue.uvoltageDev }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="频率偏差限值(Hz)"> <el-descriptions-item label="频率偏差限值(Hz)">
{{ limitValue.freqDev }} {{ limitValue.freqDev }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="三相电压不平衡度限值(%)"> <el-descriptions-item label="三相电压不平衡度限值(%)">
{{ limitValue.ubalance }} {{ limitValue.ubalance }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="负序电流限值(A)"> <el-descriptions-item label="负序电流限值(A)">
{{ limitValue.ineg }} {{ limitValue.ineg }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="长时闪变限值(%)"> <el-descriptions-item label="长时闪变限值(%)">
{{ limitValue.flicker }} {{ limitValue.flicker }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="电压总畸变率限值(%)"> <el-descriptions-item label="电压总畸变率限值(%)">
{{ limitValue.uaberrance }} {{ limitValue.uaberrance }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="奇次谐波电压含有率限值(%)"> <el-descriptions-item label="奇次谐波电压含有率限值(%)">
{{ limitValue.oddHarm }} {{ limitValue.oddHarm }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="偶次谐波电压含有率限值(%)"> <el-descriptions-item label="偶次谐波电压含有率限值(%)">
{{ limitValue.evenHarm }} {{ limitValue.evenHarm }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="3次谐波电流幅值限值(A)"> <el-descriptions-item label="3次谐波电流幅值限值(A)">
{{ limitValue.iharm3 }} {{ limitValue.iharm3 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="4次谐波电流幅值限值(A)"> <el-descriptions-item label="4次谐波电流幅值限值(A)">
{{ limitValue.iharm4 }} {{ limitValue.iharm4 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="5次谐波电流幅值限值(A)"> <el-descriptions-item label="5次谐波电流幅值限值(A)">
{{ limitValue.iharm5 }} {{ limitValue.iharm5 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="6次谐波电流幅值限值(A)"> <el-descriptions-item label="6次谐波电流幅值限值(A)">
{{ limitValue.iharm6 }} {{ limitValue.iharm6 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="7次谐波电流幅值限值(A)"> <el-descriptions-item label="7次谐波电流幅值限值(A)">
{{ limitValue.iharm7 }} {{ limitValue.iharm7 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="8次谐波电流幅值限值(A)"> <el-descriptions-item label="8次谐波电流幅值限值(A)">
{{ limitValue.iharm8 }} {{ limitValue.iharm8 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="9次谐波电流幅值限值(A)"> <el-descriptions-item label="9次谐波电流幅值限值(A)">
{{ limitValue.iharm9 }} {{ limitValue.iharm9 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="10次谐波电流幅值限值(A)"> <el-descriptions-item label="10次谐波电流幅值限值(A)">
{{ limitValue.iharm10 }} {{ limitValue.iharm10 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="11次谐波电流幅值限值(A)"> <el-descriptions-item label="11次谐波电流幅值限值(A)">
{{ limitValue.iharm11 }} {{ limitValue.iharm11 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="12次谐波电流幅值限值(A)"> <el-descriptions-item label="12次谐波电流幅值限值(A)">
{{ limitValue.iharm12 }} {{ limitValue.iharm12 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="13次谐波电流幅值限值(A)"> <el-descriptions-item label="13次谐波电流幅值限值(A)">
{{ limitValue.iharm13 }} {{ limitValue.iharm13 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="14次谐波电流幅值限值(A)"> <el-descriptions-item label="14次谐波电流幅值限值(A)">
{{ limitValue.iharm14 }} {{ limitValue.iharm14 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="15次谐波电流幅值限值(A)"> <el-descriptions-item label="15次谐波电流幅值限值(A)">
{{ limitValue.iharm15 }} {{ limitValue.iharm15 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="16次谐波电流幅值限值(A)"> <el-descriptions-item label="16次谐波电流幅值限值(A)">
{{ limitValue.iharm16 }} {{ limitValue.iharm16 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="17次谐波电流幅值限值(A)"> <el-descriptions-item label="17次谐波电流幅值限值(A)">
{{ limitValue.iharm17 }} {{ limitValue.iharm17 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="18次谐波电流幅值限值(A)"> <el-descriptions-item label="18次谐波电流幅值限值(A)">
{{ limitValue.iharm18 }} {{ limitValue.iharm18 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="19次谐波电流幅值限值(A)"> <el-descriptions-item label="19次谐波电流幅值限值(A)">
{{ limitValue.iharm19 }} {{ limitValue.iharm19 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="20次谐波电流幅值限值(A)"> <el-descriptions-item label="20次谐波电流幅值限值(A)">
{{ limitValue.iharm20 }} {{ limitValue.iharm20 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="21次谐波电流幅值限值(A)"> <el-descriptions-item label="21次谐波电流幅值限值(A)">
{{ limitValue.iharm21 }} {{ limitValue.iharm21 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="22次谐波电流幅值限值(A)"> <el-descriptions-item label="22次谐波电流幅值限值(A)">
{{ limitValue.iharm22 }} {{ limitValue.iharm22 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="23次谐波电流幅值限值(A)"> <el-descriptions-item label="23次谐波电流幅值限值(A)">
{{ limitValue.iharm23 }} {{ limitValue.iharm23 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="24次谐波电流幅值限值(A)"> <el-descriptions-item label="24次谐波电流幅值限值(A)">
{{ limitValue.iharm24 }} {{ limitValue.iharm24 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="25次谐波电流幅值限值(A)"> <el-descriptions-item label="25次谐波电流幅值限值(A)">
{{ limitValue.iharm25 }} {{ limitValue.iharm25 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="0.5-1.5次间谐波电压含有率限值(%)"> <el-descriptions-item label="0.5-1.5次间谐波电压含有率限值(%)">
{{ limitValue.inUharm }} {{ limitValue.inUharm }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="2.5-15.5次谐波电压含有率限值(%)"> <el-descriptions-item label="2.5-15.5次谐波电压含有率限值(%)">
{{ limitValue.inUharm16 }} {{ limitValue.inUharm16 }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
@@ -187,7 +187,7 @@ const open = (data: any) => {
getLineDetailData(data.id).then(res => { getLineDetailData(data.id).then(res => {
details.value = res.data details.value = res.data
res.data.objName ? (title.value = res.data.objName + '_' + title.value) : ''
}) })
getLineOverLimitData(data.id).then(res => { getLineOverLimitData(data.id).then(res => {
limitValue.value = res.data limitValue.value = res.data

View File

@@ -1,4 +1,5 @@
import { downloadFile } from '@/api/system-boot/file' import { downloadFile } from '@/api/system-boot/file'
import { ElMessage } from 'element-plus'
const sanitizeUrl = (url: string): string => { const sanitizeUrl = (url: string): string => {
return url.replace(/\[/g, '(').replace(/\]/g, ')') return url.replace(/\[/g, '(').replace(/\]/g, ')')
@@ -7,7 +8,7 @@ const sanitizeUrl = (url: string): string => {
// 下载文件 // 下载文件
export const download = (urls: string) => { export const download = (urls: string) => {
//console.log('下载', urls) //console.log('下载', urls)
ElMessage.info('下载中...')
downloadFile({ filePath: urls }) downloadFile({ filePath: urls })
.then((res: any) => { .then((res: any) => {
// 1. 确定文件MIME类型优化用更简洁的方式 // 1. 确定文件MIME类型优化用更简洁的方式
@@ -26,9 +27,9 @@ export const download = (urls: string) => {
} }
return mimeMap[ext] || '' return mimeMap[ext] || ''
} }
const blob = new Blob([res], { type: getFileType(urls) }) const blob = new Blob([res], { type: getFileType(urls) })
// 2. 提取文件名并保留原生后缀(核心修复点) // 2. 提取文件名并保留原生后缀(核心修复点)
const fileName = urls.split('/').at(-1) || '下载文件' // 先提取URL最后一段文件名 const fileName = urls.split('/').at(-1) || '下载文件' // 先提取URL最后一段文件名
@@ -43,8 +44,12 @@ export const download = (urls: string) => {
// 4. 清理资源(优化) // 4. 清理资源(优化)
link.remove() link.remove()
window.URL.revokeObjectURL(url) // 释放blob URL window.URL.revokeObjectURL(url) // 释放blob URL
setTimeout(() => {
ElMessage.success('下载成功')
}, 1000)
}) })
.catch(err => { .catch(err => {
ElMessage.warning('下载失败:', err)
console.error('下载失败:', err) console.error('下载失败:', err)
// 可添加错误提示如Toast // 可添加错误提示如Toast
}) })
@@ -61,7 +66,7 @@ export const previewFile = async (urls: any) => {
//console.log('预览', urls) //console.log('预览', urls)
let url = '' let url = ''
await downloadFile({ filePath: decodeURI(urls) }) await downloadFile({ filePath: decodeURI(urls) })
.then((res: any) => { .then((res: any) => {
// 1. 确定文件MIME类型优化用更简洁的方式 // 1. 确定文件MIME类型优化用更简洁的方式
const getFileType = (url: string) => { const getFileType = (url: string) => {
const ext = url.split('.').pop()?.toLowerCase() || '' const ext = url.split('.').pop()?.toLowerCase() || ''
@@ -77,7 +82,7 @@ export const previewFile = async (urls: any) => {
jpg: 'image/jpg' jpg: 'image/jpg'
} }
return mimeMap[ext] || '' return mimeMap[ext] || ''
} }
const blob = new Blob([res], { type: getFileType(decodeURI(urls)) }) const blob = new Blob([res], { type: getFileType(decodeURI(urls)) })
// 3. 创建下载链接 // 3. 创建下载链接
url = window.URL.createObjectURL(blob) url = window.URL.createObjectURL(blob)

View File

@@ -1,4 +1,6 @@
const MAX_CACHE_SIZE = 30 import { getWaveRawList } from './waveRawStore'
const MAX_CACHE_SIZE = 6
const cache = new Map<string, unknown>() const cache = new Map<string, unknown>()
@@ -17,8 +19,10 @@ export function buildWaveCacheKey(
boxoList: Record<string, any> boxoList: Record<string, any>
): string { ): string {
if (!wp) return '' if (!wp) return ''
const waveFp = const rawList = wp.waveStoreKey
type === 'shu' ? dataFingerprint(wp.listWaveData) : dataFingerprint(wp.listRmsData) ? 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 ?? '' const boxoFp = boxoList?.startTime ?? boxoList?.lineName ?? boxoList?.equipmentName ?? ''
return `${type}|${wp.time}|${wp.waveType}|${wp.iphasic}|${value}|${isOpen}|${waveFp}|${boxoFp}` 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) if (oldest !== undefined) cache.delete(oldest)
} }
} }
export function clearWaveCache(): void {
cache.clear()
}

View File

@@ -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 }
}

75
src/utils/waveRawStore.ts Normal file
View File

@@ -0,0 +1,75 @@
/** 原始波形数据存放于 Vue 响应式体系之外,避免大数组触发深度代理与重复持有 */
interface WaveRawEntry {
listWaveData?: unknown[][]
listRmsData?: unknown[][]
waveTimeRange?: { min: number; max: number }
}
const rawStore = new Map<string, WaveRawEntry>()
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, any>): string {
const id = boxoList?.eventId ?? boxoList?.id ?? ''
return `${id}|${boxoList?.startTime ?? ''}`
}
export function registerWaveRaw(storeKey: string, wp: Record<string, any>): 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<string, any>, storeKey: string): Record<string, any> {
const { listWaveData: _w, listRmsData: _r, ...rest } = wp
return {
...rest,
waveStoreKey: storeKey,
waveTimeRange: getWaveTimeRange(storeKey) ?? calcTimeRange(_w) ?? calcTimeRange(_r)
}
}

View File

@@ -1,4 +1,5 @@
import { toRaw } from 'vue' import { toRaw } from 'vue'
import { getWaveRawList } from './waveRawStore'
type WorkerMessageHandler = (data: any) => void type WorkerMessageHandler = (data: any) => void
@@ -53,10 +54,11 @@ export function buildWorkerPayload(
waveType: plainWp.waveType, waveType: plainWp.waveType,
yzd: plainWp.yzd yzd: plainWp.yzd
} }
const storeKey = plainWp.waveStoreKey as string | undefined
if (type === 'shu') { if (type === 'shu') {
wpPayload.listWaveData = plainWp.listWaveData wpPayload.listWaveData = getWaveRawList('shu', storeKey) ?? plainWp.listWaveData
} else { } else {
wpPayload.listRmsData = plainWp.listRmsData wpPayload.listRmsData = getWaveRawList('rms', storeKey) ?? plainWp.listRmsData
} }
const plainBoxo: Record<string, unknown> = {} const plainBoxo: Record<string, unknown> = {}

View File

@@ -212,12 +212,21 @@
})?.name })?.name
}} }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="敏感电能质量指标" v-if="detailData.userType == 6"> <el-descriptions-item :span="2" label="电能质量设备检测报告">
{{ <el-icon class="elView" v-if="detailData?.checkUrl">
energyQualityIndexList.find(item => { <View @click="openFile(detailData?.checkUrl)" />
return item.id == proviteData.energyQualityIndex </el-icon>
})?.name <span class="aLoad" @click="download(detailData.checkUrl)" target="_blank">
}} {{ detailData.checkUrl?.replace(/^.*?\/(.*?)\//, '') || '' }}
</span>
</el-descriptions-item>
<el-descriptions-item :span="2" label="电能质量评估报告">
<el-icon class="elView" v-if="detailData?.assessUrl">
<View @click="openFile(detailData?.assessUrl)" />
</el-icon>
<span class="aLoad" @click="download(detailData.assessUrl)" target="_blank">
{{ detailData.assessUrl?.replace(/^.*?\/(.*?)\//, '') || '' }}
</span>
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
</div> </div>
@@ -233,7 +242,7 @@ import { getDictTreeById } from '@/api/system-boot/dictTree'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { getFileNameAndFilePath } from '@/api/system-boot/file' import { getFileNameAndFilePath } from '@/api/system-boot/file'
import { Link, View } from '@element-plus/icons-vue' 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' // import { addOrUpdateFile, getFileById } from '@/api/supervision-boot/interfere/index'
defineOptions({ name: 'BpmUserReportDetail' }) defineOptions({ name: 'BpmUserReportDetail' })

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="default-main"> <div class="default-main">
<TableHeader ref="TableHeaderRef"> <TableHeader ref="TableHeaderRef" showExport>
<template #select> <template #select>
<el-form-item label="项目名称"> <el-form-item label="项目名称">
<el-input <el-input
@@ -32,7 +32,7 @@
<el-button icon="el-icon-Upload" type="primary" @click="importUserData">批量导入</el-button> --> <el-button icon="el-icon-Upload" type="primary" @click="importUserData">批量导入</el-button> -->
</template> </template>
</TableHeader> </TableHeader>
<Table ref="tableRef" /> <Table ref="tableRef" @cell-click="cellClickEvent" />
<el-dialog title="详情" width="1000px" v-model="dialogShow" v-if="dialogShow"> <el-dialog title="详情" width="1000px" v-model="dialogShow" v-if="dialogShow">
<DetailInfo :id="userId" :openType="'sourcesOfInterference'"></DetailInfo> <DetailInfo :id="userId" :openType="'sourcesOfInterference'"></DetailInfo>
@@ -58,6 +58,7 @@ import { downloadSensitiveReportTemplate } from '@/api/supervision-boot/userRepo
import DetailInfo from './components/detail.vue' import DetailInfo from './components/detail.vue'
import { cancelFormData, getUserReportById } from '@/api/supervision-boot/interfere/index' import { cancelFormData, getUserReportById } from '@/api/supervision-boot/interfere/index'
import { deleteUserReport } from '@/api/device-boot/sensitiveLoadMange' import { deleteUserReport } from '@/api/device-boot/sensitiveLoadMange'
import { download } from '@/utils/fileDownLoad'
const addForms = ref() const addForms = ref()
const dictData = useDictData() const dictData = useDictData()
const sensitiveUserPopup = ref() const sensitiveUserPopup = ref()
@@ -89,7 +90,7 @@ const tableStore = new TableStore({
} }
}, },
{ field: 'city', title: '所在地市', minWidth: 80 }, { field: 'city', title: '所在地市', minWidth: 80 },
/* { field: 'substation', title: '厂站名称', minWidth: 100 },*/ /* { field: 'substation', title: '厂站名称', minWidth: 100 },*/
{ field: 'projectName', title: '项目名称', minWidth: 150 }, { field: 'projectName', title: '项目名称', minWidth: 150 },
{ {
field: 'userType', field: 'userType',
@@ -104,10 +105,28 @@ const tableStore = new TableStore({
// { field: 'responsibleDepartment', title: '归口管理部门', minWidth: 130 }, // { field: 'responsibleDepartment', title: '归口管理部门', minWidth: 130 },
{ field: 'ratePower', title: '装机容量(MW)', minWidth: 80 }, { field: 'ratePower', title: '装机容量(MW)', minWidth: 80 },
{ field: 'stationId', title: '所属电站', minWidth: 100 }, { field: 'stationId', title: '所属电站', minWidth: 100 },
{ field: 'checkUrl', title: '电能质量设备检测报告', minWidth: 170 }, {
{ field: 'assessUrl', title: '电能质量评估报告', minWidth: 170 }, field: 'checkUrl',
title: '电能质量设备检测报告',
minWidth: 170,
render: 'customRender',
customRender: props => {
const val = props.renderValue?.replace(/^.*?\/(.*?)\//, '') || ''
return val ? h('span', { style: { color: 'var(--el-color-primary)', cursor: 'pointer' } }, val) : '/'
}
},
{
field: 'assessUrl',
title: '电能质量评估报告',
minWidth: 170,
render: 'customRender',
customRender: props => {
const val = props.renderValue?.replace(/^.*?\/(.*?)\//, '') || ''
return val ? h('span', { style: { color: 'var(--el-color-primary)', cursor: 'pointer' } }, val) : '/'
}
},
/* { /* {
field: 'createBy', field: 'createBy',
title: '创建人', title: '创建人',
minWidth: 80, minWidth: 80,
@@ -116,9 +135,10 @@ const tableStore = new TableStore({
} }
},*/ },*/
{ {
title: '操作',fixed: 'right', title: '操作',
fixed: 'right',
minWidth: 150, minWidth: 150,
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
{ {
@@ -141,7 +161,6 @@ const tableStore = new TableStore({
disabled: row => { disabled: row => {
return !(row.status == 0) return !(row.status == 0)
}, },
click: row => { click: row => {
addForms.value.filterUsers([6]) addForms.value.filterUsers([6])
@@ -151,9 +170,7 @@ const tableStore = new TableStore({
row: row row: row
}) })
} }
}, }
] ]
} }
], ],
@@ -212,27 +229,13 @@ const deleteEven = () => {
}) })
} }
} }
/**取消流程操作*/ // 点击行
const cancelLeave = async (row: any) => { const cellClickEvent = ({ row, column }: any) => {
// 二次确认 if (column.field == 'checkUrl') {
const { value } = await ElMessageBox.prompt('请输入取消原因', '取消流程', { row.checkUrl ? download(row.checkUrl) : ''
confirmButtonText: '确定', } else if (column.field == 'assessUrl') {
cancelButtonText: '取消', row.assessUrl ? download(row.assessUrl) : ''
inputType: 'textarea',
inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
inputErrorMessage: '取消原因不能为空'
})
// 发起取消
let data = {
id: row.id,
processInstanceId: row.processInstanceId,
dataType: 1,
reason: value
} }
await cancelFormData(data)
ElMessage.success('取消成功')
// 加载数据
tableStore.index()
} }
// 新增 // 新增