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

需求背景

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

解决效果

ISQQW代码地址

链接

index.vue

<!--/**
   * @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 `
          

${props.data.heat_power_forecast_day[dataIndex]} ${data[0].name || '暂无'}

${ 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 `

${item.color}"> ${item.seriesName} ${item.data} ${unit}

`
}).join("") }
`
} }, 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>

你可能感兴趣的:(echarts图表集,vue.js)