You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1827 lines
92 KiB
HTML

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>tool</title>
<script type="text/javascript" src="./three/three.min.js"></script>
<script type="text/javascript" src="./three/OrbitControls.js"></script>
<script type="text/javascript" src="./three/Lut.js"></script>
<script src="./js/vue.global.min.js"></script>
<script src="./js/antd.min.js"></script>
<link rel="stylesheet" href="./css/antd.min.css">
<link rel="stylesheet" href="./css/element-plus.css">
<script src="./js/element-plus.js"></script>
<!-- 引入 Element Plus 中文语言包 -->
<script src="./js/zh-cn.min.js"></script>
<script src="./js/echarts.min.js"></script>
<script src="./js/echarts-gl.min.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/echarts-gl@2.0.9/dist/echarts-gl.min.js"></script> -->
<link rel="stylesheet" href="./css/common.css">
<script type="text/javascript" src="./js/axios.min.js"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
padding: 10px;
background-color: #f5f5f5;
}
</style>
</head>
<body>
<div id="app">
<div class="content">
<!-- 树菜单 -->
<div ref="treeMenu" v-if="treeMenuInfo.menuVisible" class="context-menu"
:style="{ left: `${treeMenuInfo.menuX}px`, top: `${treeMenuInfo.menuY}px` }">
<div v-if="treeMenuInfo.data.isRoot" @click="addMonitor(treeMenuInfo.data)">新增监测点</div>
<div @click="editNodeItem(treeMenuInfo.data)">编辑</div>
<a-popconfirm title="确定要删除吗?" ok-text="确定" cancel-text="取消"
@confirm="deleteNodeItem(treeMenuInfo.data)">
<div>删除</div>
</a-popconfirm>
</div>
<div class="tree-content">
<div class="input-box">
<a-input placeholder="名称" v-model:value="searchValue" @change="search">
</a-input>
</div>
<div class="tree-box">
<el-tree ref="treeRef" node-key="key" check-strictly :check-on-click-node="false" :data="treeData"
:props="defaultProps" :load="onLoadData" lazy v-model:checked-keys="checkedKeys1"
:current-node-key="selectedKey" :show-checkbox="typeOfCheckable.includes(activeKey)"
@check="checkTree" @node-click="selectTree" highlight-current :filter-node-method="filterNode"
@node-contextmenu="handleRightClick" />
</div>
<div class="bottom-btns">
<a-button type="primary" size="small" @click="isImportMonitorModalShow=true"> 导入监测点 </a-button>
<a-button style="margin-left: 4px;" type="primary" size="small" @click="addSite"> 新增站点 </a-button>
</div>
</div>
<div class="right-content">
<!-- tab切换 -->
<a-tabs v-model:activeKey="activeKey" @change="(key)=>activeKey=key">
<a-tab-pane v-for="item in tabsArr" :key="item.key" :tab="item.tab"></a-tab-pane>
</a-tabs>
<div class="panel-views">
<div v-if="activeKey === 'historyTrend'" key="historyTrend" class="sub-content history-trend">
<!-- 历史趋势 -->
<div class="top-selector">
<el-date-picker v-model="dateRange" style="width: 360px" class="custom-date-picker"
value-format="YYYY-MM-DD HH:mm:ss" type="datetimerange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" :disabled-date="disabledDate"
@panel-change="handlePanelChange" />
</div>
<div class="trend-graph-container">
<div class="trend-item" v-for="item in trendGraphData" :key="item.id">
<div class="title">
监测点:{{ item.id }}
</div>
<div class="line-graph" :ref="(el) => trendGraphRefs(el, item.id)"></div>
</div>
</div>
</div>
<div v-if="activeKey === 'alarmConfig'" key="alarmConfig" class="sub-content alarm-config">
<!-- 告警配置 -->
<div class="search-bar">
<div class="search-item">
<span>放电类型:</span>
<a-select v-model:value="alarmFilters.pdTypes" :options="pdTypeOps" mode="tags"
placeholder="请选择" style="width: 300px">
</div>
<div class="search-item">
<span>时间:</span>
<el-date-picker v-model="alarmFilters.times" style="width: 360px"
class="custom-date-picker" value-format="YYYY-MM-DD HH:mm:ss" type="datetimerange"
range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" />
</div>
<div class="break"></div>
<div class="search-item">
<span>是否复归:</span>
<a-radio-group :options="cancelOps" v-model:value="alarmFilters.isCancel" />
</div>
<div class="search-item">
<a-button type="primary" size="small" @click="fetchAlarmGridData">查询</a-button>
</div>
<div class="search-item">
<a-button size="small" @click="initAlarmGridData">重置</a-button>
</div>
</div>
<div class="grid-content">
<a-table :columns="columns" :data-source="dataSource" :pagination="pagination"
:loading="configLoading" @change="handleTableChange" :scroll="{ y: 460 }">
<template #name1="{ text }">
{{pdTypeOps.find(item=>item.value==text).label}}
</template>
<template #name2="{ text }">
{{ cancelOps.find(item=>item.value==text).label}}
</template>
</a-table>
</div>
</div>
<div v-if="activeKey === 'countPRPD'" key="countPRPD" class="sub-content total-prpd">
<!-- 累计PRPD -->
<div class="trend-box"></div>
<div class="prpd-box">
<!-- 累计PRPD组件 -->
<total-prpd class="total-prpd-item" v-for="(item,index) in totalPrpds" :key="index"
:data="item" />
</div>
</div>
<div v-if="activeKey === 'prpdAndPrps'" key="prpdAndPrps" class="sub-content prpd-and-prps">
<!-- PRPD/PRPS -->
<div class="trend-box"></div>
<div class="prpd-and-prps-box">
<prpd-and-prps class="prpd-and-prps-item" v-for="(item,index) in 1" :key="index"
:sort="index" />
</div>
</div>
<div v-if="activeKey === 'eventCount'" key="eventCount" class="sub-content">
<!-- 事件统计 -->
事件统计
</div>
<div v-if="activeKey === 'config'" key="config" class="sub-content config-page">
<!-- 配置页面 -->
<div class="station-config">
<div class="title">站点告警配置</div>
<div class="station">
<span>站点:</span>
<a-select v-model:value="stationName1" size="small" style="width: 200px">
<a-select-option v-for="item in treeData" :key="item.key" :value="item.stationName">
{{ item.stationName }}
</a-select-option>
</a-select>
</div>
<div class="delete-bar">
<span class="delete-time">
<span class="label">清除告警时间段:</span>
<el-date-picker v-model="deleteAlarmTimes" style="width: 360px"
class="custom-date-picker" value-format="YYYY-MM-DD HH:mm:ss"
type="datetimerange" range-separator="至" start-placeholder="开始日期"
end-placeholder="结束日期" />
</span>
<a-popconfirm title="确定删除?" ok-text="是" cancel-text="否" @confirm="deleteAlarmConfirm">
<a href="#">删除</a>
</a-popconfirm>
</div>
<el-upload action="#" :http-request="alarmCustomUpload" :show-file-list="false" :limit="1"
accept=".csv,.xlsx,.xls" :before-upload="alarmBeforeUpload">
<a-button type="primary" size="small">导入告警</el-button>
</el-upload>
</div>
<div class="path-config">
<div class="title">路径配置</div>
<div class="path-item" v-for="item in allPathCongfigs" :key="item.key">
<div class="name">{{item.label}} :</div>
<div class="row">
<el-input v-model="item.value" style="width: 560px" placeholder="请输入路径" />
</div>
<a-button size="small" type="primary" style="margin-right: 4px;"
@click="savePathConfig(item)">保存</a-button>
<a-button size="small" @click="resetPathConfig(item.key)">重置</a-button>
</div>
</div>
</div>
</div>
</div>
<!-- 导入监测点弹框 -->
<a-modal width="550px" :destroyOnClose="true" :visible="isImportMonitorModalShow" title="导入监测点"
@ok="onImportMonitor" @cancel="isImportMonitorModalShow=false"
:confirmLoading="importMonitorModalloading"></a-modal>
<!-- 站点弹框 -->
<a-modal width="550px" :destroyOnClose="true" :visible="isSiteModalShow"
:title="isAddTypeOfSiteModal?'新增站点':'编辑站点'" @cancel="isSiteModalShow=false">
<a-form layout=" vertical" ref="siteModalRef" :model="siteModalForm">
<a-form-item label="站点名称" name="stationName" :rules="[
{
required: true,
message: '请输入站点名称',
},
{
max: 20,
message: '最多输入20个字符',
},
]">
<a-input v-model:value="siteModalForm.stationName" placeholder="请输入" />
</a-form-item>
<a-form-item label="背景图路径" name="img" :rules="[
{
required: true,
message: '请输入背景图路径',
},
]">
<a-input v-model:value="siteModalForm.img" placeholder="请输入" />
</a-form-item>
</a-form>
<template #footer>
<a-button @click="isSiteModalShow=false">关闭</a-button>
<a-button type="primary" @click="onSiteModalSubmit" :loading="siteModalloading">确定</a-button>
</template>
</a-modal>
<!-- 监测点弹框 -->
<a-modal width="550px" :destroyOnClose="true" :visible="isMonitorModalShow"
:title="isAddTypeOfMonitorModal?'新增监测点':'编辑监测点'" @cancel="isMonitorModalShow=false">
<a-form layout=" vertical" ref="monitorModalRef" :model="monitorModalForm">
<a-form-item label="站点名称" name="stationName" :rules="[
{
required: true,
message: '请输入监测点名称',
},
]" disabled>
<a-input v-model:value="monitorModalForm.stationName" disabled />
</a-form-item>
<a-form-item label="监测点Key" name="monitorKey" :rules="[
{
required: true,
message: '请输入监测点Key',
},
{
max: 20,
message: '最多输入20个字符',
},
]">
<a-input v-model:value="monitorModalForm.monitorKey" placeholder="请输入"
:disabled="!isAddTypeOfMonitorModal" />
</a-form-item>
<a-form-item label="监测点名称" name="name" :rules="[
{
required: true,
message: '请输入监测点名称',
},
{
max: 20,
message: '最多输入20个字符',
},
]">
<a-input v-model:value="monitorModalForm.name" placeholder="请输入" />
</a-form-item>
</a-form>
<template #footer>
<a-button @click="isMonitorModalShow=false">关闭</a-button>
<a-button type="primary" @click="onMonitorModalSubmit" :loading="monitorModalLoading">确定</a-button>
</template>
</a-modal>
</div>
<script>
axios.defaults.baseURL = 'http://192.168.1.80:9501'; // 临时服务地址
axios.defaults.timeout = 10000;
const findParentByKey = (tree, targetKey) => {
for (const parent of tree) {
// 检查子节点
if (parent?.children?.length) {
for (const child of parent.children) {
if (child.key === targetKey) {
return parent.stationName; // 返回父节点
}
}
}
}
return null;
}
const formatTreeData = (node) => {
return {
...node.data,
children: node.childNodes?.map(child => formatTreeData(child)) || []
};
};
// 生成正弦波形
const generateSineWaveData = () => {
const dataPoints = [];
// 遍历0到360度每1度取一个点
for (let angle = 0; angle <= 360; angle++) {
// 将角度转换为弧度
const radian = angle * Math.PI / 180;
// 计算增益值
const gain = 40 * Math.sin(radian) - 40;
// 将 [相位, 增益] 对添加到数组中
// 使用 toFixed(2) 来保留两位小数,避免浮点数精度问题,并将其转换为数字类型
dataPoints.push([angle, parseFloat(gain.toFixed(2))]);
}
return dataPoints;
}
const { createApp, ref, onMounted, unref, watch, reactive, computed, nextTick, onUnmounted, components } = Vue;
const { message } = antd;
// 累计prpd组件
const totalPrpd = {
template: `<div ref="prpdBoxRef" style="width: 100%; height: 100%;"></div>`,
props: ['data'], // 接收父组件传递的数据
data() {
return {
}
},
methods: {
initPrpd() {
const chartDom = this.$refs.prpdBoxRef;
this.myChart = echarts.init(chartDom);
const option = {
backgroundColor: '#ffffff', // 设置背景色为深色,与图片相似
title: {
text: 'PRPD',
left: 'center',
top: '2%',
textStyle: {
color: '#444444', // 标题文字颜色
fontSize: 20
}
},
tooltip: {
trigger: 'axis',
formatter: function (params) {
// 自定义提示框内容显示x和y的值
return `相位: ${params[0].value[0]}<br/>幅值: ${params[0].value[1]}`;
},
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
grid: {
left: '10%',
right: '10%',
top: '15%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'value',
name: '相位',
nameTextStyle: {
color: '#000000' // <-- 将轴名称“相位”的颜色设置为黑色
},
nameLocation: 'middle',
nameGap: 30, // 调整名称与轴线的距离
min: 0,
max: 360,
interval: 90, // 固定刻度为0, 90, 180, 270, 360
axisLabel: {
color: '#000' // X轴刻度文字颜色
},
axisLine: {
lineStyle: {
color: '#ccc' // X轴线颜色
}
},
splitLine: {
show: true,
lineStyle: {
color: '#ccc', // X轴网格线颜色
type: 'solid'
}
}
},
yAxis: {
type: 'value',
name: '幅值',
nameLocation: 'middle',
nameTextStyle: {
color: '#000000' // <-- 将轴名称“相位”的颜色设置为黑色
},
nameRotate: 90, // Y轴名称旋转
nameGap: 30, // 调整名称与轴线的距离
min: -80,
max: 0,
interval: 20, // 固定刻度为 -80, -60, -40, -20, 0
axisLabel: {
color: '#000', // Y轴刻度文字颜色
formatter: function (value) {
// 根据图片中的刻度值手动设置Y轴刻度显示
if (value === -80) return '-80';
if (value === -60) return '-60'; // 图片中-60没有显示数字
if (value === -40) return '-40';
if (value === -20) return '-20';
if (value === 0) return '0';
return value;
}
},
axisLine: {
lineStyle: {
color: '#ccc' // Y轴线颜色
}
},
splitLine: {
show: true,
lineStyle: {
color: '#ccc', // Y轴网格线颜色
type: 'solid'
}
}
},
// 颜色区分
visualMap: {
dimension: 2,
show: false,
inRange: {
color: ['#007ACC', '#FF4500'] // 例如:从浅蓝色到深蓝色
},
},
series: [
{
name: '正弦波形', // 系列名称
type: 'line', // 图表类型为折线图
smooth: true, // 开启平滑曲线
showSymbol: false, // 不显示数据点符号
lineStyle: {
width: 2 // 调整线条宽度
},
data: generateSineWaveData() // 调用函数生成数据
},
{
name: 'PRPD数据',
type: 'scatter', // 使用折线图来模拟曲线
symbolSize: 2.5,
data: [],
},
]
};
this.myChart.setOption(option);
this.resizeObserver = new ResizeObserver(() => {
this.myChart?.resize();
});
this.resizeObserver.observe(chartDom);
}
},
created() {
this.myChart = null
this.resizeObserver = null
},
mounted() {
this.$nextTick(() => {
this.initPrpd()
})
fetch('./three/prpd.json') // JSON文件路径
.then(response => response.json()) // 解析为JSON对象
.then(data => {
let index = 0;
const intervalId = setInterval(() => {
const curret = data[index].map(item => {
const y = Number(item[0]) - 80
return [item[1], y, item[2]]
});
const countArr = data[index].map(item => item[2])
const minCount = Math.min(...countArr)
const maxCount = Math.max(...countArr)
this.myChart.setOption({
series: [{
name: 'PRPD数据',
data: curret
}],
visualMap: {
min: minCount,
max: maxCount
}
}, { notMerge: false });
index = (index + 1) % data.length; // 循环索引
}, 1000);
})
.catch(error => console.error('加载JSON失败:', error));
},
}
// 放在vue中的data会卡顿
const prpsData = [
{
prpsChart: null,
prpsChartOps: null
},
{
prpsChart: null,
prpsChartOps: null
},
{
prpsChart: null,
prpsChartOps: null
},
{
prpsChart: null,
prpsChartOps: null
},
{
prpsChart: null,
prpsChartOps: null
},
{
prpsChart: null,
prpsChartOps: null
}
]
// const prpdAndPrps = {
// template: `<div ref='prpsRef' style="width: 100%; height: 100%;">prpd/prps组件</div>`,
// data() {
// return {
// len: 0,
// timer: 0
// };
// },
// props: {
// sort: {
// type: Number,
// default: 0
// }
// },
// async mounted() {
// this.initChart(); // 初始化 ECharts 实例和基础配置
// await fetch('./three/prps copy.json')  // JSON文件路径
// .then(response => response.json())  // 解析为JSON对象
// .then(data => {
// let databox = []
// for (let i = 0; i < 5000; i++) {
// databox[i] = -100;
// }
// const fullData = databox.concat(data); // 直接使用导入的 JSON 数
// this.dispose([...fullData]); // 启动动画
// })
// .catch(error => console.error('加载JSON失败:', error));
// },
// beforeDestroy() {
// // 在组件销毁前,清除定时器并销毁 ECharts 实例
// if (this.timer) {
// clearTimeout(this.timer);
// }
// },
// methods: {
// initChart() {
// if (this.$refs.prpsRef) {
// prpsData[this.sort].prpsChart = echarts.init(this.$refs.prpsRef);
// // 初始化一次不变的 ECharts 配置
// prpsData[this.sort].prpsChartOps = {
// backgroundColor: '#111',
// title: {
// text: 'PRPS',
// left: 'center',
// top: 10,
// textStyle: {
// fontSize: 16,
// color: 'rgba(255, 255, 255, 0.7)'
// }
// },
// animation: false,
// tooltip: {
// show: false,
// },
// toolbox: {
// show: true,
// orient: 'horizontal',
// top: 0,
// right: '0',
// showTitle: false,
// iconStyle: {
// borderColor: '#fff'
// },
// emphasis: {
// show: false,
// iconStyle: {
// borderColor: '#fff'
// }
// },
// feature: {
// mark: { show: true },
// restore: { show: true },
// saveAsImage: { show: true }
// }
// },
// visualMap: {
// min: -20,
// max: 80,
// show: false,
// itemWidth: 5,
// orient: "vertical",
// inRange: {
// color: ['transparent', '#00ee00', '#eeee00', '#ee0000', '#4e0211']
// },
// formatter: function (value) {
// return parseInt(value - 80);
// }
// },
// xAxis3D: {
// type: 'value',
// min: 0,
// max: 50,
// splitNumber: 5,
// name: '周期', // 注意:$.i18n.prop 如果是 jQuery i18n需要确保引入
// nameGap: 24,
// axisLabel: {
// fontSize: 14,
// formatter: function (value, index) { value = 50 - value; if (value >= 0) { return value; } }
// },
// },
// yAxis3D: {
// type: 'value',
// min: 0,
// max: 360,
// splitNumber: 4,
// interval: 90,
// name: '相位',
// nameGap: 24,
// splitArea: {
// interval: 4,
// },
// axisLabel: {
// fontSize: 14,
// },
// },
// zAxis3D: {
// type: 'value',
// min: 0,
// max: 80,
// scale: true,
// splitNumber: 4,
// name: '幅值',
// nameGap: 24,
// axisLabel: {
// fontSize: 14,
// formatter: function (value, index) {
// if (value >= 0) {
// return value - 80;
// }
// }
// },
// },
// grid3D: {
// boxHeight: 100,
// boxWidth: 120,
// boxDepth: 100,
// axisLine: {
// lineStyle: {
// color: '#fff',
// opacity: 0.1
// }
// },
// axisPointer: {
// show: false,
// lineStyle: {
// color: '#fff'
// },
// },
// viewControl: {
// distance: 260,
// minDistance: 40,
// maxDistance: 400,
// rotateSensitivity: [1, 1],
// zoomSensitivity: 0,
// panSensitivity: 0,
// panMouseButton: "middle",
// rotateMouseButton: "left",
// orthographicSize: 150,
// maxOrthographicSize: 400,
// minOrthographicSize: 20,
// center: [0, -10, 0],
// minBeta: 30,
// maxBeta: 180,
// minAlpha: -90,
// maxAlpha: 90,
// projection: "perspective",
// autoRotateDirection: "cw",
// autoRotateAfterStill: 3,
// damping: 0.8
// },
// light: {
// main: { intensity: 1.2 },
// ambient: { intensity: 0.3 }
// }
// },
// roam: false,
// // series 放在这里作为初始结构data 留空
// series: [{
// type: 'bar3D',
// data: [], // 初始数据为空
// shading: 'color',
// label: {
// show: false,
// textStyle: {
// fontSize: 16,
// borderWidth: 1
// },
// formatter: function (param) {
// return parseInt(param.value[2] - 80);
// }
// },
// itemStyle: {
// opacity: 0.8,
// },
// silent: true,
// emphasis: {
// label: { show: false },
// }
// }]
// };
// // 首次设置 ECharts option
// prpsData[this.sort].prpsChart.setOption(prpsData[this.sort].prpsChartOps);
// }
// },
// // 处理数据,生成 ECharts 所需的三维坐标格式
// deal(arr, dbm) {
// for (let i = 0; i < 5000; i++) {
// let j = parseInt(i / 100) + 1;
// arr[i] = [j, parseInt((i - (j - 1) * 100 + 1) * 3.6), parseInt(dbm[i]) + 80];
// }
// return arr;
// },
// // 绘制/更新 ECharts 3D 图表
// drawEcharts3d(obj) {
// if (!prpsData[this.sort].prpsChart) return; // 确保图表实例已存在
// let arr = [];
// this.deal(arr, obj); // 处理数据
// console.log(arr, 'arr');
// // 只更新 series 的 data 部分
// prpsData[this.sort].prpsChart.setOption({
// series: [{
// data: arr.map(function (item) {
// return {
// value: [item[0], item[1], item[2]]
// }
// })
// }]
// });
// },
// // 动画循环逻辑
// dispose(data) {
// clearTimeout(this.timer); // 清除上一个定时器
// this.drawEcharts3d(data); // 绘制图表
// let em = [];
// em = data.splice(0, 1000); // 移除前1000个元素
// this.len++; // 计数器加一
// if (this.len > 5) { // 如果超过5次循环将移除的元素重新加回末尾
// data = data.concat(em);
// }
// // console.log('[ data ] >', data); // **重要:在生产环境中请注释或删除此行!**
// // 设置下一个定时器
// this.timer = setTimeout(() => {
// this.dispose(data); // 递归调用自身
// }, 200);
// }
// }
// }
// prpd/prps组件
const prpdAndPrps = {
template: `<div ref='prpsRef' style="width: 100%; height: 100%;">prpd/prps组件</div>`,
data() {
return {
totalData: [],
renderData: [],
timer: [],
chart: null,
chartData: [],
// 假设您的 maxNum 是幅值的最大绝对值,用于颜色映射
maxNum: 80, // 根据您的实际幅值范围设置,例如图中的-80到0那么最大绝对值就是80
currentPeriodCycle: 50, // 当前数据从哪个周期开始
totalPeriods: 50, // 图表上显示的周期总数对应您three.js中的this.period
phasesPerPeriod: 128, // 每个周期包含的相位点数量
intervalId: null,
};
},
async mounted() {
await fetch('./three/prps.json') // JSON文件路径
.then(response => response.json()) // 解析为JSON对象
.then(data => {
this.totalData = JSON.parse(JSON.stringify(data))
this.rawAmplitudeData = [];
data.forEach(periodDataArray => {
// 确保每个周期处理 100 个点。如果原始数据多于 100只取前 100如果少于 100则补0。
for (let i = 0; i < this.phasesPerPeriod; i++) {
if (periodDataArray[i] && periodDataArray[i][2] !== undefined) {
this.rawAmplitudeData.push(parseFloat(periodDataArray[i][2]));
} else {
this.rawAmplitudeData.push(0); // 数据不足时补0
}
}
});
console.log('扁平化后的总幅值数据点数量:', this.rawAmplitudeData);
})
.catch(error => console.error('加载JSON失败:', error));
this.initChart(); // 首次初始化图表,包括所有配置
this.startDynamicData();
},
beforeDestroy() {
// 在组件销毁前清除定时器,避免内存泄漏
if (this.intervalId) {
clearInterval(this.intervalId);
}
},
methods: {
initChart() {
this.chart = echarts.init(this.$refs.prpsRef);
// 首次设置所有图表配置,包括轴、光照、视觉映射等
const option = {
tooltip: {
show: false
},
toolbox: {
show: false,
},
animation: false,
visualMap: {
max: 80,
min: -20,
show: !1,
itemWidth: 5,
dimension: 2,
orient: "vertical",
inRange: {
color: ["transparent", "#00ee00", "#eeee00", "#ee0000", "#4e0211"]
},
formatter: function (e) {
return parseInt(e - 80)
}
},
xAxis3D: {
type: "value",
min: 0,
max: 50,
splitNumber: 5,
name: '周期',
nameGap: 24,
axisLine: {
lineStyle: {
color: '#000'
}
},
axisLabel: {
color: '#000',
fontSize: 14,
formatter: function (e, t) {
if (0 <= (e = 50 - e))
return e
}
}
},
yAxis3D: {
type: "value",
min: 0,
max: 360,
splitNumber: 4,
interval: 90,
name: '相位',
nameGap: 24,
splitArea: {
interval: 4
},
axisLine: {
lineStyle: {
color: '#000'
}
},
axisLabel: {
fontSize: 14,
color: '#000'
}
},
zAxis3D: {
name: '幅值',
min: 0,
max: 80,
scale: !0,
splitNumber: 4,
name: '幅值',
nameGap: 24,
axisLine: {
lineStyle: {
color: '#000'
}
},
axisLabel: {
color: '#000',
fontSize: 14,
formatter: function (e, t) {
if (0 <= e)
return e - 80
}
}
},
grid3D: {
boxWidth: 100,
boxHeight: 100,
boxDepth: 100,
axisLine: {
lineStyle: {
color: "#fff",
opacity: .1
}
},
axisPointer: {
show: !1,
lineStyle: {
color: "#fff"
}
},
viewControl: {
distance: 260,
minDistance: 40,
maxDistance: 400,
rotateSensitivity: [1, 1],
zoomSensitivity: 0,
panSensitivity: 0,
panMouseButton: "middle",
rotateMouseButton: "left",
orthographicSize: 150,
maxOrthographicSize: 400,
minOrthographicSize: 20,
center: [0, -10, 0],
minBeta: 30,
maxBeta: 180,
minAlpha: -90,
maxAlpha: 90,
projection: "perspective",
autoRotateDirection: "cw",
autoRotateAfterStill: 3,
damping: .8
},
light: {
main: {
intensity: 1.2
},
ambient: {
intensity: .3
}
}
},
roam: !1,
series: [
{
type: 'bar3D',
data: [], // 初始为空数据,或调用 initChartData() 填充
shading: 'color', // lambert
barSize: 0.5,
label: {
show: !1,
textStyle: {
fontSize: 16,
borderWidth: 1
},
formatter: function (e) {
return parseInt(e.value[2] - 80)
}
},
itemStyle: {
opacity: .8
},
silent: !0,
emphasis: {
label: {
show: !1
}
}
}
]
};
this.chart.setOption(option);
},
delay(id, callBack, ms) {
return new Promise((resolve) => {
this.timer[id] = setTimeout(() => {
callBack();
resolve();
}, ms);
});
},
async startDynamicData() {
console.log('[ this.totalData ] >', this.totalData)
for (let [index, item] of this.totalData.entries()) {
let callBack = () => {
const currentData = JSON.parse(JSON.stringify(item)).map((item) => [50, item[1], Number(item[2])]) // 50周期开始,数据从右往左
this.renderData = [currentData, ...JSON.parse(JSON.stringify(this.renderData))]
this.renderData.forEach(periodArray => {
periodArray.forEach(dataPoint => {
dataPoint[0]-- // x轴减一
});
});
this.renderData = this.renderData.filter(item => item[0][0] >= 0) // 筛选掉x轴为负的
const aa = this.renderData.flat()
console.log('[ this.renderData ] >',)
this.chart.setOption({
series: [{
data: aa
}]
});
};
await this.delay(index, callBack, 200);
}
// 等待所有延迟完成后再次开始
// this.startDynamicData();
},
}
}
createApp({
setup() {
const typeOfCheckable = ['historyTrend', 'countPRPD', 'prpdAndPrps']
const treeData = ref([])
const fullTreeData = ref([]) // 懒加载后的完整菜单数据
const checkedKeys1 = ref([]); // 勾选
const selectedKey = ref('') // 选中
const treeMenuInfo = reactive({
menuVisible: false,
data: null,
menuX: 0,
menuY: 0
})
const treeMenu = ref()
const handleClickOutside = (e) => {
if (treeMenuInfo.menuVisible && !treeMenu.value?.contains(e.target)) {
treeMenuInfo.menuVisible = false;
}
};
const searchValue = ref('');
const treeRef = ref()
const activeKey = ref('prpdAndPrps')
const tabsArr = [
{ key: 'historyTrend', tab: '历史趋势' },
{ key: 'alarmConfig', tab: '告警管理' },
{ key: 'countPRPD', tab: '累计PRPD' },
{ key: 'prpdAndPrps', tab: 'PRPD/PRPS' },
{ key: 'eventCount', tab: '事件统计' },
{ key: 'config', tab: '配置' }
]
const isImportMonitorModalShow = ref(false)
const importMonitorModalloading = ref(false)
const siteModalloading = ref(false)
const isSiteModalShow = ref(false)
const siteModalRef = ref()
const siteHistoryData = ref() // 站点历史数据
// 站点信息
const siteModalForm = reactive({
stationName: '',
img: ''
})
const isAddTypeOfSiteModal = ref(true) // true:新增站点 false:修改站点
const isMonitorModalShow = ref(false) // 监测点弹框
const isAddTypeOfMonitorModal = ref(true) // true:新增监测点 false:修改监测点
const monitorModalLoading = ref(false)
const monitorModalRef = ref()
const monitorModalForm = reactive({
stationName: '', // 站点名称
name: '',// 监测点名称
monitorKey: '' // 监测点key
})
// 查询树
const search = () => {
treeRef.value.filter(unref(searchValue)); // 触发过滤
};
const filterNode = (value, data) => {
if (!value) return true;
return data.name.includes(value); // 根据label匹配
};
const handleRightClick = (event, data) => {
event.preventDefault();
treeMenuInfo.menuVisible = true
treeMenuInfo.menuX = event.clientX - 8
treeMenuInfo.menuY = event.clientY - 12
treeMenuInfo.data = data
}
const checkTree = (checkedKeys, { checkedKeys: currentKeys }) => {
if (currentKeys.length > 6) {
message.warning('最多只能勾选6个节点');
// 阻止勾选:回退到前一次的状态
checkedKeys1.value = checkedKeys1.value.slice(0, 6);
treeRef.value.setCheckedKeys(checkedKeys1.value);
} else {
checkedKeys1.value = currentKeys
}
}
const selectTree = (node) => {
if (node.isRoot) {
selectedKey.value = ''
} else {
selectedKey.value = node.key
}
}
const defaultProps = {
children: 'children',
label: 'name',
isLeaf: 'leaf',
class: (node) => node.isRoot ? 'is-root' : ''
}
const onLoadData = (node, resolve) => {
if (node.level !== 1) {
resolve([]);
return
}
const stationName = node?.label ?? ''
axios.get(`/ldpdtools/monitor/monitorList?stationName=${stationName}`).then(({ data: { code, result } }) => {
const data = (result || []).map(item => {
return {
title: item.name,
key: item.monitorKey,
isRoot: false,
disabled: false,
leaf: true,
...item
}
})
resolve(data)
fullTreeData.value = treeRef.value?.store?.root.childNodes.map(node => formatTreeData(node));
})
}
// 初始化树
const initTreeData = async () => {
const { data: { code, result } } = await axios.get('/ldpdtools/monitor/getStations');
if (code !== 200) {
return
}
treeData.value = result.map((item, index) => {
return {
key: index,
title: item.stationName,
isRoot: true,
disabled: true,
...item
}
})
fullTreeData.value = treeRef.value?.store?.root.childNodes.map(node => formatTreeData(node));
}
// 编辑节点
const editNodeItem = (data) => {
const { isRoot, stationName, img, monitorKey, name } = data
if (isRoot) {
// 编辑站点
isSiteModalShow.value = true
isAddTypeOfSiteModal.value = false
siteModalForm.stationName = stationName
siteModalForm.img = img
siteHistoryData.value = JSON.parse(JSON.stringify({ stationName, img }))
} else {
// 编辑监测点
isMonitorModalShow.value = true
isAddTypeOfMonitorModal.value = false
monitorModalForm.stationName = stationName
monitorModalForm.name = name
monitorModalForm.monitorKey = monitorKey
}
}
// 删除节点
const deleteNodeItem = async (data) => {
const { isRoot, stationName, monitorKey } = data
let resultCode = 200
if (isRoot) {
// 删除站点
const { data: { code } } = await axios.delete(`/ldpdtools/monitor/deleteStation?stationName=${stationName}`);
resultCode = code
} else {
// 删除检测点
const { data: { code } } = await axios.delete(`/ldpdtools/monitor/deleteMonitor?stationName=${stationName}&monitorKey=${monitorKey}`);
resultCode = code
}
if (resultCode === 200) {
message.success(`删除${isRoot ? '站点' : '监测点'}成功`);
initTreeData()
} else {
message.error(`删除${isRoot ? '站点' : '监测点'}失败`);
}
}
// 新增监测点
const addMonitor = (data) => {
const { stationName } = data
monitorModalForm.stationName = stationName
isMonitorModalShow.value = true
}
// 导入监测点
const onImportMonitor = () => {
}
const addSite = () => {
isSiteModalShow.value = true
}
// 新增、编辑站点
const onSiteModalSubmit = async () => {
await siteModalRef.value.validate()
siteModalloading.value = true
const { img, stationName } = unref(siteModalForm)
let resultCode = 200
if (unref(isAddTypeOfSiteModal)) {
// 新增
const { data: { code } } = await axios.put(`/ldpdtools/monitor/saveStateion?img=${img}&stationName=${stationName}`);
resultCode = code
} else {
// 编辑
const { data: { code } } = await axios.put(`/ldpdtools/monitor/editStateion?img=${img}&newStationName=${stationName}&stationName=${unref(siteHistoryData).stationName}`);
resultCode = code
}
siteModalloading.value = false
if (resultCode === 200) {
message.success(`${unref(isAddTypeOfSiteModal) ? '新增' : '编辑'}站点成功`);
isSiteModalShow.value = false
initTreeData()
} else {
message.error(`${unref(isAddTypeOfSiteModal) ? '新增' : '编辑'}站点失败`);
}
}
// 新增、编辑监测点
const onMonitorModalSubmit = async () => {
await monitorModalRef.value.validate()
monitorModalLoading.value = true
const { stationName, name, monitorKey } = unref(monitorModalForm)
let resultCode = 200
let resultMsg = ''
if (unref(isAddTypeOfMonitorModal)) {
// 新增
const { data: { code, message } } = await axios.put(`/ldpdtools/monitor/saveMonitor?stationName=${stationName}&name=${name}&monitorKey=${monitorKey}`);
resultCode = code
resultMsg = message
} else {
// 编辑
const { data: { code, message } } = await axios.put(`/ldpdtools/monitor/editMonitor?stationName=${stationName}&name=${name}&monitorKey=${monitorKey}`);
resultCode = code
resultMsg = message
}
monitorModalLoading.value = false
if (resultCode === 200) {
message.success(`${unref(isAddTypeOfMonitorModal) ? '新增' : '编辑'}监测点成功`);
isMonitorModalShow.value = false
initTreeData()
} else {
message.error(resultMsg);
}
}
// 监测站点弹框关闭
watch(isSiteModalShow, (bool) => {
if (bool) {
return
}
siteModalRef.value.resetFields()
isAddTypeOfSiteModal.value = true
siteModalForm.stationName = ''
siteModalForm.img = ''
siteHistoryData.value = {}
})
// 监测监测点弹框关闭
watch(isMonitorModalShow, (bool) => {
if (bool) {
return
}
monitorModalRef.value.resetFields()
isAddTypeOfMonitorModal.value = true
monitorModalForm.stationName = ''
monitorModalForm.name = ''
monitorModalForm.monitorKey = ''
})
// 历史趋势部分逻辑
const trendGraphData = ref([])
const trendGraphRefs = (el, id) => {
if (el) childRefs.value[id] = el;
};
const childRefs = ref({});
const dateRange = ref([]);
const currentYear = ref(new Date().getFullYear());
const allowedMonths = ref([]); // 允许选择的月份(如[1,5]表示1月和5月
// 禁用日期规则(核心逻辑)
const disabledDate = (time) => {
const date = new Date(time);
const year = date.getFullYear();
const month = date.getMonth() + 1;
// 规则1只允许选择当前年份
if (year !== currentYear.value) return true;
// 规则2只允许选择API返回的月份
if (!allowedMonths.value.includes(month)) return true;
// 规则3限制选择范围为1个月内当已选开始日期时
if (dateRange.value?.[0]) {
const startDate = new Date(dateRange.value[0]);
const minDate = new Date(startDate);
const maxDate = new Date(startDate);
minDate.setMonth(minDate.getMonth() - 1);
maxDate.setMonth(maxDate.getMonth() + 1);
return date < minDate || date > maxDate;
}
return false;
};
const handlePanelChange = (date) => {
currentYear.value = date?.[0]?.getFullYear() || 2025
};
const fetchHistoryTrendData = async (id, time) => {
try {
const params = {
current: 1,
startTimeStr: time[0],
endTimeStr: time[1],
monitorKey: id,
pageSize: 99999
}
const { data: { result: { result: { result } } } } = await axios.get(`/ldpdtools/trendData/listByParam`, {
params,
})
return {
id,
// label:
data: result
}
} catch (error) {
console.error(`请求 ${id} 时出错:`, error);
return null;
}
}
// 初始化趋势echart
const initTrendGraph = (el, data) => {
const legendData = [
{ id: 'avg', name: '平均值' },
{ id: 'maxValue', name: '最大值' },
{ id: 'plusAvg', name: '脉冲平均值' },
{ id: 'plusCount', name: '脉冲次数' },
]
// 图数据
const series = legendData.map((item, index) => ({
name: item.name,
type: 'line',
yAxisIndex: index,
data: data.data.map(el => el[item.id]),
smooth: true,
showSymbol: false,
lineStyle: {
width: 2,
},
}));
const legend = {
data: legendData.map(s => s.name), // 图例数据
top: 0,
};
const xAxis = {
type: 'category',
data: data.data.map(el => el.time.replace(' ', '\n')),
axisLine: {
onZero: false, // 禁止轴线对齐到0刻度强制固定在底部
},
name: '时间',
axisLabel: {
fontSize: 10,
},
};
const yAxis = legendData.map((item, index) => {
return {
type: 'value',
name: item.name,
nameTextStyle: {
color: '#666',
fontSize: 10,
padding: [0, 0, 0, 30], // 上、右、下、左左间距30px
},
triggerEvent: true, // 关键配置
axisLabel: {
formatter: '{value}',
fontSize: 10,
},
position: 'right',
offset: (index + 1) * 48, // 控制 Y 轴横向间距
splitLine: {
show: false, // 仅第一个 Y 轴显示网格线
},
splitNumber: 3, // Y轴仅显示3个刻度线
};
});
const chart = echarts.init(el);
const option = {
// 设置响应式
grid: {
top: 30,
right: 230,
bottom: 30,
left: 30
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend,
xAxis,
yAxis,
series
};
// 3. 设置配置项并渲染图表
chart.setOption(option);
// 4. 窗口大小改变时重新调整图表大小
const resizeHandler = () => chart.resize();
window.addEventListener('resize', resizeHandler);
// 返回图表实例和清理函数
return {
instance: chart,
dispose: () => {
window.removeEventListener('resize', resizeHandler);
chart.dispose();
}
};
}
const chartGroups = ref({});
// tab在历史趋势监听勾选节点
watch([checkedKeys1, dateRange], async ([keys, time]) => {
const [starTime, endTime] = time || [];
if (unref(activeKey) !== 'historyTrend' || !keys.length || !starTime || !endTime) return;
// 先清理之前的图表
Object.values(chartGroups.value).forEach(group => {
group.instances.forEach(chart => chart.dispose());
});
chartGroups.value = {};
const promises = keys.map((key) => fetchHistoryTrendData(key, time));
trendGraphData.value = await Promise.all(promises);
nextTick(() => {
// 按分组存储图表实例
const groupMap = {};
Object.keys(childRefs.value).forEach((id) => {
const childEl = childRefs.value[id];
const childData = trendGraphData.value.find((item) => item.id === id);
if (childEl && childData) {
const { instance, dispose } = initTrendGraph(childEl, childData);
// 假设你的数据中有 groupId 字段表示分组
const groupId = childData.groupId || 'default';
if (!groupMap[groupId]) {
groupMap[groupId] = [];
}
groupMap[groupId].push(instance);
}
});
// 连接同一组的图表
Object.keys(groupMap).forEach(groupId => {
if (groupMap[groupId].length > 1) {
echarts.connect(groupMap[groupId]);
}
// 存储分组信息
chartGroups.value[groupId] = {
instances: groupMap[groupId],
// 可以存储其他分组相关信息
};
});
});
});
const checkedKeys1Str = computed(() => checkedKeys1.value.join(',')) // 选中节点的字符串 为了减少监听
// 请求可选择的月份
watch([checkedKeys1Str, currentYear], async ([keys, year]) => {
if (!keys || !year) return
dateRange.value = []
const { data: { result } } = await axios.post(`/ldpdtools/trendData/multHasData?year=${year}`, unref(checkedKeys1))
allowedMonths.value = result.map(item => Number(item))
}, {
immediate: true
})
// 告警信息
const configLoading = ref(false)
const columns = [
{
title: '监测点',
dataIndex: 'name',
align: 'center',
ellipsis: true,
width: 120
},
{
title: '告警信息',
dataIndex: 'detailsInfo',
align: 'center',
ellipsis: true,
},
{
title: '放电类型',
dataIndex: 'pdType',
align: 'center',
ellipsis: true,
slots: { customRender: 'name1' },
width: 120
},
{
title: '是否复归',
dataIndex: 'isCancel',
align: 'center',
ellipsis: true,
slots: { customRender: 'name2' },
width: 120
},
{
title: '告警时间',
dataIndex: 'time',
align: 'center',
ellipsis: true,
width: 180
},
]
const dataSource = ref([])
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: ['5', '10', '20'],
showTotal: (total) => `${total} 条数据`,
});
const cancelOps = [
{ label: '是', value: '0' },
{ label: '否', value: '1' },
]
const pdTypeOps = [
{
value: '1',
label: '尖刺放电',
},
{
value: '2',
label: '悬浮放电',
},
{
value: '3',
label: '沿面放电',
},
{
value: '4',
label: '内部放电',
},
{
value: '5',
label: '颗粒放电',
}
]
const alarmFilters = reactive({
times: [],
isCancel: '',
pdTypes: []
})
// 请求告警信息表格数据
const fetchAlarmGridData = async () => {
if (!unref(selectedKey)) {
message.warning(`请先选择左侧监测点`);
return
}
configLoading.value = true;
const params = {
current: pagination.current,
pageSize: pagination.pageSize,
startTime: alarmFilters.times?.[0] || '',
endTime: alarmFilters.times?.[1] || '',
isCancel: alarmFilters.isCancel,
pdType: alarmFilters.pdTypes,
monitorKey: unref(selectedKey),
stationName: findParentByKey(unref(fullTreeData), unref(selectedKey))
}
const { data: { result } } = await axios.post(`/ldpdtools/alarm/queryAlarmList`, params)
configLoading.value = false
dataSource.value = result?.result || []
pagination.total = result?.total || 0
}
// 重置表格
const initAlarmGridData = () => {
Object.assign(alarmFilters, {
times: [],
isCancel: '',
pdTypes: []
});
fetchAlarmGridData();
}
const handleTableChange = (pag) => {
Object.assign(pagination, pag);
fetchAlarmGridData();
};
// 告警管理监听
watch(selectedKey, (key) => {
if (!key || unref(activeKey) !== 'alarmConfig') return
// 初始化筛选数据
Object.assign(alarmFilters, {
times: [],
isCancel: '',
pdTypes: []
});
// 请求数据
fetchAlarmGridData();
});
// 累计prpd
const totalPrpds = ref([
{
monitorKey: ''
},
{
monitorKey: ''
},
{
monitorKey: ''
},
{
monitorKey: ''
},
{
monitorKey: ''
},
{
monitorKey: ''
},
])
// 配置页面
const stationName1 = ref('')
const deleteAlarmTimes = ref([]) // 删除告警时间段
const deleteAlarmConfirm = async () => {
if (!unref(stationName1)) {
return message.warning(`请选择站点!`);
}
if (!unref(deleteAlarmTimes).length) {
return message.warning(`请选择需要清除告警时间段!`);
}
const res = await axios.get(`/ldpdtools/alarm/deleteAlarmByTime`, {
params: {
startTime: unref(deleteAlarmTimes)?.[0] || '',
endTime: unref(deleteAlarmTimes)?.[1] || '',
stationName: unref(stationName1)
},
})
}
// 上传前校验
const alarmBeforeUpload = (file) => {
if (!unref(stationName1)) {
message.warning(`请选择站点!`);
return false
}
const allowedTypes = ['text/csv', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
const isAllowedType = allowedTypes.includes(file.type) ||
['.csv', '.xlsx', '.xls'].some(ext => file.name.toLowerCase().endsWith(ext))
if (!isAllowedType) {
message.error('只能上传 CSV 或 Excel 文件!')
return false
}
const isLt10M = file.size / 1024 / 1024 < 10
if (!isLt10M) {
message.error('文件大小不能超过 10MB!')
return false
}
return true
}
// 自定义上传方法
const alarmCustomUpload = async (options) => {
const { file } = options
try {
const formData = new FormData()
formData.append('file', file)
const { data: { code, message: message1 } } = await axios.post(`/ldpdtools/alarm/importAlarm`, formData, {
params: {
stationName: unref(stationName1)
}
})
if (code === 200) {
message.success(message1)
} else {
message.error('导入失败')
}
} catch (error) {
console.error('上传错误:', error)
}
}
const allPathCongfigs = ref([]) // 所有路径配置
// 获取所有配置项
const getAllConfigs = async () => {
allPathCongfigs.value = []
const { data: { result } } = await axios.post('/ldpdtools/config/getConfigKey')
Object.keys(result).forEach(key => {
allPathCongfigs.value = [...unref(allPathCongfigs), {
key,
label: result[key],
value: ''
}]
})
const { data: { result: paths } } = await axios.get('/ldpdtools/config/getConfig')
// 初始化路径赋值
allPathCongfigs.value.forEach(item => {
item.value = paths[item.key] || ''
})
}
// 保存路径
const savePathConfig = async (data) => {
if (!data.value) {
return message.error(`请输入${data.label}!`);
}
const { label, ...params } = data
const { data: { code } } = await axios.get('/ldpdtools/config/editConfig', { params })
if (code !== 200) return message.error(`保存${data.label}失败`)
message.success('保存成功!')
getAllConfigs()
}
// 重置路径
const resetPathConfig = async (key) => {
const { data: { code } } = await axios.get('/ldpdtools/config/resetConfig', { params: { key } })
if (code !== 200) return message.error(`重置${data.label}失败`)
message.success('重置成功!')
getAllConfigs()
}
// 监听tab切换
watch(activeKey, (tabKey, oldKey) => {
checkedKeys1.value = []
treeRef.value.setCheckedKeys([]);
selectedKey.value = ''
treeRef.value.setCurrentKey(null);
if (oldKey === 'historyTrend') {
// 销毁历史趋势数据
trendGraphData.value = []
childRefs.value = {}
dateRange.value = []
currentYear.value = new Date().getFullYear()
allowedMonths.value = []
}
if (oldKey === 'alarmConfig') {
// 销毁告警管理
Object.assign(alarmFilters, {
times: [],
isCancel: '',
pdTypes: []
});
dataSource.value = []
}
});
onMounted(() => {
initTreeData()
getAllConfigs()
document.addEventListener('click', handleClickOutside);
})
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside);
});
return {
treeRef,
treeData,
fullTreeData,
treeMenuInfo,
treeMenu,
filterNode,
handleRightClick,
checkedKeys1,
searchValue,
search,
activeKey,
tabsArr,
onLoadData,
editNodeItem,
deleteNodeItem,
isImportMonitorModalShow,
onImportMonitor,
importMonitorModalloading,
siteModalloading,
addSite,
isSiteModalShow,
siteModalRef,
siteModalForm,
onSiteModalSubmit,
isAddTypeOfSiteModal,
isMonitorModalShow,
isAddTypeOfMonitorModal,
monitorModalLoading,
addMonitor,
monitorModalRef,
monitorModalForm,
onMonitorModalSubmit,
dateRange,
handlePanelChange,
currentYear,
disabledDate,
checkTree,
trendGraphData,
trendGraphRefs,
chartGroups,
typeOfCheckable,
columns,
dataSource,
pagination,
configLoading,
handleTableChange,
selectedKey,
selectTree,
cancelOps,
alarmFilters,
pdTypeOps,
deleteAlarmTimes,
deleteAlarmConfirm,
fetchAlarmGridData,
initAlarmGridData,
stationName1,
alarmCustomUpload,
alarmBeforeUpload,
defaultProps,
totalPrpds,
allPathCongfigs,
savePathConfig,
resetPathConfig
};
},
components: {
totalPrpd,
prpdAndPrps
}
})
.use(antd)
.use(ElementPlus, {
locale: ElementPlusLocaleZhCn // 使用中文语言包
})
.mount('#app');
</script>
</body>
</html>