优化暂态事件列表

This commit is contained in:
guanj
2026-06-09 19:51:31 +08:00
parent 03d302ded8
commit bda7373133
48 changed files with 1834 additions and 1474 deletions

View File

@@ -1,38 +1,21 @@
<template>
<div>
<!--F47曲线 -->
<TableHeader
ref="TableHeaderRef"
:showReset="false"
:timeKeyList="prop.timeKey"
@selectChange="selectChange"
datePicker
v-if="fullscreen"
></TableHeader>
<TableHeader ref="TableHeaderRef" :showReset="false" :timeKeyList="prop.timeKey" @selectChange="selectChange"
datePicker v-if="fullscreen"></TableHeader>
<el-descriptions class="mt2" direction="vertical" :column="4" border>
<el-descriptions-item align="center" label="名称">{{ data.name }}</el-descriptions-item>
<el-descriptions-item align="center" label="事件总数">{{ data.gs }}</el-descriptions-item>
<el-descriptions-item align="center" label="可容忍">{{ data.krr }}</el-descriptions-item>
<el-descriptions-item align="center" label="不可容忍">{{ data.bkrr }}</el-descriptions-item>
</el-descriptions>
<my-echart
v-loading="tableStore.table.loading"
ref="chartRef"
class="tall"
:options="echartList"
:style="{
width: prop.width,
height: `calc(${prop.height} - 80px - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
@chart-click="handleChartClick"
/>
<my-echart v-loading="tableStore.table.loading" ref="chartRef" class="tall" :options="echartList" :style="{
width: prop.width,
height: `calc(${prop.height} - 80px - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}" @chart-click="handleChartClick" />
<el-dialog v-model="isWaveCharts" v-if="isWaveCharts" draggable :title="dialogTitle" append-to-body width="70%">
<waveFormAnalysis
v-loading="loading"
ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false"
:wp="wp"
/>
<waveFormAnalysis v-loading="loading" ref="waveFormAnalysisRef" @handleHideCharts="isWaveCharts = false"
:wp="wp" />
</el-dialog>
</div>
</template>
@@ -151,7 +134,7 @@ const tableStore: any = new TableStore({
relVal += "<font style='color:" + "'>发生时间:" + a.value[2] + '</font><br/>'
relVal += "<font style='color:" + "'>持续时间:" + a.value[0] + 's</font><br/>'
relVal += "<font style='color:" + "'>特征幅值:" + a.value[1].toFixed(2) + '%</font>'
relVal += "<font style='color:" + "'>特征幅值:" + Math.floor(a.value[1] * 100) / 100 + '%</font>'
return relVal
}
},
@@ -173,10 +156,10 @@ const tableStore: any = new TableStore({
// max: function (value: any) {
// return value.max + 20
// },
max: function (value) {
// 先取原始最大值+20再向上取整到最近的10的倍数确保刻度够用且规整
return Math.ceil((value.max + 20) / 10) * 10
},
// max: function (value) {
// // 先取原始最大值+20再向上取整到最近的10的倍数确保刻度够用且规整
// return Math.ceil((value.max + 20) / 10) * 10
// },
// splitNumber: 10,
// interval: 10,
// minInterval: 10,
@@ -461,7 +444,7 @@ const handleTolerableEventClick = async (row: any) => {
if (res != undefined) {
boxoList.value = {
persistTime: row.value[0], //持续时间
featureAmplitude: (row.value[1] / 100).toFixed(2), //残余电压
featureAmplitude: (row.value[1] / 100), //残余电压
startTime: row.value[2], //时间
lineName: row.value[4] //监测点名称
}
@@ -510,6 +493,6 @@ watch(
}
)
const addMenu = () => {}
const addMenu = () => { }
</script>
<style lang="scss" scoped></style>

View File

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

View File

@@ -101,7 +101,7 @@ const tableStore: any = new TableStore({
setTime()
},
loadCallback: () => {
value.value = tableStore.table.params.searchBeginTime
value.value = dayjs(tableStore.table.params.searchBeginTime).toDate()
if (tableStore.table.data && tableStore.table.data.length > 0) {
list.value = tableStore.table.data.map((item: any) => {
// 将 items 数组转换为带换行的文本

View File

@@ -1,95 +1,64 @@
<template>
<div>
<div class="device-control">
<!--指标拟合图 -->
<TableHeader
datePicker
@selectChange="selectChange"
v-if="fullscreen"
ref="TableHeaderRef"
:timeKeyList="prop.timeKey"
>
<template v-slot:select>
<el-form-item label="监测点">
<el-select filterable v-model="tableStore.table.params.lineId" placeholder="请选择监测点" clearable>
<el-option
v-for="item in lineList"
:key="item.lineId"
:label="item.name"
:value="item.lineId"
/>
</el-select>
</el-form-item>
<el-form-item label="用户功率">
<el-select
filterable
v-model="tableStore.table.params.power"
placeholder="请选择用户功率"
clearable
>
<el-option v-for="item in powerList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="统计类型">
<el-select
style="min-width: 120px !important"
placeholder="请选择"
v-model="tableStore.table.params.valueType"
filterable
>
<el-option value="max" label="最大值"></el-option>
<el-option value="min" label="最小值"></el-option>
<el-option value="avg" label="平均值"></el-option>
<el-option value="cp95" label="cp95"></el-option>
</el-select>
</el-form-item>
<el-form-item label="电能质量指标">
<el-select
filterable
v-model="tableStore.table.params.indicator"
placeholder="请选择电能质量指标"
clearable
>
<el-option v-for="item in indicatorList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<div v-if="shouldShowHarmonicCount()" style="display: flex; color: var(--el-text-color-regular)">
<span style="width: 160px">{{ getHarmonicTypeName() }}谐波次数</span>
<el-select
v-model="tableStore.table.params.harmonicCount"
placeholder="请选择谐波次数"
style="min-width: 80px !important"
filterable
>
<el-option
v-for="num in harmonicCountOptions"
:key="num"
:label="num"
:value="num"
></el-option>
<div v-show="fullscreen">
<PointTree :height="flag ? 106 : 50" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree>
</div>
<div>
<TableHeader datePicker @selectChange="selectChange" v-if="fullscreen" ref="TableHeaderRef"
:timeKeyList="prop.timeKey">
<template v-slot:select>
<!-- <el-form-item label="监测点">
<el-select filterable v-model="tableStore.table.params.lineId" placeholder="请选择监测点" clearable>
<el-option v-for="item in lineList" :key="item.lineId" :label="item.name"
:value="item.lineId" />
</el-select>
</div>
</el-form-item>
</template>
</TableHeader>
<div v-loading="tableStore.table.loading">
<my-echart
class="tall"
v-if="lineShow"
:options="echartList"
:style="{
width: prop.width,
</el-form-item> -->
<el-form-item label="用户功率">
<el-select filterable v-model="tableStore.table.params.power" placeholder="请选择用户功率" clearable>
<el-option v-for="item in powerList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="统计类型">
<el-select style="min-width: 120px !important" placeholder="请选择"
v-model="tableStore.table.params.valueType" filterable>
<el-option value="max" label="最大值"></el-option>
<el-option value="min" label="最小值"></el-option>
<el-option value="avg" label="平均值"></el-option>
<el-option value="cp95" label="cp95"></el-option>
</el-select>
</el-form-item>
<el-form-item label="电能质量指标">
<el-select filterable v-model="tableStore.table.params.indicator" placeholder="请选择电能质量指标"
clearable>
<el-option v-for="item in indicatorList" :key="item.id" :label="item.name"
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<div v-if="shouldShowHarmonicCount()"
style="display: flex; color: var(--el-text-color-regular)">
<span style="width: 160px">{{ getHarmonicTypeName() }}谐波次数</span>
<el-select v-model="tableStore.table.params.harmonicCount" placeholder="请选择谐波次数"
style="min-width: 80px !important" filterable>
<el-option v-for="num in harmonicCountOptions" :key="num" :label="num"
:value="num"></el-option>
</el-select>
</div>
</el-form-item>
</template>
</TableHeader>
<div v-loading="tableStore.table.loading">
<my-echart class="tall" v-if="lineShow" :options="echartList" :style="{
width: `calc(${prop.width} - ${fullscreen ? 290 : 0}px)`,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px )`
}"
/>
<el-empty
v-else
description="暂无监测点"
:style="{
width: prop.width,
}" />
<el-empty v-else description="暂无监测点" :style="{
width: `calc(${prop.width} - ${fullscreen ? 290 : 0}px)`,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
}" />
</div>
</div>
</div>
</template>
@@ -103,7 +72,7 @@ import { cslineList, fittingData } from '@/api/harmonic-boot/cockpit/cockpit'
import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree'
import { ElMessage } from 'element-plus'
import { getTime } from '@/utils/formatTime'
import PointTree from '@/components/tree/govern/pointTree.vue'
const prop = defineProps({
w: { type: [String, Number] },
h: { type: [String, Number] },
@@ -111,7 +80,8 @@ const prop = defineProps({
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
interval: { type: Number },
flag: { type: Boolean }
})
const TableHeaderRef = ref()
@@ -128,6 +98,8 @@ const lineShow = ref(true)
const fullscreen = computed(() => {
const w = Number(prop.w)
const h = Number(prop.h)
if (!isNaN(w) && !isNaN(h) && w === 12 && h === 6) {
// 执行相应逻辑
return true
@@ -148,18 +120,29 @@ const exceedingTheLimitList: any = ref([
const indicatorList = ref()
const initLineList = async () => {
cslineList({}).then(res => {
setTime()
if (res.data.length == 0) {
lineShow.value = false
return (tableStore.table.loading = false)
}
lineShow.value = true
lineList.value = res.data
tableStore.table.params.lineId = lineList.value[0].lineId
// const initLineList = async () => {
// cslineList({}).then(res => {
// setTime()
// if (res.data.length == 0) {
// lineShow.value = false
// return (tableStore.table.loading = false)
// }
// lineShow.value = true
// lineList.value = res.data
// tableStore.table.params.lineId = lineList.value[0].lineId
// initCode()
// })
// }
const nodeClick = (e: any) => {
if (e == undefined) {
}
if (e.level == 3) {
tableStore.table.params.lineId = e.id
initCode()
})
}
}
const pointTypeChange = (val: any, obj: any) => {
nodeClick(obj)
}
const echartList = ref()
@@ -222,19 +205,19 @@ const setEchart = () => {
{},
indicatorName
? {
min: 0,
max: 1,
axisLabel: {
formatter: function (value: number) {
if (value === 0) {
return '不越限'
} else if (value === 1) {
return '越限'
}
return value
}
}
}
min: 0,
max: 1,
axisLabel: {
formatter: function (value: number) {
if (value === 0) {
return '不越限'
} else if (value === 1) {
return '越限'
}
return value
}
}
}
: {}
],
grid: {
@@ -451,7 +434,7 @@ watch(
)
onMounted(async () => {
await initLineList()
// await initCode()
})
watch(
@@ -493,4 +476,11 @@ watch(
// :deep(.el-select) {
// min-width: 80px;
// }
.device-control {
display: flex;
}
:deep(.cn-tree) {
padding: 0 10px 0 0 !important;
}
</style>

View File

@@ -187,6 +187,14 @@ const tableStore: any = new TableStore({
null: '/'
}
},
{
title: '电压等级(kV)',
field: 'volGrade',
minWidth: '80',
formatter: (row: any) => {
return row.cellValue == 0 ? '/' : row.cellValue || '/'
}
},
{
title: '治理对象',
field: 'sensitiveUser',
@@ -196,14 +204,7 @@ const tableStore: any = new TableStore({
}
},
{
title: '电压等级',
field: 'volGrade',
minWidth: '80',
formatter: (row: any) => {
return row.cellValue == 0 ? '/' : row.cellValue + 'kV' || '/'
}
},
{
title: '是否治理',
field: 'govern',
@@ -242,7 +243,7 @@ const tableStore: any = new TableStore({
{
title: '报告',
field: 'reportFilePath',
minWidth: '120',
minWidth: '150',
formatter: (row: any) => {
return row.cellValue == null ? '/' : row.cellValue.split('/').pop()
}
@@ -373,7 +374,9 @@ const forceDownloadPdf = async (pdfUrl:any, fileName = '文件.pdf') => {
// 4. 清理资源(避免内存泄漏)
document.body.removeChild(a)
URL.revokeObjectURL(blobUrl)
setTimeout(() => {
ElMessage.success('下载成功')
}, 2000)
} catch (error) {
console.error('下载失败:', error)
// ElMessage.error('文件下载失败,请检查网络或文件地址') // 适配 Element Plus

View File

@@ -131,7 +131,7 @@ const tableStore: any = new TableStore({
},
loadCallback: () => {
value.value = tableStore.table.params.searchBeginTime
value.value = dayjs(tableStore.table.params.searchBeginTime).toDate()
list.value = tableStore.table.data
}
})

View File

@@ -259,7 +259,7 @@ self.onmessage = function (e) {
' 发生时刻:' +
boxoList.startTime +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'%  持续时间:' +
boxoList.duration +
's'
@@ -283,7 +283,7 @@ self.onmessage = function (e) {
' 发生时刻:' +
boxoList.startTime +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'% 持续时间:' +
boxoList.persistTime +
's'
@@ -296,7 +296,7 @@ self.onmessage = function (e) {
' 发生时刻:' +
boxoList.startTime +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'% 持续时间:' +
boxoList.duration +
's'

View File

@@ -132,7 +132,7 @@ self.addEventListener('message', function (e) {
' 发生时刻:' +
boxoList.startTime +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'% 持续时间:' +
boxoList.duration +
's'
@@ -149,6 +149,7 @@ self.addEventListener('message', function (e) {
boxoList.evtParamTm +
's'
} else if (boxoList.systemType == 'YPT') {
console.log("🚀 ~ boxoList.featureAmplitude:", boxoList.featureAmplitude)
titles =
(boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
@@ -156,7 +157,7 @@ self.addEventListener('message', function (e) {
' 发生时刻:' +
boxoList.startTime +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'% 持续时间:' +
boxoList.persistTime +
's'
@@ -169,7 +170,7 @@ self.addEventListener('message', function (e) {
' 发生时刻:' +
boxoList.startTime +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'% 持续时间:' +
boxoList.duration +
's'

View File

@@ -1,25 +1,12 @@
<template>
<div style="width: 540px">
<el-select
v-model.trim="interval"
style="min-width: 90px; width: 90px; margin-right: 10px"
@change="timeChange"
>
<el-select v-model.trim="interval" style="min-width: 90px; width: 90px; margin-right: 10px"
@change="timeChange">
<el-option v-for="item in filteredTimeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-date-picker
v-model.trim="timeValue"
type="daterange"
:disabled="disabledPicker"
style="width: 220px; margin-right: 10px"
unlink-panels
:clearable="false"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
:shortcuts="shortcuts"
/>
<el-date-picker v-model.trim="timeValue" type="daterange" :disabled="disabledPicker"
style="width: 220px; margin-right: 10px" unlink-panels :clearable="false" range-separator=""
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" :shortcuts="shortcuts" />
<el-button :disabled="backDisabled" type="primary" :icon="DArrowLeft" @click="preClick"></el-button>
<el-button type="primary" :icon="VideoPause" @click="nowTime">当前</el-button>
@@ -95,7 +82,12 @@ const shortcuts = [
// 计算过滤后的 timeOptions
const filteredTimeOptions = computed(() => {
console.log("🚀 ~ props.timeKeyList :", props.timeKeyList )
// console.log("🚀 ~ props.timeKeyList:", props.timeKeyList)
if (props.timeKeyList.length > 0 && !props.timeKeyList.includes(interval.value.toString())) {
interval.value = Number(props.timeKeyList[0])
}
if (!props.timeKeyList || props.timeKeyList.length === 0) {
return timeOptions.value
}
@@ -182,10 +174,10 @@ const timeChange = (e: number) => {
timeFlag.value = 1
}
nextTick(() => {
nextTick(() => {
// 检查按钮状态
checkInitialButtonStatus()
})
checkInitialButtonStatus()
})
// 触发 change 事件
emitChange()

View File

@@ -12,7 +12,7 @@ export interface LineTreeDecorators {
statusColor: (comFlag: number) => string
applyMeta: (
node: any,
meta: { icon: string; color?: string; level?: number; disabled?: boolean }
meta: { icon: string; color?: string; level?: number; disabled?: boolean; pname?: string }
) => void
}
@@ -88,8 +88,10 @@ export function decorateLineTree(
applyMeta(leaf, {
icon: 'local-监测点',
color: statusColor(leaf.comFlag),
...LINE_LEAF_META
})
leaf.pname=item.name,
leaves.engineering.push(leaf)
})
})
@@ -113,8 +115,10 @@ export function decorateLineTree(
applyMeta(l4, {
icon: 'local-监测点',
color: statusColor(l4.comFlag),
...LINE_LEAF_META
})
l4.pname=item.name,
leaves.govern.push(l4)
})
})
@@ -127,8 +131,10 @@ export function decorateLineTree(
applyMeta(l2, {
icon: 'local-监测点',
color: statusColor(l2.comFlag),
...LINE_LEAF_META
})
l2.pname=item.name,
leaves.portable.push(l2)
})
})
@@ -148,8 +154,10 @@ export function decorateLineTree(
applyMeta(l4, {
icon: 'local-监测点',
color: statusColor(l4.comFlag),
...LINE_LEAF_META
})
l4.pname=item.name,
leaves.monitor.push(l4)
})
})

View File

@@ -6,6 +6,7 @@
default-expand-all
@changePointType="changePointType"
@changeTreeType="loadTree"
:height="height"
/>
</template>
@@ -25,10 +26,12 @@ import {
interface Props {
template?: boolean
height?: number
}
const props = withDefaults(defineProps<Props>(), {
template: false
template: false,
height: 0
})
defineOptions({

View File

@@ -181,13 +181,15 @@ interface Props {
canExpand?: boolean
type?: string
data?: any[]
height?: number
}
const props = withDefaults(defineProps<Props>(), {
width: '100%',
canExpand: true,
type: '',
data: () => []
data: () => [],
height: 0
})
const route = useRoute()
@@ -212,11 +214,11 @@ const zlDevList = ref<any[]>([])
const bxsDeviceData = ref<any[]>([])
const yqfDeviceData = ref<any[]>([])
const governTreeHeight = computed(() => 'calc(100vh - 380px)')
const governTreeHeight = computed(() => `calc(100vh - 380px - ${props.height}px)`)
const otherTreeHeight = computed(() =>
zlDeviceData.value.length ? 'calc(100vh - 340px)' : 'calc(100vh - 238px)'
zlDeviceData.value.length ? `calc(100vh - 340px - ${props.height}px)` : `calc(100vh - 238px - ${props.height}px)`
)
const engineeringTreeHeight = computed(() => 'calc(100vh - 188px)')
const engineeringTreeHeight = computed(() => `calc(100vh - 188px - ${props.height}px)`)
const treeRef1 = ref<InstanceType<typeof ElTree>>()
const treeRef2 = ref<InstanceType<typeof ElTree>>()