/**
* 封装echarts 工具
* Author: abner
* Date: 2021-1-5 更新
*/
let MyEcharts = {
echartSet: [], // 表格集合
//整理数据没有分组类型的,适合饼图
EchartsDataFormate: {
NoGroupFormate: function (data) {
//category 的数据存储
let categorys = [];
//data 的数据存储
let datas = [];
//遍历
for (let i = 0; i < data.length; i++) {
categorys.push(data[i].name || "");
//定义一个中间变量
let temp_data = {value: data[i].value || 0, name: data[i].name || ""};
datas.push(temp_data);
}
return {categorys: categorys, data: datas};
},
//整理数据有分组类型的,适合折线图、柱形图(分组,堆积)
//数据格式:{group:XXX,name:XXX,value:XXX} 注意:group不填写没有图例,也无法(分组,堆积)
/**
* @param data : json数据
* @param type Array | string: 图表类型
* let data1 = [
* { group:'类型1' , name: '1月', value: 10 },
* { group:'类型2' , name: '1月', value: 15 },
* { group:'类型1' , name: '2月', value: 25 },
* { group:'类型2' , name: '2月', value: 12 },
* { group:'类型1' , name: '3月', value: 22 },
* { group:'类型2' , name: '3月', value: 12 },
* ];
*
*/
GroupFormate: function (data, type) {
//用于存储类型名称(图例名)
let groups = [];
//用于存储xAxis.data数据(x轴名称)
let names = [];
//存储返回series数据 (一个或者多个)
let series = [];
let yAxisIndex = 0;
for (let i = 0; i < data.length; i++) {
//判断data[i].group是否存在数租groups中
if (!groups.contains(data[i].group)) {
//不存在则跳进 存放
groups.push(data[i].group);
}
//判断name数据是否存在 数组names中
if (!names.contains(data[i].name)) {
//不存在则跳进 存放
names.push(data[i].name);
}
}
//遍历分类
for (let i = 0; i < groups.length; i++) {
//定义一个series中间变量
let temp_series = {};
//定义data.value数据存储
let temp_data = [];
//定义图形类型
let temp_type = type.constructor === Array ? type[i] : type;
//遍历所有数据
for (let j = 0; j < data.length; j++) {
//遍历data.name数据
for (let k = 0; k < names.length; k++) {
//判断所有分类中的所有数据含name数据分开
if (groups[i] == data[j].group && names[k] == data[j].name) {
temp_data.push(data[j].value);
//判断y轴对应的数据,如果存在yAxisIndex则使用此数据,否则使用默认值 默认:0
yAxisIndex = data[j].yAxisIndex ? data[j].yAxisIndex : 0;
}
}
}
temp_series = {
name: groups[i],
type: temp_type === 'acrossBar' ? 'bar' : temp_type, //横向柱状图:acrossBar
data: temp_data,
yAxisIndex: yAxisIndex
};
//每个柱子或拐点对应的文字位置和颜色。echarts3是在label.normal里配置,echart4中兼容
temp_series.label = {
position: 'top',
show: true,
textStyle: {
color: '#fff' //auto则根据系列色同步
},
formatter: function (params) { //当值为0时不显示在柱子或拐点顶部
let val = params.value;
return val === 0 ? '' : val;
}
};
// 柱状图参数配置
if (temp_type === 'bar') {
temp_series.barMaxWidth = 24; //柱宽
}
// 折线图参数配置
if (temp_type === 'line') {
temp_series.lineStyle = {width: 4}; //线租
temp_series.smooth = true; //拐点平滑
}
// 横向柱状图参数配置
if (temp_type === 'acrossBar') {
temp_series.barMaxWidth = 24; //柱宽
temp_series.label = {
position: 'right',
show: true,
textStyle: {
color: 'auto' //auto则根据系列色同步
}
};
}
series.push(temp_series);
}
// console.log(groups)
// console.log(names)
// console.log(series)
return {groups: groups, category: names, series: series};
},
/**
* 雷达图数据格式化
*/
RadarFormate: function (data, type) {
//用于存储类型名称
let groups = [];
//用于存储data.name数据
let names = [];
//存储最大值数组
let indicators = [];
//定义data.value数据存储
let temp_data = [];
for (let i = 0; i < data.length; i++) {
//判断data[i].group是否存在数租groups中
if (!groups.contains(data[i].group)) {
//不存在则跳进 存放
groups.push(data[i].group);
}
//判断name数据是否存在 数组names中
if (!names.contains(data[i].name)) {
//不存在则跳进 存放
names.push(data[i].name);
}
}
for (let i = 0; i < names.length; i++) {
//中
let temp_maxValue = [];
for (let j = 0; j < data.length; j++) {
if (names[i] == data[j].name) {
temp_maxValue.push(data[j].value);
}
}
indicators.push({name: names[i], max: Number(temp_maxValue.max() * 2 / 1.5).toFixed(2)})
}
//遍历分类
for (let i = 0; i < groups.length; i++) {
//定义一个series中间变量
let temp_series = {};
//定义datavalue数组
let dataValues = [];
//遍历所有数据
for (let j = 0; j < data.length; j++) {
if (groups[i] == data[j].group) {
dataValues.push(data[j].value);
}
}
temp_data.push({value: dataValues, name: groups[i]});
}
let series = {type: type, data: temp_data};
return {indicators: indicators, groups: groups, category: names, series: series};
},
/**
* 漏斗图数据格式化
*/
FunnelFormate: function (data, type) {
//用于存储类型名称
let groups = [];
//用于存储data.name数据
let names = [];
//定义一个存放series的数组
let series = [];
for (let i = 0; i < data.length; i++) {
//判断data[i].group是否存在数租groups中
if (!groups.contains(data[i].group)) {
//不存在则跳进 存放
groups.push(data[i].group);
}
//判断name数据是否存在 数组names中
if (!names.contains(data[i].name)) {
//不存在则跳进 存放
names.push(data[i].name);
}
}
let width = parseInt(100 / groups.length);
//遍历分类
for (let i = 0; i < groups.length; i++) {
//定义data.value数据存储
let temp_data = [];
let k = 0;
//遍历所有数据
for (let j = 0; j < data.length; j++) {
//判断所有分类中的所有数据含name数据分开
if (groups[i] == data[j].group) {
k++;
temp_data.push({value: k, name: data[j].name + ":" + data[j].value});
}
}
let left = width * i;
series.push({
name: groups[i],
type: type,
sort: 'ascending',
grap: 2,
left: left + "%",
width: width - 5 + "%",
label: {
normal: {
show: true,
position: 'inside'
},
emphasis: {
textStyle: {
fontSize: 20
}
}
},
data: temp_data
});
}
return {groups: groups, category: names, series: series};
},
/**
* 仪表盘图数据格式化
*/
GaugeFormate: function (data, type) {
let temp_datas = [{value: data.value, name: data.name}];
let names = data.name;
//判断最大值和最小值几位数
maxNum = Number(parseInt(data.value)).toString().length;
minNum = Number(parseInt(data.value)).toString().length;
if (minNum <= 2) {
min = 0;
} else {
//最小值
min = Math.pow(10, (maxNum - 1));
}
//最大值
max = Math.pow(10, maxNum);
let series = [];
series.push({
name: data.group,
type: type,
min: min,
max: max,
radius: '70%',
startAngle: 180,
endAngle: -0,
axisLine: { // 坐标轴线
lineStyle: { // 属性lineStyle控制线条样式
color: [[0.09, 'lime'], [0.82, '#1e90ff'], [1, '#ff4500']],
width: 3,
shadowColor: '#fff', //默认透明
shadowBlur: 10
}
},
axisLabel: { // 坐标轴小标记
textStyle: { // 属性lineStyle控制线条样式
fontWeight: 'bolder',
color: '#444',
shadowColor: '#fff', //默认透明
shadowBlur: 10
}
},
axisTick: { // 坐标轴小标记
length: 15, // 属性length控制线长
lineStyle: { // 属性lineStyle控制线条样式
color: 'auto',
shadowColor: '#fff', //默认透明
shadowBlur: 10
}
},
splitLine: { // 分隔线
length: 25, // 属性length控制线长
lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式
width: 3,
color: 'auto',
shadowColor: '#fff', //默认透明
shadowBlur: 10
}
},
pointer: { // 分隔线
shadowColor: '#fff', //默认透明
shadowBlur: 5
},
title: {
offsetCenter: ['-10%', '30%'],
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
fontWeight: 'bolder',
fontSize: 14,
fontStyle: 'italic',
color: '#',
shadowColor: '#fff', //默认透明
shadowBlur: 10
}
},
detail: {
backgroundColor: 'rgba(30,144,255,0.8)',
borderWidth: 1,
borderColor: '#fff',
shadowColor: '#fff', //默认透明
shadowBlur: 5,
fontSize: 14,
offsetCenter: ['20%', '30%'], // x, y,单位px
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
fontWeight: 'bolder',
color: '#fff'
}
},
data: temp_datas
});
return {category: names, series: series};
}
},
//生成图形option
EchartsOption: {
/**
* 饼图
* @param title : 标题
* @param subtext :副标题
* @param data : json 数据
* @param radiusArr : array [内环百分比,外环百分比],不指定为普通饼图
*
*/
pie: function (title, subtext, data, radiusArr) {
//数据格式
let datas = MyEcharts.EchartsDataFormate.NoGroupFormate(data);
let option = {
//标题
title: {
text: title || "", //标题
subtext: subtext || "", //副标题
},
//提示
tooltip: {
show: true,
trigger: 'item',
formatter: "{a}
{b} : {c} ({d}%)"
},
//组建
legend: {
data: datas.categorys
},
series: [
{
name: title || "",
type: 'pie', //类型
radius: radiusArr || '48%', //圆的大小
center: ['50%', '50%'],//位置居中
data: datas.data,
emphasis: { //高亮的扇区和标签样式。
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
},
labelLine: { //引导线
show: false,
},
label: {
textStyle: {
color: 'auto' //改变标示文字的颜色
}
}
}
]
};
return option;
},
/**
* 柱形图
* @param title : 标题
* @param subtext :副标题
* @param data : json 数据
*/
bar: function (title, subtext, data) {
let datas = MyEcharts.EchartsDataFormate.GroupFormate(data, 'bar');
let option = {
//标题
title: {
text: title || "", //标题
subtext: subtext || "", //副标题
},
//提示
tooltip: {
show: true,
trigger: 'axis', //散点图,饼图等无类目轴的图表中使用:'item';
axisPointer: { //鼠标指向背景
type: 'shadow', //'line' 直线指示器 ,'shadow' 阴影指示器 ,'none' 无指示器 ,'cross' 十字准星指示器。其实是种简写,表示启用两个正交的轴的 axisPointer。
label: {
show: true
}
},
// formatter: function (params) {
// return MyEcharts.resetTooltip(params);
// }
},
// 工具条
toolbox: {
show: false, // 默认显示
feature: {
dataZoom: {
yAxisIndex: 'none'
},
dataView: {readOnly: false},
magicType: {type: ['line', 'bar']},
restore: {},
saveAsImage: {}
}
},
// 数据滑竿
dataZoom: [
{
show: false,
realtime: true,
},
{
type: 'slider',
realtime: true,
}
],
//组建
legend: {
data: datas.groups.map((text) => {
return {name: text, icon: 'rect'} //icon图形样式:'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow'
})
},
//水平坐标
xAxis: [
{
name: '', //x轴单位
nameTextStyle: {//x轴上方单位的颜色
color: 'auto'
},
type: 'category',
data: datas.category
}
],
//垂直坐标
yAxis: [
{
name: '', //y轴单位
nameTextStyle: {//y轴上方单位的颜色
color: '#151515'
},
type: 'value'
}
],
//series数据
series: datas.series
};
return option;
},
/**
* 横向柱形图
* @param title : 标题
* @param subtext :副标题
* @param data : json 数据
*/
acrossBar: function (title, subtext, data) {
let datas = MyEcharts.EchartsDataFormate.GroupFormate(data, 'acrossBar');
let option = {
//标题
title: {
text: title || "", //标题
subtext: subtext || "", //副标题
},
//提示
tooltip: {
show: true,
trigger: 'axis', //散点图,饼图等无类目轴的图表中使用:'item';
axisPointer: { //鼠标指向背景
type: 'none', //'line' 直线指示器 ,'shadow' 阴影指示器 ,'none' 无指示器 ,'cross' 十字准星指示器。其实是种简写,表示启用两个正交的轴的 axisPointer。
label: {
show: true
}
},
formatter: function (params) {
return MyEcharts.resetTooltip(params);
}
},
//组建
legend: {
data: datas.groups.map((text) => {
return {name: text, icon: 'rect'} //icon图形样式:'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow'
})
},
//水平坐标
xAxis: [
{
name: '', //x轴单位
nameTextStyle: {//x轴上方单位的颜色
color: '#151515'
},
type: 'value'
}
],
//垂直坐标
yAxis: [
{
name: '', //y轴单位
nameTextStyle: {//y轴上方单位的颜色
color: '#151515'
},
type: 'category',
data: datas.category
}
],
//series数据
series: datas.series
};
return option;
},
/**
* 双y轴/混搭
* @param title : string 标题
* @param subtext : string 副标题
* @param data : json 数据
* @param type : Array 或 string,数组时需要和图例个数相对应,字符串时为统一类型 **注意:type不管为哪种类型时都需要在数据中指定yAxisIndex的值**
*/
doubleYMixUp: function (title, subtext, data, type) {
let datas = MyEcharts.EchartsDataFormate.GroupFormate(data, type);
let option = {
//标题
title: {
text: title || "", //标题
subtext: subtext || "", //副标题
},
//提示
tooltip: {
show: true,
trigger: 'axis', //散点图,饼图等无类目轴的图表中使用:'item';
axisPointer: { //鼠标指向背景
type: 'shadow', //'line' 直线指示器 ,'shadow' 阴影指示器 ,'none' 无指示器 ,'cross' 十字准星指示器。其实是种简写,表示启用两个正交的轴的 axisPointer。
label: {
show: true
}
}
},
//组建
legend: {
data: datas.groups.map((text) => {
return {name: text, icon: 'rect'} //icon图形样式:'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow'
})
},
//水平坐标
xAxis: [
{
name: '', //x轴单位
type: 'category',
data: datas.category,
}
],
//垂直坐标
yAxis: [
{
name: '', //y轴单位
type: 'value',
splitLine: {
show: false,
}
},
{
name: '', //右y轴单位
type: 'value',
splitLine: {
show: false,
}
}
],
//series数据
series: datas.series
};
return option;
},
/**
* 折线图
* @param title : 标题
* @param subtext :副标题
* @param data : json 数据
*/
Line: function (title, subtext, data) {
let datas = MyEcharts.EchartsDataFormate.GroupFormate(data, 'line');
let option = {
//标题
title: {
text: title || "", //标题
subtext: subtext || "", //副标题
},
//提示
tooltip: {
show: true,
trigger: 'axis',
// formatter: function (params) {
// debugger
// return MyEcharts.resetTooltip(params);
// }
},
//组建
legend: {
data: datas.groups.map((text) => {
return {name: text, icon: 'rect'} //icon图形样式:'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow'
})
},
//水平坐标
xAxis: [
{
type: 'category',
boundaryGap: false,
splitLine: {
show: false,
},
data: datas.category,
}
],
//垂直坐标
yAxis: [
{
type: 'value'
}
],
//series数据
series: datas.series
};
return option;
},
/**
* 雷达图
* @param title : 标题
* @param subtext :副标题
* @param data : json 数据
*/
Radar: function (title, subtext, data) {
let datas = MyEcharts.EchartsDataFormate.RadarFormate(data, 'radar');
let option = {
//标题
title: {
text: title || "", //标题
subtext: subtext || "", //副标题
},
//提示
tooltip: {
show: true,
},
//组建
legend: {
data: datas.groups
},
radar: {
name: {
textStyle: {
color: '#fff',
backgroundColor: '#999',
borderRadius: 3,
padding: [3, 5]
}
},
indicator: datas.indicators
},
series: datas.series
};
return option;
},
/**
* 漏斗图
* @param title : 标题
* @param subtext :副标题
* @param data : json 数据
*/
Funnel: function (title, subtext, data) {
let datas = MyEcharts.EchartsDataFormate.FunnelFormate(data, 'funnel');
let option = {
//标题
title: {
text: title || "", //标题
subtext: subtext || "", //副标题
},
//提示
tooltip: {
show: true,
trigger: 'item',
formatter: "{a}
{b} ({c}%)"
},
//组建
legend: {
data: datas.groups,
},
series: datas.series
};
return option;
},
/**
* 仪表图
* @param title : 标题
* @param subtext :副标题
* @param data : json 数据
*/
Gauge: function (title, subtext, data) {
let datas = MyEcharts.EchartsDataFormate.GaugeFormate(data, 'gauge');
let option = {
//标题
title: {
text: title || "", //标题
subtext: subtext || "", //副标题
},
//提示
tooltip: {
show: true,
formatter: "{a}
{b}:{c}"
},
series: datas.series
};
return option;
}
},
/**
* 重置Tooltip样式
* @param params : object tooltip函数返回值
*/
resetTooltip: function (params) {
if (params.length === 1) { //只有一组数据时
let text = params[0].seriesName + '
';
let colorStops = params[0].color;
if (typeof colorStops === 'object') { //色彩为渐变时
colorStops = params[0].color.colorStops[0].color; //***待优化,此处color不一定有颜色,第一个可能为透明色
}
text += '' + params[0].axisValue + ' : ' + params[0].value + '
';
return text;
} else { //一组 多条数据时
let text = params[0].axisValue + '
';
params.forEach((item) => {
let colorStops = item.color;
if (typeof colorStops === 'object') { //色彩为渐变时
colorStops = item.color.colorStops[0].color; //***待优化,此处color不一定有颜色,第一个可能为透明色
}
text += '' + item.seriesName + ' : ' + item.value + '
';
})
return text;
}
},
/**
* @param option : option
* @param echartId : 图表的id 需要加引号
*/
initChart: function (option, echartId) {
let container = eval("document.getElementById('" + echartId + "')");
let myChart = echarts.init(container, myTheme);
myChart.setOption(option, true); // (1图表数据: Object, 2是否不跟之前设置的option进行合并?默认为false,即合并: boolean, 3在设置完option后是否不立即更新图表?默认为false,即立即更新。: boolean)
this.echartSet.push(myChart); // 存储已初始化的图表实例
this.resize(); // 添加图表大小改变事件
return myChart;
},
initChart: function (option, echartId, themeId) {
let container = eval("document.getElementById('" + echartId + "')");
let myChart = echarts.init(container, themeId);
myChart.setOption(option, true); // (1图表数据: Object, 2是否不跟之前设置的option进行合并?默认为false,即合并: boolean, 3在设置完option后是否不立即更新图表?默认为false,即立即更新。: boolean)
this.echartSet.push(myChart); // 存储已初始化的图表实例
this.resize(); // 添加图表大小改变事件
return myChart;
},
//图表大小改变事件监听
resize: function () {
try {
window.onresize = function () {
isDebounce(function () {
MyEcharts.echartSet.forEach(item => {
item.resize();
})
}, 300);
}.bind(this);
} catch (error) {
}
}
};
/**
* 数组是否存在某数据
* @param obj
* @returns {Boolean}
*/
Array.prototype.contains = function (obj) {
let i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
};
/**
* 数组中最大值 最小值
* @param array
* @returns
*/
Array.prototype.max = function () {
return Math.max.apply({}, this);
};
Array.prototype.min = function () {
return Math.min.apply({}, this);
};
/**
* 判断是否为整数
* @param obj
* @returns {Boolean}
*/
function isInteger(obj) {
return obj % 1 === 0;
}
/**
* 判断值是否包含指定字符
* @param str
* @returns {Boolean}
*/
function myRegExp(str) {
let patt = new RegExp(/[%|$]/)
return patt.test(str);
}
/**防抖函数
* 当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
* 适用于延迟时间后触发一次事件。
*使用方式:isDebounce(函数名,延迟时间)();
*/
let isTimer; // 维护一个 timer
function isDebounce(fn, delay) {
try {
let _this = this; // 取debounce执行作用域的this
let args = arguments;
if (isTimer) {
clearTimeout(isTimer);
}
isTimer = setTimeout(function () {
fn.apply(_this, args); // 用apply指向调用debounce的对象,相当于_this.fn(args);
}, delay);
} catch (error) {
console.log(error);
}
};
/**自定义loading
* 当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
* 适用于延迟时间后触发一次事件。
*使用方式:isDebounce(函数名,延迟时间)();
*/
function loadingNodata(api, opts) {
let PI = Math.PI;
opts = opts || {};
opts = new echarts.util.defaults(opts, {
text: 'loading', // loading显示的文字
textColor: '#000', // 文字颜色
fontSize: '12px', // 文字大小
maskColor: 'rgba(255, 255, 255, 0.8)',
showSpinner: true, // 是否显示gif动画
color: '#c23531', // gif填充的颜色
spinnerRadius: 10,
lineWidth: 5,
zlevel: 0
})
var group = new echarts.graphic.Group();
var mask = new echarts.graphic.Rect({
style: {
fill: opts.maskColor
},
zlevel: opts.zlevel,
z: 10000
});
group.add(mask);
var font = opts.fontSize + ' sans-serif';
var labelRect = new echarts.graphic.Rect({
style: {
fill: 'none',
text: opts.text,
font: font,
textPosition: 'right',
textDistance: 10,
textFill: opts.textColor
},
zlevel: opts.zlevel,
z: 10001
});
group.add(labelRect);
if (opts.showSpinner) {
var arc = new echarts.graphic.Arc({
shape: {
startAngle: -PI / 2,
endAngle: -PI / 2 + 0.1,
r: opts.spinnerRadius
},
style: {
stroke: opts.color,
lineCap: 'round',
lineWidth: opts.lineWidth
},
zlevel: opts.zlevel,
z: 10001
});
arc.animateShape(true)
.when(1000, {
endAngle: PI * 3 / 2
})
.start('circularInOut');
arc.animateShape(true)
.when(1000, {
startAngle: PI * 3 / 2
})
.delay(300)
.start('circularInOut');
group.add(arc);
}
//添加计算canvas文字宽度方法
api.getTextWith = function (
text = '',
fontStyle = '14px/1.5715 "Source Sans Pro, Helvetica Neue, Helvetica, Arial, sans-serif"', // 设置字体大小和字体
) {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
context.font = fontStyle;
const elem = context.measureText(text);
return elem.width;
}
group.resize = function () {
var textWidth = api.getTextWith(opts.text, font);
var r = opts.showSpinner ? opts.spinnerRadius : 0;
console.log("@r" + r);
console.log("@showSpinner" + opts.showSpinner);
// cx = (containerWidth - arcDiameter - textDistance - textWidth) / 2
// textDistance needs to be calculated when both animation and text exist
var cx = (api.getWidth() - r * 2 - (opts.showSpinner && textWidth ? 10 : 0) - textWidth) / 2
// only show the text
// - (opts.showSpinner ? 0 : textWidth / 2);
var cy = api.getHeight() / 2;
opts.showSpinner && arc.setShape({
cx: cx,
cy: cy
});
labelRect.setShape({
x: cx - r,
y: cy - r,
width: r * 2,
height: r * 2
});
mask.setShape({
x: 0,
y: 0,
width: api.getWidth(),
height: api.getHeight()
});
};
group.resize();
return group;
};
//向全局echarts对象注册新的loading: nodatas
echarts.registerLoading("nodatas", loadingNodata);