echarts 同x轴半实线半虚线图表

需求背景

需要实线一个同x轴,且图表的的多图表联动效果

解决效果

2023-11-13

ISQQW代码地址

链接

index.vue

javascript 复制代码
<!--/**
   * @author: liuk
   * @date: 2023/11/13
   * @describe: 共x轴,半虚线天气图表
  */-->
<template>
  <div ref="chatDom" class="HeatPredictionChart"></div>
</template>

<script lang="ts" setup>
import {ref, onMounted, watch, nextTick} from "vue"
import * as echarts from 'echarts'
import moment from "moment";

// Props
const props = defineProps(['data', 'typeArr'])

let myChart = null // Vue3 使用 proxy 对象代理,而 echarts 则使用了大量的全等(===), 对比失败从而导致了bug。
const chatDom = ref(null)

watch(() => props.typeArr, () => {
  const option = myChart.getOption()
  myChart.clear()
  myChart.setOption(renderFn(option, props.data, props.typeArr))
}, {deep: true})

watch(() => props.data, () => {
  nextTick(() => {
    const option = myChart.getOption()
    myChart.clear()
    myChart.setOption(renderFn(option, props.data, props.typeArr))
  })
}, {immediate: true})

const renderFn = (option, data, types) => {
  types = types.map(id => selectOptions.find(item => item.id === id))
  const day = data.heat_power_forecast_day[0]
  const thatDayTime = +new Date(day)  //指定天8点,秒级时间戳
  const curDayTime = new Date(new Date().toLocaleDateString()).setHours(8)//今天8点,秒级时间戳
  option.title[0].text = day?.slice(-5)
  option.yAxis[0].name = ''
  option.yAxis[1].name = ''
  option.series[2].name = ''
  option.series[2].data = []
  option.series[3].name = ''
  option.series[3].data = []
  option.series[4].data = []
  option.series[5].data = []
  if (moment(new Date()).format('YYYY-MM-DD') === day) { // 如果是当天
    const curRemainHourNum = new Date().getHours() <= 7 ? 8 - new Date().getHours() - 1 : 24 - new Date().getHours() + 8;
    option.series[4].data = data.t3_forecast_hours.slice(0, 24 - curRemainHourNum + 1).concat(data.t3_forecast_hours.slice(24 - curRemainHourNum - 1).fill('-'))
    option.series[5].data = data.t3_forecast_hours.slice(0, 24 - curRemainHourNum).fill('-').concat(data.t3_forecast_hours.slice(24 - curRemainHourNum))
    option.series[5].lineStyle.type = 'dashed'
    new Array(2).fill('').forEach((_, i) => {
      if (!types[i]) {
        option.series[i].data = []
        return
      }
      const arr = data[types[i].value]
      option.series[i].data = arr.slice(0, 24 - curRemainHourNum + 1).concat(arr.slice(24 - curRemainHourNum - 1).fill('-'))
      option.series[i].name = types[i].label
      option.series[i].lineStyle.type = 'solid'
      option.series[i].color = types[i].color
      option.series[i + 2].data = arr.slice(0, 24 - curRemainHourNum).fill('-').concat(arr.slice(24 - curRemainHourNum))
      option.series[i + 2].name = types[i].label
      option.series[i + 2].lineStyle.type = 'dashed'
      option.series[i + 2].color = types[i].color
      option.yAxis[i].name = types[i].label?.slice(-2) + ': ' + types[i].unit
    })
  } else {
    option.series[4].data = data.t3_forecast_hours
    new Array(2).fill('').forEach((_, i) => {
      if (!types[i]) {
        option.series[i].data = []
        return
      }
      option.series[i].data = data[types[i].value]
      option.series[i].name = types[i].label
      option.series[i].color = types[i].color
      option.series[i].lineStyle.type = thatDayTime - 24 * 60 * 60 * 1e3 >= curDayTime ? 'dashed' : 'solid'
    })
  }
  return option
}

onMounted(() => {
  drawChart()
  window.addEventListener('resize', () => {
    const option = myChart.getOption()
    myChart.clear()
    myChart.setOption(renderFn(option, props.data, props.typeArr))
  }, {passive: true});
})

