1 Commits

Author SHA1 Message Date
guanj
4f907a80c4 优化项目 2026-06-04 19:06:36 +08:00
53 changed files with 987 additions and 3499 deletions

View File

@@ -154,7 +154,7 @@ const tableStore: any = new TableStore({
series: [ series: [
{ {
type: 'bar', type: 'bar',
name: '越限占比', name: '指标越限严重度',
data: tableStore.table.data.map((item: any) => Math.floor(item.extent * 100) / 100), data: tableStore.table.data.map((item: any) => Math.floor(item.extent * 100) / 100),
barMaxWidth: 30 barMaxWidth: 30
} }

View File

@@ -52,9 +52,10 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, provide, reactive, watch, h, computed, nextTick } from 'vue' import { ref, onMounted, onUnmounted, provide, reactive, watch, h, computed, nextTick } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import { exportExcel } from '@/views/govern/reportForms/export.js' import { exportExcel } from '@/views/govern/reportForms/export.js'
import { destroyLuckysheet, renderLuckysheetReport } from '@/utils/luckysheetHelper'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { querySysExcel } from '@/api/harmonic-boot/luckyexcel' import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit' import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
@@ -119,6 +120,9 @@ const downloadExcel = () => {
onMounted(() => { onMounted(() => {
initListByIds() initListByIds()
}) })
onUnmounted(() => {
destroyLuckysheet()
})
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => { const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
if (datePickerValue && datePickerValue.timeValue) { if (datePickerValue && datePickerValue.timeValue) {
@@ -159,16 +163,7 @@ const tableStore: any = new TableStore({
// } // }
}, },
loadCallback: () => { loadCallback: () => {
luckysheet.create({ renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
container: 'luckysheet',
title: '', // 表 头名
lang: 'zh', // 中文
showtoolbar: false, // 是否显示工具栏
showinfobar: false, // 是否显示顶部信息栏
showsheetbar: true, // 是否显示底部sheet按钮
allowEdit: false, // 禁止所有编辑操作(必填)
data: tableStore.table.data
})
} }
}) })

View File

@@ -220,7 +220,7 @@ const tableStore: any = new TableStore({
icon: 'el-icon-DataLine', icon: 'el-icon-DataLine',
render: 'basicButton', render: 'basicButton',
disabled: row => { disabled: row => {
return !!row.wavePath return row.wavePath
} }
} }
] ]

View File

@@ -214,7 +214,7 @@ const tableStore: any = new TableStore({
icon: 'el-icon-DataLine', icon: 'el-icon-DataLine',
render: 'basicButton', render: 'basicButton',
disabled: row => { disabled: row => {
return !!row.wavePath return row.wavePath
} }
} }
] ]

View File

