基于Echarts进行图表组件的封装

news2025/2/24 16:32:42

什么是Echarts

是一个使用js实现的开源可视库,提供了多种图表,但是当我们在项目中进行使用的时候可能就是需要进行一系列的相关配置如: 标题,类型,x轴,y轴等,当我们使用较为频繁的时候就容易导致代码的冗余,并且整体将echarts进行安装引入也是比较大的,我们可以按照自己的需要进行对应组件的引入

Echarts的使用

安装Echarts

Echarts官网-安装echarst并实现按需引入Echarts图表和组件

基于Echarts相关配置进行组件的封装

// components/BarChart.vue
/* @/components/BarChart.vue */

<template>
  <div ref="chartDom" :style="{ height: getHeight }"></div>
</template>

<script setup lang="ts">
import { echarts, type ECOption } from '@/utils/echarts'
import { ref, shallowRef, watch, computed, onMounted, onBeforeUnmount, type ShallowRef, type Ref } from 'vue'
import type { EChartsType } from 'echarts/types/dist/core'
import type {
  XAXisOption, YAXisOption, LegendComponentOption, BarSeriesOption, DataZoomComponentOption
} from 'echarts/types/dist/shared'
import resize from '@/utils/resize'
import type { ChartSetting } from '@/types/ChartData'

//定义组件属性
const props = withDefaults(
  defineProps<{
    //数据
    data?: Array<string | number>
    //x轴数据
    xAxisData?: Array<string>
    //图表标题
    title?: string
    //系列配置
    series?: Array<BarSeriesOption>
    //x轴配置
    xAxis?: Array<XAXisOption>
    //y轴配置
    yAxis?: Array<YAXisOption>
    //图例配置
    legend?: LegendComponentOption
    //区域缩放配置
    dataZoom?: Array<DataZoomComponentOption>
    //图形高度
    height?: number | string
    //数据集
    datasetSource?: Array<any>
    //综合配置
    options: ChartSetting
  }>(),
  {
    data: () => [],
    xAxisData: () => [],
    title: 'ECharts柱状图',
  }
)
//要渲染的Dom元素
const chartDom: Ref<HTMLDivElement | null> = ref(null)
//渲染的chart对象要用shallowRef
const chart: ShallowRef<EChartsType | null | undefined> = shallowRef(null)
//高度同时支持string和number
const getHeight = computed(() => {
  return typeof props.height === 'number' ? props.height + 'px' : props.height
})
//监听数据变化,重新绘制
watch(
  () => props,
  () => {
    drawChart()
  },
  { deep: true }
)

//绘制
async function drawChart() {
  let datasetSource: Array<any> | undefined = props.datasetSource,
    series: Array<BarSeriesOption> = [],
    xAxisData: Array<string> = props.xAxisData
  let chartType = props.options.chartType || 'bar'; // 默认为柱状图,如果需要折线图则设置为 'line'

  if (props.options) {
    if (props.options.apiMethod) {
      //获取接口数据作为数据集
      let allx = await props.options.apiMethod()
      datasetSource = allx.data
      if (props.options.xProp) {
        //根据配置的x轴属性名生成x轴数据
        xAxisData = []
        datasetSource?.forEach(data => {
          xAxisData.push(data[props.options.xProp])
        })

      }
    }
    if (props.options.seriesOption) {
      props.options.seriesOption.forEach((opt: any) => {
        series.push({
          name: '车牌',
          barMaxWidth: 30,
          emphasis: { focus: 'series' },
          label: { show: true, position: 'top', color: 'inherit' },
          ...opt,
          type: chartType, // 设置图表类型
        })
      })
    }
  }
  // else {
  //   series = props.series ? props.series : [{
  //     name: '车牌',
  //     type: 'bar',
  //     barMaxWidth: 30,
  //     emphasis: { focus: 'self' },
  //     label: { show: true, position: 'inside', color: '#fff' },
  //     data: props.data
  //   }]
  // }

  let xAxis: Array<XAXisOption> = props.xAxis ? props.xAxis : [{
    type: 'category',
    axisTick: { show: false },
    data: xAxisData
  }]

  let yAxis: Array<YAXisOption> = props.yAxis ? props.yAxis : [{ type: 'value', minInterval: 1 }]

  let legend: LegendComponentOption = props.legend ? props.legend : {
    show: true,
    type: 'scroll',
    orient: 'horizontal',
    top: 25,
    left: 'center'
  }

  let dataZoom: Array<DataZoomComponentOption> = props.dataZoom ? props.dataZoom : []

  const options: ECOption = {
    backgroundColor: '',
    title: {
      text: props.title
    },
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'shadow'
      },
      // appendToBody:true
    },
    legend: legend,
    grid: {
      left: 10,
      right: 10,
      bottom: props.dataZoom ? 40 : 10,
      containLabel: true
    },
    toolbox: {
      show: true,
      feature: {
        magicType: { type: ['line', 'bar'] },
        dataView: { readOnly: false },
        saveAsImage: {}
      }
    },
    xAxis: xAxis,
    yAxis: yAxis,
    dataZoom: dataZoom,
    dataset: {
      source: datasetSource
    },
    series: series
  }
  //开启notMerge保证配置数据不会叠加
  chart.value?.setOption(options, { notMerge: true });
}

