稳态、暂态电能质量,稳态质量效果各个弹框

This commit is contained in:
stt
2025-10-28 11:23:17 +08:00
parent 608be23687
commit 55a30a323d
7 changed files with 408 additions and 13 deletions

View File

@@ -8,10 +8,16 @@
<el-descriptions-item align="center" label="不可容忍">{{ data.bkrr }}</el-descriptions-item> <el-descriptions-item align="center" label="不可容忍">{{ data.bkrr }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<my-echart <my-echart
ref="chartRef"
class="tall" class="tall"
:options="echartList" :options="echartList"
:style="{ width: prop.width, height: `calc(${prop.height} - 80px)` }" :style="{ width: prop.width, height: `calc(${prop.height} - 80px)` }"
@chart-click="handleChartClick"
/> />
<el-dialog v-model="isWaveCharts" draggable title="瞬时/RMS波形" append-to-body width="70%">
<waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false" :wp="wp" />
</el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -22,6 +28,7 @@ import MyEchart from '@/components/echarts/MyEchart.vue'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { getTimeOfTheMonth } from '@/utils/formatTime' import { getTimeOfTheMonth } from '@/utils/formatTime'
import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue';
const prop = defineProps({ const prop = defineProps({
width: { type: String }, width: { type: String },
height: { type: String }, height: { type: String },
@@ -30,6 +37,15 @@ const prop = defineProps({
}) })
const echartList = ref({}) const echartList = ref({})
const chartRef = ref()
// 波形
const isWaveCharts = ref(false)
const loading = ref(false)
const wp = ref({})
const OverLimitDetailsRef = ref() const OverLimitDetailsRef = ref()
const data = reactive({ const data = reactive({
name: '事件个数', name: '事件个数',
@@ -64,7 +80,11 @@ const tableStore: any = new TableStore({
text: `F47曲线` text: `F47曲线`
}, },
legend: { legend: {
data: ['上限', '下限', '可容忍事件', '不可容忍事件'] // data: ['上限', '下限', '可容忍事件', '不可容忍事件'],
data: ['可容忍事件', '不可容忍事件'],
itemWidth: 10,
itemHeight: 10,
itemGap: 15
}, },
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
@@ -141,13 +161,40 @@ const tableStore: any = new TableStore({
name: '可容忍事件', name: '可容忍事件',
type: 'scatter', type: 'scatter',
symbol: 'circle', symbol: 'circle',
data: gongData.pointF symbolSize: 8,
// data: gongData.pointF,
data: [
[0.2, 10, '2023-01-01 10:00:00'],
[0.4, 50, '2023-01-01 11:00:00']
],
legendSymbol: 'circle',
emphasis: {
focus: 'series',
itemStyle: {
borderColor: '#fff',
borderWidth: 2,
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
tooltip: {
show: true,
trigger: 'item',
formatter: function (params: any) {
return `<strong>可容忍事件</strong><br/>
持续时间: ${params.value[0]}s<br/>
特征幅值: ${params.value[1].toFixed(2)}%<br/>
发生时间: ${params.value[2] || 'N/A'}`
}
}
}, },
{ {
name: '不可容忍事件', name: '不可容忍事件',
type: 'scatter', type: 'scatter',
symbol: 'circle', symbol: 'rect',
data: gongData.pointFun symbolSize: 8,
data: gongData.pointFun,
legendSymbol: 'rect'
} }
] ]
} }
@@ -323,6 +370,32 @@ onMounted(() => {
tableStore.index() tableStore.index()
}, 100) }, 100)
}) })
// 点击事件处理函数
const handleChartClick = (params: any) => {
if (params.seriesName === '可容忍事件') {
// 处理可容忍事件点击
ElMessage.info(`点击了可容忍事件: 持续时间${params.value[0]}s, 幅值${params.value[1].toFixed(2)}%`)
handleTolerableEventClick(params)
} else if (params.seriesName === '不可容忍事件') {
// 处理不可容忍事件点击
ElMessage.info(`点击了不可容忍事件: 持续时间${params.value[0]}s, 幅值${params.value[1].toFixed(2)}%`)
handleIntolerableEventClick(params)
}
}
// 可容忍事件点击处理函数
const handleTolerableEventClick = (params: any) => {
console.log('可容忍事件详情:', params)
isWaveCharts.value = true
}
// 不可容忍事件点击处理函数
const handleIntolerableEventClick = (params: any) => {
console.log('不可容忍事件详情:', params)
}
watch( watch(
() => prop.timeKey, () => prop.timeKey,
val => { val => {

View File

@@ -63,8 +63,8 @@ const tableStore: any = new TableStore({
minWidth: '80', minWidth: '80',
render: 'customTemplate', render: 'customTemplate',
customTemplate: (row: any) => { customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.type2}</span>` return `<span style='cursor: pointer;text-decoration: underline;' onclick="handleReportClick('${row.name}')">${row.type2}</span>`
} },
}, },
{ title: '监测点名称', field: 'type3', minWidth: '70' }, { title: '监测点名称', field: 'type3', minWidth: '70' },
{ title: '监测类型', field: 'type4', minWidth: '60' }, { title: '监测类型', field: 'type4', minWidth: '60' },
@@ -164,6 +164,30 @@ watch(
} }
) )
const addMenu = () => {} const handleReportClick = (id: string) => {
const row = tableStore.table.data.find((item: any) => item.id === id)
if (row && row.type2 !== '/') {
// 示例:触发下载逻辑(根据你注释掉的代码)
// getFileZip({ eventId: id }).then(res => {
// let blob = new Blob([res], { type: 'application/zip' })
// const url = window.URL.createObjectURL(blob)
// const link = document.createElement('a')
// link.href = url
// link.download = row.wavePath?.split('/')[2] || '报告文件.zip'
// link.click()
// })
console.log('点击了报告:', row.type2)
} else {
ElMessage.warning('暂无报告可下载')
}
}
// 挂载到 window 供 HTML 调用
window.handleReportClick = handleReportClick
// 组件销毁时清理全局方法
onBeforeUnmount(() => {
delete window.handleReportClick
})
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -0,0 +1,131 @@
<template>
<div>
<!-- 暂态事件列表 -->
<el-dialog draggable title="暂态事件列表" v-model="dialogVisible" append-to-body width="70%">
<!-- <TableHeader datePicker showExport :showReset="false">
<template v-slot:select>
<el-form-item label="监测点名称">
<el-select
v-model="tableStore.table.params.searchValue"
placeholder="请选择监测点名称"
style="width: 240px"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</template>
</TableHeader> -->
<Table ref="tableRef" isGroup :height="height"></Table>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, provide } from 'vue'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import TableStore from '@/utils/tableStore'
import { mainHeight } from '@/utils/layout'
const dialogVisible: any = ref(false)
const harmonicRatioRef: any = ref(null)
const options = [
{
value: '35kV进线',
label: '35kV进线'
}
]
const height = mainHeight(0, 2).height as any
const tableStore: any = new TableStore({
url: '/user-boot/role/selectRoleDetail?id=0',
method: 'POST',
publicHeight: 30,
showPage: false,
exportName: '主要监测点列表',
column: [
{
field: 'index',
title: '序号',
width: '80',
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{
title: '暂态时间',
field: 'time',
},
{
title: '测点名称',
field: 'name',
width: '150'
},
{
title: '暂态类型',
field: 'flicker',
width: '100',
},
{
title: '特征幅值(%)',
field: 'flicker',
width: '100'
},
{
title: '暂降深度(%)',
field: 'flicker',
width: '100'
},
{
title: '持续时间(S)',
field: 'flicker',
width: '100'
},
{
title: '严重度',
field: 'flicker',
width: '80'
},
],
beforeSearchFun: () => {},
loadCallback: () => {
tableStore.table.data = [
{
time: '2024-01-01 00:00:00',
name: '35kV进线',
flicker: '0'
},
{
time: '2024-01-01 00:00:00',
name: '35kV进线',
flicker: '0'
},
{
time: '2024-01-01 00:00:00',
name: '35kV进线',
flicker: '0'
}
]
}
})
tableStore.table.params.searchValue = ''
provide('tableStore', tableStore)
const open = async (row: any) => {
dialogVisible.value = true
tableStore.index()
}
// 点击行
const cellClickEvent = ({ row, column }: any) => {
if (column.field != 'name') {
harmonicRatioRef.value.openDialog(row)
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped></style>

View File

@@ -23,10 +23,11 @@
</div> </div>
</template> </template>
<div <div
style="text-decoration: underline"
:style="{ height: `calc(${prop.height} / 5 - 47px)`, overflow: 'auto' }" :style="{ height: `calc(${prop.height} / 5 - 47px)`, overflow: 'auto' }"
v-for="item in list?.filter(item => item.time == data.day)" v-for="item in list?.filter(item => item.time == data.day)"
> >
<div>电压暂降:{{ item.type || '' }}</div> <div @click="descentClick">电压暂降:{{ item.type || '' }}</div>
<div>电压中断:{{ item.type1 || '' }}</div> <div>电压中断:{{ item.type1 || '' }}</div>
<div>电压暂升:{{ item.type2 || '' }}</div> <div>电压暂升:{{ item.type2 || '' }}</div>
</div> </div>
@@ -34,6 +35,8 @@
</div> </div>
</template> </template>
</el-calendar> </el-calendar>
<!-- 暂态事件列表 -->
<TransientList ref="transientListRef" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -45,6 +48,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import { getTimeOfTheMonth } from '@/utils/formatTime' import { getTimeOfTheMonth } from '@/utils/formatTime'
import { overflow } from 'html2canvas/dist/types/css/property-descriptors/overflow' import { overflow } from 'html2canvas/dist/types/css/property-descriptors/overflow'
import { dayjs } from 'element-plus' import { dayjs } from 'element-plus'
import TransientList from './components/transientList.vue'
dayjs.en.weekStart = 1 //设置日历的周起始日为星期一 dayjs.en.weekStart = 1 //设置日历的周起始日为星期一
const value = ref(new Date()) const value = ref(new Date())
@@ -54,6 +58,8 @@ const prop = defineProps({
timeKey: { type: String }, timeKey: { type: String },
timeValue: { type: Object } timeValue: { type: Object }
}) })
const transientListRef = ref()
const list = ref([ const list = ref([
{ {
time: '2025-10-01', time: '2025-10-01',
@@ -154,7 +160,12 @@ watch(
} }
) )
const addMenu = () => {} // 电压暂降点击事件
const descentClick = () => {
transientListRef.value.open()
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.el-calendar) { :deep(.el-calendar) {

View File

@@ -0,0 +1,150 @@
<template>
<div>
<!-- 暂态事件详情 -->
<el-dialog draggable title="暂态事件详情 " v-model="dialogVisible" append-to-body width="70%">
<TableHeader datePicker showExport :showReset="false">
<template v-slot:select>
<el-form-item label="监测点名称">
<el-select
v-model="tableStore.table.params.searchValue"
placeholder="请选择监测点名称"
style="width: 240px"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</template>
</TableHeader>
<Table ref="tableRef" @cell-click="cellClickEvent" isGroup :height="height"></Table>
</el-dialog>
<!-- 查看波形 -->
<HarmonicRatio ref="harmonicRatioRef" />
</div>
</template>
<script setup lang="ts">
import { ref, provide } from 'vue'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import TableStore from '@/utils/tableStore'
import { mainHeight } from '@/utils/layout'
import HarmonicRatio from '@/components/cockpit/listOfMainMonitoringPoints/components/harmonicRatio.vue'
const dialogVisible: any = ref(false)
const harmonicRatioRef: any = ref(null)
const options = [
{
value: '35kV进线',
label: '35kV进线'
}
]
const height = mainHeight(0, 2).height as any
const tableStore: any = new TableStore({
url: '/user-boot/role/selectRoleDetail?id=0',
method: 'POST',
publicHeight: 30,
showPage: false,
exportName: '主要监测点列表',
column: [
{
field: 'index',
title: '序号',
width: '80',
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{
title: '暂态时间',
field: 'time',
},
{
title: '测点名称',
field: 'name',
width: '150'
},
{
title: '暂态类型',
field: 'flicker',
width: '100',
},
{
title: '特征幅值(%)',
field: 'flicker',
width: '100'
},
{
title: '暂降深度(%)',
field: 'flicker',
width: '100'
},
{
title: '持续时间(S)',
field: 'flicker',
width: '100'
},
{
title: '严重度',
field: 'flicker',
width: '80'
},
{
title: '波形',
width: '100',
render: 'buttons',
buttons: [
{
name: 'check',
title: '查看波形',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: row => {
}
}
]
}
],
beforeSearchFun: () => {},
loadCallback: () => {
tableStore.table.data = [
{
time: '2024-01-01 00:00:00',
name: '35kV进线',
flicker: '0'
},
{
time: '2024-01-01 00:00:00',
name: '35kV进线',
flicker: '0'
},
{
time: '2024-01-01 00:00:00',
name: '35kV进线',
flicker: '0'
}
]
}
})
tableStore.table.params.searchValue = ''
provide('tableStore', tableStore)
const open = async (row: any) => {
dialogVisible.value = true
tableStore.index()
}
// 点击行
const cellClickEvent = ({ row, column }: any) => {
if (column.field != 'name') {
harmonicRatioRef.value.openDialog(row)
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped></style>

View File

@@ -7,6 +7,7 @@
:style="{ width: prop.width, height: `calc(${prop.height} / 2 )` }" :style="{ width: prop.width, height: `calc(${prop.height} / 2 )` }"
/> />
<Table ref="tableRef" @cell-click="cellClickEvent" :height="`calc(${prop.height} / 2 )`" isGroup></Table> <Table ref="tableRef" @cell-click="cellClickEvent" :height="`calc(${prop.height} / 2 )`" isGroup></Table>
<TransientStatisticsDetail ref="transientStatisticsDetailRef"></TransientStatisticsDetail>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -18,6 +19,7 @@ import { useDictData } from '@/stores/dictData'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { getTimeOfTheMonth } from '@/utils/formatTime' import { getTimeOfTheMonth } from '@/utils/formatTime'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import TransientStatisticsDetail from './components/transientStatisticsDetail.vue'
const config = useConfig() const config = useConfig()
const prop = defineProps({ const prop = defineProps({
width: { type: String }, width: { type: String },
@@ -95,7 +97,7 @@ const echartList = ref({
] ]
} }
}) })
const OverLimitDetailsRef = ref() const transientStatisticsDetailRef = ref()
const tableStore: any = new TableStore({ const tableStore: any = new TableStore({
url: '/user-boot/dept/deptTree', url: '/user-boot/dept/deptTree',
method: 'POST', method: 'POST',
@@ -187,8 +189,7 @@ provide('tableStore', tableStore)
// 点击行 // 点击行
const cellClickEvent = ({ row, column }: any) => { const cellClickEvent = ({ row, column }: any) => {
if (column.field != 'name') { if (column.field != 'name') {
console.log(row) transientStatisticsDetailRef.value.open(row)
OverLimitDetailsRef.value.open(row)
} }
} }

View File

@@ -15,6 +15,7 @@ import { color, gradeColor3 } from './color'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
// import { nextTick } from 'process' // import { nextTick } from 'process'
const emit = defineEmits(['chartClick'])
const config = useConfig() const config = useConfig()
color[0] = config.layout.elementUiPrimary[0] color[0] = config.layout.elementUiPrimary[0]
const chartRef = ref<HTMLDivElement>() const chartRef = ref<HTMLDivElement>()
@@ -131,6 +132,10 @@ const initChart = () => {
// 处理柱状图 // 处理柱状图
chart.setOption(options, true) chart.setOption(options, true)
chart.group = 'group' chart.group = 'group'
// 添加点击事件
chart.on('click', function (params: any) {
emit('chartClick', params)
})
setTimeout(() => { setTimeout(() => {
chart.resize() chart.resize()
}, 0) }, 0)