@@ -236,7 +236,7 @@ const fliteWaveData = (wp, step, iphasicValue, isOpen) => {
// 监听消息 // 监听消息
self.onmessage = function (e) { self.onmessage = function (e) {
const { wp, isOpen, value, boxoList } = JSON.parse(e.data) const { wp, isOpen, value, boxoList, requestId } = e.data
try { try {
const iphasicValue = wp.iphasic || 1 const iphasicValue = wp.iphasic || 1
@@ -303,6 +303,7 @@ self.onmessage = function (e) {
} }
// 发送处理结果回主线程 // 发送处理结果回主线程
self.postMessage({ self.postMessage({
requestId,
titles: titles, titles: titles,
success: true, success: true,
waveDatas, waveDatas,
@@ -313,6 +314,7 @@ self.onmessage = function (e) {
}) })
} catch (error) { } catch (error) {
self.postMessage({ self.postMessage({
requestId,
success: false, success: false,
error: error.message error: error.message
}) })

View File

@@ -14,9 +14,15 @@ import html2canvas from 'html2canvas'
import $ from 'jquery' import $ from 'jquery'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { calcRmsYAxisRange, formatAxisLabel } from '@/utils/chartAxisHelper'
import url from '@/assets/img/point.png' import url from '@/assets/img/point.png'
import url2 from '@/assets/img/dw.png' import url2 from '@/assets/img/dw.png'
const worker = ref<Worker | null>(null) import { buildWaveCacheKey, getWaveCache, setWaveCache } from '@/utils/waveCache'
import { getRmsWorker, buildWorkerPayload } from '@/utils/waveWorkerPool'
let waveRequestId = 0
const pendingCacheKeys = new Map<number, string>()
let rmsWorker: Worker | null = null
interface WaveData { interface WaveData {
instantF: { max: number; min: number } instantF: { max: number; min: number }
instantS: { max: number; min: number } instantS: { max: number; min: number }
@@ -125,52 +131,61 @@ const vw = computed(() => '100%')
watch( watch(
() => props.value, () => props.value,
newVal => { () => {
if (newVal == 2) { query()
initWaves()
} else {
$('#wave1').remove()
initWaves()
}
} }
) )
watch(
() => props.wp,
() => {
query()
}
)
const applyWorkerResult = (data: any) => {
titles.value = data.titles
waveDatas.value = data.waveDatas
time.value = data.time
type.value = data.type
severity.value = data.severity
iphasic.value = data.iphasic
if (Number(severity.value) < 0) {
severity.value = '/'
type.value = '/'
}
initWave(waveDatas.value, time.value, type.value, severity.value, isOpen.value)
loading.value = false
}
onMounted(() => { onMounted(() => {
const zoomValue = document.body.style.getPropertyValue('zoom') const zoomValue = document.body.style.getPropertyValue('zoom')
zoom.value = 1 / (zoomValue ? parseFloat(zoomValue) : 1) zoom.value = 1 / (zoomValue ? parseFloat(zoomValue) : 1)
window.addEventListener('resize', handleResize) window.addEventListener('resize', handleResize)
// 初始化 Web Worker rmsWorker = getRmsWorker(data => {
worker.value = new Worker(new URL('./rmsWorker.js', import.meta.url)) if (data.requestId !== waveRequestId) return
worker.value.onmessage = e => { if (!data.success) {
if (e.data.success) { console.error('Worker error:', data.error)
const data = e.data
titles.value = data.titles
waveDatas.value = data.waveDatas
time.value = data.time
type.value = data.type
severity.value = data.severity
iphasic.value = data.iphasic
// 初始化波形图
initWave(waveDatas.value, time.value, type.value, severity.value, isOpen.value)
} else {
console.error('Worker error:', e.data.error)
loading.value = false loading.value = false
return
} }
} const cacheKey = pendingCacheKeys.get(data.requestId)
if (cacheKey) {
setWaveCache(cacheKey, data)
pendingCacheKeys.delete(data.requestId)
}
applyWorkerResult(data)
})
nextTick(() => { nextTick(() => {
setTimeout(() => { query()
query()
}, 500)
}) })
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
if (worker.value) {
worker.value.terminate()
}
backbxlb() backbxlb()
window.removeEventListener('resize', handleResize) window.removeEventListener('resize', handleResize)
}) })
@@ -195,23 +210,15 @@ const download = () => {
} }
} }
const EXTRA_PANEL_CLASS = 'wave-extra-panel'
const resetWaveDom = () => {
backbxlb()
$('#rmsp').nextAll(`.${EXTRA_PANEL_CLASS}`).remove()
}
const query = () => { const query = () => {
loading.value = true initWaves()
if (props.wp) {
// 使用 Worker 处理数据
if (worker.value) {
worker.value.postMessage(
JSON.stringify({
wp: props.wp,
isOpen: isOpen.value,
value: props.value,
boxoList: props.boxoList
})
)
}
} else {
initWave(null, null, null, null, null)
}
} }
const waveData = ( const waveData = (
@@ -245,29 +252,31 @@ const waveData = (
} }
const initWaves = () => { const initWaves = () => {
if (props.wp) { if (!props.wp?.listRmsData?.length) {
iphasic.value = props.wp.iphasic || 1
const picCounts = (props.wp.waveTitle.length - 1) / iphasic.value
waveDatas.value = []
for (let i = 0; i < picCounts; i++) {
const data = fliteWaveData(props.wp, i)
waveDatas.value.push(data)
}
time.value = props.wp.time
type.value = props.wp.waveType
severity.value = props.wp.yzd
if (Number(severity.value) < 0) {
severity.value = '/'
type.value = '/'
}
initWave(waveDatas.value, time.value, type.value, severity.value, isOpen.value)
} else {
initWave(null, null, null, null, null) initWave(null, null, null, null, null)
loading.value = false
return
} }
const cacheKey = buildWaveCacheKey('rms', props.wp, props.value, isOpen.value, props.boxoList)
const cached = getWaveCache<any>(cacheKey)
if (cached) {
applyWorkerResult(cached)
return
}
loading.value = true
iphasic.value = props.wp.iphasic || 1
const currentRequestId = ++waveRequestId
pendingCacheKeys.set(currentRequestId, cacheKey)
rmsWorker?.postMessage(
buildWorkerPayload('rms', props.wp, props.boxoList, {
requestId: currentRequestId,
value: props.value,
isOpen: isOpen.value
})
)
} }
const fliteWaveData = (wp: any, step: number): WaveData => { const fliteWaveData = (wp: any, step: number): WaveData => {
@@ -482,7 +491,7 @@ const initWave = (
severity: string | null, severity: string | null,
isOpen: boolean | null isOpen: boolean | null
) => { ) => {
$('div.bx').remove() resetWaveDom()
let picHeight = vh.value let picHeight = vh.value
const show = !isOpen const show = !isOpen
@@ -594,7 +603,7 @@ const initWave = (
for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) { for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) {
const rmsId = 'rms' + step const rmsId = 'rms' + step
const newDivRms = $( const newDivRms = $(
`<div style="height:${vh.value};overflow: hidden;min-height: 200px;"><div class='bx' id='${rmsId}'></div></div>` `<div class="${EXTRA_PANEL_CLASS}" style="height:${vh.value};overflow: hidden;min-height: 200px;"><div class='bx' id='${rmsId}'></div></div>`
) )
newDivRms.insertAfter($('#rmsp')) newDivRms.insertAfter($('#rmsp'))
$(`#${rmsId}`).css('height', picHeight).css('width', vw.value).css('min-height', '200px') $(`#${rmsId}`).css('height', picHeight).css('width', vw.value).css('min-height', '200px')
@@ -607,6 +616,12 @@ const initWave = (
const rms = document.getElementById('rmsp') const rms = document.getElementById('rmsp')
if (!rms) return if (!rms) return
const yRange = calcRmsYAxisRange(rmscu[0]?.[1] ?? 0, rmscm[0]?.[1] ?? 0)
const existingChart = echarts.getInstanceByDom(rms)
if (existingChart) existingChart.dispose()
const myChartes = echarts.init(rms) const myChartes = echarts.init(rms)
const echartsColor = { const echartsColor = {
WordColor: '#000', WordColor: '#000',
@@ -750,8 +765,8 @@ const initWave = (
}, },
// max: rmscm[0]?.[1] * 1.06 || 0, // max: rmscm[0]?.[1] * 1.06 || 0,
// min: rmscu[0]?.[1] - rmscu[0]?.[1] * 0.2 || 0, // min: rmscu[0]?.[1] - rmscu[0]?.[1] * 0.2 || 0,
max: Math.floor((rmscm[0]?.[1] * 1.06 || 0) * 1.1 * 10) / 10, max: yRange.max,
min: Math.floor((rmscu[0]?.[1] - rmscu[0]?.[1] * 0.2 || 0) * 10) / 10, min: yRange.min,
boundaryGap: [0, '100%'], boundaryGap: [0, '100%'],
showLastLabel: true, showLastLabel: true,
opposite: false, opposite: false,
@@ -770,7 +785,7 @@ const initWave = (
fontSize: '12px', fontSize: '12px',
color: props.DColor ? '#000' : echartsColor.WordColor, color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) { formatter: function (value: number) {
return Math.floor(value * 1000) / 1000 return formatAxisLabel(value)
} }
}, },
splitLine: { splitLine: {
@@ -955,6 +970,10 @@ const drawPics = (
const rmsIds = document.getElementById(rmsId) const rmsIds = document.getElementById(rmsId)
if (!rmsIds) return if (!rmsIds) return
const subMin = props.value === 1 ? waveDataTemp.RMSF.min : waveDataTemp.RMSS.min
const subMax = props.value === 1 ? waveDataTemp.RMSF.max : waveDataTemp.RMSS.max
const yRange = calcRmsYAxisRange(subMin, subMax)
const myChartes = echarts.init(rmsIds) const myChartes = echarts.init(rmsIds)
const echartsColor = { const echartsColor = {
WordColor: '#000', WordColor: '#000',
@@ -1078,6 +1097,8 @@ const drawPics = (
}, },
boundaryGap: [0, '100%'], boundaryGap: [0, '100%'],
showLastLabel: true, showLastLabel: true,
max: yRange.max,
min: yRange.min,
opposite: false, opposite: false,
// max: Math.floor((rmscm[0]?.[1] * 1.06 || 0) * 1.1 * 10) / 10, // max: Math.floor((rmscm[0]?.[1] * 1.06 || 0) * 1.1 * 10) / 10,
// min: Math.floor((rmscu[0]?.[1] - rmscu[0]?.[1] * 0.2 || 0) * 10) / 10, // min: Math.floor((rmscu[0]?.[1] - rmscu[0]?.[1] * 0.2 || 0) * 10) / 10,
@@ -1096,8 +1117,7 @@ const drawPics = (
fontSize: '12px', fontSize: '12px',
color: props.DColor ? '#000' : echartsColor.WordColor, color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) { formatter: function (value: number) {
// return (value - 0).toFixed(2) return formatAxisLabel(value)
return Math.floor(value * 1000) / 1000
} }
}, },
splitLine: { splitLine: {
@@ -1213,12 +1233,7 @@ const backbxlb = () => {
myChartess4.value = null myChartess4.value = null
myChartess5.value = null myChartess5.value = null
// echarts.disconnect(charts.filter(Boolean) as echarts.ECharts[]) $('#rmsp').nextAll(`.${EXTRA_PANEL_CLASS}`).remove()
charts.filter(Boolean).forEach(chart => {
if (chart && typeof chart.dispose === 'function') {
chart.dispose()
}
})
} }
const getMax = (temp: number, tempA: number, tempB: number, tempC: number): number => { const getMax = (temp: number, tempA: number, tempB: number, tempC: number): number => {

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
// waveData.worker.js // waveData.worker.js
self.addEventListener('message', function (e) { self.addEventListener('message', function (e) {
const { wp, value, iphasic, isOpen, boxoList } = JSON.parse(e.data) const { wp, value, iphasic, isOpen, boxoList, requestId } = e.data
// 处理波形数据的函数 // 处理波形数据的函数
const fliteWaveData = (wp, step) => { const fliteWaveData = (wp, step) => {
@@ -195,6 +195,8 @@ self.addEventListener('message', function (e) {
// 将处理结果发送回主线程 // 将处理结果发送回主线程
self.postMessage({ self.postMessage({
requestId,
success: true,
waveDatas, waveDatas,
time, time,
type, type,

View File

@@ -1,8 +1,8 @@
<template> <template>
<div v-loading="loading" class="boxbx" style="position: relative; height: 100%"> <div v-loading="loading" class="boxbx" style="position: relative; height: 100%">
<div id="boxsj"> <div id="boxsj">
<div id="shushi" :style="`height:${vh};overflow: hidden;min-height: 200px;`"> <div id="shushi" :style="containerStyle">
<div class="bx" id="wave" style="min-height: 200px"></div> <div class="bx" id="wave" :style="waveStyle"></div>
</div> </div>
</div> </div>
</div> </div>
@@ -14,9 +14,14 @@ import html2canvas from 'html2canvas'
import $ from 'jquery' import $ from 'jquery'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { calcShuYAxisRange, formatAxisLabel } from '@/utils/chartAxisHelper'
import url from '@/assets/img/point.png' import url from '@/assets/img/point.png'
// 创建Worker import { buildWaveCacheKey, getWaveCache, setWaveCache } from '@/utils/waveCache'
let waveDataWorker: Worker | null = null import { getShuWorker, buildWorkerPayload } from '@/utils/waveWorkerPool'
let waveRequestId = 0
const pendingCacheKeys = new Map<number, string>()
let shuWorker: Worker | null = null
interface WaveData { interface WaveData {
instantF: { max: number; min: number } instantF: { max: number; min: number }
instantS: { max: number; min: number } instantS: { max: number; min: number }
@@ -110,38 +115,82 @@ const vh = computed(() => {
const vw = computed(() => '100%') const vw = computed(() => '100%')
const containerStyle = computed(() => ({
height: vh.value,
overflow: 'hidden',
minHeight: '200px'
}))
const waveStyle = computed(() => ({
width: '100%',
height: '100%',
minHeight: '200px'
}))
const applyChartSize = (el: HTMLElement) => {
el.style.width = '100%'
el.style.height = vh.value
}
const finishChartRender = (chart: echarts.ECharts, endLoading = false) => {
nextTick(() => {
chart.resize()
if (endLoading) {
loading.value = false
}
})
}
watch( watch(
() => props.value, () => props.value,
newVal => { () => {
if (newVal == 2) { query()
initWaves()
} else {
$('#wave1').remove()
initWaves()
}
} }
) )
watch(
() => props.wp,
() => {
query()
}
)
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)
loading.value = false
}
onMounted(() => { onMounted(() => {
const zoomValue = document.body.style.getPropertyValue('zoom') const zoomValue = document.body.style.getPropertyValue('zoom')
zoom.value = 1 / (zoomValue ? parseFloat(zoomValue) : 1) zoom.value = 1 / (zoomValue ? parseFloat(zoomValue) : 1)
window.addEventListener('resize', handleResize) window.addEventListener('resize', handleResize)
shuWorker = getShuWorker(data => {
if (data.requestId !== waveRequestId) return
if (!data.success) {
console.error('Worker error:', data.error)
loading.value = false
return
}
const cacheKey = pendingCacheKeys.get(data.requestId)
if (cacheKey) {
setWaveCache(cacheKey, data)
pendingCacheKeys.delete(data.requestId)
}
applyWorkerResult(data)
})
nextTick(() => { nextTick(() => {
setTimeout(() => { query()
query()
}, 500)
}) })
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
console.log('组件卸载')
if (waveDataWorker) {
waveDataWorker.terminate()
waveDataWorker = null
}
backbxlb() backbxlb()
window.removeEventListener('resize', handleResize) window.removeEventListener('resize', handleResize)
}) })
@@ -166,6 +215,13 @@ const download = () => {
} }
} }
const EXTRA_PANEL_CLASS = 'wave-extra-panel'
const resetWaveDom = () => {
backbxlb()
$('#shushi').nextAll(`.${EXTRA_PANEL_CLASS}`).remove()
}
const query = () => { const query = () => {
loading.value = true loading.value = true
initWaves() initWaves()
@@ -184,48 +240,32 @@ const waveData = (instantF: any, instantS: any, shunshiF: any, shunshiS: any, ti
// 在组件中修改initWaves函数 // 在组件中修改initWaves函数
const initWaves = () => { const initWaves = () => {
if (props.wp) { if (!props.wp?.listWaveData?.length) {
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) initWave(null, null, null, null, null)
loading.value = false
return
} }
const cacheKey = buildWaveCacheKey('shu', props.wp, props.value, isOpen.value, props.boxoList)
const cached = getWaveCache<any>(cacheKey)
if (cached) {
applyWorkerResult(cached)
return
}
loading.value = true
iphasic.value = props.wp.iphasic || 1
const currentRequestId = ++waveRequestId
pendingCacheKeys.set(currentRequestId, cacheKey)
shuWorker?.postMessage(
buildWorkerPayload('shu', props.wp, props.boxoList, {
requestId: currentRequestId,
value: props.value,
isOpen: isOpen.value,
iphasic: iphasic.value
})
)
} }
const initWave = ( const initWave = (
@@ -235,6 +275,7 @@ const initWave = (
severity: string | null, severity: string | null,
isOpen: boolean | null isOpen: boolean | null
) => { ) => {
resetWaveDom()
$('div.bx1').remove() $('div.bx1').remove()
let picHeight = vh.value let picHeight = vh.value
@@ -327,7 +368,7 @@ const initWave = (
for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) { for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) {
const waveId = 'wave' + step const waveId = 'wave' + step
const newDivShunshi = $(`<div style="height:${vh.value};overflow: hidden;min-height: 200px;"> const newDivShunshi = $(`<div class="${EXTRA_PANEL_CLASS}" style="height:${vh.value};overflow: hidden;min-height: 200px;">
<div class='bx1' id='${waveId}'></div> <div class='bx1' id='${waveId}'></div>
</div>`) </div>`)
newDivShunshi.insertAfter($('#shushi')) newDivShunshi.insertAfter($('#shushi'))
@@ -342,6 +383,13 @@ const initWave = (
const wave = document.getElementById('wave') const wave = document.getElementById('wave')
if (!wave) return if (!wave) return
applyChartSize(wave)
const yRange = calcShuYAxisRange(Number(min), Number(max))
const existingChart = echarts.getInstanceByDom(wave)
if (existingChart) existingChart.dispose()
const myChartes = echarts.init(wave) const myChartes = echarts.init(wave)
const echartsColor = { const echartsColor = {
WordColor: '#000', WordColor: '#000',
@@ -363,11 +411,6 @@ const initWave = (
] ]
} }
setTimeout(() => {
wave.style.width = '100%'
wave.style.height = vh.value
}, 0)
const option = { const option = {
tooltip: { tooltip: {
top: '10px', top: '10px',
@@ -481,10 +524,8 @@ const initWave = (
}, },
boundaryGap: [0, '100%'], boundaryGap: [0, '100%'],
showLastLabel: true, showLastLabel: true,
// max: max.toFixed(2) * 1.1, max: yRange.max,
// min: min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1, min: yRange.min,
max: Math.floor(max.toFixed(2) * 1.1 * 10) / 10,
min: Math.floor(min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1 * 10) / 10 ,
opposite: false, opposite: false,
nameTextStyle: { nameTextStyle: {
fontSize: '12px', fontSize: '12px',
@@ -501,8 +542,7 @@ const initWave = (
fontSize: '12px', fontSize: '12px',
color: props.DColor ? '#000' : echartsColor.WordColor, color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) { formatter: function (value: number) {
// return (value - 0).toFixed(2) return formatAxisLabel(value)
return Math.floor(value * 1000) / 1000
} }
}, },
splitLine: { splitLine: {
@@ -518,7 +558,6 @@ const initWave = (
right: '45px', right: '45px',
bottom: '40px', bottom: '40px',
top: '60px' top: '60px'
// containLabel: true
}, },
dataZoom: [ dataZoom: [
{ {
@@ -578,11 +617,7 @@ const initWave = (
myChartes.setOption(option) myChartes.setOption(option)
myChartess.value = myChartes myChartess.value = myChartes
finishChartRender(myChartes, true)
setTimeout(() => {
myChartes.resize()
loading.value = false
}, 400)
if (waveDatas && waveDatas.length > 1) { if (waveDatas && waveDatas.length > 1) {
const waveDatasTemp = waveDatas.slice(1) const waveDatasTemp = waveDatas.slice(1)
@@ -679,6 +714,13 @@ const drawPics = (
const waveIds = document.getElementById(waveId) const waveIds = document.getElementById(waveId)
if (!waveIds) return if (!waveIds) return
applyChartSize(waveIds)
const yRange = calcShuYAxisRange(Number(min), Number(max))
const existingChart = echarts.getInstanceByDom(waveIds)
if (existingChart) existingChart.dispose()
const myChartes = echarts.init(waveIds) const myChartes = echarts.init(waveIds)
const echartsColor = { const echartsColor = {
WordColor: '#000', WordColor: '#000',
@@ -791,8 +833,8 @@ const drawPics = (
}, },
boundaryGap: [0, '100%'], boundaryGap: [0, '100%'],
showLastLabel: true, showLastLabel: true,
max: Math.floor(max.toFixed(2) * 1.1 * 10) / 10, max: yRange.max,
min: Math.floor(min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1 * 10) / 10 , min: yRange.min,
opposite: false, opposite: false,
nameTextStyle: { nameTextStyle: {
fontSize: '12px', fontSize: '12px',
@@ -809,8 +851,7 @@ const drawPics = (
fontSize: '12px', fontSize: '12px',
color: props.DColor ? '#000' : echartsColor.WordColor, color: props.DColor ? '#000' : echartsColor.WordColor,
formatter: function (value: number) { formatter: function (value: number) {
// return (value - 0).toFixed(2) return formatAxisLabel(value)
return Math.floor(value * 1000) / 1000
} }
}, },
splitLine: { splitLine: {
@@ -897,10 +938,7 @@ const drawPics = (
break break
} }
setTimeout(() => { finishChartRender(myChartes)
myChartes.resize()
loading.value = false
}, 400)
echarts.connect([myChartes1, myChartes]) echarts.connect([myChartes1, myChartes])
} }
@@ -929,12 +967,8 @@ const backbxlb = () => {
myChartess4.value = null myChartess4.value = null
myChartess5.value = null myChartess5.value = null
// echarts.disconnect(charts.filter(Boolean) as echarts.ECharts[]) $('#shushi').nextAll(`.${EXTRA_PANEL_CLASS}`).remove()
charts.filter(Boolean).forEach(chart => { $('div.bx1').remove()
if (chart && typeof chart.dispose === 'function') {
chart.dispose()
}
})
} }
const getMax = (temp: number, tempA: number, tempB: number, tempC: number): number => { const getMax = (temp: number, tempA: number, tempB: number, tempC: number): number => {

File diff suppressed because it is too large Load Diff

View File

@@ -10,10 +10,11 @@
@checkbox-all="selectChangeEvent" @checkbox-all="selectChangeEvent"
@checkbox-change="selectChangeEvent" @checkbox-change="selectChangeEvent"
:showOverflow="showOverflow" :showOverflow="showOverflow"
:sort-config="{ remote: true }"
@sort-change="handleSortChange"
>
>
<!-- :sort-config="{ remote: true }" -->
<!-- @sort-change="handleSortChange" -->
<!-- Column 组件内部是 el-table-column --> <!-- Column 组件内部是 el-table-column -->
<template v-if="isGroup"> <template v-if="isGroup">
<GroupColumn :column="tableStore.table.column" /> <GroupColumn :column="tableStore.table.column" />

View File

@@ -152,7 +152,7 @@ async function selectInitialNode(
treeInstance?.setCurrentKey(node.id) treeInstance?.setCurrentKey(node.id)
emit('init', { level, ...node }) emit('init', { ...node })
return return

View File

@@ -64,7 +64,7 @@ async function loadTree() {
await selectTreeNode(treRef.value, node, { await selectTreeNode(treRef.value, node, {
level: 3, level: 3,
onSelect: selected => { onSelect: selected => {
emit('init', { level: 3, ...selected }) emit('init', { ...selected, level: 3 })
changePointType('4', selected) changePointType('4', selected)
} }
}) })

View File

@@ -48,7 +48,7 @@ async function initTree(list: any[]) {
} }
await selectTreeNode(treRef.value, node, { await selectTreeNode(treRef.value, node, {
onSelect: selected => emit('init', { level: 2, ...selected }) onSelect: selected => emit('init', { ...selected, level: 2 })
}) })
} }

View File

@@ -1,16 +1,7 @@
<template> <template>
<Tree <Tree ref="treRef" @check-change="handleCheckChange" :default-checked-keys="defaultCheckedKeys"
ref="treRef" :show-checkbox="props.showCheckbox" :data="tree" :height="props.height" @changeDeviceType="changeDeviceType"
@check-change="handleCheckChange" @changeTreeType="loadTree" :engineering="props.engineering" leaf-mode="device" />
:default-checked-keys="defaultCheckedKeys"
:show-checkbox="props.showCheckbox"
:data="tree"
:height="props.height"
@changeDeviceType="changeDeviceType"
@changeTreeType="loadTree"
:engineering="props.engineering"
leaf-mode="device"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -55,17 +46,17 @@ async function selectInitialNode(type: string | undefined, leaves: LineTreeLeave
type === '2' type === '2'
? [{ refKey: 'treeRef4', list: leaves.engineering, level: 2 }] ? [{ refKey: 'treeRef4', list: leaves.engineering, level: 2 }]
: [ : [
{ refKey: 'treeRef1', list: leaves.govern, level: 2 }, { refKey: 'treeRef1', list: leaves.govern, level: 2 },
{ refKey: 'treeRef2', list: leaves.portable, level: 2 }, { refKey: 'treeRef2', list: leaves.portable, level: 2 },
{ refKey: 'treeRef3', list: leaves.monitor, level: 2 } { refKey: 'treeRef3', list: leaves.monitor, level: 2 }
] ]
for (const { refKey, list, level } of candidates) { for (const { refKey, list, level } of candidates) {
const node = list[0] const node = list[0]
if (!node) continue if (!node) continue
const treeInstance = await waitForTreeRef(treRef.value, refKey) const treeInstance = await waitForTreeRef(treRef.value, refKey)
treeInstance?.setCurrentKey(node.id) treeInstance?.setCurrentKey(node.id)
emit('init', { level, ...node }) emit('init', { ...node })
return return
} }
emit('init') emit('init')
@@ -81,13 +72,10 @@ const loadTree = (type?: string) => {
onMounted(() => loadTree(props.engineering ? '2' : '1')) onMounted(() => loadTree(props.engineering ? '2' : '1'))
const handleCheckChange = throttle( const handleCheckChange =
(data: any, checked: any, indeterminate: any) => { (data: any, checked: any, indeterminate: any) => {
emit('checkChange', { data, checked, indeterminate }) emit('checkChange', { data, checked, indeterminate })
}, }
300,
{ leading: true, trailing: false }
)
defineExpose({ treRef }) defineExpose({ treRef })
</script> </script>

View File

@@ -17,7 +17,8 @@ export function decorateDeviceTree(
child.children?.forEach((grand: any) => { child.children?.forEach((grand: any) => {
applyMeta(grand, { applyMeta(grand, {
icon: 'el-icon-Platform', icon: 'el-icon-Platform',
color: statusColor(grand.comFlag) color: statusColor(grand.comFlag),
level: 3
}) })
leaves.engineering.push(grand) leaves.engineering.push(grand)
}) })
@@ -68,6 +69,7 @@ export function decorateDeviceTree(
l3.pName = '监测设备' l3.pName = '监测设备'
applyMeta(l3, { applyMeta(l3, {
icon: 'el-icon-Platform', icon: 'el-icon-Platform',
level: 3,
color: l3.comFlag === 1 ? '#e26257 !important' : primary() color: l3.comFlag === 1 ? '#e26257 !important' : primary()
}) })
leaves.monitor.push(l3) leaves.monitor.push(l3)

View File

@@ -39,6 +39,21 @@ export function createLineTreeDecorators(getPrimaryColor: () => string): LineTre
export type TreeRefKey = 'treeRef1' | 'treeRef2' | 'treeRef3' | 'treeRef4' export type TreeRefKey = 'treeRef1' | 'treeRef2' | 'treeRef3' | 'treeRef4'
/** 线路树可选叶子节点元数据 */
export const LINE_LEAF_META = { level: 3, type: 'line' as const }
/** 是否为线路树可选叶子(监测点/线路) */
export function isLineTreeLeaf(node: any): boolean {
if (!node?.id) return false
return node.type === 'line' || node.level === 3
}
/** 是否为报告/导出可选监测点 */
export function isReportMonitorPoint(node: any): boolean {
if (!node?.id) return false
return isLineTreeLeaf(node) || node.level === 3 || (!node.children?.length && !!node.pid)
}
export interface DecorateLineTreeOptions { export interface DecorateLineTreeOptions {
/** 是否禁用父级节点(分析树隐藏父节点,测点树不禁用) */ /** 是否禁用父级节点(分析树隐藏父节点,测点树不禁用) */
disableParents?: boolean disableParents?: boolean
@@ -69,7 +84,11 @@ export function decorateLineTree(
...parentDisabled ...parentDisabled
}) })
grand.children?.forEach((leaf: any) => { grand.children?.forEach((leaf: any) => {
applyMeta(leaf, { icon: 'el-icon-Platform', color: statusColor(leaf.comFlag) }) applyMeta(leaf, {
icon: 'el-icon-Platform',
color: statusColor(leaf.comFlag),
...LINE_LEAF_META
})
leaves.engineering.push(leaf) leaves.engineering.push(leaf)
}) })
}) })
@@ -90,7 +109,11 @@ export function decorateLineTree(
...parentDisabled ...parentDisabled
}) })
l3.children?.forEach((l4: any) => { l3.children?.forEach((l4: any) => {
applyMeta(l4, { icon: 'el-icon-Platform', color: statusColor(l4.comFlag) }) applyMeta(l4, {
icon: 'el-icon-Platform',
color: statusColor(l4.comFlag),
...LINE_LEAF_META
})
leaves.govern.push(l4) leaves.govern.push(l4)
}) })
}) })
@@ -100,7 +123,11 @@ export function decorateLineTree(
item.children?.forEach((l1: any) => { item.children?.forEach((l1: any) => {
applyMeta(l1, { icon: 'el-icon-Platform', color: statusColor(l1.comFlag) }) applyMeta(l1, { icon: 'el-icon-Platform', color: statusColor(l1.comFlag) })
l1.children?.forEach((l2: any) => { l1.children?.forEach((l2: any) => {
applyMeta(l2, { icon: 'el-icon-Platform', color: statusColor(l2.comFlag) }) applyMeta(l2, {
icon: 'el-icon-Platform',
color: statusColor(l2.comFlag),
...LINE_LEAF_META
})
leaves.portable.push(l2) leaves.portable.push(l2)
}) })
}) })
@@ -117,7 +144,11 @@ export function decorateLineTree(
...parentDisabled ...parentDisabled
}) })
l3.children?.forEach((l4: any) => { l3.children?.forEach((l4: any) => {
applyMeta(l4, { icon: 'el-icon-Platform', color: statusColor(l4.comFlag) }) applyMeta(l4, {
icon: 'el-icon-Platform',
color: statusColor(l4.comFlag),
...LINE_LEAF_META
})
leaves.monitor.push(l4) leaves.monitor.push(l4)
}) })
}) })

View File

@@ -64,7 +64,7 @@ async function selectInitialNode(type: string | undefined, leaves: LineTreeLeave
const treeInstance = await waitForTreeRef(treRef.value, refKey) const treeInstance = await waitForTreeRef(treRef.value, refKey)
treeInstance?.setCurrentKey(node.id) treeInstance?.setCurrentKey(node.id)
emit('init', { level, ...node }) emit('init', { ...node })
if (type === '2') { if (type === '2') {
changePointType('4', node) changePointType('4', node)

View File

@@ -59,7 +59,7 @@ interface Props {
const props = withDefaults(defineProps<Props>(), { template: false }) const props = withDefaults(defineProps<Props>(), { template: false })
const emit = defineEmits(['init', 'checkChange', 'nodeChange', 'editNode', 'getChart', 'Policy']) const emit = defineEmits(['init', 'checkChange', 'nodeChange', 'node-click', 'editNode', 'getChart', 'Policy'])
const config = useConfig() const config = useConfig()
const tree = ref<any[]>([]) const tree = ref<any[]>([])
@@ -109,6 +109,7 @@ const clickNode = (e: any) => {
planId.value = e?.children ? e.id : e.pid planId.value = e?.children ? e.id : e.pid
id.value = e.id id.value = e.id
emit('nodeChange', e) emit('nodeChange', e)
emit('node-click', e)
} }
bootstrapWithTemplate( bootstrapWithTemplate(

View File

@@ -52,6 +52,7 @@ import useCurrentInstance from '@/utils/useCurrentInstance'
import { ElMessage, ElTree } from 'element-plus' import { ElMessage, ElTree } from 'element-plus'
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import { createTreeFilterNode } from './govern/treeFilterUtils' import { createTreeFilterNode } from './govern/treeFilterUtils'
import { isLineTreeLeaf } from './govern/lineTreeUtils'
defineOptions({ name: 'govern/select', inheritAttrs: false }) defineOptions({ name: 'govern/select', inheritAttrs: false })
@@ -75,7 +76,8 @@ const filterNode = createTreeFilterNode()
const checkedNodes = ref<any[]>([]) const checkedNodes = ref<any[]>([])
const defaultCheckedKeys = ref<string[]>([]) const defaultCheckedKeys = ref<string[]>([])
const MAX_CHECK = 5 const MAX_CHECK = 5
const MONITOR_LEVEL = 3
const isMonitorLeaf = (node: any) => isLineTreeLeaf(node)
watch(filterText, val => treeRef.value?.filter(val)) watch(filterText, val => treeRef.value?.filter(val))
@@ -85,7 +87,7 @@ const onMenuCollapse = () => {
} }
const handleCheckChange = (_data: any, checkInfo: any) => { const handleCheckChange = (_data: any, checkInfo: any) => {
const monitoringPointNodes = (checkInfo.checkedNodes as any[]).filter(node => node.level === MONITOR_LEVEL) const monitoringPointNodes = (checkInfo.checkedNodes as any[]).filter(isMonitorLeaf)
if (monitoringPointNodes.length > MAX_CHECK) { if (monitoringPointNodes.length > MAX_CHECK) {
const previousCheckedNodes = checkedNodes.value const previousCheckedNodes = checkedNodes.value
@@ -122,7 +124,7 @@ const updateNodeCheckStatus = (currentCount: number) => {
if (!treeRef.value) return if (!treeRef.value) return
const isMaxSelected = currentCount >= MAX_CHECK const isMaxSelected = currentCount >= MAX_CHECK
treeRef.value.store._getAllNodes().forEach((node: any) => { treeRef.value.store._getAllNodes().forEach((node: any) => {
if (node.level === MONITOR_LEVEL) { if (isMonitorLeaf(node.data)) {
node.data.disabled = isMaxSelected && !node.checked node.data.disabled = isMaxSelected && !node.checked
} }
}) })

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="layout-logo"> <div class="layout-logo">
<img v-if="!config.layout.menuCollapse" class="logo-img" :src="getTheme.logoUrl" /> <img v-if="!config.layout.menuCollapse && getTheme.logoUrl" class="logo-img" :src="getTheme.logoUrl" />
<!-- <div--> <!-- <div-->
<!-- v-if="!config.layout.menuCollapse"--> <!-- v-if="!config.layout.menuCollapse"-->
<!-- :style="{ color: config.getColorVal('menuActiveColor') }"--> <!-- :style="{ color: config.getColorVal('menuActiveColor') }"-->
@@ -8,7 +8,7 @@
<!-- >--> <!-- >-->
<!-- 灿能--> <!-- 灿能-->
<!-- </div>--> <!-- </div>-->
<!-- <Icon <Icon
v-if="config.layout.layoutMode != 'Streamline'" v-if="config.layout.layoutMode != 'Streamline'"
@click="onMenuCollapse" @click="onMenuCollapse"
:name="config.layout.menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" :name="config.layout.menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
@@ -17,18 +17,18 @@
style="margin: 15px;" style="margin: 15px;"
size="18" size="18"
class="fold" class="fold"
/> --> />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { closeShade } from '@/utils/pageShade' import { closeShade } from '@/utils/pageShade'
import { Session } from '@/utils/storage' import { getStoredTheme } from '@/utils/storage'
import { setNavTabsWidth } from '@/utils/layout' import { setNavTabsWidth } from '@/utils/layout'
const config = useConfig() const config = useConfig()
const getTheme = JSON.parse(window.localStorage.getItem('getTheme') as string) const getTheme = getStoredTheme()
const onMenuCollapse = function () { const onMenuCollapse = function () {
if (config.layout.shrink && !config.layout.menuCollapse) { if (config.layout.shrink && !config.layout.menuCollapse) {
closeShade() closeShade()

View File

@@ -4,7 +4,7 @@
<!-- <Icon @click="onMenuCollapse" name="fa fa-indent" :color="config.getColorVal('menuActiveColor')" <!-- <Icon @click="onMenuCollapse" name="fa fa-indent" :color="config.getColorVal('menuActiveColor')"
size="18" /> --> size="18" /> -->
</div> </div>
<span class="nav-bar-title">{{ getTheme.name }} <span style="font-size: 14px;" v-if="Version?.versionName"> <span class="nav-bar-title">{{ themeName }} <span style="font-size: 14px;" v-if="Version?.versionName">
({{ Version?.versionName }}) ({{ Version?.versionName }})
</span></span> </span></span>
<NavMenus /> <NavMenus />
@@ -19,8 +19,9 @@ import NavMenus from '../navMenus.vue'
import { showShade } from '@/utils/pageShade' import { showShade } from '@/utils/pageShade'
import { onMounted } from 'vue' import { onMounted } from 'vue'
import { getLastData } from '@/api/systerm' import { getLastData } from '@/api/systerm'
import { getStoredThemeName } from '@/utils/storage'
const config = useConfig() const config = useConfig()
const getTheme = JSON.parse(window.localStorage.getItem('getTheme') as string) const themeName = getStoredThemeName()
const Version: any = ref({}) const Version: any = ref({})
const onMenuCollapse = () => { const onMenuCollapse = () => {
showShade('ba-aside-menu-shade', () => { showShade('ba-aside-menu-shade', () => {
@@ -33,7 +34,7 @@ onMounted(() => {
Version.value = res.data Version.value = res.data
}) })
document.title = getTheme.name document.title = themeName
}) })
</script> </script>

View File

@@ -0,0 +1,59 @@
const AXIS_DECIMALS = 2
export function roundAxisValue(val: number, decimals = AXIS_DECIMALS): number {
if (!Number.isFinite(val)) return 0
const factor = 10 ** decimals
return Math.round(val * factor) / factor
}
/** Y 轴刻度:最多 2 位小数,末尾 0 去掉(如 1.00 → 10.10 → 0.1 */
export function formatAxisLabel(value: number): string {
if (!Number.isFinite(value)) return '0'
return String(Number(roundAxisValue(value).toFixed(AXIS_DECIMALS)))
}
/** 瞬间波形 Y 轴范围 */
export function calcShuYAxisRange(dataMin: number, dataMax: number): { min: number; max: number } {
const min = Number(dataMin)
const max = Number(dataMax)
if (!Number.isFinite(min) || !Number.isFinite(max)) {
return { min: 0, max: 1 }
}
let axisMax = max * 1.1
let axisMin = min > 0 ? min - min * 0.1 : min * 1.1
if (axisMax <= axisMin) {
const pad = Math.abs(max) * 0.1 || 0.01
axisMax = max + pad
axisMin = min - pad
}
return {
min: roundAxisValue(axisMin),
max: roundAxisValue(axisMax)
}
}
/** RMS 波形 Y 轴范围 */
export function calcRmsYAxisRange(dataMin: number, dataMax: number): { min: number; max: number } {
const min = Number(dataMin)
const max = Number(dataMax)
if (!Number.isFinite(min) || !Number.isFinite(max)) {
return { min: 0, max: 1 }
}
let axisMax = max * 1.06 * 1.1
let axisMin = min - min * 0.2
if (axisMax <= axisMin) {
const pad = Math.abs(max - min) * 0.1 || Math.abs(max) * 0.1 || 0.01
axisMax = max + pad
axisMin = min - pad
}
return {
min: roundAxisValue(axisMin),
max: roundAxisValue(axisMax)
}
}

View File

@@ -0,0 +1,83 @@
import { ElMessage } from 'element-plus'
import { exportExcel } from '@/views/system/reportForms/export.js'
/** 解析 Luckysheet 接口返回的 sheet 数据 */
export function parseLuckysheetSheets(sheets: any[]) {
sheets.forEach((item: any) => {
if (item.data1) {
try {
item.data = JSON.parse(item.data1)
} catch {
/* ignore invalid json */
}
}
item.celldata?.forEach((cell: any) => {
if (item.data?.[cell.r]?.[cell.c]?.v != null) {
item.data[cell.r][cell.c] = cell.v
}
})
})
}
declare const luckysheet: any
const DEFAULT_REPORT_OPTIONS = {
title: '',
lang: 'zh',
showtoolbar: false,
showinfobar: false,
showsheetbar: true,
}
/** 销毁已有 Luckysheet 实例,避免重复 create 导致 DOM 堆积 */
export function destroyLuckysheet() {
try {
if (typeof luckysheet !== 'undefined' && luckysheet.destroy) {
luckysheet.destroy()
}
} catch {
/* ignore */
}
}
/** 解析 sheet 数据、销毁旧实例并渲染报表 */
export function renderLuckysheetReport(
container: string,
sheets: any[],
options: Record<string, any> = {}
) {
parseLuckysheetSheets(sheets)
destroyLuckysheet()
setTimeout(() => {
luckysheet.create({
container,
...DEFAULT_REPORT_OPTIONS,
...options,
data: sheets,
})
}, 10)
}
/** 安全导出 Luckysheet无数据时提示并返回 false */
export function exportLuckysheetFile(filename: string, hasData = true): boolean {
if (!hasData) {
ElMessage.warning('暂无数据')
return false
}
try {
if (typeof luckysheet === 'undefined' || !luckysheet.getAllSheets) {
ElMessage.warning('暂无数据')
return false
}
const sheets = luckysheet.getAllSheets()
if (!sheets?.length) {
ElMessage.warning('暂无数据')
return false
}
exportExcel(sheets, filename)
return true
} catch {
ElMessage.warning('导出失败,请先加载报表数据')
return false
}
}

View File

@@ -28,6 +28,21 @@ export const Local = {
* @method remove 移除会话缓存 * @method remove 移除会话缓存
* @method clear 移除全部会话缓存 * @method clear 移除全部会话缓存
*/ */
const DEFAULT_THEME_NAME = '电能质量监测系统'
export function getStoredTheme(): { name?: string; logoUrl?: string; [key: string]: any } {
try {
const raw = window.localStorage.getItem('getTheme')
return raw ? JSON.parse(raw) : {}
} catch {
return {}
}
}
export function getStoredThemeName(): string {
return getStoredTheme().name || DEFAULT_THEME_NAME
}
export const Session = { export const Session = {
set(key: string, val: any) { set(key: string, val: any) {
window.sessionStorage.setItem(key, JSON.stringify(val)) window.sessionStorage.setItem(key, JSON.stringify(val))

View File

@@ -19,7 +19,7 @@ interface TableStoreParams {
resetCallback?: () => void // 重置 resetCallback?: () => void // 重置
loadCallback?: () => void // 接口调用后的回调 loadCallback?: () => void // 接口调用后的回调
exportProcessingData?: () => void //导出处理数据 exportProcessingData?: () => void //导出处理数据
beforeSearchFun?: () => void // 接口调用前的回调 beforeSearchFun?: () => void | boolean // 接口调用前的回调,返回 false 中止请求
} }
export default class TableStore { export default class TableStore {
@@ -75,7 +75,13 @@ export default class TableStore {
} }
index() { index() {
this.table.beforeSearchFun && this.table.beforeSearchFun() if (this.table.beforeSearchFun) {
const canSearch = this.table.beforeSearchFun()
if (canSearch === false) {
this.table.loading = false
return
}
}
this.table.data = [] this.table.data = []
this.table.loading = true this.table.loading = true
// 重置用的数据数据 // 重置用的数据数据

42
src/utils/waveCache.ts Normal file
View File

@@ -0,0 +1,42 @@
const MAX_CACHE_SIZE = 30
const cache = new Map<string, unknown>()
function dataFingerprint(data: unknown[][] | undefined): string {
if (!data?.length) return '0'
const first = data[0]
const last = data[data.length - 1]
return `${data.length}:${first?.[0]}:${last?.[0]}`
}
export function buildWaveCacheKey(
type: 'shu' | 'rms',
wp: Record<string, any> | undefined,
value: number,
isOpen: boolean,
boxoList: Record<string, any>
): string {
if (!wp) return ''
const waveFp =
type === 'shu' ? dataFingerprint(wp.listWaveData) : dataFingerprint(wp.listRmsData)
const boxoFp = boxoList?.startTime ?? boxoList?.lineName ?? boxoList?.equipmentName ?? ''
return `${type}|${wp.time}|${wp.waveType}|${wp.iphasic}|${value}|${isOpen}|${waveFp}|${boxoFp}`
}
export function getWaveCache<T>(key: string): T | null {
if (!key || !cache.has(key)) return null
const value = cache.get(key) as T
cache.delete(key)
cache.set(key, value)
return value
}
export function setWaveCache(key: string, value: unknown): void {
if (!key) return
if (cache.has(key)) cache.delete(key)
cache.set(key, value)
while (cache.size > MAX_CACHE_SIZE) {
const oldest = cache.keys().next().value
if (oldest !== undefined) cache.delete(oldest)
}
}

View File

@@ -0,0 +1,96 @@
import { toRaw } from 'vue'
type WorkerMessageHandler = (data: any) => void
let shuWorker: Worker | null = null
let rmsWorker: Worker | null = null
/** 递归剥离 Vue 响应式代理,得到可 structuredClone 的纯对象 */
export function toPlainDeep<T>(value: T): T {
const raw = toRaw(value as object) as T
if (Array.isArray(raw)) {
return raw.map(item => toPlainDeep(item)) as T
}
if (raw !== null && typeof raw === 'object') {
const out: Record<string, unknown> = {}
for (const [key, val] of Object.entries(raw)) {
out[key] = toPlainDeep(val)
}
return out as T
}
return raw
}
const BOXO_LIST_KEYS = [
'systemType',
'powerStationName',
'measurementPointName',
'startTime',
'featureAmplitude',
'duration',
'engineeringName',
'equipmentName',
'evtParamVVaDepth',
'evtParamTm',
'lineName',
'persistTime',
'subName'
] as const
export function buildWorkerPayload(
type: 'shu' | 'rms',
wp: Record<string, any>,
boxoList: Record<string, any>,
extras: { requestId: number; value: number; isOpen: boolean; iphasic?: number }
) {
const plainWp = toPlainDeep(wp)
const wpPayload: Record<string, unknown> = {
pt: plainWp.pt,
ct: plainWp.ct,
waveTitle: plainWp.waveTitle,
iphasic: plainWp.iphasic,
time: plainWp.time,
waveType: plainWp.waveType,
yzd: plainWp.yzd
}
if (type === 'shu') {
wpPayload.listWaveData = plainWp.listWaveData
} else {
wpPayload.listRmsData = plainWp.listRmsData
}
const plainBoxo: Record<string, unknown> = {}
const rawBoxo = toPlainDeep(boxoList)
for (const key of BOXO_LIST_KEYS) {
if (rawBoxo[key] !== undefined) {
plainBoxo[key] = rawBoxo[key]
}
}
return {
requestId: extras.requestId,
value: extras.value,
isOpen: extras.isOpen,
iphasic: extras.iphasic,
wp: wpPayload,
boxoList: plainBoxo
}
}
export function getShuWorker(onMessage: WorkerMessageHandler): Worker {
if (!shuWorker) {
shuWorker = new Worker(new URL('../components/echarts/shuWorker.js', import.meta.url))
}
shuWorker.onmessage = e => onMessage(e.data)
shuWorker.onerror = error => console.error('Shu worker error:', error)
return shuWorker
}
export function getRmsWorker(onMessage: WorkerMessageHandler): Worker {
if (!rmsWorker) {
rmsWorker = new Worker(new URL('../components/echarts/rmsWorker.js', import.meta.url))
}
rmsWorker.onmessage = e => onMessage(e.data)
rmsWorker.onerror = error => console.error('Rms worker error:', error)
return rmsWorker
}

View File

@@ -92,8 +92,8 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1 return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
} }
}, },
{ title: '设备名称', field: 'ndid', align: 'center' },
{ title: '异常时间', field: 'evtTime', align: 'center', sortable: true }, { title: '异常时间', field: 'evtTime', align: 'center', sortable: true },
{ title: '设备名称', field: 'ndid', align: 'center' },
{ {
title: '告警代码', title: '告警代码',
field: 'code', field: 'code',

View File

@@ -99,16 +99,17 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1 return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
} }
}, },
{ title: '设备名称', field: 'equipmentName', align: 'center', width: 120 }, { title: '发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true },
{ title: '监测点名称', field: 'lineName', align: 'center', width: 140 }, { title: '工程名称', field: 'engineeringName', align: 'center', minWidth: 120 },
{ title: '工程名称', field: 'engineeringName', align: 'center', width: 120 }, { title: '项目名称', field: 'projectName', align: 'center', minWidth: 120 },
{ title: '项目名称', field: 'projectName', align: 'center', width: 120 }, { title: '设备名称', field: 'equipmentName', align: 'center', minWidth: 120 },
{ title: '发生时刻', field: 'startTime', align: 'center', width: 180, sortable: true }, { title: '监测点名称', field: 'lineName', align: 'center', minWidth: 120 },
{ {
title: '模块信息', title: '模块信息',
field: 'moduleNo', field: 'moduleNo',
align: 'center', align: 'center',
width: 100, minWidth: 100,
formatter: (row: any) => { formatter: (row: any) => {
return row.cellValue ? row.cellValue : '/' return row.cellValue ? row.cellValue : '/'
} }
@@ -117,7 +118,7 @@ const tableStore = new TableStore({
title: '告警代码', title: '告警代码',
field: 'code', field: 'code',
align: 'center', align: 'center',
width: 100, minWidth: 100,
formatter: (row: any) => { formatter: (row: any) => {
return row.cellValue ? '\u200B' + row.cellValue : '/' return row.cellValue ? '\u200B' + row.cellValue : '/'
}, },
@@ -125,13 +126,13 @@ const tableStore = new TableStore({
}, },
{ {
title: '事件描述', title: '事件描述',
minWidth: 250, minWidth: 300,
field: 'showName' field: 'showName'
}, },
{ {
title: '级别', title: '级别',
field: 'level', field: 'level',
width: 100, width: 110,
render: 'tag', render: 'tag',
custom: { custom: {
// 1:Ⅰ级 2:Ⅱ级 3:Ⅲ级 4:DEBUG 5:NORMAL 6:WARN 7:ERROR // 1:Ⅰ级 2:Ⅱ级 3:Ⅲ级 4:DEBUG 5:NORMAL 6:WARN 7:ERROR
@@ -202,7 +203,7 @@ tableStore.table.params.deviceTypeId = ''
tableStore.table.params.deviceTypeName = '' tableStore.table.params.deviceTypeName = ''
const deviceTreeOptions = ref<any>(props.deviceTree) const deviceTreeOptions = ref<any>(props.deviceTree)
deviceTreeOptions.value.map((item: any, index: any) => { deviceTreeOptions.value.map((item: any, index: any) => {
if (item.children.length == 0) { if (item?.children.length == 0) {
deviceTreeOptions.value.splice(index, 1) deviceTreeOptions.value.splice(index, 1)
} }
}) })

View File

@@ -78,10 +78,10 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1 return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
} }
}, },
{ title: '前置服务器名称', field: 'lineId', align: 'center', width: 120 },
{ title: '前置服务器ip', field: 'wavePath', align: 'center', width: 120 },
{ title: '进程号', field: 'clDid', align: 'center', width: 60 },
{ title: '发生时刻', field: 'startTime', align: 'center', width: 180, sortable: true }, { title: '发生时刻', field: 'startTime', align: 'center', width: 180, sortable: true },
{ title: '前置服务器名称', field: 'lineId', align: 'center', width: 150 },
{ title: '前置服务器ip', field: 'wavePath', align: 'center', width: 150 },
{ title: '进程号', field: 'clDid', align: 'center', width: 70 },
{ {
title: '事件描述', title: '事件描述',
@@ -102,7 +102,7 @@ const tableStore = new TableStore({
{ {
title: '级别', title: '级别',
field: 'level', field: 'level',
width: 100, width: 110,
render: 'tag', render: 'tag',
custom: { custom: {
// 1:Ⅰ级 2:Ⅱ级 3:Ⅲ级 4:DEBUG 5:NORMAL 6:WARN 7:ERROR // 1:Ⅰ级 2:Ⅱ级 3:Ⅲ级 4:DEBUG 5:NORMAL 6:WARN 7:ERROR
@@ -153,7 +153,7 @@ tableStore.table.params.searchValue = ''
tableStore.table.params.level = '' tableStore.table.params.level = ''
const deviceTreeOptions = ref<any>(props.deviceTree) const deviceTreeOptions = ref<any>(props.deviceTree)
deviceTreeOptions.value.map((item: any, index: any) => { deviceTreeOptions.value.map((item: any, index: any) => {
if (item.children.length == 0) { if (item?.children.length == 0) {
deviceTreeOptions.value.splice(index, 1) deviceTreeOptions.value.splice(index, 1)
} }
}) })

View File

@@ -2,16 +2,9 @@
<TableHeader datePicker ref="refheader" showExport> <TableHeader datePicker ref="refheader" showExport>
<template v-slot:select> <template v-slot:select>
<el-form-item label="数据来源"> <el-form-item label="数据来源">
<el-cascader <el-cascader v-model.trim="tableStore.table.params.cascader" filterable placeholder="请选择数据来源"
v-model.trim="tableStore.table.params.cascader" @change="sourceChange" :options="deviceTreeOptions" :show-all-levels="false"
filterable :props="{ checkStrictly: true, value: 'id', label: 'name' }" clearable></el-cascader>
placeholder="请选择数据来源"
@change="sourceChange"
:options="deviceTreeOptions"
:show-all-levels="false"
:props="{ checkStrictly: true, value: 'id', label: 'name' }"
clearable
></el-cascader>
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> --> <!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> -->
</el-form-item> </el-form-item>
<!-- <el-form-item label="级别"> <!-- <el-form-item label="级别">
@@ -83,14 +76,15 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1 return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
} }
}, },
{ title: '设备名称', field: 'equipmentName', align: 'center' }, { title: '发生时刻', field: 'startTime', align: 'center', sortable: true, minWidth: 180, },
{ title: '工程名称', field: 'engineeringName', align: 'center' }, { title: '工程名称', field: 'engineeringName', align: 'center', minWidth: 120, },
{ title: '项目名称', field: 'projectName', align: 'center' }, { title: '项目名称', field: 'projectName', align: 'center', minWidth: 120, },
{ title: '发生时刻', field: 'startTime', align: 'center', sortable: true }, { title: '设备名称', field: 'equipmentName', align: 'center', minWidth: 120, },
{ title: '事件描述', field: 'showName', align: 'center' }
{ title: '事件描述', field: 'showName', align: 'center', minWidth: 250, }
], ],
beforeSearchFun: () => {} beforeSearchFun: () => { }
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
@@ -112,7 +106,7 @@ tableStore.table.params.deviceTypeName = ''
const deviceTreeOptions = ref<any>(props.deviceTree) const deviceTreeOptions = ref<any>(props.deviceTree)
deviceTreeOptions.value.map((item: any, index: any) => { deviceTreeOptions.value.map((item: any, index: any) => {
if (item.children.length == 0) { if (item?.children.length == 0) {
deviceTreeOptions.value.splice(index, 1) deviceTreeOptions.value.splice(index, 1)
} }
}) })
@@ -143,6 +137,6 @@ onMounted(() => {
setTimeout(() => { setTimeout(() => {
tableStore.table.height = mainHeight(200).height as any tableStore.table.height = mainHeight(200).height as any
}, 0) }, 0)
const addMenu = () => {} const addMenu = () => { }
</script> </script>
<style></style> <style></style>

View File

@@ -3,16 +3,10 @@
<TableHeader datePicker showExport> <TableHeader datePicker showExport>
<template v-slot:select> <template v-slot:select>
<el-form-item label="数据来源"> <el-form-item label="数据来源">
<el-cascader <el-cascader placeholder="请选择数据来源" @change="sourceChange" filterable
placeholder="请选择数据来源" v-model.trim="tableStore.table.params.cascader" :options="deviceTreeOptions"
@change="sourceChange" :show-all-levels="false" :props="{ checkStrictly: true, value: 'id', label: 'name' }"
filterable clearable></el-cascader>
v-model.trim="tableStore.table.params.cascader"
:options="deviceTreeOptions"
:show-all-levels="false"
:props="{ checkStrictly: true, value: 'id', label: 'name' }"
clearable
></el-cascader>
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> --> <!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> -->
</el-form-item> </el-form-item>
<!-- <el-form-item label="级别"> <!-- <el-form-item label="级别">
@@ -26,13 +20,8 @@
<Table></Table> <Table></Table>
</div> </div>
<waveFormAnalysis <waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
v-loading="loading" @handleHideCharts="isWaveCharts = false" :wp="wp" />
v-if="isWaveCharts"
ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false"
:wp="wp"
/>
<!-- <div style="height: 300px;"> --> <!-- <div style="height: 300px;"> -->
<!-- <div style="padding: 10px" v-if="!view" v-loading="loading"> <!-- <div style="padding: 10px" v-if="!view" v-loading="loading">
@@ -129,16 +118,20 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1 return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
} }
}, },
{ title: '设备名称', field: 'equipmentName', minWidth: 120, align: 'center' }, { title: '暂降发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true },
{ title: '暂降(骤升)幅值(%)', minWidth: 160, field: 'evtParamVVaDepth', align: 'center', sortable: true },
{ title: '持续时间(s)', field: 'evtParamTm', minWidth: 110, align: 'center', sortable: true },
{ title: '相别', field: 'evtParamPhase', minWidth: 80, align: 'center' },
{ title: '触发类型', field: 'showName', minWidth: 120, align: 'center' },
{ title: '工程名称', field: 'engineeringName', minWidth: 120, align: 'center' }, { title: '工程名称', field: 'engineeringName', minWidth: 120, align: 'center' },
{ title: '项目名称', field: 'projectName', minWidth: 120, align: 'center' }, { title: '项目名称', field: 'projectName', minWidth: 120, align: 'center' },
{ title: '发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true }, { title: '设备名称', field: 'equipmentName', minWidth: 120, align: 'center' },
{ title: '监测点名称', field: 'lineName', minWidth: 120, align: 'center' }, { title: '监测点名称', field: 'lineName', minWidth: 120, align: 'center' },
{ title: '事件描述', field: 'showName', minWidth: 120, align: 'center' }, { title: '发生位置', field: 'evtParamPosition', minWidth: 150, align: 'center' },
{ title: '事件发生位置', field: 'evtParamPosition', minWidth: 150, align: 'center' },
{ title: '相别', field: 'evtParamPhase', minWidth: 80, align: 'center' },
{ title: '持续时间(s)', field: 'evtParamTm', minWidth: 80, align: 'center', sortable: true },
{ title: '暂降(聚升)幅值(%)', minWidth: 100, field: 'evtParamVVaDepth', align: 'center', sortable: true },
{ {
title: '操作', title: '操作',
@@ -219,6 +212,7 @@ const tableStore = new TableStore({
return !row.wavePath return !row.wavePath
}, },
click: row => { click: row => {
ElMessage.info('下载中......')
getFileZip({ eventId: row.id }).then(res => { getFileZip({ eventId: row.id }).then(res => {
let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接 let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接
const url = window.URL.createObjectURL(blob) const url = window.URL.createObjectURL(blob)
@@ -228,6 +222,7 @@ const tableStore = new TableStore({
document.body.appendChild(link) document.body.appendChild(link)
link.click() //执行下载 link.click() //执行下载
document.body.removeChild(link) //释放标签 document.body.removeChild(link) //释放标签
ElMessage.success('波形下载成功')
}) })
} }
}, },
@@ -238,7 +233,7 @@ const tableStore = new TableStore({
icon: 'el-icon-DataLine', icon: 'el-icon-DataLine',
render: 'basicButton', render: 'basicButton',
disabled: row => { disabled: row => {
return row.showName != '未知' return row.wavePath
} }
}, },
{ {
@@ -295,7 +290,7 @@ tableStore.table.params.deviceTypeName = ''
const deviceTreeOptions: any = ref<any>(props.deviceTree) const deviceTreeOptions: any = ref<any>(props.deviceTree)
deviceTreeOptions.value.map((item: any, index: any) => { deviceTreeOptions.value.map((item: any, index: any) => {
if (item.children.length == 0) { if (item?.children.length == 0) {
deviceTreeOptions.value.splice(index, 1) deviceTreeOptions.value.splice(index, 1)
} }
}) })
@@ -380,6 +375,6 @@ setTimeout(() => {
tableStore.table.height = mainHeight(200).height as any tableStore.table.height = mainHeight(200).height as any
}, 0) }, 0)
const addMenu = () => {} const addMenu = () => { }
</script> </script>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>

View File

@@ -9,44 +9,23 @@
<DatePicker ref="datePickerRef"></DatePicker> <DatePicker ref="datePickerRef"></DatePicker>
</el-form-item> </el-form-item>
<el-form-item label="统计指标:"> <el-form-item label="统计指标:">
<el-select <el-select style="width: 200px" v-model.trim="formInline.statisticalId" filterable
style="width: 200px" @change="frequencyFlag" placeholder="请选择">
v-model.trim="formInline.statisticalId" <el-option v-for="item in zblist" :key="item.value" :label="item.label"
filterable :value="item.value"></el-option>
@change="frequencyFlag"
placeholder="请选择"
>
<el-option
v-for="item in zblist"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="谐波次数:" v-show="frequencyShow"> <el-form-item label="谐波次数:" v-show="frequencyShow">
<el-select <el-select v-model.trim="formInline.frequency" filterable placeholder="请选择"
v-model.trim="formInline.frequency" style="width: 100px">
filterable <el-option v-for="item in 49" :key="item + 1" :label="item + 1"
placeholder="请选择" :value="item + 1"></el-option>
style="width: 100px"
>
<el-option
v-for="item in 49"
:key="item + 1"
:label="item + 1"
:value="item + 1"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="值类型:"> <el-form-item label="值类型:">
<el-select v-model.trim="formInline.valueType" filterable placeholder="请选择"> <el-select v-model.trim="formInline.valueType" filterable placeholder="请选择">
<el-option <el-option v-for="item in typelist" :key="item.value" :label="item.label"
v-for="item in typelist" :value="item.value"></el-option>
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</template> </template>
@@ -149,7 +128,7 @@ const deviceTypeChange = (val: any, obj: any) => {
nodeClick(obj) nodeClick(obj)
} }
const nodeClick = async (e: anyObj) => { const nodeClick = async (e: anyObj) => {
if (e.level == 2 && flag.value) { if ((e.level == 2 || e.level == 3) && flag.value) {
formInline.devId = e.id formInline.devId = e.id
loading.value = true loading.value = true
if (zblist.value.length === 0) { if (zblist.value.length === 0) {
@@ -261,19 +240,19 @@ const setEchart = () => {
// ]), // ]),
data: timeControl.value data: timeControl.value
? completeTimeSeries( ? completeTimeSeries(
phaseList[j].map(item => [ phaseList[j].map(item => [
item.time, item.time,
Math.floor(item.statisticalData * 100) / 100, Math.floor(item.statisticalData * 100) / 100,
unit, unit,
lineName.type lineName.type
]) ])
) )
: phaseList[j].map(item => [ : phaseList[j].map(item => [
item.time, item.time,
Math.floor(item.statisticalData * 100) / 100, Math.floor(item.statisticalData * 100) / 100,
unit, unit,
lineName.type lineName.type
]), ]),
lineStyle: lineName, lineStyle: lineName,
yAxisIndex: unit.indexOf(units) yAxisIndex: unit.indexOf(units)
@@ -315,9 +294,8 @@ const setEchart = () => {
marker = `<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>` marker = `<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>`
} }
str += `${marker}${el.seriesName.split('(')[0]}${ str += `${marker}${el.seriesName.split('(')[0]}${el.value[1] != null ? el.value[1] + ' ' + (el.value[2] == 'null' ? '' : el.value[2]) : '-'
el.value[1] != null ? el.value[1] + ' ' + (el.value[2] == 'null' ? '' : el.value[2]) : '-' }<br>`
}<br>`
}) })
return str return str
} }

View File

@@ -122,15 +122,16 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1 return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
} }
}, },
{ title: '事件描述', field: 'showName', minWidth: 150 }, { title: '暂降发生时刻', field: 'startTime', sortable: true, minWidth: 180 },
{ {
title: '发生位置', field: 'evtParamPosition', minWidth: 150, title: '暂降(骤升)幅值(%)',
field: 'evtParamVVaDepth',
minWidth: 160,
formatter: (row: any) => { formatter: (row: any) => {
const val = row.cellValue let a = row.cellValue.split('%')[0] - 0
if (val === null || val === undefined || val === '' || val === '-') return '/' return a ? a.toFixed(2) : '/'
},
return val sortable: true
}
}, },
{ {
title: '持续时间(s)', title: '持续时间(s)',
@@ -145,17 +146,19 @@ const tableStore = new TableStore({
return Math.floor(num * 10000) / 100 return Math.floor(num * 10000) / 100
} }
}, },
{ title: '触发类型', field: 'showName', minWidth: 150 },
{ {
title: '暂降(聚升)幅值(%)', title: '发生位置', field: 'evtParamPosition', minWidth: 150,
field: 'evtParamVVaDepth',
minWidth: 150,
formatter: (row: any) => { formatter: (row: any) => {
let a = row.cellValue.split('%')[0] - 0 const val = row.cellValue
return a ? a.toFixed(2) : '/' if (val === null || val === undefined || val === '' || val === '-') return '/'
},
sortable: true return val
}
}, },
{ title: '发生时刻', field: 'startTime', sortable: true, minWidth: 180 },
{ {
title: '操作', title: '操作',
fixed: 'right', fixed: 'right',
@@ -229,6 +232,7 @@ const tableStore = new TableStore({
return !row.wavePath return !row.wavePath
}, },
click: row => { click: row => {
ElMessage.info('下载中......')
getFileZip({ eventId: row.id }).then(res => { getFileZip({ eventId: row.id }).then(res => {
let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接 let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接
const url = window.URL.createObjectURL(blob) const url = window.URL.createObjectURL(blob)
@@ -238,6 +242,7 @@ const tableStore = new TableStore({
document.body.appendChild(link) document.body.appendChild(link)
link.click() //执行下载 link.click() //执行下载
document.body.removeChild(link) //释放标签 document.body.removeChild(link) //释放标签
ElMessage.success('波形下载成功')
}) })
} }
} }
@@ -262,7 +267,7 @@ const deviceTypeChange = (val: any, obj: any) => {
} }
const nodeClick = async (e: anyObj) => { const nodeClick = async (e: anyObj) => {
// console.log("🚀 ~ nodeClick ~ e:", e) // console.log("🚀 ~ nodeClick ~ e:", e)
if (e.level == 2 && flag.value) { if ((e.level == 2 || e.level == 3) && flag.value) {
loading.value = false loading.value = false
tableStore.table.params.deviceId = e.id tableStore.table.params.deviceId = e.id
nextTick(() => { nextTick(() => {

View File

@@ -39,7 +39,7 @@
</TableHeader> </TableHeader>
</div> </div>
<el-empty description="暂无数据" v-if="!echartsData" style="flex: 1"></el-empty> <el-empty description="暂无数据" v-if="!echartsData" style="flex: 1;margin-top: 10%"></el-empty>
<template v-else> <template v-else>
<div :style="echartHeight"> <div :style="echartHeight">
<MyEchart :options="echartsData" /> <MyEchart :options="echartsData" />

View File

@@ -42,13 +42,14 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, provide } from 'vue' import { onMounted, onUnmounted, ref, provide } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue' import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { exportExcel } from '@/views/system/reportForms/export.js' import { destroyLuckysheet, exportLuckysheetFile, renderLuckysheetReport } from '@/utils/luckysheetHelper'
import { isLineTreeLeaf } from '@/components/tree/govern/lineTreeUtils'
import DatePicker from '@/components/form/datePicker/time.vue' import DatePicker from '@/components/form/datePicker/time.vue'
import CloudDeviceEntryTree from '@/components/tree/govern/cloudDeviceEntryTreeZL.vue' import CloudDeviceEntryTree from '@/components/tree/govern/cloudDeviceEntryTreeZL.vue'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit' import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
@@ -77,35 +78,20 @@ const tableStore = new TableStore({
// ;(tableStore.table.params.startTime = datePickerRef.value.timeValue[0]), // ;(tableStore.table.params.startTime = datePickerRef.value.timeValue[0]),
// (tableStore.table.params.endTime = datePickerRef.value.timeValue[1]), // (tableStore.table.params.endTime = datePickerRef.value.timeValue[1]),
if (!tableStore.table.params.tempId) { if (!tableStore.table.params.tempId) {
return ElMessage.warning('请选择模板') ElMessage.warning('请选择模板')
return false
}
if (!dotList.value?.id) {
ElMessage.warning('请选择监测点')
return false
} }
delete tableStore.table.params.searchBeginTime delete tableStore.table.params.searchBeginTime
delete tableStore.table.params.searchEndTime delete tableStore.table.params.searchEndTime
delete tableStore.table.params.timeFlag delete tableStore.table.params.timeFlag
}, },
loadCallback: () => { loadCallback: () => {
console.log('🚀 ~ tableStore.table:', tableStore.table.data)
name.value = dotList.value.name name.value = dotList.value.name
// tableStore.table.data.forEach((item: any) => { renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
// item.data1 ? (item.data = JSON.parse(item.data1)) : ''
// item.celldata.forEach((k: any) => {
// item.data[k.r][k.c].v ? (item.data[k.r][k.c] = k.v) : ''
// })
// })
setTimeout(() => {
luckysheet.create({
container: 'luckysheet',
title: '', // 表 头名
lang: 'zh', // 中文
showtoolbar: false, // 是否显示工具栏
showinfobar: false, // 是否显示顶部信息栏
showsheetbar: true, // 是否显示底部sheet按钮
allowEdit: false, // 禁止所有编辑操作(必填)
data: tableStore.table.data
// tableStore.table.data
})
}, 10)
} }
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
@@ -115,6 +101,9 @@ const flag = ref(true)
onMounted(() => { onMounted(() => {
initListByIds() initListByIds()
}) })
onUnmounted(() => {
destroyLuckysheet()
})
const idList = ref([]) const idList = ref([])
// 监测对象 // 监测对象
@@ -131,7 +120,6 @@ const initListByIds = () => {
}) })
} }
const stencil = (val: any) => { const stencil = (val: any) => {
console.log('🚀 ~ stencil ~ val:', val)
templatePolicy.value = val.filter((item: any) => item.excelType == '4') templatePolicy.value = val.filter((item: any) => item.excelType == '4')
Template.value = templatePolicy.value[0] Template.value = templatePolicy.value[0]
reportForm.value = templatePolicy.value[0]?.excelType reportForm.value = templatePolicy.value[0]?.excelType
@@ -142,7 +130,7 @@ const changetype = (val: any) => {
} }
const handleNodeClick = (data: any, node: any) => { const handleNodeClick = (data: any, node: any) => {
if (data?.level == 3) { if (isLineTreeLeaf(data) || data?.level == 3) {
dotList.value = data dotList.value = data
setTimeout(() => { setTimeout(() => {
tableStore.index() tableStore.index()
@@ -160,7 +148,7 @@ const exportEvent = () => {
// 格式化YYYY - MM - DD补零 // 格式化YYYY - MM - DD补零
const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}` const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}`
exportExcel(luckysheet.getAllSheets(), name.value + formattedDate) exportLuckysheetFile(name.value + formattedDate, tableStore.table.data.length > 0)
} }
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@@ -484,7 +484,7 @@
<div style="height: calc(100vh - 340px)" v-if="dataSet.indexOf('_trenddata') != -1"> <div style="height: calc(100vh - 340px)" v-if="dataSet.indexOf('_trenddata') != -1">
<Trend ref="trendRef" :TrendList="TrendList"></Trend> <Trend ref="trendRef" :TrendList="TrendList"></Trend>
</div> </div>
<!-- 电数据 --> <!-- 电数据 -->
<div style="height: calc(100vh - 340px)" v-if="dataSet.indexOf('_kilowattHour') != -1"> <div style="height: calc(100vh - 340px)" v-if="dataSet.indexOf('_kilowattHour') != -1">
<electroplating ref="electroplatingRef" :TrendList="TrendList"></electroplating> <electroplating ref="electroplatingRef" :TrendList="TrendList"></electroplating>
</div> </div>
@@ -585,7 +585,7 @@ import { ElMessage } from 'element-plus'
import DatePicker from '@/components/form/datePicker/index.vue' import DatePicker from '@/components/form/datePicker/index.vue'
import Trend from './tabs/trend.vue' //趋势数据 import Trend from './tabs/trend.vue' //趋势数据
import realTime from './tabs/realtime.vue' //实时数据-主界面 import realTime from './tabs/realtime.vue' //实时数据-主界面
import electroplating from './tabs/electroplating.vue' //电数据-主界面 import electroplating from './tabs/electroplating.vue' //电数据-主界面
import realTrend from './tabs/components/realtrend.vue' //实时数据-实时趋势 import realTrend from './tabs/components/realtrend.vue' //实时数据-实时趋势
import operatingTrend from './tabs/operatingTrend.vue' //运行趋势 import operatingTrend from './tabs/operatingTrend.vue' //运行趋势
import harmonicSpectrum from './tabs/components/harmonicSpectrum.vue' //实时数据-谐波频谱子页面 import harmonicSpectrum from './tabs/components/harmonicSpectrum.vue' //实时数据-谐波频谱子页面
@@ -705,6 +705,25 @@ const activeTrendName: any = ref(0)
const trendTimer: any = ref() const trendTimer: any = ref()
const trendDataTime: any = ref() const trendDataTime: any = ref()
const showButton = ref(false) const showButton = ref(false)
const decodeMqttPayload = (message: any) => {
try {
return JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message))))
} catch {
return {}
}
}
/** 谐波频谱 MQTT 消息(命名函数,便于 off 避免重复注册) */
const onMqttTrendMessage = (topic: any, message: any) => {
const obj = decodeMqttPayload(message) || {}
if ((obj.hasOwnProperty('data1') || obj.hasOwnProperty('data2')) && obj.dataTime) {
trendDataTime.value = obj.dataTime
realTrendRef.value?.setRealTrendData(obj)
tableLoading.value = false
}
}
//谐波频谱方法 //谐波频谱方法
const handleTrend = async () => { const handleTrend = async () => {
realTimeFlag.value = false realTimeFlag.value = false
@@ -728,21 +747,7 @@ const handleTrend = async () => {
// console.log(res, '获取谐波频谱数据') // console.log(res, '获取谐波频谱数据')
}) })
}, 30000) }, 30000)
mqttRef.value.on('message', (topic: any, message: any) => { bindMqttMessage(onMqttTrendMessage)
let obj = JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message)))) || {}
if ((obj.hasOwnProperty('data1') || obj.hasOwnProperty('data2')) && obj.dataTime) {
trendDataTime.value = obj.dataTime
realTrendRef.value && realTrendRef.value.setRealTrendData(obj)
tableLoading.value = false
// console.log(
// '谐波频谱---mqtt接收到消息',
// JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message))))
// )
}
// else {
// trendDataTime.value = obj.dataTime
// }
})
} else { } else {
ElMessage.warning('设备应答失败') ElMessage.warning('设备应答失败')
} }
@@ -885,8 +890,8 @@ const lineId: any = ref('')
const dataLevel: any = ref('') const dataLevel: any = ref('')
const dataSource = ref([]) const dataSource = ref([])
const engineeringName = ref('') const engineeringName = ref('')
const nodeClick = async (e: anyObj, node: any) => { const nodeClick = async (e: anyObj, node?: any) => {
if (e == undefined || e.level == 2) { if (e == undefined) {
return (loading.value = false) return (loading.value = false)
} }
searchValue.value = '' searchValue.value = ''
@@ -900,7 +905,7 @@ const nodeClick = async (e: anyObj, node: any) => {
} }
//选中设备名称后,点击标签页也能查询数据,要求点击设备名称后,点击标签页默认查询第一个监测点数据 //选中设备名称后,点击标签页也能查询数据,要求点击设备名称后,点击标签页默认查询第一个监测点数据
if (e.level == 3 || e.level == 2) { if (e.level == 3 ) {
engineeringName.value = node?.parent.parent.data.name engineeringName.value = node?.parent.parent.data.name
await queryDictType({ await queryDictType({
@@ -940,7 +945,7 @@ const nodeClick = async (e: anyObj, node: any) => {
if (item.type === 'trenddata') { if (item.type === 'trenddata') {
item.id = item.id + '_trenddata' item.id = item.id + '_trenddata'
} }
//电数据 //电数据
if (item.type === 'kilowattHour') { if (item.type === 'kilowattHour') {
item.id = item.id + '_kilowattHour' item.id = item.id + '_kilowattHour'
} }
@@ -995,6 +1000,14 @@ const trendRef: any = ref()
const eventRef: any = ref() const eventRef: any = ref()
const mqttRef = ref() const mqttRef = ref()
const url: any = window.localStorage.getItem('MQTTURL') const url: any = window.localStorage.getItem('MQTTURL')
/** 同一 handler 先 off 再 on避免重复 message 监听 */
const bindMqttMessage = (handler: (topic: any, message: any) => void) => {
if (!mqttRef.value) return
mqttRef.value.off('message', handler)
mqttRef.value.on('message', handler)
}
const connectMqtt = () => { const connectMqtt = () => {
if (mqttRef.value) { if (mqttRef.value) {
if (mqttRef.value.connected) { if (mqttRef.value.connected) {
@@ -1035,16 +1048,44 @@ const getRealDataMqttMsg = async () => {
// console.log(res, '获取基础实时数据') // console.log(res, '获取基础实时数据')
}) })
}, 30000) }, 30000)
mqttRef.value.on('message', (topic: any, message: any) => { bindMqttMessage(onMqttRealDataMessage)
// console.log( //2.建立mqtt通讯
// '实时数据&实时趋势---mqtt接收到消息', //每隔30s调用一下接口通知后台推送mqtt消息
// JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message))))
// )
let obj = JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message))))
if (lineId.value != obj.lineId || adminInfo.userIndex != obj.userId) return mqttRef.value.on('error', (error: any) => {
console.log('mqtt连接失败...', error)
mqttRef.value.end()
})
//处理mqtt数据 1转2除 2转1乘 mqttRef.value.on('close', function () {
console.log('mqtt客户端已断开连接.....')
})
setTimeout(() => {
tableLoading.value = false
}, 6000)
} else {
ElMessage.success('设备应答失败')
tableLoading.value = false
}
})
.catch(e => {
setTimeout(() => {
tableLoading.value = false
}, 0)
})
}
//tab点击事件
const realDataTimer: any = ref()
const mqttMessage = ref<any>({})
/** 实时数据 / 实时趋势 MQTT 消息(命名函数,便于 off 避免重复注册) */
const onMqttRealDataMessage = (topic: any, message: any) => {
let obj = decodeMqttPayload(message)
if (lineId.value != obj.lineId || adminInfo.userIndex != obj.userId) return
//处理mqtt数据 1转2除 2转1乘
//如果消息返回值是二次值,下拉框是二次值只需要单位换算 除以1000 //如果消息返回值是二次值,下拉框是二次值只需要单位换算 除以1000
//如果消息返回值是一次值,下拉框是一次值只需要单位换算 除以1000 //如果消息返回值是一次值,下拉框是一次值只需要单位换算 除以1000
if (obj.dataLevel == formInline.dataLevel) { if (obj.dataLevel == formInline.dataLevel) {
@@ -1178,36 +1219,8 @@ const getRealDataMqttMsg = async () => {
// sonTab.value == 1 && // sonTab.value == 1 &&
// realTrendRef.value && // realTrendRef.value &&
// realTrendRef.value.setRealTrendData(obj) // realTrendRef.value.setRealTrendData(obj)
})
//2.建立mqtt通讯
//每隔30s调用一下接口通知后台推送mqtt消息
mqttRef.value.on('error', (error: any) => {
console.log('mqtt连接失败...', error)
mqttRef.value.end()
})
mqttRef.value.on('close', function () {
console.log('mqtt客户端已断开连接.....')
})
setTimeout(() => {
tableLoading.value = false
}, 6000)
} else {
ElMessage.success('设备应答失败')
tableLoading.value = false
}
})
.catch(e => {
setTimeout(() => {
tableLoading.value = false
}, 0)
})
} }
//tab点击事件
const realDataTimer: any = ref()
const mqttMessage = ref<any>({})
const handleClick = async (tab?: any) => { const handleClick = async (tab?: any) => {
tableLoading.value = true tableLoading.value = true
showButton.value = false showButton.value = false
@@ -1272,7 +1285,7 @@ const handleClick = async (tab?: any) => {
tableLoading.value = false tableLoading.value = false
}, 0) }, 0)
} }
//电数据 //电数据
if (dataSet.value.includes('_kilowattHour')) { if (dataSet.value.includes('_kilowattHour')) {
let obj = { let obj = {
devId: deviceId.value, //e.id devId: deviceId.value, //e.id
@@ -1475,6 +1488,8 @@ const handleClick = async (tab?: any) => {
window.clearInterval(trendTimer.value) window.clearInterval(trendTimer.value)
} }
if (mqttRef.value) { if (mqttRef.value) {
mqttRef.value.off('message', onMqttTrendMessage)
mqttRef.value.off('message', onMqttRealDataMessage)
mqttRef.value.end() mqttRef.value.end()
} }
} }
@@ -1557,6 +1572,8 @@ onBeforeUnmount(() => {
realDataTimer.value = 0 realDataTimer.value = 0
trendTimer.value = 0 trendTimer.value = 0
if (mqttRef.value) { if (mqttRef.value) {
mqttRef.value.off('message', onMqttTrendMessage)
mqttRef.value.off('message', onMqttRealDataMessage)
mqttRef.value.end() mqttRef.value.end()
} }
}) })

View File

@@ -293,31 +293,31 @@ const setRealTrendData = (val: any) => {
if (selectValue.value == '2') { if (selectValue.value == '2') {
if (activeName.value == 2) { if (activeName.value == 2) {
if (numberPart % 2 !== 0 && numberPart < 17) { if (numberPart % 2 !== 0 && numberPart < 17) {
tableData.value[key] = val[key] tableData.value[key] = val[key].toFixed(2)
} }
} else { } else {
if (numberPart % 2 === 0) { if (numberPart % 2 === 0) {
tableData.value[key] = val[key] tableData.value[key] = val[key].toFixed(2)
} }
} }
} else { } else {
if (activeName.value == 2) { if (activeName.value == 2) {
if (numberPart % 2 === 0 && numberPart < 17) { if (numberPart % 2 === 0 && numberPart < 17) {
tableData.value[key] = val[key] tableData.value[key] = val[key].toFixed(2)
} }
} else { } else {
if (numberPart % 2 !== 0) { if (numberPart % 2 !== 0) {
tableData.value[key] = val[key] tableData.value[key] = val[key].toFixed(2)
} }
} }
} }
} else { } else {
if (activeName.value == 2) { if (activeName.value == 2) {
if (numberPart < 17) { if (numberPart < 17) {
tableData.value[key] = val[key] tableData.value[key] = val[key].toFixed(2)
} }
} else { } else {
tableData.value[key] = val[key] tableData.value[key] = val[key].toFixed(2)
} }
} }
} }

View File

@@ -20,25 +20,15 @@
<el-button @click="handleBack" :icon="Back">返回</el-button> <el-button @click="handleBack" :icon="Back">返回</el-button>
</div> </div>
<!-- v-loading="loading" --> <!-- -->
<el-tabs class="home_body" type="border-card" v-model.trim="activeName1" @tab-click="handleClick"> <el-tabs class="home_body" v-loading="loading" type="border-card" v-model.trim="activeName1" @tab-click="handleClick">
<el-tab-pane label="瞬时波形" name="ssbx" :style="'height:' + bxecharts + ';overflow-y: auto;'"> <el-tab-pane label="瞬时波形" name="ssbx" :style="'height:' + bxecharts + ';overflow-y: auto;'">
<shushiboxi <shushiboxi v-if="isWp && wp && activeName == 'ssbx' && showBoxi" :value="value" :boxoList="boxoList"
v-if="isWp && wp && activeName == 'ssbx' && showBoxi" :parentHeight="parentHeight" :wp="wp"></shushiboxi>
:value="value"
:boxoList="boxoList"
:parentHeight="parentHeight"
:wp="wp"
></shushiboxi>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="RMS波形" name="rmsbx" :style="'height:' + bxecharts + ';overflow-y: auto;'"> <el-tab-pane label="RMS波形" name="rmsbx" :style="'height:' + bxecharts + ';overflow-y: auto;'">
<rmsboxi <rmsboxi v-if="isWp && wp && activeName == 'rmsbx' && showBoxi" :value="value" :boxoList="boxoList"
v-if="isWp && wp && activeName == 'rmsbx' && showBoxi" :parentHeight="parentHeight" :wp="wp"></rmsboxi>
:value="value"
:boxoList="boxoList"
:parentHeight="parentHeight"
:wp="wp"
></rmsboxi>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
@@ -117,11 +107,10 @@ const getWpData = (val: any, list: any) => {
const changeView = () => { const changeView = () => {
showBoxi.value = false showBoxi.value = false
loading.value = true loading.value = true
setTimeout(() => { setTimeout(() => {
value.value = theTypeOfValue.value value.value = theTypeOfValue.value
showBoxi.value = true showBoxi.value = true
}, 500)
setTimeout(() => {
loading.value = false loading.value = false
}, 1000) }, 1000)
} }
@@ -148,7 +137,7 @@ const setHeight = (h: any, vh: any, num = 1) => {
bxecharts.value = mainHeight(vh, num).height bxecharts.value = mainHeight(vh, num).height
}, 100) }, 100)
} }
onMounted(() => {}) onMounted(() => { })
defineExpose({ getWpData, setHeight }) defineExpose({ getWpData, setHeight })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<!-- 数据数据 --> <!-- 数据数据 -->
<div> <div>
<TableHeader ref="tableHeaderRef" :showSearch="false" @selectChange="selectChange"> <TableHeader ref="tableHeaderRef" :showSearch="false" @selectChange="selectChange">
<template v-slot:select> <template v-slot:select>
@@ -310,7 +310,7 @@ const setEchart = () => {
exportCSV( exportCSV(
echartsData.value.options.series.map((item: any) => item.name), echartsData.value.options.series.map((item: any) => item.name),
dataList, dataList,
'电数据.csv' '电数据.csv'
) )
} }
} }

View File

@@ -4,13 +4,8 @@
<TableHeader datePicker ref="headerRef" :showReset="false"></TableHeader> <TableHeader datePicker ref="headerRef" :showReset="false"></TableHeader>
<Table ref="tableRef" /> <Table ref="tableRef" />
</div> </div>
<waveFormAnalysis <waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
v-loading="loading" @handleHideCharts="isWaveCharts = false" :wp="wp" />
v-if="isWaveCharts"
ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false"
:wp="wp"
/>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -57,16 +52,23 @@ const tableStore: any = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1 return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
} }
}, },
{ field: 'startTime', title: '发生时刻', minWidth: 170, sortable: true }, { field: 'startTime', title: '暂降发生时刻', minWidth: 180, sortable: true },
{ field: 'showName', title: '事件描述', minWidth: 120 },
{ {
field: 'phaseType', field: 'featureAmplitude',
title: '相别', title: '暂降(骤升)幅值(%)',
minWidth: 80, minWidth: 160,
sortable: true,
formatter: (row: any) => { formatter: (row: any) => {
return row.cellValue || '/' //row.cellValue = row.cellValue + '' ? row.cellValue.toFixed(2) : '/'
row.cellValue = row.cellValue != null ? Number(row.cellValue).toFixed(2) : '/'
if (String(row.cellValue).split('.')[1] == '00') {
row.cellValue = String(row.cellValue).split('.')[0]
}
return row.cellValue
} }
}, },
{ {
field: 'persistTime', field: 'persistTime',
title: '持续时间(s)', title: '持续时间(s)',
@@ -79,18 +81,17 @@ const tableStore: any = new TableStore({
sortable: true sortable: true
}, },
{ {
field: 'featureAmplitude', field: 'phaseType',
title: '暂降(聚升)幅值(%)', title: '相别',
minWidth: 130, minWidth: 80,
formatter: (row: any) => { formatter: (row: any) => {
//row.cellValue = row.cellValue + '' ? row.cellValue.toFixed(2) : '/' return row.cellValue || '/'
row.cellValue = row.cellValue != null ? Number(row.cellValue).toFixed(2) : '/'
if (String(row.cellValue).split('.')[1] == '00') {
row.cellValue = String(row.cellValue).split('.')[0]
}
return row.cellValue
} }
}, },
{ field: 'showName', title: '触发类型', minWidth: 120 },
{ {
title: '操作', title: '操作',
fixed: 'right', fixed: 'right',
@@ -149,7 +150,7 @@ const tableStore: any = new TableStore({
icon: 'el-icon-DataLine', icon: 'el-icon-DataLine',
render: 'basicButton', render: 'basicButton',
disabled: row => { disabled: row => {
return row.showName != '未知' return row.wavePath
} }
}, },
{ {
@@ -164,6 +165,7 @@ const tableStore: any = new TableStore({
return !row.wavePath return !row.wavePath
}, },
click: row => { click: row => {
ElMessage.info('下载中......')
getFileZip({ eventId: row.id }).then(res => { getFileZip({ eventId: row.id }).then(res => {
let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接 let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接
const url = window.URL.createObjectURL(blob) const url = window.URL.createObjectURL(blob)
@@ -173,6 +175,7 @@ const tableStore: any = new TableStore({
document.body.appendChild(link) document.body.appendChild(link)
link.click() //执行下载 link.click() //执行下载
document.body.removeChild(link) //释放标签 document.body.removeChild(link) //释放标签
ElMessage.success('波形下载成功')
}) })
} }
}, },
@@ -202,7 +205,7 @@ const tableStore: any = new TableStore({
tableStore.table.params.list = tableParams.value.list tableStore.table.params.list = tableParams.value.list
tableStore.table.params.type = 3 tableStore.table.params.type = 3
}, },
loadCallback: () => {} loadCallback: () => { }
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
const isWaveCharts = ref(false) const isWaveCharts = ref(false)

View File

@@ -238,7 +238,7 @@ const deviceTypeChange = (val: any, obj: any) => {
nodeClick(obj) nodeClick(obj)
} }
const nodeClick = (e: any) => { const nodeClick = (e: any) => {
if (e && (e.level == 2 || e.type == 'device')) { if (e && (e.level == 2 || e.level == 3 || e.type == 'device')) {
loading.value = true loading.value = true
nDid.value = e.ndid nDid.value = e.ndid
devId.value = e.id devId.value = e.id

View File

@@ -137,12 +137,11 @@ const deviceTypeChange = (val: any, obj: any) => {
} }
// 树节点点击 // 树节点点击
const nodeClick = (e: anyObj) => { const nodeClick = (e: anyObj) => {
console.log('🚀 ~ nodeClick ~ e:', e)
if (!e) { if (!e) {
loading.value = false loading.value = false
return return
} }
if (e.level == 2) { if (e.level == 2 || e.level == 3) {
pName.value = e.pName pName.value = e.pName
nDid.value = e.ndid nDid.value = e.ndid
loading.value = true loading.value = true

View File

@@ -44,8 +44,28 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1 return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
} }
}, },
{ field: 'startTime', title: '发生时刻', minWidth: 170, sortable: true }, { field: 'startTime', title: '暂降发生时刻', minWidth: 170, sortable: true },
{ field: 'showName', title: '事件描述', minWidth: 170 }, {
field: 'featureAmplitude',
title: '暂降(骤升)幅值(%)',
minWidth: 160,
formatter: (row: any) => {
row.cellValue = row.cellValue + '' ? row.cellValue.toFixed(2) : '/'
if (String(row.cellValue).split('.')[1] == '00') {
row.cellValue = String(row.cellValue).split('.')[0]
}
return row.cellValue
}, sortable: true
},
{
field: 'persistTime',
title: '持续时间(s)',
minWidth: 110,
formatter: (row: any) => {
row.cellValue = row.cellValue ? row.cellValue.toFixed(2) : '/'
return row.cellValue
}, sortable: true
},
{ {
field: 'phaseType', field: 'phaseType',
title: '相别', title: '相别',
@@ -55,27 +75,9 @@ const tableStore = new TableStore({
return row.cellValue return row.cellValue
} }
}, },
{
field: 'persistTime',
title: '持续时间(s)', { field: 'showName', title: '触发类型', minWidth: 170 },
minWidth: 100,
formatter: (row: any) => {
row.cellValue = row.cellValue ? row.cellValue.toFixed(2) : '/'
return row.cellValue
}, sortable: true
},
{
field: 'featureAmplitude',
title: '暂降(聚升)幅值(%)',
minWidth: 100,
formatter: (row: any) => {
row.cellValue = row.cellValue + '' ? row.cellValue.toFixed(2) : '/'
if (String(row.cellValue).split('.')[1] == '00') {
row.cellValue = String(row.cellValue).split('.')[0]
}
return row.cellValue
}, sortable: true
},
{ {
title: '操作', fixed: 'right', title: '操作', fixed: 'right',
width: 180, width: 180,
@@ -143,6 +145,7 @@ const tableStore = new TableStore({
return !row.wavePath return !row.wavePath
}, },
click: row => { click: row => {
ElMessage.info('下载中......')
getFileZip({ eventId: row.id }).then(res => { getFileZip({ eventId: row.id }).then(res => {
let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接 let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接
const url = window.URL.createObjectURL(blob) const url = window.URL.createObjectURL(blob)
@@ -152,7 +155,7 @@ const tableStore = new TableStore({
document.body.appendChild(link) document.body.appendChild(link)
link.click() //执行下载 link.click() //执行下载
document.body.removeChild(link) //释放标签 document.body.removeChild(link) //释放标签
ElMessage.success('波形下载成功')
}) })
} }

View File

@@ -1,13 +1,7 @@
<template> <template>
<div class="device-manage" :style="{ height: pageHeight.height }" v-loading="loading"> <div class="device-manage" :style="{ height: pageHeight.height }" v-loading="loading">
<DeviceTree <DeviceTree ref="treeRef" :showCheckbox="true" :default-checked-keys="defaultCheckedKeys"
ref="treeRef" @checkChange="checkChange" :height="35" :engineering="true"></DeviceTree>
:showCheckbox="true"
:default-checked-keys="defaultCheckedKeys"
@checkChange="checkChange"
:height="35"
:engineering="true"
></DeviceTree>
<div class="device-manage-right" :style="{ height: pageHeight.height }"> <div class="device-manage-right" :style="{ height: pageHeight.height }">
<vxe-table v-bind="defaultAttribute" :data="tableData" height="auto" style="width: 100%"> <vxe-table v-bind="defaultAttribute" :data="tableData" height="auto" style="width: 100%">
<vxe-column field="enginerName" title="工程名称"></vxe-column> <vxe-column field="enginerName" title="工程名称"></vxe-column>
@@ -36,6 +30,7 @@ const tableData = ref([])
const treeRef = ref(null) const treeRef = ref(null)
const ignoreCheckChange = ref(false) const ignoreCheckChange = ref(false)
const checkChange = (data: any) => { const checkChange = (data: any) => {
console.log("🚀 ~ checkChange ~ data:", data)
if (data == undefined) return (loading.value = false) if (data == undefined) return (loading.value = false)
if (data.data.pName == '便携式设备') { if (data.data.pName == '便携式设备') {
if (ignoreCheckChange.value) { if (ignoreCheckChange.value) {
@@ -47,7 +42,7 @@ const checkChange = (data: any) => {
return treeRef.value?.treRef?.treeRef2?.setCheckedKeys([]) return treeRef.value?.treRef?.treeRef2?.setCheckedKeys([])
} }
if (data.data.level === 2) { if (data.data.level === 2 || data.data.level === 3) {
if (data.checked) { if (data.checked) {
defaultCheckedKeys.value.push(data.data.id) defaultCheckedKeys.value.push(data.data.id)
} else { } else {
@@ -100,6 +95,7 @@ onMounted(() => {
overflow: hidden; overflow: hidden;
flex: 1; flex: 1;
padding: 10px 10px 10px 0; padding: 10px 10px 10px 0;
.el-descriptions__header { .el-descriptions__header {
height: 36px; height: 36px;
margin-bottom: 7px; margin-bottom: 7px;

View File

@@ -83,7 +83,7 @@
</div> </div>
</el-col> </el-col>
<el-col :span="12" class="mTop"> <el-col :span="12" class="mTop">
<el-checkbox v-model="formd.glfbfz">暂降幅值</el-checkbox> <el-checkbox v-model="formd.glfbfz">暂降(骤升)幅值</el-checkbox>
<el-checkbox v-model="formd.glfbsj">持续时间</el-checkbox> <el-checkbox v-model="formd.glfbsj">持续时间</el-checkbox>
</el-col> </el-col>
</el-row> </el-row>
@@ -144,6 +144,7 @@ import { genFileId, ElMessage, ElNotification } from 'element-plus'
import type { UploadProps, UploadUserFile } from 'element-plus' import type { UploadProps, UploadUserFile } from 'element-plus'
import pointTree from '@/components/tree/govern/pointTree.vue' import pointTree from '@/components/tree/govern/pointTree.vue'
import { getLineExport } from '@/api/harmonic-boot/cockpit/cockpit' import { getLineExport } from '@/api/harmonic-boot/cockpit/cockpit'
import { isReportMonitorPoint } from '@/components/tree/govern/lineTreeUtils'
defineOptions({ defineOptions({
name: 'TransientReport/monitoringpointReport' name: 'TransientReport/monitoringpointReport'
}) })
@@ -194,7 +195,7 @@ const pointTypeChange = (val: any, obj: any) => {
handleNodeClick(obj) handleNodeClick(obj)
} }
const handleNodeClick = (data: any,) => { const handleNodeClick = (data: any,) => {
dotList.value = data dotList.value = data
} }
// 上传 // 上传
const choose = (files: any) => { const choose = (files: any) => {
@@ -212,7 +213,7 @@ const choose = (files: any) => {
//生成报告 //生成报告
const exportEvent = () => { const exportEvent = () => {
if (dotList.value?.level != 3) { if (!isReportMonitorPoint(dotList.value)) {
return ElMessage.warning('请选择监测点进行报告生成!') return ElMessage.warning('请选择监测点进行报告生成!')
} }
let a = '' let a = ''

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="default-main" :style="height" style="display: flex; height: 100%; overflow: hidden"> <div class="default-main" :style="height" style="display: flex; overflow: hidden">
<div style="width: 280px; flex-shrink: 0; height: 100%; overflow: hidden"> <div style="width: 280px; flex-shrink: 0; height: 100%; overflow: hidden">
<pointTree <pointTree
ref="TerminalRef" ref="TerminalRef"
@@ -73,6 +73,7 @@ import { genFileId, ElMessage, ElNotification } from 'element-plus'
import type { UploadProps, UploadUserFile } from 'element-plus' import type { UploadProps, UploadUserFile } from 'element-plus'
import pointTree from '@/components/tree/govern/pointTree.vue' import pointTree from '@/components/tree/govern/pointTree.vue'
import { exportModel } from '@/api/cs-harmonic-boot/datatrend' import { exportModel } from '@/api/cs-harmonic-boot/datatrend'
import { isReportMonitorPoint } from '@/components/tree/govern/lineTreeUtils'
defineOptions({ defineOptions({
name: 'harmonic-boot/report/word' name: 'harmonic-boot/report/word'
}) })
@@ -97,7 +98,9 @@ const pointTypeChange = (val: any, obj: any) => {
handleNodeClick(obj) handleNodeClick(obj)
} }
const handleNodeClick = (data: any) => { const handleNodeClick = (data: any) => {
dotList.value = data if (isReportMonitorPoint(data)) {
dotList.value = data
}
} }
// 上传 // 上传
const choose = (files: any) => { const choose = (files: any) => {
@@ -115,7 +118,7 @@ const choose = (files: any) => {
// 生成 // 生成
const exportEvent = () => { const exportEvent = () => {
console.log('🚀 ~ exportEvent ~ dotList.value:', dotList.value) console.log('🚀 ~ exportEvent ~ dotList.value:', dotList.value)
if (dotList.value?.level != 3) { if (!isReportMonitorPoint(dotList.value)) {
return ElMessage.warning('请选择监测点进行报告生成!') return ElMessage.warning('请选择监测点进行报告生成!')
} }
let form = new FormData() let form = new FormData()

View File

@@ -29,13 +29,14 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, provide } from 'vue' import { onMounted, onUnmounted, ref, provide } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import PointTree from '@/components/tree/govern/pointTree.vue' import PointTree from '@/components/tree/govern/pointTree.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { exportExcel } from '@/views/system/reportForms/export.js' import { destroyLuckysheet, exportLuckysheetFile, renderLuckysheetReport } from '@/utils/luckysheetHelper'
import { isLineTreeLeaf } from '@/components/tree/govern/lineTreeUtils'
import 'splitpanes/dist/splitpanes.css' import 'splitpanes/dist/splitpanes.css'
import { Splitpanes, Pane } from 'splitpanes' import { Splitpanes, Pane } from 'splitpanes'
// import data from './123.json' // import data from './123.json'
@@ -74,25 +75,7 @@ const tableStore = new TableStore({
tableStore.table.params.lineId = dotList.value.id tableStore.table.params.lineId = dotList.value.id
}, },
loadCallback: () => { loadCallback: () => {
tableStore.table.data.forEach((item: any) => { renderLuckysheetReport('luckysheet', tableStore.table.data)
item.data1 ? (item.data = JSON.parse(item.data1)) : ''
item.celldata.forEach((k: any) => {
item.data[k.r][k.c].v ? (item.data[k.r][k.c] = k.v) : ''
})
})
setTimeout(() => {
luckysheet.create({
container: 'luckysheet',
title: '', // 表 头名
lang: 'zh', // 中文
showtoolbar: false, // 是否显示工具栏
showinfobar: false, // 是否显示顶部信息栏
showsheetbar: true, // 是否显示底部sheet按钮
data: tableStore.table.data
// tableStore.table.data
})
}, 10)
} }
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
@@ -104,6 +87,9 @@ onMounted(() => {
size.value = ((280 / (dom.offsetWidth - 7)) * 100) size.value = ((280 / (dom.offsetWidth - 7)) * 100)
} }
}) })
onUnmounted(() => {
destroyLuckysheet()
})
const stencil = (val: any) => { const stencil = (val: any) => {
@@ -118,7 +104,7 @@ const changetype = (val: any) => {
} }
const handleNodeClick = (data: any, node: any) => { const handleNodeClick = (data: any, node: any) => {
if (data?.type == "line") { if (isLineTreeLeaf(data)) {
dotList.value = data dotList.value = data
setTimeout(() => { setTimeout(() => {
tableStore.index() tableStore.index()
@@ -127,7 +113,7 @@ const handleNodeClick = (data: any, node: any) => {
} }
const exportEvent = () => { const exportEvent = () => {
exportExcel(luckysheet.getAllSheets(), '统计报表下载') exportLuckysheetFile('统计报表下载', tableStore.table.data.length > 0)
} }
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@@ -31,14 +31,15 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, provide } from 'vue' import { ref, provide, onUnmounted } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue' import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { exportExcel } from '@/views/system/reportForms/export.js' import { destroyLuckysheet, exportLuckysheetFile, renderLuckysheetReport } from '@/utils/luckysheetHelper'
import DatePicker from '@/components/form/datePicker/time.vue' import DatePicker from '@/components/form/datePicker/time.vue'
import { ElMessage } from 'element-plus'
const name = ref('') const name = ref('')
// import data from './123.json' // import data from './123.json'
defineOptions({ defineOptions({
@@ -61,33 +62,19 @@ const tableStore = new TableStore({
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.tempId = Template.value.id tableStore.table.params.tempId = Template.value.id
tableStore.table.params.lineId = dotList.value.id tableStore.table.params.lineId = dotList.value.id
tableStore.table.params.startTime = datePickerRef.value.timeValue[0], if (!datePickerRef.value?.timeValue?.[0] || !datePickerRef.value?.timeValue?.[1]) {
tableStore.table.params.endTime = datePickerRef.value.timeValue[1], ElMessage.warning('请选择时间')
return false
}
tableStore.table.params.startTime = datePickerRef.value.timeValue[0]
tableStore.table.params.endTime = datePickerRef.value.timeValue[1]
delete tableStore.table.params.searchBeginTime delete tableStore.table.params.searchBeginTime
delete tableStore.table.params.searchEndTime delete tableStore.table.params.searchEndTime
delete tableStore.table.params.timeFlag delete tableStore.table.params.timeFlag
}, },
loadCallback: () => { loadCallback: () => {
name.value = dotList.value.name name.value = dotList.value.name
tableStore.table.data.forEach((item: any) => { renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
item.data1 ? (item.data = JSON.parse(item.data1)) : ''
item.celldata.forEach((k: any) => {
item.data[k.r][k.c].v ? (item.data[k.r][k.c] = k.v) : ''
})
})
setTimeout(() => {
luckysheet.create({
container: 'luckysheet',
title: '', // 表 头名
lang: 'zh', // 中文
showtoolbar: false, // 是否显示工具栏
showinfobar: false, // 是否显示顶部信息栏
showsheetbar: true, // 是否显示底部sheet按钮
data: tableStore.table.data
// tableStore.table.data
})
}, 10)
} }
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
@@ -127,8 +114,11 @@ const exportEvent = () => {
// 格式化YYYY - MM - DD补零 // 格式化YYYY - MM - DD补零
const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}` const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}`
exportExcel(luckysheet.getAllSheets(), name.value + formattedDate) exportLuckysheetFile(name.value + formattedDate, tableStore.table.data.length > 0)
} }
onUnmounted(() => {
destroyLuckysheet()
})
</script> </script>
<style lang="scss"> <style lang="scss">
.report-zl-page { .report-zl-page {

View File

@@ -55,13 +55,14 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, provide } from 'vue' import { ref, provide, onUnmounted } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue' import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { exportExcel } from '@/views/system/reportForms/export.js' import { destroyLuckysheet, exportLuckysheetFile, renderLuckysheetReport } from '@/utils/luckysheetHelper'
import { isLineTreeLeaf } from '@/components/tree/govern/lineTreeUtils'
import DatePicker from '@/components/form/datePicker/time.vue' import DatePicker from '@/components/form/datePicker/time.vue'
import pointTree from '@/components/tree/govern/pointTree.vue' import pointTree from '@/components/tree/govern/pointTree.vue'
// import data from './123.json' // import data from './123.json'
@@ -92,36 +93,8 @@ const tableStore = new TableStore({
delete tableStore.table.params.timeFlag delete tableStore.table.params.timeFlag
}, },
loadCallback: () => { loadCallback: () => {
console.log('🚀 ~ tableStore.table:', tableStore.table)
name.value = dotList.value.name name.value = dotList.value.name
// tableStore.table.data.forEach((item: any) => { renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
// item.data1 ? (item.data = JSON.parse(item.data1)) : ''
// item.celldata.forEach((k: any) => {
// item.data[k.r][k.c].v ? (item.data[k.r][k.c] = k.v) : ''
// })
// })
tableStore.table.data.forEach((item: any) => {
item.data1 ? (item.data = JSON.parse(item.data1)) : ''
item.celldata.forEach((k: any) => {
item.data[k.r][k.c].v ? (item.data[k.r][k.c] = k.v) : ''
})
})
console.log('🚀 ~ tableStore.table:', tableStore.table)
setTimeout(() => {
luckysheet.create({
container: 'luckysheet',
title: '', // 表 头名
lang: 'zh', // 中文
showtoolbar: false, // 是否显示工具栏
showinfobar: false, // 是否显示顶部信息栏
showsheetbar: true, // 是否显示底部sheet按钮
allowEdit: false, // 禁止所有编辑操作(必填)
data: tableStore.table.data
// tableStore.table.data
})
}, 10)
} }
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
@@ -143,7 +116,7 @@ const pointTypeChange = (val: any, obj: any) => {
} }
const handleNodeClick = (data: any) => { const handleNodeClick = (data: any) => {
if (data?.level == 3) { if (isLineTreeLeaf(data)) {
dotList.value = data dotList.value = data
setTimeout(() => { setTimeout(() => {
tableStore.index() tableStore.index()
@@ -155,14 +128,15 @@ const handleNodeClick = (data: any) => {
const exportEvent = () => { const exportEvent = () => {
const now = new Date() const now = new Date()
const year = now.getFullYear() // 4位年份 const year = now.getFullYear()
const month = now.getMonth() + 1 // 月份0-11需+1 const month = now.getMonth() + 1
const day = now.getDate() // 日期1-31 const day = now.getDate()
// 格式化YYYY - MM - DD补零
const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}` const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}`
exportExcel(luckysheet.getAllSheets(), name.value + formattedDate) exportLuckysheetFile(name.value + formattedDate, tableStore.table.data.length > 0)
} }
onUnmounted(() => {
destroyLuckysheet()
})
</script> </script>
<style lang="scss"> <style lang="scss">
.report-zl-page { .report-zl-page {

View File

@@ -43,13 +43,14 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, provide } from 'vue' import { onMounted, onUnmounted, ref, provide } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue' import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { exportExcel } from '@/views/system/reportForms/export.js' import { destroyLuckysheet, exportLuckysheetFile, renderLuckysheetReport } from '@/utils/luckysheetHelper'
import { isLineTreeLeaf } from '@/components/tree/govern/lineTreeUtils'
import DatePicker from '@/components/form/datePicker/time.vue' import DatePicker from '@/components/form/datePicker/time.vue'
import CloudDeviceEntryTree from '@/components/tree/govern/cloudDeviceEntryTreeZL.vue' import CloudDeviceEntryTree from '@/components/tree/govern/cloudDeviceEntryTreeZL.vue'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit' import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
@@ -80,35 +81,20 @@ const tableStore = new TableStore({
// ;(tableStore.table.params.startTime = datePickerRef.value.timeValue[0]), // ;(tableStore.table.params.startTime = datePickerRef.value.timeValue[0]),
// (tableStore.table.params.endTime = datePickerRef.value.timeValue[1]), // (tableStore.table.params.endTime = datePickerRef.value.timeValue[1]),
if (!tableStore.table.params.tempId) { if (!tableStore.table.params.tempId) {
return ElMessage.warning('请选择模板') ElMessage.warning('请选择模板')
return false
}
if (!dotList.value?.id) {
ElMessage.warning('请选择监测点')
return false
} }
delete tableStore.table.params.searchBeginTime delete tableStore.table.params.searchBeginTime
delete tableStore.table.params.searchEndTime delete tableStore.table.params.searchEndTime
delete tableStore.table.params.timeFlag delete tableStore.table.params.timeFlag
}, },
loadCallback: () => { loadCallback: () => {
console.log('🚀 ~ tableStore.table:', tableStore.table.data)
name.value = dotList.value.name name.value = dotList.value.name
// tableStore.table.data.forEach((item: any) => { renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
// item.data1 ? (item.data = JSON.parse(item.data1)) : ''
// item.celldata.forEach((k: any) => {
// item.data[k.r][k.c].v ? (item.data[k.r][k.c] = k.v) : ''
// })
// })
setTimeout(() => {
luckysheet.create({
container: 'luckysheet',
title: '', // 表 头名
lang: 'zh', // 中文
showtoolbar: false, // 是否显示工具栏
showinfobar: false, // 是否显示顶部信息栏
showsheetbar: true, // 是否显示底部sheet按钮
allowEdit: false, // 禁止所有编辑操作(必填)
data: tableStore.table.data
// tableStore.table.data
})
}, 10)
} }
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
@@ -118,6 +104,9 @@ const flag = ref(true)
onMounted(() => { onMounted(() => {
initListByIds() initListByIds()
}) })
onUnmounted(() => {
destroyLuckysheet()
})
const idList = ref([]) const idList = ref([])
// 监测对象 // 监测对象
@@ -134,7 +123,6 @@ const initListByIds = () => {
}) })
} }
const stencil = (val: any) => { const stencil = (val: any) => {
console.log('🚀 ~ stencil ~ val:', val)
templatePolicy.value = val.filter((item: any) => item.excelType == '4') templatePolicy.value = val.filter((item: any) => item.excelType == '4')
Template.value = templatePolicy.value[0] Template.value = templatePolicy.value[0]
reportForm.value = templatePolicy.value[0]?.excelType reportForm.value = templatePolicy.value[0]?.excelType
@@ -145,7 +133,7 @@ const changetype = (val: any) => {
} }
const handleNodeClick = (data: any, node: any) => { const handleNodeClick = (data: any, node: any) => {
if (data?.level == 3) { if (isLineTreeLeaf(data) || data?.level == 3) {
dotList.value = data dotList.value = data
setTimeout(() => { setTimeout(() => {
tableStore.index() tableStore.index()
@@ -163,7 +151,7 @@ const exportEvent = () => {
// 格式化YYYY - MM - DD补零 // 格式化YYYY - MM - DD补零
const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}` const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}`
exportExcel(luckysheet.getAllSheets(), name.value + formattedDate) exportLuckysheetFile(name.value + formattedDate, tableStore.table.data.length > 0)
} }
</script> </script>
<style lang="scss"> <style lang="scss">

2
types/table.d.ts vendored
View File

@@ -30,7 +30,7 @@ declare global {
} }
loadCallback: (() => void) | null loadCallback: (() => void) | null
resetCallback: (() => void) | null resetCallback: (() => void) | null
beforeSearchFun: (() => void) | null beforeSearchFun: (() => void | boolean) | null
exportProcessingData: (() => void) | null exportProcessingData: (() => void) | null
height: string height: string
publicHeight: number publicHeight: number