const drawChart = () => {
  let chartDom = chatDom.value
  if (chartDom == '-') {
    return
  }
  echarts.dispose(chartDom)
  myChart = echarts.init(chartDom)
  const option = {
    title: {
      x: 'left',
      padding: [28, 0, 0, 10],
      text: '11-09',
      textStyle: {//设置主标题的文字风格
        color: "rgba(165,166,166,1)",  //字体颜色
        fontSize: 13,  //文字大小
        opacity: 0.9
      },
    },
    grid: [
      {
        top: '17%',
        left: 90,
        right: 90,
        height: '36%'
      },
      {
        top: '60%',
        left: 90,
        right: 90,
        height: '36%'
      }
    ],
    axisPointer: {
      type: 'shadow',
      link: {xAxisIndex: 'all'},
    },
    tooltip: {
      trigger: 'axis',
      axisPointer: { // 设置指示线
        type: 'line', // 默认为直线,可选为:'line' | 'shadow'
        lineStyle: {
          color: '#8C8C8C',
          type: [4.5, 5],	 //设置折线类型
          dashOffset: 5,
          width: 2,
        }
      },
      transitionDuration: 0,
      confine: true,
      borderRadius: 2,
      borderWidth: 0,
      backgroundColor: 'rgba(149,149,149,0.15)',
      backdropFilter: 'brightness(80%) blur(4px)',
      borderColor: 'rgba(149,149,149,0.8)',
      textStyle: {fontSize: 14, color: '#fff'},
      formatter: function (param) {
        let data = param.filter(item => item.data !== '-' && item.name)
        data = data.filter((item, i) => data.findIndex(x => x.seriesName === item.seriesName) === i)
        const dataIndex = param[0].dataIndex
        return `
          <div class="detailChat-popup">
            <p class="top">
                <span>${props.data.heat_power_forecast_day[dataIndex]}</span>
                <span>${data[0].name || '暂无'} </span>
            </p>
            ${
              data.map(item => {
                let unit
                switch (true) {
                  case ['实际热耗', '预测热需'].includes(item.seriesName):
                    unit = 'GJ'
                    break
                  case ['预测供水温度', '预测回水温度', '室外温度'].includes(item.seriesName):
                    unit = '℃'
                    break
                  case ['预测流量'].includes(item.seriesName):
                    unit = 'T'
                    break
                }
                return `
                  <p class="item">
                      <i class="icon" style="background-color:${item.color}"></i>
                      <span class="name">${item.seriesName}</span>
                      <span>${item.data}</span>
                      <span>${unit}</span>
                  </p>`
              }).join("")
            }
          </div>
        `
      }
    },
    xAxis: [
      {
        data: ['08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00', '00:00', '01:00', '02:00', '03:00', '04:00', '05:00', '06:00', '07:00'],
        boundaryGap: false,
        min: 0,
        axisLine: {
          show: true,
        },
        position: 'top',
        axisTick: {
          show: false,
        },
        axisLabel: {
          margin: 80,
          show: true,
          textStyle: {
            color: 'rgba(165,166,166,1)',
            fontSize: '12',
          },
        },
      },
      {
        gridIndex: 1,
        data: ['08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00', '00:00', '01:00', '02:00', '03:00', '04:00', '05:00', '06:00', '07:00'],
        boundaryGap: false,
        min: 0,
        axisLabel: {show: false},
        axisLine: {
          show: false,
        },
        position: 'top',
        axisTick: {
          show: false,
        }
      }
    ],
    yAxis: [
      {
        gridIndex: 0,
        name: '热耗:GJ/h',
        nameGap: '25',
        offset: 20,
        nameTextStyle: {
          padding: [0, 55, 5, 0],
          color: '#fff',
          fontSize: '14',
          opacity: 0.7,
          align: 'center'
        },
        nameLocation: 'end',
        position: 'left',
        axisTick: {show: false},
        splitLine: {
          show: true,
          lineStyle: {
            type: 'dashed',
            color: 'rgba(52,52,52,1)'
          }
        },
        axisLine: {show: false,},
        axisLabel: {
          show: true,
          showMinLabel: false,
          textStyle: {
            color: 'rgba(165,166,166,1)',
            fontSize: '14',
          }
        },
      },
      {
        gridIndex: 0,
        nameGap: '25',
        name: '流量:T/h',
        offset: 20,
        nameTextStyle: {
          padding: [0, 55, 5, 0],
          color: '#fff',
          fontSize: '14',
          opacity: 0.7,
          align: 'center'
        },
        nameLocation: 'end',
        position: 'right',
        type: 'value',
        axisTick: {show: false},
        splitLine: {
          show: true,
          lineStyle: {
            type: 'dashed',
            color: 'rgba(52,52,52,1)'
          }
        },
        axisLine: {show: false,},
        axisLabel: {
          show: true,
          showMinLabel: false,
          textStyle: {
            color: 'rgba(165,166,166,1)',
            fontSize: '14',
          }
        },
      },
      {
        gridIndex: 1,
        name: '室外温度/°C',
        nameGap: '25',
        offset: 20,
        nameTextStyle: {
          padding: [0, 55, 5, 0],
          color: '#fff',
          fontSize: '14',
          opacity: 0.7,
          align: 'center'
        },
        nameLocation: 'end',
        position: 'left',
        axisTick: {show: false},
        splitLine: {
          show: true,
          lineStyle: {
            type: 'dashed',
            color: 'rgba(52,52,52,1)'
          }
        },
        axisLine: {show: false,},
        axisLabel: {
          show: true,
          showMinLabel: true,
          textStyle: {
            color: 'rgba(165,166,166,1)',
            fontSize: '14',
          }
        },
      },
    ],
    series: [
      {
        name: '实际热耗',
        type: 'line',
        showSymbol: false,
        data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-'],
        xAxisIndex: 0,
        yAxisIndex: 0,
        lineStyle: {
          normal: {
            type: 'solid'
          }
        },
        color: '#ffffff'
      },
      {
        name: '实际热耗',
        type: 'line',
        showSymbol: false,
        data: ['-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
        xAxisIndex: 0,
        yAxisIndex: 1,
        lineStyle: {
          normal: {
            type: 'dashed'
          }
        },
        color: '#ffffff'
      },
      {
        name: '实际热耗',
        type: 'line',
        showSymbol: false,
        data: ["-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"],
        xAxisIndex: 0,
        yAxisIndex: 0,
        lineStyle: {
          normal: {
            type: 'dashed'
          }
        },
        color: '#ffffff'
      },
      {
        name: '实际热耗',
        type: 'line',
        showSymbol: false,
        data: ["-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"],
        xAxisIndex: 0,
        yAxisIndex: 1,
        lineStyle: {
          normal: {
            type: 'dashed'
          }
        },
        color: '#ffffff'
      },
      {
        name: '室外温度',
        type: 'line',
        showSymbol: false,
        data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-'],
        xAxisIndex: 1,
        yAxisIndex: 2,
        color: 'rgba(0,207,163,1)',
      },
      {
        name: '室外温度',
        type: 'line',
        showSymbol: false,
        data: ["-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"],
        xAxisIndex: 1,
        yAxisIndex: 2,
        color: 'rgba(0,207,163,1)',
      },
    ]
  }
  option && myChart.setOption(option)
}

const selectOptions = [
  {
    id: '1',
    value: 'heat_power_forecast_loss',
    label: '实际热耗',
    color: '#ffffff',
    unit: 'GJ/h'
  },
  {
    id: '2',
    value: 'heat_power_forecast_hours',
    label: '预测热需',
    color: 'rgba(255,191,0,1)',
    unit: 'GJ/h'
  },
  {
    id: '3',
    value: 'tt011_forecast_hours',
    label: '预测供水温度',
    color: 'rgba(255, 64, 25,1)',
    unit: '°C'
  },
  {
    id: '4',
    value: 'ft021_forecast_hours',
    label: '预测流量',
    color: 'rgba(184,102,238,1)',
    unit: 'T/h'
  },
  {
    id: '5',
    value: 'tt011_forecast_hours',
    label: '预测回水温度',
    color: 'rgba(0, 132, 255,1)',
    unit: '°C'
  },
]
</script>

<style lang="scss" scoped>
.HeatPredictionChart {
  width: 100%;
  height: 100%;
}
</style>

<style lang="scss">
.detailChat-popup {
  overflow: hidden;

  .top {
    margin-bottom: 5px;
  }

  .item {
    display: flex;
    align-items: center;
    margin: 10px 0;

    &:last-child {
      margin-bottom: 0;
    }

    .icon {
      display: inline-block;
      width: 12px;
      height: 2px;
      margin-right: 10px;
      border-radius: 2px;
    }

    .name {
      margin-right: 40px;

    }
  }
}
</style>
相关推荐
小阮的学习笔记12 分钟前
Vue3中使用LogicFlow实现简单流程图
javascript·vue.js·流程图
YBN娜12 分钟前
Vue实现登录功能
前端·javascript·vue.js
杨荧15 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
minDuck17 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
嚣张农民2 小时前
推荐3个实用的760°全景框架
前端·vue.js·程序员
落魄小二3 小时前
el-table 表格索引不展示问题
javascript·vue.js·elementui
neter.asia3 小时前
vue中如何关闭eslint检测?
前端·javascript·vue.js
十一吖i3 小时前
前端将后端返回的文件下载到本地
vue.js·elementplus
光影少年3 小时前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
熊的猫5 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js