Files
admin-govern/src/components/echarts/shushiboxi.vue
2025-11-27 15:08:04 +08:00

863 lines
27 KiB
Vue

<template>
<div v-loading="loading" class="boxbx"
style="position: relative;height: 100%;">
<div id="boxsj" >
<div id="shushi" :style="`height:${vh};overflow: hidden;`">
<div class="bx" id="wave"></div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import html2canvas from 'html2canvas'
import $ from 'jquery'
import * as echarts from 'echarts'
import url from '@/assets/img/point.png'
// 创建Worker
let waveDataWorker: Worker | null = null;
interface WaveData {
instantF: { max: number; min: number }
instantS: { max: number; min: number }
shunshiF: {
shunshiFA: number[][]
shunshiFB: number[][]
shunshiFC: number[][]
}
shunshiS: {
shunshiSA: number[][]
shunshiSB: number[][]
shunshiSC: number[][]
}
title: {
aTitle: string
bTitle: string
cTitle: string
unit: string
}
unit: string
}
interface Props {
value?: number
flag?: string | number | boolean
parentHeight?: string | number | boolean
DColor?: boolean
boxoList?: any
wp?: any
}
const props = withDefaults(defineProps<Props>(), {
value: 2,
flag: 3,
parentHeight: 0,
DColor: false,
boxoList: () => ({}),
wp: () => ({})
})
const loading = ref(true)
const tabvalue = ref(props.value)
const valA: any = ref(0)
const valB: any = ref(0)
const isOpen = ref(false)
const time = ref('')
const type = ref('')
const severity: any = ref('')
const iphasic: any = ref('')
const eventValue = ref('')
const persistTime = ref('')
const lineName = ref('')
const subName = ref('')
const waveDatas = ref<WaveData[]>([])
const ptpass = ref('')
const waveHeight = ref<number>()
const rmsHeight = ref<number>()
const color = ref('#006565')
const charts = ref({})
const arrpoints = ref([])
const titles = ref('')
const zoom = ref(1)
const myChartess = ref<echarts.ECharts | null>(null)
const myChartess1 = ref<echarts.ECharts | null>(null)
const myChartess2 = ref<echarts.ECharts | null>(null)
const myChartess3 = ref<echarts.ECharts | null>(null)
const myChartess4 = ref<echarts.ECharts | null>(null)
const myChartess5 = ref<echarts.ECharts | null>(null)
const vh = computed(() => {
if (props.flag == 1) {
return '690px'
} else if (props.parentHeight != 0) {
return '310px'
} else {
return '350px'
}
})
const vw = computed(() => '100%')
watch(() => props.value, (newVal) => {
if (newVal == 2) {
initWaves()
} else {
$('#wave1').remove()
initWaves()
}
})
onMounted(() => {
const zoomValue = document.body.style.getPropertyValue('zoom');
zoom.value = 1 / (zoomValue ? parseFloat(zoomValue) : 1);
window.addEventListener('resize', handleResize)
nextTick(() => {
setTimeout(() => {
query()
}, 500)
})
})
onBeforeUnmount(() => {
console.log('组件卸载');
if (waveDataWorker) {
waveDataWorker.terminate();
waveDataWorker = null;
}
backbxlb();
window.removeEventListener('resize', handleResize);
})
const handleResize = () => {
const zoomValue = document.body.style.getPropertyValue('zoom');
zoom.value = 1 / (zoomValue ? parseFloat(zoomValue) : 1);
}
const download = () => {
const boxsj = document.getElementById('boxsj')
if (boxsj) {
html2canvas(boxsj, {
scale: 2,
}).then(function (canvas) {
const creatIMg = document.createElement('a')
creatIMg.download = '瞬间波形.png'
creatIMg.href = canvas.toDataURL()
creatIMg.click()
creatIMg.remove()
})
}
}
const query = () => {
loading.value = true
initWaves()
}
const waveData = (instantF: any, instantS: any, shunshiF: any, shunshiS: any, title: any, unit: any): WaveData => {
return {
instantF,
instantS,
shunshiF,
shunshiS,
title,
unit
}
}
// 在组件中修改initWaves函数
const initWaves = () => {
if (props.wp) {
loading.value = true;
iphasic.value = props.wp.iphasic || 1
// 使用Web Worker处理数据
if (!waveDataWorker) {
waveDataWorker = new Worker(new URL('./shuWorker.js', import.meta.url));
waveDataWorker.onmessage = function (e) {
const data = e.data;
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);
loading.value = false;
};
waveDataWorker.onerror = function (error) {
console.error('Worker error:', error);
loading.value = false;
// 备用方案:在主线程处理数据
// processDataInMainThread();
};
}
// 发送数据到Worker
waveDataWorker.postMessage(JSON.stringify({
wp: props.wp,
value: props.value,
iphasic: iphasic.value,
isOpen: isOpen.value,
boxoList: props.boxoList
}));
} else {
initWave(null, null, null, null, null);
}
}
const initWave = (waveDatas: WaveData[] | null, time: string | null, type: string | null, severity: string | null, isOpen: boolean | null) => {
$('div.bx1').remove()
let picHeight = vh.value
const show = !isOpen
let isvisible = false
let cu: number[][] = []
let titleText = ''
let unit = ''
let max: any = 0, min: any = 0
let a: string | null = null, b: string | null = null, c: string | null = null
let adata: number[][] = [], bdata: number[][] = [], cdata: number[][] = []
const colors: string[] = []
if (!waveDatas) {
titleText = '该事件暂无波形图'
} else if (waveDatas.length > 0) {
titleText = titles.value
if (Number(eventValue.value) <= 90) {
isvisible = true
}
switch (iphasic.value) {
case 1:
a = waveDatas[0].title.aTitle
if (props.value === 1) {
cu = [[0, waveDatas[0].instantF.min]]
max = waveDatas[0].instantF.max
min = waveDatas[0].instantF.min
adata = waveDatas[0].shunshiF.shunshiFA
} else {
cu = [[0, waveDatas[0].instantS.min]]
max = waveDatas[0].instantS.max
min = waveDatas[0].instantS.min
adata = waveDatas[0].shunshiS.shunshiSA
}
colors.push('#DAA520', '#000', '#000')
break
case 2:
a = waveDatas[0].title.aTitle
b = waveDatas[0].title.bTitle
if (props.value === 1) {
cu = [[0, waveDatas[0].instantF.min]]
max = waveDatas[0].instantF.max
min = waveDatas[0].instantF.min
adata = waveDatas[0].shunshiF.shunshiFA
bdata = waveDatas[0].shunshiF.shunshiFB
} else {
cu = [[0, waveDatas[0].instantS.min]]
max = waveDatas[0].instantS.max
min = waveDatas[0].instantS.min
adata = waveDatas[0].shunshiS.shunshiSA
bdata = waveDatas[0].shunshiS.shunshiSB
}
colors.push('#DAA520', '#2E8B57', '#000')
break
case 3:
a = waveDatas[0].title.aTitle
b = waveDatas[0].title.bTitle
c = waveDatas[0].title.cTitle
if (props.value === 1) {
cu = [[0, waveDatas[0].instantF.min]]
max = waveDatas[0].instantF.max
min = waveDatas[0].instantF.min
adata = waveDatas[0].shunshiF.shunshiFA
bdata = waveDatas[0].shunshiF.shunshiFB
cdata = waveDatas[0].shunshiF.shunshiFC
} else {
cu = [[0, waveDatas[0].instantS.min]]
max = waveDatas[0].instantS.max
min = waveDatas[0].instantS.min
adata = waveDatas[0].shunshiS.shunshiSA
bdata = waveDatas[0].shunshiS.shunshiSB
cdata = waveDatas[0].shunshiS.shunshiSC
}
colors.push('#DAA520', '#2E8B57', '#A52a2a')
break
}
if (waveDatas[0].title.unit === '电压') {
unit = props.value === 1 ? 'kV' : 'V'
} else {
unit = 'A'
}
for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) {
const waveId = 'wave' + step
const newDivShunshi = $(`<div style="height:${vh.value};overflow: hidden;">
<div class='bx1' id='${waveId}'></div>
</div>`)
newDivShunshi.insertAfter($('#shushi'))
$(`#${waveId}`).css('height', picHeight).css('width', vw.value)
}
} else {
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 残余电压:${(Number(eventValue.value) * 1).toFixed(0)}% 持续时间:${persistTime.value}s`
}
const wave = document.getElementById('wave')
if (!wave) return
const myChartes = echarts.init(wave)
const echartsColor = { WordColor: "#000", thread: "#000", FigureColor: ["#07CCCA ", "#00BFF5", "#FFBF00", "#77DA63", "#D5FF6B", "#Ff6600", "#FF9100", "#5B6E96", "#66FFCC", "#B3B3B3", "#FF00FF", "#CC00FF", "#FF9999"] }
setTimeout(() => {
wave.style.width = '100%'
wave.style.height = vh.value
}, 0)
const option = {
tooltip: {
top: '10px',
trigger: 'axis',
borderColor: 'grey',
style: {
color: '#fff',
fontSize: '15px',
padding: 10
},
formatter: function (params: any) {
let tips = '时刻:' + params[0].data[0] + '</br/>'
for (let i = 0; i < params.length; i++) {
if (params[i].seriesName != '暂降触发点') {
tips += params[i].marker + params[i].seriesName + ':' + (params[i].value[1] - 0).toFixed(2) + '<br/>'
}
}
return tips
},
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0
},
title: {
left: 'center',
text: titleText,
textStyle: {
fontSize: '16px',
color: props.DColor ? '#000' : echartsColor.WordColor
}
},
legend: {
right: 50,
top: 25,
verticalAlign: 'top',
enabled: true,
itemDistance: 5,
textStyle: {
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor,
rich: { a: { verticalAlign: 'middle' } },
padding: [0, 0, 0, 0]
}
},
toolbox: {
right: 20,
top: 15,
feature: {
myCustomDownload: {
title: '',
icon: 'path://M892.342857 463.238095l-73.142857-68.266666-258.438095 258.438095V29.257143h-97.52381v624.152381L204.8 394.971429 131.657143 463.238095l380.342857 380.342857zM107.27619 897.219048h804.571429v97.523809H107.27619z',
onclick: () => download()
}
}
},
xAxis: {
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,
title: {
text: 'ms',
textStyle: {
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor
},
enabled: true,
align: 'high'
},
splitLine: { show: false },
nameTextStyle: { fontSize: '14px' },
axisTick: { alignWithLabel: true },
axisLine: {
lineStyle: {
color: props.DColor ? '#000' : echartsColor.thread
},
onZero: false
},
axisLabel: {
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) {
if (valA.value != (value - 0).toFixed(0)) {
valA.value = Number((value - 0).toFixed(0))
return (value - 0).toFixed(0)
}
return ''
}
}
},
yAxis: {
type: 'value',
name: unit,
title: {
align: 'high',
offset: 0,
text: unit,
rotation: 0,
y: -10
},
boundaryGap: [0, '100%'],
showLastLabel: true,
max: max.toFixed(2) * 1.1,
min: min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1,
opposite: false,
nameTextStyle: {
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor
},
axisLine: {
show: true,
lineStyle: {
color: props.DColor ? '#000' : echartsColor.thread
},
onZero: false
},
axisLabel: {
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) {
return (value - 0).toFixed(2)
}
},
splitLine: {
lineStyle: {
color: [props.DColor ? '#000' : echartsColor.thread],
type: 'dashed',
opacity: 0.5
}
}
},
grid: {
left: '1%',
right: '2.8%',
bottom: '40px',
top: '70px',
containLabel: true
},
dataZoom: [
{
type: 'inside',
height: 13,
start: 0,
bottom: '20px',
end: 100
},
{
start: 0,
height: 13,
bottom: '20px',
end: 100
}
],
series: [
{
name: a,
type: 'line',
large: true,
smooth: true,
symbol: 'none',
sampling: 'average',
itemStyle: { color: '#DAA520' },
data: adata
},
{
name: b,
type: 'line',
large: true,
smooth: true,
symbol: 'none',
sampling: 'average',
itemStyle: { color: '#2E8B57' },
data: bdata
},
{
name: c,
type: 'line',
large: true,
smooth: true,
symbol: 'none',
sampling: 'average',
itemStyle: { color: '#A52a2a' },
data: cdata
},
{
name: '暂降触发点',
type: 'scatter',
symbol: 'image://' + url,
itemStyle: { width: 18, height: 18 },
data: cu
}
]
}
myChartes.setOption(option)
myChartess.value = myChartes
setTimeout(() => {
myChartes.resize()
loading.value = false
}, 400)
if (waveDatas && waveDatas.length > 1) {
const waveDatasTemp = waveDatas.slice(1)
for (let step = 0; step < waveDatasTemp.length; step++) {
drawPics(waveDatasTemp[step], picHeight, step, show, myChartes, titleText)
}
}
}
const drawPics = (waveDataTemp: WaveData, picHeight: string, step: number, show: boolean, myChartes1: echarts.ECharts, title: string) => {
step = step + 1
const waveId = 'wave' + step
let a: string | null = null, b: string | null = null, c: string | null = null
let max: any = 0, min: any = 0, unit = ''
let adata: number[][] = [], bdata: number[][] = [], cdata: number[][] = []
const colors: string[] = []
switch (iphasic.value) {
case 1:
a = waveDataTemp.title.aTitle
colors.push('#DAA520', '#000', '#000')
break
case 2:
a = waveDataTemp.title.aTitle
b = waveDataTemp.title.bTitle
colors.push('#DAA520', '#2E8B57', '#000')
break
case 3:
a = waveDataTemp.title.aTitle
b = waveDataTemp.title.bTitle
c = waveDataTemp.title.cTitle
colors.push('#DAA520', '#2E8B57', '#A52a2a')
break
}
if (props.value === 1) {
max = waveDataTemp.instantF.max
min = waveDataTemp.instantF.min
adata = waveDataTemp.shunshiF.shunshiFA
bdata = waveDataTemp.shunshiF.shunshiFB
cdata = waveDataTemp.shunshiF.shunshiFC
} else {
max = waveDataTemp.instantS.max
min = waveDataTemp.instantS.min
adata = waveDataTemp.shunshiS.shunshiSA
bdata = waveDataTemp.shunshiS.shunshiSB
cdata = waveDataTemp.shunshiS.shunshiSC
}
if (waveDataTemp.title.unit === '电压') {
unit = props.value === 1 ? 'kV' : 'V'
} else {
unit = 'A'
}
let titlename = ''
if (props.boxoList.systemType == 'ZL') {
const str = waveId.split('e')
const str1 = Number(str[1])
props.wp.channelNames.forEach((element: string, i: number) => {
if (i == 4 || i == 7 || i == 10) {
if (str1 == 1 && i == 4) {
const s = element.split('A')
const s1 = s[0] == 'LI' ? '电网侧-电流' : s[0] + '侧' + s[1]
titlename = s1 + ' ' + title
}
if (str1 == 2 && i == 7) {
const s = element.split('A')
const s1 = s[0] == 'SU' ? '负载侧-电压' : s[0] + '侧' + s[1]
titlename = s1 + ' ' + title
}
if (str1 == 3 && i == 10) {
const s = element.split('A')
const s1 = s[0] == 'SI' ? '负载侧-电流' : s[0] + '侧' + s[1]
titlename = s1 + ' ' + title
}
}
})
}
const waveIds = document.getElementById(waveId)
if (!waveIds) return
const myChartes = echarts.init(waveIds)
const echartsColor = { WordColor: "#000", thread: "#000", FigureColor: ["#07CCCA ", "#00BFF5", "#FFBF00", "#77DA63", "#D5FF6B", "#Ff6600", "#FF9100", "#5B6E96", "#66FFCC", "#B3B3B3", "#FF00FF", "#CC00FF", "#FF9999"] }
const option = {
tooltip: {
trigger: 'axis',
borderColor: 'grey',
formatter: function (params: any) {
let tips = '时刻:' + params[0].data[0] + '</br/>'
for (let i = 0; i < params.length; i++) {
if (params[i].seriesName != '暂降触发点') {
tips += params[i].seriesName + ':' + (params[i].value[1] - 0).toFixed(2) + '<br/>'
}
}
return tips
},
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0
},
title: {
left: 'center',
text: '',//titlename || title,
textStyle: {
fontSize: '16px',
color: props.DColor ? '#000' : echartsColor.WordColor
}
},
legend: {
right: 50,
top: 25,
verticalAlign: 'top',
enabled: true,
itemDistance: 5,
textStyle: {
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor,
rich: { a: { verticalAlign: 'middle' } },
padding: [0, 0, 0, 0]
}
},
xAxis: {
type: 'value',
name: '时间\n(ms)',
boundaryGap: false,
min: props.wp.listWaveData[0][0],
max: props.wp.listWaveData[props.wp.listWaveData.length - 1][0] + 1,
title: {
text: 'ms',
textStyle: {
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor
},
enabled: true,
align: 'high'
},
splitLine: { show: false },
axisTick: { alignWithLabel: true },
axisLine: {
lineStyle: {
color: props.DColor ? '#000' : echartsColor.thread
},
onZero: false
},
nameTextStyle: { fontSize: '14px' },
axisLabel: {
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) {
if (valB.value != (value - 0).toFixed(0)) {
valB.value = Number((value - 0).toFixed(0))
return (value - 0).toFixed(0)
}
return ''
}
}
},
yAxis: {
type: 'value',
name: unit,
title: {
align: 'high',
offset: 0,
text: unit,
rotation: 0,
y: -10
},
boundaryGap: [0, '100%'],
showLastLabel: true,
max: max.toFixed(2) * 1.1,
min: min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1,
opposite: false,
nameTextStyle: {
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor
},
axisLine: {
show: true,
lineStyle: {
color: props.DColor ? '#000' : echartsColor.thread
},
onZero: false
},
axisLabel: {
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) {
return (value - 0).toFixed(2)
}
},
splitLine: {
lineStyle: {
color: [props.DColor ? '#000' : echartsColor.thread],
type: 'dashed',
opacity: 0.5
}
}
},
grid: {
left: '1%',
right: '2.8%',
bottom: '40px',
top: '70px',
containLabel: true
},
dataZoom: [
{
type: 'inside',
height: 13,
start: 0,
bottom: '20px',
end: 100
},
{
start: 0,
height: 13,
bottom: '20px',
end: 100
}
],
series: [
{
name: a,
type: 'line',
large: true,
smooth: true,
symbol: 'none',
sampling: 'average',
itemStyle: { color: '#DAA520' },
data: adata
},
{
name: b,
type: 'line',
large: true,
smooth: true,
symbol: 'none',
sampling: 'average',
itemStyle: { color: '#2E8B57' },
data: bdata
},
{
name: c,
type: 'line',
large: true,
smooth: true,
symbol: 'none',
sampling: 'average',
itemStyle: { color: '#A52a2a' },
data: cdata
}
]
}
myChartes.setOption(option)
switch (step) {
case 1: myChartess1.value = myChartes; break
case 2: myChartess2.value = myChartes; break
case 3: myChartess3.value = myChartes; break
case 4: myChartess4.value = myChartes; break
case 5: myChartess5.value = myChartes; break
}
setTimeout(() => {
myChartes.resize()
loading.value = false
}, 400)
echarts.connect([myChartes1, myChartes])
}
const backbxlb = () => {
waveDatas.value = []
const charts = [myChartess.value, myChartess1.value, myChartess2.value, myChartess3.value, myChartess4.value, myChartess5.value]
charts.forEach(chart => {
if (chart) {
chart.dispose()
}
})
myChartess.value = null
myChartess1.value = null
myChartess2.value = null
myChartess3.value = null
myChartess4.value = null
myChartess5.value = null
// echarts.disconnect(charts.filter(Boolean) as echarts.ECharts[])
charts
.filter(Boolean)
.forEach(chart => {
if (chart && typeof chart.dispose === 'function') {
chart.dispose();
}
});
}
const getMax = (temp: number, tempA: number, tempB: number, tempC: number): number => {
temp = temp > tempA ? temp : tempA
temp = temp > tempB ? temp : tempB
temp = temp > tempC ? temp : tempC
return temp
}
const getMaxTwo = (temp: number, tempA: number, tempB: number): number => {
temp = temp > tempA ? temp : tempA
temp = temp > tempB ? temp : tempB
return temp
}
const getMin = (temp: number, tempA: number, tempB: number, tempC: number): number => {
temp = temp < tempA ? temp : tempA
temp = temp < tempB ? temp : tempB
temp = temp < tempC ? temp : tempC
return temp
}
const getMinOpen = (temp: number, tempA: number, tempB: number): number => {
temp = temp < tempA ? temp : tempA
temp = temp < tempB ? temp : tempB
return temp
}
</script>