Vue3+Ts Echarts折线图组件封装

1. 效果图:
在这里插入图片描述

2. 功能
1. 根据选项动态修改图表显示
2. 动态修改颜色、Y轴单位
3. 可根据配置项动态修改x轴数据为年月日,年(Y)默认展示最近五年,月(M)展示12个月或展示到当前月份,日(D)展示当前月份的天数或展示到当前月当前天

3.话不多说直接上代码,具体说明在最后
c-line-chart.vue

<template>
  <div :id="randomId" :style="`width: ${width};height: ${height}`"></div>
</template>

<script setup lang="ts">
import * as echarts from 'echarts'
import { ECharts } from 'echarts'
import { merge, debounce } from 'lodash'
import { parseColorString } from '@/utils/tools'
import { ref, onMounted, watch, shallowRef } from 'vue'

import dayjs from 'dayjs'

interface configXDataType {
  allXData?: boolean
  xDataType?: 'Y' | 'M' | 'D'
}

interface Props {
  width?: string
  height?: string
  option?: any
  unit?: string
  configXData: configXDataType
}
const props = withDefaults(defineProps<Props>(), {
  width: '100%',
  height: '100%',
  configXData: () => {
    return {
      xDataType: 'D',
      allXData: false
    }
  }
})
const randomId = (Math.random() * 100000).toFixed(0)

let color = ref<string[]>([
  '#5470c6',
  '#91cc75',
  '#fac858',
  '#ee6666',
  '#73c0de',
  '#3ba272',
  '#fc8452',
  '#9a60b4',
  '#ea7ccc'
])

let myChart = shallowRef<ECharts>()

const hexToRgba = (hex: any, opacity: any) => {
  let rgbaColor = ''
  let reg = /^#[\da-f]{6}$/i
  const colorTest = parseColorString(hex)
  if (reg.test(hex)) {
    rgbaColor = `rgba(${colorTest.r},${colorTest.g},${colorTest.b},${opacity})`
  }
  return rgbaColor
}

const option = (color: string[], xData?: string[]) => {
  return {
    color,
    legend: {
      padding: [20, 20, 0, 0],
      icon: 'rect',
      itemWidth: 15,
      itemHeight: 4,
      x: 'right',
      y: 'top',
      textStyle: {
        color: '#BFD5E0',
        fontSize: 14
      }
    },
    tooltip: {
      show: true,
      trigger: 'axis',
      borderColor: '#000',
      backgroundColor: 'rgba(0, 0, 0, 0.50)',
      formatter: function (params: any) {
        let html = ''
        params.forEach((v: any) => {
          html += `<div style="color: #fff;font-size: 18px;line-height: 25px;">
          <span style="margin-right:8px"> ${v.axisValue}</span>
                <span style="color:${color[v.componentIndex]};
                 font-weight:700;font-size: 18px;font-family: QuartzEF;">${
                   v.value
                 }</span>
                <span style='color:#97A5C5;font-size: 15px'>${
                  props.unit || ''
                }</span>`
        })
        return html
      },
      axisPointer: {
        type: 'line',
        lineStyle: {
          type: 'solid',
          color: 'rgba(168, 181, 189, 0.8)'
        }
      }
    },
    grid: {
      top: '18%',
      left: '12%',
      right: '5%',
      bottom: '15%'
    },
    xAxis: [
      {
        type: 'category',
        interval: 1,
        splitNumber: 4,
        // boundaryGap: false,
        axisLabel: {
          margin: 15,
          textStyle: {
            color: '#BFD5E0',
            fontSize: 14
          }
        },
        axisTick: {
          show: false
        },
        axisLine: {
          show: false
        },
        data: xData
      }
    ],
    yAxis: [
      {
        type: 'value',
        name: props.unit,
        axisLabel: {
          textStyle: {
            color: '#BFD5E0',
            fontSize: 14
          }
        },
        nameTextStyle: {
          color: '#BFD5E0',
          fontSize: 14,
          padding: [0, 0, 0, -40]
        },
        splitLine: {
          lineStyle: {
            type: 'solid',
            width: 1,
            color: 'rgba(0, 179, 254, 0.12)'
          }
        },
        axisLine: {
          show: false
        }
      }
    ],
    series: [
      {
        type: 'line',
        smooth: true,
        symbol: 'none',
        areaStyle: {
          normal: {
            color: {
              type: 'linear',
              x: 0,
              y: 0,
              x2: 0,
              y2: 1,
              colorStops: [
                {
                  offset: 0,
                  color: hexToRgba(color[0], 0.6)
                },
                {
                  offset: 1,
                  color: hexToRgba(color[0], 0.2)
                }
              ]
            }
          }
        },
        data: []
      },
      {
        type: 'line',
        smooth: true,
        symbol: 'none',
        symbolSize: 8,
        zlevel: 3,
        areaStyle: {
          normal: {
            color: new echarts.graphic.LinearGradient(
              0,
              0,
              0,
              1,
              [
                {
                  offset: 0,
                  color: hexToRgba(color[1], 0.6)
                },
                {
                  offset: 1,
                  color: hexToRgba(color[1], 0.2)
                }
              ],
              false
            )
          }
        },
        data: []
      },
      {
        type: 'line',
        smooth: true,
        symbol: 'none',
        symbolSize: 8,
        zlevel: 3,
        areaStyle: {
          normal: {
            color: new echarts.graphic.LinearGradient(
              0,
              0,
              0,
              1,
              [
                {
                  offset: 0,
                  color: hexToRgba(color[2], 0.6)
                },
                {
                  offset: 1,
                  color: hexToRgba(color[2], 0.2)
                }
              ],
              false
            )
          }
        },
        data: []
      },
      {
        type: 'line',
        smooth: true,
        symbol: 'none',
        symbolSize: 8,
        zlevel: 3,
        areaStyle: {
          normal: {
            color: new echarts.graphic.LinearGradient(
              0,
              0,
              0,
              1,
              [
                {
                  offset: 0,
                  color: hexToRgba(color[3], 0.6)
                },
                {
                  offset: 1,
                  color: hexToRgba(color[3], 0.2)
                }
              ],
              false
            )
          }
        },
        data: []
      }
    ]
  }
}
const drawChart = debounce(() => {
  myChart.value = echarts.init(document.getElementById(randomId) as HTMLElement)
  color.value = props.option?.color
    ? [...props.option?.color, ...color.value]
    : [
        '#5470c6',
        '#91cc75',
        '#fac858',
        '#ee6666',
        '#73c0de',
        '#3ba272',
        '#fc8452',
        '#9a60b4',
        '#ea7ccc'
      ]
  myChart.value.setOption(
    merge(option(color.value, getXData(props.configXData)), props.option)
  )
}, 500)
onMounted(() => {
  drawChart()
})

