ECharts图表工厂,完整代码+思路逻辑
Echart工厂支持柱状图(bar)折线图(line)散点图(scatter)饼图(pie)雷达图(radar)极坐标柱状图(polarBar)和极坐标折线图(polarLine)等多种图表,及其对应扩展图表:
git链接:sq/UI/src/components/Echarts at main · afigzb/sq (github.com)https://github.com/afigzb/sq/tree/main/UI/src/components/Echarts
展示页面,后续附带详细的说明:
引言
ECharts 是一个功能强大的图表库,广泛应用于数据可视化场景。然而,其复杂的配置项和高学习曲线常常让开发者望而却步。本文将介绍一个精心设计的图表工厂系统,通过封装 ECharts 的复杂性,提供简洁的 API 和统一的开发体验,帮助开发者快速构建图表,提高效率和代码可维护性。本文将全面介绍其设计背景、架构、功能特性及使用方法,带你了解如何利用它简化图表开发。
设计背景:为什么我要重新封装一个图表工厂?
见此代码:
const option = {color: ['#5470c6', '#91cc75', '#fac858', '#ee6666'],backgroundColor: '#ffffff',xAxis: {type: 'category',data: ['A', 'B', 'C'],axisLine: { lineStyle: { color: '#cccccc' } },axisLabel: { color: '#666666' },splitLine: { lineStyle: { color: '#cccccc', opacity: 0.4 } }},yAxis: {type: 'value',axisLine: { lineStyle: { color: '#cccccc' } },axisLabel: { color: '#666666' },splitLine: { lineStyle: { color: '#cccccc', opacity: 0.4 } }},series: [{type: 'bar',data: [120, 200, 150]}],tooltip: {backgroundColor: '#333333',textStyle: { color: '#ffffff' }},grid: { left: '5%', right: '5%', bottom: '15%', top: '5%' }
};
这是一个标准的Echart 配置项,在实际开发过程中项目中往往不止一个Echart图表,同时图表的配置项也远比这负责,这就导致了:
- 配置重复:每个图表都需要重复设置颜色、背景、提示框等通用配置。
- 维护困难:修改主题或样式时,需要逐一调整每个图表的配置。
- 类型散乱:不同图表类型的配置差异大,缺乏统一抽象。
- 维护成本高:ECharts 的 API 庞大,写好的配置项难以更改。
基于这些问题,EChartFactory2 的设计目标是:
- 简化配置:从繁琐的手动配置转为简单的数据输入。
- 统一接口:让所有图表类型共享一致的调用方式。
- 集中管理:通过主题系统统一管理样式,支持动态切换。
- 易于扩展:方便添加新图表类型和功能。
架构设计:分层抽象的思考过程
核心设计思想:分离通用与特定
为了探寻Echart图表的设计规律我收集了项目中常见的图表配置,进行对比分析,如下:
// 柱状图配置示例
const barOption = {color: ['#5470c6', '#91cc75', '#fac858'], // 🔄 重复出现backgroundColor: '#ffffff', // 🔄 重复出现grid: { left: '5%', right: '5%', top: '5%', bottom: '15%' }, // 🔄 重复出现tooltip: { // 🔄 重复出现backgroundColor: '#333333',textStyle: { color: '#ffffff' }},xAxis: { // ✨ 图表特定type: 'category',data: ['销售', '市场', '研发'],axisLine: { lineStyle: { color: '#cccccc' } },axisLabel: { color: '#666666' }},yAxis: { // ✨ 图表特定 type: 'value',axisLine: { lineStyle: { color: '#cccccc' } },axisLabel: { color: '#666666' }},series: [{ // ✨ 图表特定type: 'bar',data: [320, 280, 450]}]
};// 折线图配置示例
const lineOption = {color: ['#5470c6', '#91cc75', '#fac858'], // 🔄 重复出现backgroundColor: '#ffffff', // 🔄 重复出现grid: { left: '5%', right: '5%', top: '5%', bottom: '15%' }, // 🔄 重复出现tooltip: { // 🔄 重复出现backgroundColor: '#333333',textStyle: { color: '#ffffff' }},xAxis: { // ✨ 图表特定(和柱状图相似)type: 'category', data: ['1月', '2月', '3月'],axisLine: { lineStyle: { color: '#cccccc' } },axisLabel: { color: '#666666' }},yAxis: { // ✨ 图表特定(和柱状图相似)type: 'value',axisLine: { lineStyle: { color: '#cccccc' } },axisLabel: { color: '#666666' }},series: [{ // ✨ 图表特定(配置差异大)type: 'line',data: [820, 932, 901],smooth: true,symbol: 'circle'}]
};// 饼图配置示例
const pieOption = {color: ['#5470c6', '#91cc75', '#fac858'], // 🔄 重复出现backgroundColor: '#ffffff', // 🔄 重复出现 tooltip: { // 🔄 重复出现backgroundColor: '#333333',textStyle: { color: '#ffffff' }},// ❌ 注意:饼图没有 xAxis、yAxis、gridseries: [{ // ✨ 图表特定(完全不同)type: 'pie',radius: '50%',data: [{ value: 1048, name: '搜索引擎' },{ value: 735, name: '直接访问' },{ value: 580, name: '邮件营销' }]}]
};// 雷达图配置示例:
const radarOption = {color: ['#5470c6', '#91cc75', '#fac858'], // 🔄 重复出现backgroundColor: '#ffffff', // 🔄 重复出现tooltip: { // 🔄 重复出现backgroundColor: '#333333',textStyle: { color: '#ffffff' }},// ❌ 注意:雷达图没有 xAxis、yAxis、gridradar: { // ✨ 图表特定(独有的坐标系)indicator: [{ name: '销售', max: 100 },{ name: '管理', max: 100 },{ name: '技术', max: 100 }]},series: [{ // ✨ 图表特定(又是不同的结构)type: 'radar',data: [{value: [60, 73, 85],name: '预算分配'}]}]
};
通过对比分析,我们可以发现这些图标基本可以划分成以下几部分:
// 通用配置(所有图表都需要,配置内容基本相同)
const universalConfig = {color: [], // 调色板 - 所有图表都需要backgroundColor: '', // 背景色 - 所有图表都需要tooltip: {}, // 提示框 - 所有图表都需要,但触发方式可能不同legend: {}, // 图例 - 大部分图表需要toolbox: {} // 工具箱 - 看项目需求,但配置方式变化很小
};// 特定配置(每种图表独有,配置内容差异很大)
const specificConfig = {// 直角坐标系图表(柱状图、折线图、散点图)xAxis: {}, // X轴配置yAxis: {}, // Y轴配置 grid: {}, // 网格配置// 极坐标图表polar: {}, // 极坐标配置angleAxis: {}, // 角度轴radiusAxis: {}, // 径向轴// 雷达图radar: {}, // 雷达图配置(带指示器)// 所有图表都有,但配置差异巨大series: [] // 系列配置(每种图表类型完全不同)
};
如果能自动生成通用配置,只让用户关心数据和特定需求,Echart代码将得到极大程度的简化。
配置映射系统的设计
有了这个思路后,我开始思考:如果每种图表类型都有一个"配置生成器",那么我只需要告诉它图表类型和数据,它就能自动生成完整的配置。
最初的想法很简单,只要吧需要配置的东西单独抽象出来统一配置不就可以了:
const CHART_TYPE_CONFIGS = {bar: {series: (data) => ({ type: 'bar', data: data.data })},line: {series: (data) => ({ type: 'line', data: data.data })}// ...
};
但很快我就发现问题了——不同图表需要的坐标系完全不同!
第一个难题:坐标系的差异
当我试图处理饼图时,发现它根本不需要 xAxis 和 yAxis,而雷达图需要的是 radar 配置。如果还是用传统思路,我又要写很多 if-else:
// 这样写太丑了...
if (chartType === 'pie') {// 不要坐标轴
} else if (chartType === 'radar') {// 要雷达配置
} else {// 要直角坐标系
}
这时我意识到,坐标系才是图表的核心差异。于是我重新整理思路:
| 坐标系类型 | 适用图表 | 需要的配置 |
|-----------|---------|-----------|
| 直角坐标系 (cartesian) | 柱状图、折线图、散点图 | xAxis + yAxis + grid |
| 极坐标系 (polar) | 极坐标柱状图、极坐标折线图 | polar + angleAxis + radiusAxis |
| 雷达坐标系 (radar) | 雷达图 | radar (带indicator) |
| 无坐标系 (none) | 饼图 | 隐藏所有坐标轴 |
这样一来,我的配置映射就变成了两层结构:
const CHART_TYPE_CONFIGS = {bar: {coordinateSystem: 'cartesian', // 👈 指定用哪种坐标系series: (data, theme, config) => ({ /* 系列配置 */ })},pie: {coordinateSystem: 'none', // 👈 饼图不需要坐标系series: (data, theme, config) => ({ /* 系列配置 */ })}
};
第二个难题:主题样式的统一
有了坐标系分类,我又遇到新问题:每次创建图表都要设置颜色、背景色、字体等样式,这些重复工作能否自动化?
我回顾了之前写的图表,发现比较常见的是几种风格:
- 默认风格:白底黑字,全给Echart自动化
- 科技风格:黑底彩色,大屏常用
- 简约风格:浅色背景,较为正式
与其每次都手写这些样式,不如做成主题系统:
const themes = {default: {colors: {series: ['#5470c6', '#91cc75', '#fac858'],background: { chart: '#ffffff', tooltip: '#333333' },text: { primary: '#333333', secondary: '#666666' }}},futuristic: {colors: {series: ['#00d4ff', '#ff6b9d', '#7fff00'],background: { chart: '#0a0a0a', tooltip: 'rgba(0,0,0,0.8)' },text: { primary: '#ffffff', secondary: '#cccccc' }}}
};
这样我们就能一键切换整个图表的视觉风格了。
第三个难题:如何合并配置?
现在有了图表类型配置、坐标系配置、主题配置,但我们的逻辑是把Echart拆解成一个个独立部分,最终要合并在一起才是我们需要的ECharts 配置
显然简单的 Object.assign 不可行,因为Echarts配置是多层嵌套的:
const config1 = { series: [{ itemStyle: { color: 'red' } }] };
const config2 = { series: [{ itemStyle: { borderWidth: 2 } }] };// Object.assign 会直接覆盖,丢失 color 配置
Object.assign(config1, config2);
// 结果:{ series: [{ itemStyle: { borderWidth: 2 } }] } ❌// 我需要的是深度合并
// 结果:{ series: [{ itemStyle: { color: 'red', borderWidth: 2 } }] } ✅
所以我写了一个深度合并函数,确保所有配置都能正确合并。
整合:EChartFactory2 的诞生
有了这些基础设施,我开始设计核心的工厂类。我的设计原则是:
- 使用简单:简单调用函数就能创建
- 配置灵活:支持自定义配置覆盖默认值
- 功能完整:支持主题切换、类型切换、动态更新
于是有了这样的 API:
// 创建图表
const factory = new EChartFactory2(container, 'bar', 'default');// 更新数据
factory.update({xAxis: ['产品A', '产品B', '产品C'],series: { data: [120, 200, 150] }
});// 切换主题
factory.switchTheme('futuristic');// 切换类型
factory.switchType('line');
实际效果:还算让人满意
我们做一个简单的对比,假设用户的需求是:
创建一个销售数据的柱状图,要求科技风格,支持堆叠显示。
传统写法:
const option = {color: ['#00d4ff', '#ff6b9d', '#7fff00', '#ffaa00'],backgroundColor: '#0a0a0a',grid: {left: '3%', right: '4%', bottom: '3%', top: '4%',containLabel: true,borderColor: '#333333'},tooltip: {trigger: 'axis',backgroundColor: 'rgba(0, 0, 0, 0.8)',borderColor: '#00d4ff',borderWidth: 1,textStyle: { color: '#ffffff', fontSize: 12 },axisPointer: {type: 'shadow',shadowStyle: { color: 'rgba(0, 212, 255, 0.2)' }}},legend: {textStyle: { color: '#ffffff' },icon: 'rect',itemHeight: 8,itemGap: 20},xAxis: {type: 'category',data: ['1月', '2月', '3月', '4月'],axisLine: { lineStyle: { color: '#333333', width: 1 } },axisLabel: { color: '#cccccc', fontSize: 11 },splitLine: { show: false }},yAxis: {type: 'value',axisLine: { lineStyle: { color: '#333333', width: 1 } },axisLabel: { color: '#cccccc', fontSize: 11 },splitLine: {lineStyle: { color: '#333333', width: 0.5, opacity: 0.6 }}},series: [{name: '销售额',type: 'bar',stack: 'total',data: [120, 132, 101, 134],itemStyle: {color: '#00d4ff',borderRadius: [2, 2, 0, 0]}},{name: '利润',type: 'bar', stack: 'total',data: [220, 182, 191, 234],itemStyle: {color: '#ff6b9d',borderRadius: [2, 2, 0, 0]}}]
};const chart = echarts.init(document.getElementById('chart'));
chart.setOption(option);
用工厂后:
const chart = createChart(document.getElementById('chart'), 'bar', 'futuristic');
chart.update({xAxis: ['1月', '2月', '3月', '4月'],series: [{ name: '销售额', data: [120, 132, 101, 134] },{ name: '利润', data: [220, 182, 191, 234] }]
}, { stack: 'total' });
代码量从 60 行减少到 5 行,减少了 92%!
更重要的是,假设现在客户说"把这个图改成折线图",我只需要把代码中的bar改成line即可:
const chart = createChart(document.getElementById('chart'), 'line', 'futuristic');
chart.update({xAxis: ['1月', '2月', '3月', '4月'],series: [{ name: '销售额', data: [120, 132, 101, 134] },{ name: '利润', data: [220, 182, 191, 234] }]
}, { stack: 'total' });
而且越复杂的配置,我这边修改起来就越简单,越统一。
详细代码可以从git中获取:
sq/UI/src/components/Echarts at main · afigzb/sq (github.com)https://github.com/afigzb/sq/tree/main/UI/src/components/Echarts
相关文章:

ECharts图表工厂,完整代码+思路逻辑
Echart工厂支持柱状图(bar)折线图(line)散点图(scatter)饼图(pie)雷达图(radar)极坐标柱状图(polarBar)和极坐标折线图(po…...
Logback 在 Spring Boot 中的详细配置
1. Logback 配置文件 Spring Boot 默认会加载 classpath 下的 logback-spring.xml(推荐)或 logback.xml 作为 Logback 的配置文件。 推荐使用 logback-spring.xml,因为 Spring Boot 提供了扩展支持(例如基于 Profile 的配置&am…...
写起来比较复杂的深搜题目
年轻的拉尔夫开玩笑地从一个小镇上偷走了一辆车,但他没想到的是那辆车属于警察局,并且车上装有用于发射车子移动路线的装置。 那个装置太旧了,以至于只能发射关于那辆车的移动路线的方向信息。 编写程序,通过使用一张小镇的地图…...
MySQL强化关键_016_存储引擎
目 录 一、概述 二、MySQL 支持的存储引擎 三、指定存储引擎 四、修改存储引擎 五、常用存储引擎及适用场景 一、概述 MySQL 存储引擎决定了数据在磁盘上的存储方式和访问方式;不同的存储引擎实现了不同的存储和检索算法;MySQL 常见的存储引擎&…...

CSS:margin的塌陷与合并问题
文章目录 一、margin塌陷问题二、margin合并问题 一、margin塌陷问题 二、margin合并问题...
防护等级IPxx含义 -雨天充电需要防护盖吗
指标快要到期,新买的电车,第一次碰到雨天充电的问题,有点担心漏电。然后电商平台上一查,果然有卖防护罩的,但是真的需要吗? 下面从充电口防护等级,国标要求、注意事项等几个方面分析。 一、防护…...
【设计模式】责任链+模板+工程模式使用模板
前言 方便写出优雅,解耦,高内聚,高复用的代码。 Demo // 1. 定义验证器接口(责任链模式) public interface Validator {Validator setNext(Validator next);boolean validate(Data data); }// 2. 创建抽象验证器&am…...

探索服务网格(Service Mesh):云原生时代的网络新范式
文章目录 一、引言二、什么是服务网格基本定义形象比喻 三、服务网格解决了哪些问题微服务通信复杂性可观察性安全性 四、常见的服务网格实现IstioLinkerdConsul Connect 五、服务网格的应用场景大型微服务架构混合云环境 六、服务网格的未来发展与其他技术的融合标准化和行业规…...

SQL SERVER中实现类似LEAST函数的功能,返回多列数据中的最小值
使用 LEAST()函数可以简洁地在一行SQL语句中找出多个值中的最小值,但在SQLServer数据库中,没有内置的LEAST函数。 我们可以使用values子句创建临时的数据集的办法,返回多列数据中的最小值。 创建表 CREATE TABLE stu…...

SymPy | 获取表达式自由变量方法与因式分解
SymPy 是 Python 中强大的符号计算库,广泛应用于数学建模、公式推导和科学计算。本文将从两个核心功能展开:表达式中自由变量的获取与因式分解的实现,通过完整代码示例和深入分析,帮助读者掌握其使用方法。 第一部分:获…...

深度剖析并发I/O模型select、poll、epoll与IOCP核心机制
核心概要:select、poll、epoll 和 IOCP 是四种用于提升服务器并发处理能力的I/O模型或机制。前三者主要属于I/O多路复用范畴,允许单个进程或线程监视多个I/O流的状态;而 IOCP 则是一种更为彻底的异步I/O模型。 一、引言:为何需要这…...
单片机——实现交通信号灯管理
随便写写,汇总一下,就一个单片机,没有模拟软件,什么都没有~ 点阵、数码管、led同时点亮 #include <reg52.h>sbit ADDR0 P1^0; sbit ADDR1 P1^1; sbit ADDR2 P1^2; sbit ADDR3 P1^3; sbit ENLED P1^4;// 交通灯控制引…...

数据结构 -- 交换排序(冒泡排序和快速排序)
冒泡排序 基于“交换”的排序:根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置 //交换 void swap(int &a,int &b){int temp a;a b;b temp; }//冒泡排序 void BubbleSort(int A[],int n){for(int i0;i<n-1;i){bool flag false; …...

【算法】: 前缀和算法(利用o(1)的时间复杂度快速求区间和)
前缀和算法:高效处理区间求和的利器 目录 引言什么是前缀和前缀和的基本实现前缀和的作用前缀和的典型应用场景前缀和的优缺点分析实战例题解析 引言 区间求和问题的普遍性暴力解法的时间复杂度问题前缀和算法的核心思想 什么是前缀和 前缀和的数学定义 通俗来…...

macOS 安装 PostgreSQL
文章目录 安装安装信息 验证GUI 工具下载 安装 最简单的方式是通过 brew 安装 brew install postgresql17该版本在 brew 上的详情页:https://formulae.brew.sh/formula/postgresql17 你也可以根据需要,搜索 安装更新版本 如果你没有安装 brew…...
打破传统范式,线上 3D 画展彰显多元亮点
(一)沉浸式体验,身临其境赏画 线上 3D 画展运用先进的 3D 建模和虚拟现实(VR)技术,高度还原了真实的展厅环境 。展厅内的布局、灯光,甚至墙壁的质感都被完美复刻,让观众仿佛置身于…...

Linux系统:基础命令之 ls~pwd~cd
文章目录 前言一、ls命令📘 命令简介:🧠 基本语法:演示ls🔧 常用选项:-l选项-a选项-h选项 小结 ls 二、pwd命令📘 命令简介:何为绝对路径!何为相对路径!&…...
MuJoCo安装记录
一、Anaconda安装 1. 下载安装包:https://repo.anaconda.com/archive/Anaconda3-2021.11-Linux-x86_64.sh 2. 进入下载界面执行以下命令安装 sudo chmod x Anaconda3-2021.11-Linux-x86_64.sh ./Anaconda3-2021.11-Linux-x86_64.sh 3. 如果安装anaconda之后打开…...
软件工程(八):UML类图的几种关系
依赖(Dependency) 定义:一个类使用到了另一个类(例如作为参数、局部变量等)。表示:虚线箭头,箭头指向被依赖的类。关键词:uses、depends on。示例:类 A 的某个方法使用类…...
python定时删除指定索引
脚本 import logging from datetime import datetime, timedelta from elasticsearch import Elasticsearch# 配置日志记录 logging.basicConfig(filenamedelete_uat_indices.log,levellogging.INFO,format%(asctime)s - %(levelname)s - %(message)s )# Elasticsearch 集群的…...

基于OAuth2-proxy和Keycloak为comfyui实现SSO
背景 comfyui无认证被漏扫后易被rce挖矿 攻击过程 https://www.oschina.net/news/340226 https://github.com/comfyanonymous/ComfyUI/discussions/5165 阿里云漏洞库关于comfyui的漏洞 https://avd.aliyun.com/search?qcomfyui×tamp__1384n4%2BxBD0GitGQ0QD8ID%2F…...

SmartSoftHelp 之 SQL Server 数据库安全备份与安全还原详解---深度优化版:SmartSoftHelp DeepCore XSuite
SmartSoftHelp 菜单之 DBMS 数据库备份与还原 (DBBackRest) 使用实例 SQL Server 数据库备份与还原详解 SQL Server 数据库的备份与还原是管理数据库的核心任务之一,涉及本地与远程操作、大小监控及目录管理等多个方面。以下是详细说明: 一、数据库…...

Spring 代理与 Redis 分布式锁冲突:一次锁释放异常的分析与解决
Spring 代理与 Redis 分布式锁冲突:一次锁释放异常的分析与解决 Spring 代理与 Redis 分布式锁冲突:一次锁释放异常的分析与解决1. 问题现象与初步分析2 . 原因探究:代理机制对分布式锁生命周期的干扰3. 问题复现伪代码4. 解决方案࿱…...

【数据结构】队列的完整实现
队列的完整实现 队列的完整实现github地址前言1. 队列的概念及其结构1.1 概念1.2 组织结构 2. 队列的实现接口一览结构定义与架构初始化和销毁入队和出队取队头队尾数据获取size和判空 完整代码与功能测试结语 队列的完整实现 github地址 有梦想的电信狗 前言 队列&…...
2025 全球优质 AI 产品深度测评:从通用工具到垂直领域的技术突围 —— 轻量聚合工具篇
在 AI 技术爆发式增长的 2025 年,全球范围内涌现出大量兼具技术创新与场景价值的优质产品。本文从通用对话、多模态生成、开发者工具、企业级方案及垂直领域深耕五个维度,深度解析 18 款国内外标杆产品,附独家对比数据与选型策略,…...
Python爬虫实战:获取天气网最近一周北京的天气数据,为日常出行做参考
1. 引言 随着互联网技术的发展,气象数据的获取与分析已成为智慧城市建设的重要组成部分。天气网作为权威的气象信息发布平台,其数据具有较高的准确性和实时性。然而,人工获取和分析天气数据效率低下,无法满足用户对精细化、个性化气象服务的需求。本文设计并实现了一套完整…...

根据YOLO数据集标签计算检测框内目标面积占比(YOLO7-10都适用)
程序: 路径改成自己的,阈值可以修改也可以默认 #zhouzhichao #25年5月17日 #计算时频图中信号面积占检测框面积的比值import os import numpy as np import pandas as pd from PIL import Image# Define the path to the directory containing the lab…...
Helm简介、安装、配置、使用!
一、简介 Helm 是 Kubernetes 的包管理器。包管理器类似于我们在 Ubuntu 中使用的apt、Centos中使用的yum 或者Python中的 pip 一样,能快速查找、下载和安装软件包。Helm 由客户端组件 helm 和服务端组件 Tiller 组成, 能够将一组K8S资源打包统一管理, 是查找、共享…...

LLM笔记(九)KV缓存(2)
文章目录 1. 背景与动机2. 不使用 KV Cache 的情形2.1 矩阵形式展开2.2 计算复杂度 3. 使用 KV Cache 的优化3.1 核心思想3.2 矩阵形式展开3.3 计算复杂度对比 4. 总结5. GPT-2 中 KV 缓存的实现分析5.1 缓存的数据结构与类型5.2 在注意力机制 (GPT2Attention) 中使用缓存5.3 缓…...
开发 前端搭建npm v11.4.0 is known not to run on Node.js v14.18.1.
错误nodejs 和npm 版本不一致 ERROR: npm v11.4.0 is known not to run on Node.js v14.18.1. This version of npm supports the following node versions: ^20.17.0 || >22.9.0. You can find the latest version at https://nodejs.org/. ERROR: D:\softTool\node-v14…...