const { chartObject, addResize, removeResize } = resize()
onMounted(() => {
  chart.value = echarts.init(chartDom.value);
  drawChart()
  //添加窗口自适应
  chartObject.value = chart.value
  addResize()
})

onBeforeUnmount(() => {
  removeResize()
  chart.value?.dispose()
})
</script>

在父组件中使用封装好的组件

<template>
  <el-row :gutter="16">
    <el-col v-for="(item, index) in chartOptionList" :key="index" :lg="12" style="margin-bottom: 10px;">
      <el-card>
        <BarChart :title="item.title" :height="item.height || 300" :options="item.chartOption" />
      </el-card>
    </el-col>
  </el-row>
</template>

<script setup lang="ts">
import BarChart from '@/components/BarChart.vue'
import type { ChartSetting } from '@/types/ChartData'
import { ref } from 'vue'
//axios api请求方法
import { getPayRecord, delicacies } from '@/services/http'

interface ChartCard {
  //标题
  title?: string,
  //高度
  height?: number,
  //图表y轴配置
  yAxis?: Array<any>
  chartOption: ChartSetting
}
const chartOptionList = ref<ChartCard[]>([
  {
    title: '图1',
    chartOption: {
      apiMethod: () => getPayRecord(),
      xProp: 'name',
      chartType: 'bar', // 设置为 'line' 以生成折线图
      seriesOption: [
        { name: '数量', encode: { x: 'name', y: 'count' } }
      ]
    }
  },
  {
    title: '图2',
    chartOption: {
      apiMethod: () => delicacies(),
      xProp: 'name',
      chartType: 'line', // 设置为 'line' 以生成折线图
      seriesOption: [
        { name: '销量', encode: { x: 'name', y: 'saleNum' } },
        { name: '好评', encode: { x: 'name', y: 'positiveReviews' } },

      ]
    }
  },
])
</script>

效果展示

在这里插入图片描述
其他:
在这里插入图片描述

// types.ChartData.ts
export interface SeriesData {
  name?: string
  data?: number[]
  color?: string
  yAxisIndex?: number
  radius?: string | string[]
  itemStyle?: any
  encode?: {
    x?: string
    y?: string
    itemName?: string
    value?: string
  }
}

export interface ChartSetting {
  //api接口方法
  apiMethod: Function
  // x轴属性名
  xProp: string
  chartType: string|undefined // 设置为 'line' 以生成折线图
  //图例配置
  seriesOption: SeriesData[]
}
// reqTypes.ts
import axiosInstance from '../utils/request'

export interface ApiResult<T> {
  code: number
  message: string
  data: T
}
export async function get<T>(url: string, params?: any): Promise<ApiResult<T>> {
  const response = await axiosInstance.get<ApiResult<T>>(url, { params })
  return response.data
}
export async function post<T>(url: string, data?: any): Promise<ApiResult<T>> {
  const response = await axiosInstance.post<ApiResult<T>>(url, data)
  return response.data
}
export async function put<T>(url: string, data?: any): Promise<ApiResult<T>> {
  const response = await axiosInstance.put<ApiResult<T>>(url, data)
  return response.data
}
export async function del<T>(url: string, params?: any): Promise<ApiResult<T>> {
  const response = await axiosInstance.delete<ApiResult<T>>(url, { params })
  return response.data
}