watch(
  () => props,
  () => {
    console.log(props.option)

    drawChart()
  },
  {
    deep: true
  }
)

// 获取x轴数据
const getXData = (configXData: configXDataType) => {
  const allVal = configXData.allXData
  const xDataType = configXData.xDataType
  let xArr: any[] = []
  // 获取近五年数据 //  [2019, 2020, 2021, 2022, 2023]
  if (xDataType == 'Y') {
    // 获取近五年数据
    const year = parseInt(dayjs().format('YYYY'))
    for (let i = year; i > year - 5; i--) {
      xArr.unshift(i)
    }
  }
  // 获取今年 所有月份
  if (xDataType == 'M') {
    if (!allVal) {
      console.log(dayjs().format('M'))
      for (let i = 1; i <= parseInt(dayjs().format('M')); i++) {
        xArr.push(i)
      }
    } else {
      for (let i = 1; i <= 12; i++) {
        xArr.push(i)
      }
    }
  }
  // 获取本月所有天
  if (xDataType == 'D') {
    if (!allVal) {
      for (let i = 1; i <= parseInt(dayjs().format('D')); i++) {
        xArr.push(i)
      }
    } else {
      let dayLength = parseFloat(dayjs().endOf('month').format('DD'))
      for (let i = 1; i <= dayLength; i++) {
        xArr.push(i)
      }
    }
  }
  return xArr
}
</script>

<style scoped lang="scss">
:deep(.charts-bg) {
  // padding: 23px 32px;
  background-size: 100% 100%;
  font-family: 'Source Han Sans CN';
  position: absolute;
  display: block;

  .charts-value {
    font-family: QuartzEF;
  }
}
</style>


------------------------------------- tools.ts -----------------------------------------------

方法用于颜色转换
export const parseColorString = (color: string) => {
  if (color.startsWith('#')) {
    return parseHexColor(color)
  } else if (color.startsWith('rgb')) {
    return parseRgbaColor(color)
  } else if (color === 'transparent') {
    return parseHexColor('#00000000')
  }
  throw new Error(`color string error: ${color}`)
}

/**
 * 16进制颜色字符串解析为颜色对象
 * @param color 颜色字符串
 * @returns IColorObj
 */
