|
@@ -0,0 +1,533 @@
|
|
|
+<template>
|
|
|
+ <el-radio-group v-model="dateRadio.value" style="margin:0 50px 20px 0">
|
|
|
+ <el-radio-button label="day" value="day">日</el-radio-button>
|
|
|
+ <el-radio-button label="month" value="month">月</el-radio-button>
|
|
|
+ <el-radio-button label="year" value="year">年</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :md="24" :lg="12" style="margin-bottom: 20px;" v-for="(area, index) in areaData" :key="index">
|
|
|
+ <el-card class="card-area">
|
|
|
+ <div class="card-area-header">
|
|
|
+ <div class="title"> {{ area.title }} </div>
|
|
|
+ </div>
|
|
|
+ <div class="card-area-chart">
|
|
|
+ <countChart :ref="'chart' + (index + 1)" :id="'chart' + (index + 1)" :height="300" />
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+</template>
|
|
|
+<script setup>
|
|
|
+/*----------------------------------依赖引入-----------------------------------*/
|
|
|
+import { ElMessage, ElNotification } from 'element-plus'
|
|
|
+import { ref, onMounted, watch, getCurrentInstance, reactive, toRefs, nextTick } from 'vue'
|
|
|
+/*----------------------------------接口引入-----------------------------------*/
|
|
|
+/*----------------------------------组件引入-----------------------------------*/
|
|
|
+import countChart from './components/countChart.vue'
|
|
|
+/*----------------------------------store引入-----------------------------------*/
|
|
|
+/*----------------------------------公共方法引入-----------------------------------*/
|
|
|
+import dayjs from 'dayjs';
|
|
|
+import * as echarts from 'echarts'
|
|
|
+import { throttle } from 'lodash-es'; // 引入防抖/节流库
|
|
|
+/*----------------------------------公共变量-----------------------------------*/
|
|
|
+const props = defineProps({
|
|
|
+ dataType: String
|
|
|
+}) //数据双向绑定
|
|
|
+const emit = defineEmits([]);
|
|
|
+const { proxy } = getCurrentInstance();
|
|
|
+/*----------------------------------变量声明-----------------------------------*/
|
|
|
+const state = reactive({
|
|
|
+ dateRadio: {
|
|
|
+ value: "day"
|
|
|
+ },
|
|
|
+ areaData: [
|
|
|
+ {
|
|
|
+ title: "GYJLG_183信号",
|
|
|
+ chartLoading: false,
|
|
|
+ chartData: {
|
|
|
+ subtext: '',
|
|
|
+ legendData: ['设备信号强度'],
|
|
|
+ xAxisData: [],
|
|
|
+ seriesData: [
|
|
|
+ { name: '设备信号强度', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ ],
|
|
|
+ unit: "",
|
|
|
+ yAxisData: []
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "低压进线",
|
|
|
+ chartLoading: false,
|
|
|
+ chartData: {
|
|
|
+ subtext: '',
|
|
|
+ legendData: ['A相电压(V)'],
|
|
|
+ xAxisData: [],
|
|
|
+ seriesData: [
|
|
|
+ { name: 'A相电压(V)', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ ],
|
|
|
+ unit: "V",
|
|
|
+ yAxisData: []
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "进线电压",
|
|
|
+ chartLoading: false,
|
|
|
+ chartData: {
|
|
|
+ subtext: '',
|
|
|
+ legendData: ['A相电压(V)'],
|
|
|
+ xAxisData: [],
|
|
|
+ seriesData: [
|
|
|
+ { name: 'A相电压(V)', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ ],
|
|
|
+ unit: "V",
|
|
|
+ yAxisData: []
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "需量",
|
|
|
+ chartLoading: false,
|
|
|
+ chartData: {
|
|
|
+ subtext: '',
|
|
|
+ legendData: ['实时需量(kW)'],
|
|
|
+ xAxisData: [],
|
|
|
+ seriesData: [
|
|
|
+ { name: '实时需量(kW)', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ ],
|
|
|
+ unit: "kW",
|
|
|
+ yAxisData: []
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "总有功功率",
|
|
|
+ chartLoading: false,
|
|
|
+ chartData: {
|
|
|
+ subtext: '',
|
|
|
+ legendData: ['总有功功率(kW)'],
|
|
|
+ xAxisData: [],
|
|
|
+ seriesData: [
|
|
|
+ { name: '总有功功率(kW)', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ ],
|
|
|
+ unit: "kW",
|
|
|
+ yAxisData: []
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "正有功电度",
|
|
|
+ chartLoading: false,
|
|
|
+ chartData: {
|
|
|
+ subtext: '',
|
|
|
+ legendData: ['正有功电度(kWh)'],
|
|
|
+ xAxisData: [],
|
|
|
+ seriesData: [
|
|
|
+ { name: '正有功电度(kWh)', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ ],
|
|
|
+ unit: "kWh",
|
|
|
+ yAxisData: []
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "总进线三相电流",
|
|
|
+ chartLoading: false,
|
|
|
+ chartData: {
|
|
|
+ subtext: '',
|
|
|
+ legendData: ['A相电流(A)', 'B相电流(A)', 'C相电流(A)'],
|
|
|
+ xAxisData: [],
|
|
|
+ seriesData: [
|
|
|
+ { name: 'A相电流(A)', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ { name: 'B相电流(A)', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ { name: 'C相电流(A)', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ ],
|
|
|
+ unit: "A",
|
|
|
+ yAxisData: []
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "总进线三相电压",
|
|
|
+ chartLoading: false,
|
|
|
+ chartData: {
|
|
|
+ subtext: '',
|
|
|
+ legendData: ['A相电压(kV)', 'B相电压(kV)', 'C相电压(kV)'],
|
|
|
+ xAxisData: [],
|
|
|
+ seriesData: [
|
|
|
+ { name: 'A相电压(kV)', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ { name: 'B相电压(kV)', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ { name: 'C相电压(kV)', type: 'line', data: [], yAxisIndex: 0, },
|
|
|
+ ],
|
|
|
+ unit: "kV",
|
|
|
+ yAxisData: []
|
|
|
+ },
|
|
|
+
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ dateTime: dayjs().format('YYYY-MM-DD'),
|
|
|
+})
|
|
|
+const { dateRadio, areaData } = toRefs(state)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+function generateTimeSlots(startDate, intervalMinutes) {
|
|
|
+ var today = dayjs(startDate).startOf('day'); // 获取当天的开始时间
|
|
|
+ var endOfDay = dayjs(startDate).endOf('day'); // 获取当天的结束时间
|
|
|
+ const slots = [];
|
|
|
+
|
|
|
+ while (today.isBefore(endOfDay)) {
|
|
|
+ slots.push(today.format('YYYY-MM-DD HH:mm')); // 添加当前时间到数组
|
|
|
+ today = today.add(intervalMinutes, 'minute'); // 增加间隔时间
|
|
|
+ }
|
|
|
+
|
|
|
+ return slots;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function init_FPG_Data(startDate) {
|
|
|
+ return {
|
|
|
+ yAxisIndex: 1,
|
|
|
+ type: 'line',
|
|
|
+ markArea: {
|
|
|
+ data: [
|
|
|
+ [
|
|
|
+ {
|
|
|
+ name: `谷\n0.3406元`,
|
|
|
+ yAxis: 0,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 00:00'),
|
|
|
+ itemStyle: {
|
|
|
+ color: 'rgba(184, 232 , 197, 0.7)', // 区域填充色
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ color: '#b8e8c5'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ yAxis: 0.3406,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 06:00'),
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ {
|
|
|
+ name: '平\n0.7279元',
|
|
|
+ yAxis: 0,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 06:00'),
|
|
|
+ itemStyle: {
|
|
|
+ color: 'rgba(178, 210 , 242, 0.7)', // 区域填充色
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ color: '#b2d2f2'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ yAxis: 0.7279,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 08:00'),
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ {
|
|
|
+ name: '峰\n1.2442元',
|
|
|
+ yAxis: 0,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 08:00'),
|
|
|
+ itemStyle: {
|
|
|
+ color: 'rgba(251, 191, 191, 0.7)', // 区域填充色
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ color: '#fbbfbf'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ yAxis: 1.2442,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 15:00'),
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ {
|
|
|
+ name: '平\n0.7279元',
|
|
|
+ yAxis: 0,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 15:00'),
|
|
|
+ itemStyle: {
|
|
|
+ color: 'rgba(178, 210 , 242, 0.7)', // 区域填充色
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ color: '#b2d2f2'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ yAxis: 0.7279,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 18:00'),
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ {
|
|
|
+ name: '峰\n1.2442元',
|
|
|
+ yAxis: 0,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 18:00'),
|
|
|
+ itemStyle: {
|
|
|
+ color: 'rgba(251, 191, 191, 0.7)', // 区域填充色
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ color: '#fbbfbf'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ yAxis: 1.2442,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 21:00'),
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ {
|
|
|
+ name: '平\n0.7279元',
|
|
|
+ yAxis: 0,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 21:00'),
|
|
|
+ itemStyle: {
|
|
|
+ color: 'rgba(178, 210 , 242, 0.7)', // 区域填充色
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ color: '#b2d2f2'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ yAxis: 0.7279,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 22:00'),
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ {
|
|
|
+ name: `谷\n0.3406元`,
|
|
|
+ yAxis: 0,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 22:00'),
|
|
|
+ itemStyle: {
|
|
|
+ color: 'rgba(184, 232 , 197, 0.7)', // 区域填充色
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ color: '#b8e8c5'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ yAxis: 0.3406,
|
|
|
+ xAxis: dayjs(startDate).format('YYYY-MM-DD 23:55'),
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function init_yAxis_Data(num) {
|
|
|
+ const yAxis1 = {
|
|
|
+ type: 'value',
|
|
|
+ axisTick: {
|
|
|
+ show: true, // 刻度线
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ show: true,
|
|
|
+ lineStyle: { type: 'dashed', color: '#eee' }
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ show: true, // 轴线
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ const yAxis2 = {
|
|
|
+ type: 'value',
|
|
|
+ axisTick: {
|
|
|
+ show: true, // 刻度线
|
|
|
+ },
|
|
|
+ min: 0,
|
|
|
+ max: 1.5,
|
|
|
+ interval: 0.3,
|
|
|
+ position: 'right', // 显示在右侧
|
|
|
+ splitLine: { show: false },// 不与左侧 Y 轴共享刻度(可选)
|
|
|
+ axisLine: {
|
|
|
+ show: true, // 轴线
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ if (num == 1) {
|
|
|
+ return [yAxis1,]
|
|
|
+ } else if (num == 2) {
|
|
|
+ return [yAxis1, yAxis2,]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function initChartData() {
|
|
|
+ const timeSlots = generateTimeSlots(state.dateTime, 5);
|
|
|
+ const chartData = []
|
|
|
+
|
|
|
+ state.areaData.forEach((event, index) => {
|
|
|
+ event.chartData.xAxisData = timeSlots;
|
|
|
+ event.chartData.seriesData.forEach((item) => {
|
|
|
+ item.data = Array.from({ length: timeSlots.length }, () => index + 800)
|
|
|
+ })
|
|
|
+ event.chartData.yAxisData = init_yAxis_Data(1)
|
|
|
+
|
|
|
+ if (event.chartData.unit == 'kW' || event.chartData.unit == 'kWh') {
|
|
|
+ event.chartData.seriesData[event.chartData.seriesData.length] = init_FPG_Data(state.dateTime)
|
|
|
+ event.chartData.yAxisData = init_yAxis_Data(2)
|
|
|
+ }
|
|
|
+
|
|
|
+ chartData.push(proxy.$refs['chart' + (index + 1)][0].initEcharts('chart' + (index + 1), event.chartData))
|
|
|
+ })
|
|
|
+
|
|
|
+ syncTooltip(chartData)
|
|
|
+}
|
|
|
+
|
|
|
+function syncTooltip(charts) {
|
|
|
+ // 1. 状态管理:记录当前激活的 tooltip
|
|
|
+ let activeTooltipIndex = -1;
|
|
|
+ let activePoint = null;
|
|
|
+
|
|
|
+ // 2. 节流处理鼠标事件(每 50ms 执行一次)
|
|
|
+ const handleMouseMove = throttle((event, chartIndex) => {
|
|
|
+ const pointInPixel = [event.offsetX, event.offsetY];
|
|
|
+ const targetX = pointInPixel[0];
|
|
|
+ const targetY = pointInPixel[1];
|
|
|
+
|
|
|
+ // 3. 计算鼠标位置对应的 xAxis 索引(只处理网格区域内的事件)
|
|
|
+ if (!charts[chartIndex].containPixel('grid', pointInPixel)) {
|
|
|
+ if (activeTooltipIndex !== -1) {
|
|
|
+ hideAllTooltips();
|
|
|
+ activeTooltipIndex = -1;
|
|
|
+ activePoint = null;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 获取当前鼠标位置对应的 xAxis 索引
|
|
|
+ const xAxisIndex = Math.round(charts[chartIndex].convertFromPixel('grid', [targetX, targetY])[0]);
|
|
|
+ const isNewPoint = activePoint === null ||
|
|
|
+ activePoint[0] !== xAxisIndex ||
|
|
|
+ activePoint[1] !== chartIndex;
|
|
|
+
|
|
|
+ if (isNewPoint) {
|
|
|
+ // 5. 只有当鼠标移动到新的数据点时才更新 tooltip
|
|
|
+ activePoint = [xAxisIndex, chartIndex];
|
|
|
+ updateAllTooltips(xAxisIndex);
|
|
|
+ }
|
|
|
+ }, 50); // 节流时间 50ms,可根据需要调整
|
|
|
+
|
|
|
+ // 6. 批量更新所有图表的 tooltip
|
|
|
+ function updateAllTooltips(xAxisIndex) {
|
|
|
+ charts.forEach(chart => {
|
|
|
+ chart.dispatchAction({
|
|
|
+ type: 'showTip',
|
|
|
+ seriesIndex: 0, // 假设只有一个 series,根据实际情况调整
|
|
|
+ dataIndex: xAxisIndex
|
|
|
+ });
|
|
|
+
|
|
|
+ chart.setOption({
|
|
|
+ tooltip: {
|
|
|
+ axisPointer: { type: 'line' },
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ activeTooltipIndex = xAxisIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 隐藏所有图表的 tooltip
|
|
|
+ function hideAllTooltips() {
|
|
|
+ charts.forEach(chart => {
|
|
|
+ chart.dispatchAction({
|
|
|
+ type: 'hideTip',
|
|
|
+ });
|
|
|
+
|
|
|
+ chart.setOption({
|
|
|
+ tooltip: {
|
|
|
+ axisPointer: { type: 'none' },
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 8. 绑定事件监听器
|
|
|
+ charts.forEach((chart, index) => {
|
|
|
+ // 鼠标移动事件(使用节流处理)
|
|
|
+ chart.getZr().on('mousemove', (event) => handleMouseMove(event, index));
|
|
|
+
|
|
|
+ // 鼠标离开图表区域时隐藏所有 tooltip
|
|
|
+ chart.getZr().on('mouseout', () => {
|
|
|
+ if (activeTooltipIndex !== -1) {
|
|
|
+ hideAllTooltips();
|
|
|
+ activeTooltipIndex = -1;
|
|
|
+ activePoint = null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ setTimeout(() => {
|
|
|
+ initChartData()
|
|
|
+ }, 500);
|
|
|
+})
|
|
|
+
|
|
|
+// 暴露变量
|
|
|
+defineExpose({
|
|
|
+});
|
|
|
+</script>
|
|
|
+<style scoped>
|
|
|
+.chart-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.chart {
|
|
|
+ width: 100%;
|
|
|
+ height: 400px;
|
|
|
+}
|
|
|
+</style>
|
|
|
+<style lang="scss" scoped>
|
|
|
+.card-area {
|
|
|
+ font-size: 14px;
|
|
|
+
|
|
|
+ &-header {
|
|
|
+ display: flex;
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ >.title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+
|
|
|
+ >.subTitle {
|
|
|
+ font-size: 12px;
|
|
|
+ margin: auto 0 0 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &-center {
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ >.value {
|
|
|
+ color: #41BED8;
|
|
|
+ font-size: 54px;
|
|
|
+ text-align: center;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ >.unit {
|
|
|
+ font-size: 16px;
|
|
|
+ text-align: center;
|
|
|
+ color: #666666
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &-footer {
|
|
|
+ display: flex;
|
|
|
+ color: #666666;
|
|
|
+
|
|
|
+ >.title {}
|
|
|
+
|
|
|
+ >.percent {
|
|
|
+ margin: auto 5px auto 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ >.image {
|
|
|
+ margin: auto 0;
|
|
|
+ width: 12px;
|
|
|
+ height: 8px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|