// utils.echarts.ts
/* @/utils/echarts.ts */
/**在ts中实现按需引入echarts 图表和组件*/
import * as Echarts from 'echarts/core'
import { BarChart, PieChart, LineChart } from 'echarts/charts'
import {
  // 标题组件
  TitleComponent,
  // 图例组件
  LegendComponent,
  // 提示框组件
  TooltipComponent,
  // 坐标系网格组件
  GridComponent,
  // 数据集组件
  DatasetComponent,
  // 内置数据转换器组件 (filter, sort)
  TransformComponent,
  // 工具栏组件
  ToolboxComponent,
  // 区域缩放组件
  DataZoomComponent,
  // 原生图形元素组件
} from 'echarts/components'
// 标签自动布局、全局过渡动画等特性
import { LabelLayout, UniversalTransition } from 'echarts/features'
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步 
// 使用canvas进行渲染 也可以使用 SVGRenderer 进行渲染
import { CanvasRenderer } from 'echarts/renderers'
import type {
  // 系列类型的定义后缀都为 SeriesOption
  BarSeriesOption, // 柱状图
  PieSeriesOption, //饼图
  LineSeriesOption, // 折线/面积图
} from 'echarts/charts'
import type {
  // 组件类型的定义后缀都为 ComponentOption
  TitleComponentOption,
  TooltipComponentOption,
  GridComponentOption,
  DatasetComponentOption,
  ToolboxComponentOption,
  DataZoomComponentOption,
  GraphicComponentOption,
} from 'echarts/components'
import type { ComposeOption } from 'echarts/core'

// 注册必须的组件
Echarts.use([
  TitleComponent,
  LegendComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  ToolboxComponent,
  DataZoomComponent,
  GridComponent,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer,
  BarChart,
  PieChart,
  LineChart,
])

// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
export type ECOption = ComposeOption<
  | BarSeriesOption
  | PieSeriesOption
  | LineSeriesOption
  | TitleComponentOption
  | TooltipComponentOption
  | GridComponentOption
  | DatasetComponentOption
  | ToolboxComponentOption
  | DataZoomComponentOption
  | GraphicComponentOption
>

export const echarts = Echarts
// request.ts  这里的baseUrl 是基于EasyMock进行模拟的数据
/**
 * 使用 axios.create() 创建了一个 axios 实例,并设置了基本 URL 和请求超时时间。我们还添加了请求和响应拦截器
 *
 */
import axios, {
  AxiosInstance,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from 'axios'
const axiosInstance: AxiosInstance = axios.create({
  baseURL:
    'https://mock.presstime.cn/mock/6686990ecb2f4f1158f2a7b8/screen-big-sys',
  timeout: 5000,
})
// 添加请求拦截器
// 自定义请求头
axiosInstance.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // 在发送请求之前做些什么
    const token = localStorage.getItem('ACCESS_TOKEN')
    if (token) {
      // 配置请求头
      config.headers.Authorization = 'Bearer ' + token
    }
    return config
  },
  (error: any) => {
    // 处理请求错误
    return Promise.reject(error)
  }
)
// 添加响应拦截器
axiosInstance.interceptors.response.use(
  (response: AxiosResponse) => {
    // 对响应数据做点什么
    return response
  },
  (error: any) => {
    // 处理响应错误
    return Promise.reject(error)
  }
)
export default axiosInstance
// resize.ts
/**
 * 实现页面大小的自适应
 * ECharts提供的API会发现,它提供了一个resize 方法 重新渲染图表结合window.addEventListener
 * 功能:echarts图表自适应窗口变化封装方法
 */
//echarts图表自适应窗口变化封装方法
import { ref } from 'vue'
import { debounce } from 'lodash'

export default function () {
  //echarts图的实例
  const chartObject = ref()

  //使用防抖debounce函数,减少resize的次数
  const chartResizeHandler = debounce(() => {
    if (chartObject.value) {
      chartObject.value.resize()
    }
  }, 100)
  const initResizeEvent = () => {
    //添加窗口大小变化监听
    window.addEventListener('resize', chartResizeHandler)
  }
  const destroyResizeEvent = () => {
    //移除窗口大小变化监听
    window.removeEventListener('resize', chartResizeHandler)
  }

  const addResize = () => {
    initResizeEvent()
  }
  const removeResize = () => {
    destroyResizeEvent()
  }

  return {
    chartObject,
    addResize,
    removeResize,
  }
}
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import { createPinia } from 'pinia'
import 'element-plus/dist/index.css'
// 实现持久化标记
import { createPersistedState } from 'pinia-plugin-persistedstate'