export const parseHexColor = (color: string) => {
  let hex = color.slice(1)
  let a = 1
  if (hex.length === 3) {
    hex = `${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`
  }
  if (hex.length === 8) {
    a = parseInt(hex.slice(6), 16) / 255
    hex = hex.slice(0, 6)
  }
  const bigint = parseInt(hex, 16)
  return {
    r: (bigint >> 16) & 255,
    g: (bigint >> 8) & 255,
    b: bigint & 255,
    a
  } as IColorObj
}

/**
 * rgba颜色字符串解析为颜色对象
 * @param color 颜色字符串
 * @returns IColorObj
 */
export const parseRgbaColor = (color: string) => {
  const arr = color.match(/(\d(\.\d+)?)+/g) || []
  const res = arr.map((s: string) => parseInt(s, 10))
  return {
    r: res[0],
    g: res[1],
    b: res[2],
    a: parseFloat(arr[3])
  } as IColorObj
}

组件说明

组件名称 c-line-chart

依赖包 echarts、dayjs、lodash
传参
  width?:   图表宽度、默认父盒子宽度
  height?:  图表高度、默认父盒子高度
  option:   图表配置项、与echarts官网配置相同,具体配置见 echarts官网
  unit?:    图表Y轴单位
  configXData: {
    allXData?: boolean              是否展示x轴xDataType类型的所有值,true:展示所有  false:展示到当前日期
    xDataType?: 'Y' | 'M' | 'D'     Y:默认展示最近五年、M:展示月份、D:当前月的天
  }

echarts 官网

注意事项

  1. option 默认提供颜色选项,如要修改图表中颜色,在 option 中添加属性 color 即可,动态修改可指定 option 为 reactive 类型, 使用 option.color= [“#ff0000”,“#2e375s”] 即可动态修改
    颜色建议使用 AEX(“#FF0000”)类型

        const option =reactive({
            color:[""]  // 指定默认值,不指定删除即可,使用echarts默认颜色
        })
        option.color = ["#ff0000","#2e375s"]  // 修改颜色
    
  2. 修改折线图渐变, 在开发中折线图图表渐变样式一般都统一,暂不做配置项修改,直接修改代码 option.series.areaStyle.colorStops 设置颜色透明度,达到满意即可

 colorStops: [
        {
            offset: 0,
            color: hexToRgba(color[0], 0.6)
        },
        {
            offset: 1,
            color: hexToRgba(color[0], 0.2)
        }
]

  1. 图表数据改变时,图例依然保持上一个图表的数据,举个例子:数据一三条线三个图例,数据二两条线两个图例,由数据一的图表切换到数据二的图表时,option 中的 series 的第三条数据依然存在,这是因为传入组件的 series 只有前两条改变了数据,第三条没有变化。解决:切换到第二个图表时手动将整个 series 重置(可以先置空,然后对 name 和 data 赋值),对于 series 有很多自定义配置项时,可将其他非本图表的所有 series 的 name 和 data 手动置空
    options.series=[]
    options.series[0].data = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17]
    options.series[1].data = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
    或者
    options.series[2] = { name: '', data: [] }

本文只提供一种封装思路,有更好的方法或问题欢迎评论或私聊🙂

以下是使用Vue3和TypeScript实现折线图的步骤: 1.安装echartsvue-echarts依赖: ```shell npm install echarts vue-echarts@5.0.0-beta.3 ``` 2.在Vue组件中引入依赖: ```typescript import { defineComponent } from 'vue' import { use } from 'echarts/core' import { CanvasRenderer } from 'echarts/renderers' import { LineChart } from 'echarts/charts' import { GridComponent, TooltipComponent } from 'echarts/components' import { EChartsOption } from 'echarts/types/dist/shared' import VueECharts from 'vue-echarts' use([CanvasRenderer, LineChart, GridComponent, TooltipComponent]) ``` 3.定义数据和配置项: ```typescript const data = { xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [{ data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'line' }] } const options: EChartsOption = { title: { text: '折线图示例' }, tooltip: { trigger: 'axis' }, legend: { data: ['销量'] }, xAxis: { type: 'category', boundaryGap: false, data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] }, yAxis: { type: 'value', axisLabel: { formatter: '{value} 件' } }, series: [{ name: '销量', type: 'line', data: [120, 132, 101, 134, 90, 230, 210] }] } ``` 4.在Vue组件中使用VueECharts组件: ```typescript export default defineComponent({ components: { VueECharts }, data() { return { data, options } }, template: ` <div> <vue-echarts :options="options" :data="data" /> </div> ` }) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值