/** * 封装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);