const app = createApp(App)
const pinia = createPinia()
// 使用pinia-plugin-persistedstate插件
pinia.use(createPersistedState())

app.use(ElementPlus)
app.mount('#app')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1902173.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[数据结构] 基于交换的排序 冒泡排序快速排序

标题&#xff1a;[数据结构] 基于交换的排序 冒泡排序&&快速排序 水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 &#xff08;一&#xff09;冒泡排序 优化后实现&#xff1a; &#xff08;二&#xff09;快速排序 I、实现方法&#xff1a; &#…

Adobe Photoshop 2024 v25.5.1 中文激活版下载以及安装方法教程

软件介绍 Adobe Photoshop 2024 v25.5.1 是Adobe公司的最新版图像处理软件&#xff0c;它提供了强大的图像编辑工具和智能自动化功能&#xff0c;包括图像修复、色彩校正和滤镜效果&#xff0c;以满足专业人士和业余爱好者的需求。这款软件还支持矢量图形设计和实时协作&#…

【程序人生】来CSDN五周年了,简单总结下初心、收获、未来憧憬

最近CSDN站内私信说&#xff0c;已经创作五周年了。想想确实应该写一点东西&#xff0c;总结一下初心是什么、经历了什么、收获了什么、现状怎么样、未来会如何规划写文章这件事。算是我自己的一份总结&#xff0c;也许也可以给一些刚上大学的年轻朋友参考一下&#xff0c;坚持…

【Linux】进程创建和终止 | slab分配器

进程创建 fork 1.fork 之后发生了什么 将给子进程分配新的内存块和内核数据结构&#xff08;形成了新的页表映射&#xff09;将父进程部分数据结构内容拷贝至子进程添加子进程到系统进程列表当中fork 返回&#xff0c;开始调度器调度 这样就可以回答之前返回两个值&#xff1f…

Autosar Dcm配置-0x85服务配置及使用-基于ETAS软件

文章目录 前言Dcm配置DcmDsdDcmDsp代码实现总结前言 0x85服务用来控制DTC设置的开启和关闭。某OEM3.0架构强制支持0x85服务,本文介绍ETAS工具中的配置 Dcm配置 DcmDsd 配置0x85服务 此处配置只在扩展会话下支持(具体需要根据需求决定),两个子服务Disable为0x02,Enable…

3.pwn 函数调用流程,调用约定

前置准备 pop: Pop指令的作用是弹栈&#xff0c;将栈顶的数据弹出到寄存器&#xff0c;然后栈顶指针向下移动一个单位。 具体来说:如pop rax&#xff0c;作用就是mov rax[rsp];add rsp 8; push: Push指令的作用就是压栈&#xff0c;将栈顶指针向上移动一个单位的距离&#xf…

38 IO流

目录 C语言的输入和输出流是什么CIO流stringstream的简单介绍 1. C语言的输入与输出 C语言中我们用到的最频繁的输出方式是scanf和printf&#xff0c;scanf&#xff1a;从标准输入设备&#xff08;键盘&#xff09;读取数据&#xff0c;并将值存在变量中。printf&#xff1a;…

【MySQL】锁(黑马课程)

【MySQL】锁 0. 锁的考察点1. 概述1. 锁的分类1.1 属性分类1.2 粒度分类 2. 全局锁2.1 全局锁操作2.2.1 备份问题 3. 表级锁3.1 表锁3.2 语法3.3 表共享读锁&#xff08;读锁&#xff09;3.4 表独占写锁&#xff08;写锁&#xff09;3.5 元数据锁(meta data lock, MDL)3.6 意向…

第5章 认证授权:需求分析,Security介绍(OAuth2,JWT),用户认证,微信扫码登录,用户授权

1 模块需求分析 1.1 什么是认证授权 截至目前&#xff0c;项目已经完成了课程发布功能&#xff0c;课程发布后用户通过在线学习页面点播视频进行学习。如何去记录学生的学习过程呢&#xff1f;要想掌握学生的学习情况就需要知道用户的身份信息&#xff0c;记录哪个用户在什么…

