Files
admin-sjzx/src/views/pqs/harmonicMonitoring/detailed/division/components/compute.vue
2025-12-15 09:33:34 +08:00

630 lines
26 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 class="default-main" :style="height">
<div class="title">
贡献度计算
<div style="font-size: 14px; font-weight: 500">
<!-- {{ dotList.alias || '' }} -->
<span class="monitoring-point1">当前位置{{ dotList.alias || '' }}</span>
<back-component />
</div>
</div>
<splitpanes :style="heightB" class="default-theme" id="navigation-splitpanes">
<pane :size="size">
<PointTree
:showSelect="false"
:default-expand-all="false"
@node-click="handleNodeClick"
@init="handleNodeClick"
></PointTree>
</pane>
<pane style="background: #fff" :style="heightB" :size="100 - size">
<el-form :model="form" inline label-width="auto">
<el-form-item label="谐波类型:">
<el-radio-group v-model="form.type">
<el-radio-button label="谐波电压" value="1" />
<el-radio-button label="谐波电流" value="0" />
</el-radio-group>
</el-form-item>
<el-form-item label="谐波次数:">
<el-select
v-model="form.index"
filterable
multiple
:multiple-limit="5"
collapse-tags
collapse-tags-tooltip
clearable
placeholder="请选择次数"
>
<el-option
v-for="item in harmonic"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="负荷数据:">
<el-select v-model="form.loadDataId" clearable filterable placeholder="请选择负荷数据">
<el-option
v-for="item in loadDataOptions"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-Plus" @click="push('/admin/division/aListOfLoadData')">
新增
</el-button>
<el-button type="primary" icon="el-icon-Select" @click="submit">确定</el-button>
</el-form-item>
</el-form>
<el-tabs v-model="activeName" type="border-card" class="mr10" v-if="showTabs">
<el-tab-pane v-for="(item, index) in tabList" :key="item" :label="item.label" :name="index">
<div class="pd10">
<div>
<span style="color: var(--el-text-color-regular)">时间范围:</span>
<el-date-picker
v-model="item.time"
class="mr10 ml10"
type="daterange"
start-placeholder="起始时间"
end-placeholder="结束时间"
format="YYYY-MM-DD"
date-format="YYYY-MM-DD"
time-format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
:disabled-date="handleDisabledDate"
/>
<el-button type="primary" icon="el-icon-CaretRight" @click="execute(item, index)">
执行
</el-button>
</div>
<div v-if="item.showEcahr == 1" class="harmonicButton">
<el-form :inline="true" v-model="item.form">
<el-form-item label="限值:" v-if="item.showDynamic">
<el-input v-model="item.form.limit" placeholder="请选择限值" disabled>
<template #append>
<el-button
:icon="Edit"
:class="[code == 0 ? 'frontBox' : '']"
@click="setCode(0)"
/>
</template>
</el-input>
</el-form-item>
<el-form-item label="时间点一:" v-if="item.showDynamic">
<el-input v-model="item.form.time1" placeholder="请选择时间点一" disabled>
<template #append>
<el-button
:icon="Edit"
:class="[code == 1 ? 'frontBox' : '']"
@click="setCode(1)"
/>
</template>
</el-input>
</el-form-item>
<el-form-item label="时间点二:" v-if="item.showDynamic">
<el-input v-model="item.form.time2" placeholder="请选择时间点二" disabled>
<template #append>
<el-button
:icon="Edit"
:class="[code == 2 ? 'frontBox' : '']"
@click="setCode(2)"
/>
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="el-icon-Document"
@click="generateFn"
v-if="!item.showDynamic"
>
生成动态谐波责任数据
</el-button>
<el-button
type="primary"
icon="el-icon-Document"
v-else
@click="generateMetrics"
>
生成谐波责任指标
</el-button>
</el-form-item>
</el-form>
</div>
<div class="box" v-loading="loading">
<MyEChart :options="item.options" v-if="item.showEcahr == 1" @group="group" />
<el-empty description="时间范围内无谐波数据" v-if="item.showEcahr == 2" />
</div>
<!-- 生成动态谐波责任数据 -->
<div class="box boxTab" v-loading="loading1">
<MyEChart :options="item.dynamicOptions" style="flex: 1" v-if="item.showDynamic" />
<div style="width: 500px">
<vxe-table
v-if="item.showDynamic"
ref="tableRef"
:data="item.dynamicData"
height="auto"
v-bind="defaultAttribute"
>
<vxe-column field="customerName" title="用户名(用户号)"></vxe-column>
<vxe-column field="responsibilityData" title="责任数据(%)" width="120">
<template v-slot="{ row }">
{{ Math.floor(row.responsibilityData * 10000) / 10000 }}
</template>
</vxe-column>
</vxe-table>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
</pane>
</splitpanes>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted } from 'vue'
import { mainHeight } from '@/utils/layout'
import 'splitpanes/dist/splitpanes.css'
import { Splitpanes, Pane } from 'splitpanes'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import PointTree from '@/components/tree/pqs/pointTree.vue'
import BackComponent from '@/components/icon/back/index.vue'
import { harmonicOptions } from '@/utils/dictionary'
import { userDataList, getHistoryHarmData, getDynamicData, getResponsibilityData } from '@/api/advance-boot/division'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { Edit } from '@element-plus/icons-vue'
import MyEChart from '@/components/echarts/MyEchart.vue'
import { timeFormat } from '@/utils/common'
import { yMethod } from '@/utils/echartMethod'
defineOptions({
name: 'division/compute'
})
const { push } = useRouter()
const dotList: any = ref({})
const height = mainHeight(20)
const heightB = mainHeight(70)
const harmonic = harmonicOptions.slice(1)
const size = ref(23)
const showTabs = ref(false)
const loadDataOptions: any = ref([])
const form: any = reactive({
type: '1',
index: [],
loadDataId: ''
})
const code = ref(3)
const xAxisData = ref([])
const loading = ref(false)
const loading1 = ref(false)
const tabList: any = ref([])
const activeName = ref(0)
const xValue = ref('')
const handleNodeClick = (data: any, node: any) => {
if (data.level == 6) {
dotList.value = data
}
}
// 设置时间
const timeFrame = ref(['', ''])
// 处理日期禁用逻辑
const handleDisabledDate = date => {
// 定义时间边界
const startLimit = new Date(timeFrame.value[0]).getTime() - 86400000 //向前推1天
const endLimit = new Date(timeFrame.value[1]).setHours(23, 59, 59, 999)
// 如果日期不存在(选择今天时可能出现),不禁用
if (!date) return false
// 禁用 2025-08-01 之前和 2025-08-31 之后的日期
return date.getTime() < startLimit || date.getTime() > endLimit
}
// 这是按钮变色
const setCode = (num: number) => {
if (code.value == num) {
return (code.value = 3)
}
code.value = num
}
// 确定
const submit = () => {
if (form.loadDataId == '') {
return ElMessage.warning('请选择负荷数据')
}
if (form.index.length == 0) {
return ElMessage.warning('请选择谐波次数')
}
if (form.index.length == 0) {
showTabs.value = false
} else {
let timeList = loadDataOptions.value.filter((item: any) => item.id == form.loadDataId)[0]
showTabs.value = true
let list = JSON.parse(JSON.stringify(form.index)).sort((a, b) => a - b)
tabList.value = []
list.forEach((item: any) => {
tabList.value.push({
label: item + '次谐波',
key: item,
time: [timeList.startTime, timeList.endTime],
showExecute: false,
form: {
limit: '',
time1: '',
time2: ''
},
showEcahr: 3, //1显示echart 2显示无数据 3什么都没有
options: {},
dynamicOptions: {}, //动态echarts
dynamicList: {}, //动态echarts
showDynamic: false //动态执行展示
})
timeFrame.value = [timeList.startTime, timeList.endTime]
})
code.value = 3
activeName.value = 0
}
}
// 执行
const execute = async (item: any, index: number) => {
tabList.value[activeName.value].showDynamic = false
loading.value = true
await getHistoryHarmData({
searchBeginTime: item.time[0],
searchEndTime: item.time[1],
type: form.type,
time: item.key,
lineId: dotList.value.id
})
.then((res: any) => {
let [min, max] = yMethod(res.data.historyData.map((item: any) => item.value + 0.1))
xAxisData.value = res.data.historyData.map((item: any) => item.time)
tabList.value[index].options = {
title: {
text: ''
},
xAxis: {
type: 'time',
name: '时间',
axisLabel: {
formatter: {
day: '{MM}-{dd}',
month: '{MM}',
year: '{yyyy}'
}
}
},
tooltip: {
formatter(params: any) {
xValue.value = params[0].value[0]
let str = params[0].value[0] + '<br/>'
for (let i = 0; i < params.length; i++) {
str = str + params[i].marker + params[i].seriesName + '' + params[i].value[1] + '<br/>'
}
return str
}
},
grid: {
top: 30
},
legend: {
show: false
},
yAxis: {
name: form.type == 1 ? '%' : 'A',
min: min,
max: max
},
toolbox: {
show: false
},
series: [
{
name: item.key + (form.type == 1 ? '次谐波电压' : '次谐波电流'),
data: res.data.historyData.map((item: any) => [
item.time,
Math.floor(item.value * 10000) / 10000
]),
type: 'line',
symbol: 'none',
markLine: {
symbol: 'none', // 去除箭头
label: {
show: false // 隐藏标签
},
data: [
{
yAxis: ''
},
{
xAxis: ''
},
{
xAxis: ''
}
],
// 样式配置
lineStyle: {
color: 'red',
type: 'dashed' // 虚线
}
}
}
]
}
tabList.value[index].showEcahr = 1
loading.value = false
})
.catch(() => {
tabList.value[index].showEcahr = 2
loading.value = false
})
}
const resDataId = ref('')
// 生成动态谐波责任数据
const generateFn = async () => {
loading1.value = true
await getDynamicData({
lineId: dotList.value.id,
searchBeginTime: tabList.value[activeName.value].time[0],
searchEndTime: tabList.value[activeName.value].time[1],
time: tabList.value[activeName.value].key,
type: form.type,
userDataId: form.loadDataId
})
.then((res: any) => {
resDataId.value = res.data.responsibilityDataIndex
tabList.value[activeName.value].dynamicData = res.data.responsibilities
let [min, max] = yMethod(res.data.datas.map((item: any) => item.valueDatas).flat())
let series: any[] = []
let time: any[] = res.data.timeDatas.map((item: any) => timeFormat(item))
res.data.datas.forEach((item: any) => {
series.push({
name: item.customerName,
data: item.valueDatas.map((k: any, i: number) => [time[i], Math.floor(k * 10000) / 10000]),
type: 'line',
symbol: 'none'
})
})
tabList.value[activeName.value].dynamicOptions = {
title: {
text: ''
},
xAxis: {
type: 'time',
name: '时间',
axisLabel: {
formatter: {
day: '{MM}-{dd}',
month: '{MM}',
year: '{yyyy}'
}
}
},
tooltip: {
formatter(params: any) {
let str = params[0].value[0] + '<br/>'
for (let i = 0; i < params.length; i++) {
str = str + params[i].marker + params[i].seriesName + '' + params[i].value[1] + '<br/>'
}
return str
}
},
grid: {
top: 30
},
legend: {
show: false
},
yAxis: {
name: form.type == 1 ? '%' : 'A',
min: min,
max: max
},
toolbox: {
show: false
},
options: {
series: series
}
}
tabList.value[activeName.value].showDynamic = true
})
.catch(() => {
loading1.value = false
})
loading1.value = false
}
// 生成指标
const generateMetrics = async () => {
if (tabList.value[activeName.value].form.limit == '') return ElMessage.warning('请选择限值!')
if (tabList.value[activeName.value].form.time1 == '') return ElMessage.warning('请选择时间一!')
if (tabList.value[activeName.value].form.time2 == '') return ElMessage.warning('请选择时间二!')
loading1.value = true
await getResponsibilityData({
limitEndTime: tabList.value[activeName.value].form.time2,
limitStartTime: tabList.value[activeName.value].form.time1,
limitValue: tabList.value[activeName.value].form.limit,
resDataId: resDataId.value,
time: tabList.value[activeName.value].key,
type: form.type
})
.then((res: any) => {
tabList.value[activeName.value].dynamicData = res.data.responsibilities
let [min, max] = yMethod(res.data.datas.map((item: any) => item.valueDatas).flat())
let series: any[] = []
let time: any[] = res.data.timeDatas.map((item: any) => timeFormat(item))
res.data.datas.forEach((item: any) => {
series.push({
name: item.customerName,
data: item.valueDatas.map((k: any, i: number) => [time[i], Math.floor(k * 10000) / 10000]),
type: 'line',
symbol: 'none'
})
})
tabList.value[activeName.value].dynamicOptions = {
title: {
text: ''
},
xAxis: {
type: 'time',
name: '时间',
axisLabel: {
formatter: {
day: '{MM}-{dd}',
month: '{MM}',
year: '{yyyy}'
}
}
},
tooltip: {
formatter(params: any) {
let str = params[0].value[0] + '<br/>'
for (let i = 0; i < params.length; i++) {
str = str + params[i].marker + params[i].seriesName + '' + params[i].value[1] + '<br/>'
}
return str
}
},
grid: {
top: 30
},
legend: {
show: false
},
yAxis: {
name: form.type == 1 ? '%' : 'A',
min: min,
max: max
},
toolbox: {
show: false
},
options: {
series: series
}
}
tabList.value[activeName.value].showDynamic = true
})
.catch(() => {
loading1.value = false
})
loading1.value = false
}
// 监听echart点击
const group = (chart: any, myChartDom: any) => {
myChartDom.addEventListener('click', function (event: any) {
// 获取点击位置相对于图表容器的坐标
const rect = myChartDom.getBoundingClientRect()
const x = event.clientX - rect.left
const y = event.clientY - rect.top
const pointInPixel = [x, y]
// 转换为逻辑坐标(相对于图表坐标系)
const pointInGrid = chart.convertFromPixel({ gridIndex: 0 }, pointInPixel)
// 计算X轴和Y轴的对应数据
// 处理X轴数据分类轴
// 处理Y轴数据数值轴
let yValue = pointInGrid[1].toFixed(4)
// xValue = timeFormat(pointInGrid[0].toFixed(0) - 0)
if (code.value == 0) {
tabList.value[activeName.value].form.limit = yValue
tabList.value[activeName.value].options.series[0].markLine.data[0].yAxis = yValue
chart.setOption(tabList.value[activeName.value].options)
} else if (code.value == 1) {
tabList.value[activeName.value].form.time1 = xValue.value
tabList.value[activeName.value].options.series[0].markLine.data[1].xAxis = xValue.value
chart.setOption(tabList.value[activeName.value].options)
} else if (code.value == 2) {
tabList.value[activeName.value].form.time2 = xValue.value
tabList.value[activeName.value].options.series[0].markLine.data[2].xAxis = xValue.value
chart.setOption(tabList.value[activeName.value].options)
}
// 控制台输出详细信息
// console.log('点击事件详情:', {
// X轴数据: xValue,
// Y轴数据: yValue
// })
})
}
onMounted(() => {
const dom = document.getElementById('navigation-splitpanes')
if (dom) {
size.value = Math.round((180 / dom.offsetHeight) * 120)
}
userDataList({
pageNum: 1,
pageSize: 10000,
searchValue: ''
}).then((res: any) => {
loadDataOptions.value = res.data.records
})
})
</script>
<style lang="scss" scoped>
.title {
display: flex;
justify-content: space-between;
padding: 10px;
font-size: 16px;
font-weight: 550;
}
:deep(.upload-demo) {
display: flex;
.el-upload-list__item-info {
width: 300px;
}
}
.monitoring-point1 {
font-size: 14px;
font-weight: 700;
color: var(--el-color-primary);
}
.box {
// height: 280px;
height: calc((100vh - 370px) / 2);
}
.boxTab {
display: flex;
}
.harmonicButton {
height: 42px;
display: flex;
justify-content: end;
align-items: center;
}
:deep(.el-tabs__content) {
height: calc(100vh - 265px);
}
:deep(.el-input-group__append, .el-input-group__prepend) {
background-color: #ffffff00;
}
:deep(.frontBox) {
background-color: var(--el-color-primary) !important;
color: #fff !important;
}
</style>