Files
admin-govern/src/components/cockpit/trendComparison/index.vue
2025-12-08 13:30:46 +08:00

463 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<!--趋势对比 -->
<TableHeader
datePicker
ref="TableHeaderRef"
:timeKeyList="prop.timeKey"
:showReset="false"
@selectChange="selectChange"
v-if="fullscreen"
>
<template v-slot:select>
<el-form-item label="监测对象">
<el-select
filterable
v-model="tableStore.table.params.sensitiveUserId"
placeholder="请选择监测对象"
clearable
>
<el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<!-- <el-form-item label="监测点名称">
<el-select v-model="tableStore.table.params.lineId" placeholder="请选择监测点名称" clearable>
<el-option
v-for="item in lineIdList"
:key="item.lineId"
:label="item.name"
:value="item.lineId"
/>
</el-select>
</el-form-item> -->
<el-form-item label="电能质量指标">
<el-select 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>
<el-radio-group v-model="tableStore.table.params.dataLevel">
<el-radio-button label="一次值" value="Primary" />
<el-radio-button label="二次值" value="Secondary" />
</el-radio-group>
</el-form-item>
<el-form-item label="统计类型">
<el-select
style="min-width: 120px !important"
placeholder="请选择"
v-model="tableStore.table.params.valueType"
>
<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>
<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"
>
<el-option
v-for="num in harmonicCountOptions"
:key="num"
:label="num"
:value="num"
></el-option>
</el-select>
</div>
</el-form-item>
</template>
</TableHeader>
<my-echart
v-loading="tableStore.table.loading"
class="tall"
:options="echartList"
:style="{
width: prop.width,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide, reactive, watch, h, computed, nextTick } from 'vue'
import TableStore from '@/utils/tableStore'
import TableHeader from '@/components/table/header/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { useConfig } from '@/stores/config'
import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({
w: { type: [String, Number] },
h: { type: [String, Number] },
width: { type: [String, Number] },
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
})
const TableHeaderRef = ref()
const config = useConfig()
// 计算是否全屏展示
const fullscreen = computed(() => {
const w = Number(prop.w)
const h = Number(prop.h)
if (!isNaN(w) && !isNaN(h) && w === 12 && h === 6) {
// 执行相应逻辑
return true
} else {
return false
}
})
// 添加谐波次数选项2-50
const harmonicCountOptions = ref(Array.from({ length: 49 }, (_, i) => i + 2))
const indicatorList = ref()
const echartList = ref()
const headerHeight = ref(57)
// 监测对象
const idList = ref()
// 监测对象
const initListByIds = () => {
getListByIds({}).then((res: any) => {
if (res.data.length > 0) {
idList.value = res.data
initCode()
}
})
}
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height
if (datePickerValue && datePickerValue.timeValue) {
// 更新时间参数
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
}
}
const initCode = () => {
queryByCode('steady_state_limit_trend').then(res => {
queryCsDictTree(res.data.id).then(item => {
indicatorList.value = item.data
tableStore.table.params.indicator = indicatorList.value[0].id
nextTick(() => {
tableStore.index()
})
})
})
}
// 治理前
const chartsListBefore = ref()
// 治理后
const chartsListAfter = ref()
const setEchart = () => {
// 从接口数据中提取治理前和治理后的数据
const beforeData = chartsListBefore.value || []
const afterData = chartsListAfter.value || []
// 按相位分组数据
const beforeGroupedByPhase: any = {}
const afterGroupedByPhase: any = {}
// 处理治理前数据
beforeData.forEach((item: any) => {
const phase = item.phase || 'default'
if (!beforeGroupedByPhase[phase]) {
beforeGroupedByPhase[phase] = []
}
beforeGroupedByPhase[phase].push([item.time, item.statisticalData])
})
// 处理治理后数据
afterData.forEach((item: any) => {
const phase = item.phase || 'default'
if (!afterGroupedByPhase[phase]) {
afterGroupedByPhase[phase] = []
}
afterGroupedByPhase[phase].push([item.time, item.statisticalData])
})
// 构建系列数据
const series: any = []
// 定义相位颜色
const phaseColors: any = {
A: '#DAA520',
B: '#2E8B57',
C: '#A52a2a'
}
// 添加治理前数据系列(实线)
Object.keys(beforeGroupedByPhase).forEach(phase => {
const phaseName = phase === 'default' ? '' : `${phase}`
const color = phaseColors[phase] || config.layout.elementUiPrimary[0]
series.push({
name: `治理前${phaseName}`,
type: 'line',
showSymbol: false,
smooth: true,
symbol: 'none',
data: beforeGroupedByPhase[phase],
itemStyle: {
normal: {
color: color
}
},
lineStyle: {
type: 'solid', // 实线
width: 2 // 线条宽度
},
yAxisIndex: 0
})
})
// 添加治理后数据系列(虚线)
Object.keys(afterGroupedByPhase).forEach(phase => {
const phaseName = phase === 'default' ? '' : `${phase}`
const color = phaseColors[phase] || config.layout.elementUiPrimary[0]
series.push({
name: `治理后${phaseName}`,
type: 'line',
showSymbol: false,
smooth: true,
symbol: 'none',
data: afterGroupedByPhase[phase],
itemStyle: {
normal: {
color: color
}
},
lineStyle: {
type: 'dashed', // 虚线
width: 2 // 线条宽度
},
yAxisIndex: 0
})
})
// 获取指标名称用于图表标题
let titleText = '治理前后对比'
if (beforeData.length > 0 && beforeData[0].anotherName) {
titleText = beforeData[0].anotherName
} else if (afterData.length > 0 && afterData[0].anotherName) {
titleText = afterData[0].anotherName
}
// 构建图例数据
const legendData = series.map((item: any, index: number) => {
let color = config.layout.elementUiPrimary[0]
const name = item.name
if (name.includes('A相')) {
color = '#DAA520'
} else if (name.includes('B相')) {
color = '#2E8B57'
} else if (name.includes('C相')) {
color = '#A52a2a'
}
// 判断是治理前还是治理后,设置不同的线条样式
const isBefore = name.includes('治理前')
return {
name: item.name,
icon: isBefore
? 'rect'
: 'path://M0,2 L8,2 L8,6 L0,6 Z M12,2 L20,2 L20,6 L12,6 Z M24,2 L32,2 L32,6 L24,6 Z M36,2 L44,2 L44,6 L36,6 Z', // 矩形组成的粗虚线
itemStyle: {
color: color // 明确指定图例图标的颜色
},
lineStyle: {
type: isBefore ? 'solid' : 'dashed', // 治理前实线,治理后虚线
width: 2
}
}
})
echartList.value = {
title: {
text: titleText
},
legend: {
data: legendData,
icon: 'rect',
itemWidth: 18,
itemHeight: 3,
itemStyle: {
borderWidth: 0
},
lineStyle: {
width: 2 // 确保图例线条宽度与系列线条宽度一致
}
},
xAxis: {
type: 'time',
axisLabel: {
formatter: {
day: '{MM}-{dd}',
month: '{MM}',
year: '{yyyy}'
}
}
},
yAxis: {
name: beforeData.length > 0 ? beforeData[0].unit : afterData.length > 0 ? afterData[0].unit : ''
},
grid: {
left: '10px',
right: '20px'
},
series: series
}
}
const tableStore: any = new TableStore({
url: '/cs-device-boot/csGroup/sensitiveUserTrendData',
method: 'POST',
showPage: false,
exportName: '趋势对比',
column: [],
beforeSearchFun: () => {
setTime()
if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
tableStore.table.params.sensitiveUserId = idList.value[0].id
}
let lists: any = []
// 处理电能质量指标
const selectedIndicator = indicatorList.value?.find(
(item: any) => item.id === tableStore.table.params.indicator
)
if (selectedIndicator) {
let frequencys = ''
if (selectedIndicator.name.includes('谐波含有率')) {
frequencys = tableStore.table.params.harmonicCount
}
lists.push({
statisticalId: tableStore.table.params.indicator,
frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : ''
})
}
// 将 lists 添加到请求参数中
tableStore.table.params.list = lists
},
loadCallback: () => {
tableStore.table.height = `calc(${prop.height} - 80px)`
// 数据加载完成后的处理
if (tableStore.table.data) {
chartsListBefore.value = tableStore.table.data.before
chartsListAfter.value = tableStore.table.data.after
setEchart()
}
}
})
tableStore.table.params.indicator = '1'
tableStore.table.params.exceedingTheLimit = '1'
tableStore.table.params.dataLevel = 'Primary'
tableStore.table.params.valueType = 'avg'
provide('tableStore', tableStore)
onMounted(() => {
initListByIds()
})
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
// 判断是否应该显示谐波次数选择框
const shouldShowHarmonicCount = () => {
if (!tableStore.table.params.indicator || !indicatorList.value) return false
const currentIndicator = indicatorList.value.find((item: any) => item.id === tableStore.table.params.indicator)
return (
currentIndicator &&
(currentIndicator.name.includes('电压谐波含有率') || currentIndicator.name.includes('电流谐波含有率'))
)
}
// 获取谐波类型名称
const getHarmonicTypeName = () => {
const currentIndicator = indicatorList.value.find((item: any) => item.id === tableStore.table.params.indicator)
if (currentIndicator) {
if (currentIndicator.name.includes('电压谐波含有率')) {
return '电压'
} else if (currentIndicator.name.includes('电流谐波含有率')) {
return '电流'
}
}
return ''
}
watch(
() => prop.timeKey,
val => {
tableStore.index()
}
)
watch(
() => prop.timeValue,
(newVal, oldVal) => {
tableStore.index()
},
{
deep: true
}
)
// 监听指标变化,当指标变化时重置谐波次数
watch(
() => tableStore.table.params.indicator,
newVal => {
if (shouldShowHarmonicCount()) {
// 如果之前没有设置过谐波次数则默认设置为2
if (!tableStore.table.params.harmonicCount) {
tableStore.table.params.harmonicCount = 2
}
} else {
// 如果不是谐波含有率指标,则清除谐波次数设置
tableStore.table.params.harmonicCount = ''
}
}
)
</script>
<style lang="scss" scoped>
// :deep(.el-select) {
// min-width: 80px;
// }
</style>