AIGC专栏12——EasyAnimateV3发布详解 支持图文生视频 最大支持960x960x144帧视频生成

AIGC专栏12——EasyAnimateV3发布详解 支持图&文生视频 最大支持960x960x144帧视频生成 学习前言项目特点生成效果相关地址汇总项目主页Huggingface体验地址Modelscope体验地址源码下载地址 EasyAnimate V3详解技术储备Diffusion Transformer (DiT)Hybrid Motion ModuleU-V…

智慧校园-教职工管理系统总体概述

在当今信息化时代&#xff0c;智慧校园教职工管理系统成为了提升教育机构管理效能的重要工具。该系统巧妙融合了先进的信息技术&#xff0c;为教职工的日常管理带来了一场静悄悄的革命。它不仅是一个信息存储库&#xff0c;记录着每位教职工从加入到离开的完整职业轨迹&#xf…

笔记12:if语句编程练习(打印输出三个数据中的最小值)

输入三个数&#xff0c;分别放入变量x&#xff0c;y&#xff0c;z中 打印输入数据中最小的那一个数 解决方案1 定义中间变量 t 1.比较x和y的大小关系&#xff0c;将较小的值赋值给t 2.比较t和z的大小关系&#xff0c;将较小的值赋值给t 3.t 中保存的就是3个数中的较小值 &am…

限时免费!国产Sora快手可灵Web网页端及全新功能上线!国货之光!

大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 快手可灵&#xff08;Kling&#xff09;这回是真的出息了&…

Python爬取股票信息-并进行数据可视化分析,绘股票成交量柱状图

为了使用Python爬取股票信息并进行数据可视化分析&#xff0c;我们可以使用几个流行的库&#xff1a;requests 用于网络请求&#xff0c;pandas 用于数据处理&#xff0c;以及 matplotlib 或 seaborn 用于数据可视化。 步骤 1: 安装必要的库 首先&#xff0c;确保安装了以下P…

Hack The Box -- Blazorized

一、准备工作 端口扫描 详细扫描 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-06-30 21:39 EDT Nmap scan report for 10.10.11.22 Host is up (0.26s latency).PORT STATE SERVICE VERSION 53/tcp open domain Simple DNS Plus 80/tcp op…

海外发稿: 秘鲁-区块链新闻媒体通稿宣发

秘鲁媒体单发 随着全球化的不断深入&#xff0c;海外发稿已经成为众多企业宣传推广的重要方式之一。而在海外发稿的选择中&#xff0c;秘鲁媒体的地位尤为重要。秘鲁作为南美洲的重要国家之一&#xff0c;拥有众多知名媒体平台&#xff0c;包括diariodelcusco、serperuano、el…

非堆成加密是公私钥使用

对称加密学习-CSDN博客 加密算法学习-CSDN博客 非对称加密算法使用一对密钥&#xff0c;包括一个公钥和一个私钥&#xff0c;它们是数学上相关联的&#xff0c;但公钥可以公开分享&#xff0c;而私钥必须保密。以下是使用非对称加密算法的一般步骤&#xff1a; 密钥生成&…

【IT领域新生必看】深入浅出Java:揭秘`Comparator`与`Comparable`的神奇区别

文章目录 引言什么是Comparable接口&#xff1f;Comparable接口的定义实现Comparable接口示例&#xff1a; 什么是Comparator接口&#xff1f;Comparator接口的定义实现Comparator接口示例&#xff1a; Comparable与Comparator的区别排序逻辑位置示例&#xff1a; 可扩展性示例…

HashMap中的put()方法

一. HashMap底层结构 HashMap底层是由哈希表(数组),链表,红黑树构成,哈希表存储的类型是一个节点类型,哈希表默认长度为16,它不会每个位置都用,当哈希表中的元素个数大于等于负载因子(0.75)*哈希表长度就会扩容到原来的2倍 二. 底层的一些常量 三. HashMap的put()方法 当插入一…

简单的手动实现spring中的自动装配案例

简简单单的实现一个spring中的自动装配和容器管理的小骚操作。 1&#xff0c;创建AutoSetBean.java 使用injectBeans静态方法&#xff0c;可以扫描指定包下的所有带MyInject注解的字段&#xff0c;如果在beans的Map中存在这个字段的实例化类&#xff0c;则执行装配。 import…