/* * ECharts图表 * 作者:江鸿宾(QQ33080907) * 最近修改:2020.05.26 * 本插件只用于作者参与的项目,未经许可请勿转载 */ /* 需在内部引入(echarts.common、echarts.simple或echarts选其一): 如果要动态载入脚本用以下方式: var echartsScript = document.createElement('script') echartsScript.src = '/scripts/chart.js?e=chart&m=1&r=45&a=1&x=' + x + '&v=' + v.join('|') document.body.appendChild(echartsScript) */ (function () { 'use strict' // 获取url var $script = $('script[src*="/chart.js"]').last() var src = $script.attr('src') // 获取参数 var e = src.match(/[?&]e=([^&]+)/) // 容器元素ID,默认自动创建 var c = src.match(/[?&]c=([^&]+)/) // 统计图类型,默认为折线图(line),其他为平滑曲线图(spline)、柱状图(column)、条形图(bar)、饼图(pie)、漏斗图(funnel)、阶梯折线(step)、玫瑰图(rose)、errorbar、雷达图(radar) var s = src.match(/[?&]s=([^&]+)/) // 是否堆叠,默认不堆叠,只有折线图(line)、柱状图(column)、条形图(bar)能够使用堆叠方式,堆叠传入1,不堆叠传入0或留空 var t = src.match(/[?&]t=([^&]+)/) // 标题 var w = src.match(/[?&]w=([^&]+)/) // 宽度,默认100% var h = src.match(/[?&]h=([^&]+)/) // 高度,默认400px var x = src.match(/[?&]x=([^&]+)/) // 项目,逗号或竖线分割 var r = src.match(/[?&]r=([^&]+)/) // x轴标签旋转角度,默认0为不旋转,bar类型不支持 var v = src.match(/[?&]v=([^&]+)/) // 单序列采用值(与项目对应,逗号或竖线分割),数值之间可以用竖线或逗号。注意:pie/funnel类型不支持多序列 var p = src.match(/[?&]p=([^&]+)/) // 渲染类型,默认canvas,图片传入1,不是图片传入0或留空 var l = src.match(/[?&]l=([^&]+)/) // 是否显示数字标签,默认显示,不显示传入0 var o = src.match(/[?&]o=([^&]+)/) // 颜色序列,用竖线或逗号分割,需要#开头,不传则用主题色 var z = src.match(/[?&]z=([^&]+)/) // 主题名称,默认为macarons,可参考:http://echarts.baidu.com/download-theme.html var m = src.match(/[?&]m=([^&]+)/) // 是否标记最大值,标记传入1,默认0不标记,只有折线图和柱状图支持 var a = src.match(/[?&]a=([^&]+)/) // 是否标记平均值,标记传入1,默认0不标记,只有折线图和柱状图支持 var y = src.match(/[?&]y=([^&]+)/) // 是否显示y轴,默认显示,不显示传入0 var n = src.match(/[?&]n=([^&]+)/) // y轴名称,默认为空即不显示名称 var b = src.match(/[?&]b=([^&]+)/) // x轴标签与底部图例之间的距离,默认30 var d = src.match(/[?&]d=([^&]+)/) // 是否在调试,调试传入1 // 处理参数 if (e === null || e.length < 1) { e = '' } else { e = e[1] } if (c === null || c.length < 1) { c = 'line' } else { c = c[1] } if (s === null || s.length < 1) { s = false } else { s = s[1] === '1' } if (t === null || t.length < 1) { t = '' } else { t = t[1] } if (w === null || w.length < 1) { w = '100%' } else { w = w[1] + 'px' } if (h === null || h.length < 1) { h = 400 } else { h = h[1] } if (x === null || x.length < 1) { x = '' } else { x = x[1] } if (r === null || r.length < 1) { r = 0 } else { r = r[1] } if (v === null || v.length < 1) { v = '' } else { v = v[1] } if (p === null || p.length < 1) { p = false } else { p = p[1] === '1' } if (l === null || l.length < 1) { l = true } else { l = !(l[1] === '0') } if (o === null || o.length < 1) { o = '' } else { o = o[1] } if (z === null || z.length < 1) { z = 'macarons' } else { z = z[1] } if (m === null || m.length < 1) { m = false } else { m = m[1] === '1' } if (a === null || a.length < 1) { a = false } else { a = a[1] === '1' } if (y === null || y.length < 1) { y = true } else { y = !(y[1] === '0') } if (n === null || n.length < 1) { n = '' } else { n = n[1] } if (b === null || b.length < 1) { b = 30 } else { b = b[1] } if (d === null || d.length < 1) { d = false } else { d = d[1] === '1' } // 如果图标类型不支持,则什么都不做 if ('|line|spline|column|bar|pie|funnel|step|rose|errorbar|radar|'.indexOf('|' + c + '|') < 0) { return } // 有时避免查询字符串过长,采用隐藏表单传值,寻找当前之前最近的一个表单 if (x === '') { x = $script.prevAll('input.x').first().val() } if (v === '') { v = $script.prevAll('input.v').first().val() } if (typeof (x) === 'undefined' || x === '') { x = $('#x').val() } if (typeof (v) === 'undefined' || v === '') { v = $('#v').val() } // 如果没有x/v数据,则什么都不做 if (typeof (x) === 'undefined' || x === '') { return } if (typeof (v) === 'undefined' || v === '') { return } // 共用变量 var option = {} var json = null var i = 0 // 处理x/y轴数据 x = x.replace(/\|/g, ',').split(',') v = v.replace(/\|/g, ',').replace(/'/g, '"') // 生成dataset数据(丢弃多余x个数的项目) // 兼容旧的传值(完整JSON字串)转换为key-value形式,原始数据例如:[{'name':'专注度','data':[10,90,60,60,60]},{'name':'放松度','data':[5,95,50,30,10]}] if (v.indexOf('{"name":') > 0 && v.indexOf('"data":[') > 0) { v = v.replace(/"name":/g, '').replace(/,"data":/g, ':').replace(/}|{/g, '') v = v.substring(1, v.length - 1) // 空字符串导致堆叠不显示,所以设为一个空格 v = '{" ":' + JSON.stringify(x) + ',' + v + '}' v = JSON.parse(v) } // key-value传值(带两侧花括号,键名是图例项目),原始数据例如:{'专注度':[10,90,60,60,60],'放松度':[5,95,50,30,10]} else if (v.indexOf(']}') > 0) { v = v.replace('{M:[', '{"M":[').replace(',SD:[', ',"SD":[') v = v.substring(1, v.length - 1) v = '{" ":' + JSON.stringify(x) + ',' + v + '}' v = JSON.parse(v) } // 数组传值(不带两侧方括号,行是x轴项目,列是图例项目,原始数据中第一行文字为列名即图例名),原始数据例如:['delta','theta','lowAlpha','highAlpha','lowBeta','highBeta','lowGamma','middleGamma'],[60,77,43,77,78,43,77,78],[60,77,43,77,78,43,77,78],[60,77,43,77,78,43,77,78] else if (v.indexOf('],[') > 0) { v = v.replace(/,\./g, ',0.').replace(/\[\./g, '[0.') v = JSON.parse('[' + v + ']') v = v.slice(0, x.length + 1) v[0].unshift('总量') for (i = 0; i < x.length; i++) { v[i + 1].unshift(x[i]) } } // 单序列(只是逗号隔开) else { v = JSON.parse('[[""],[' + v.replace(/,/g, '],[') + ']]') v = v.slice(0, x.length + 1) v[0].unshift('') for (i = 0; i < x.length; i++) { v[i + 1].unshift(x[i]) } } // 开始处理option // 数据 option.dataset = { source: v } // 图表标题 option.title = t ? { text: t } : { show: false } // 颜色序列 if (o) { option.color = o.replace(/\|/g, ',').split(',') } // 饼图、漏斗图、玫瑰图、雷达图 if (c === 'pie' || c === 'funnel' || c === 'rose') { $.extend(true, option, { toolbox: { feature: { restore: {}, saveAsImage: {} } }, tooltip: { confine: true, formatter: '{c} ({d}%)' }, series: { type: c === 'rose' ? 'pie' : c, label: { formatter: '{c} ({d}%)', show: l }, emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0, 0, 0, 0.5)' } } } }) // 饼图 if (c === 'pie') { $.extend(true, option, { legend: { orient: 'vertical', left: 'right', top: 'middle' }, series: { radius: [0, '70%'] } }) } // 漏斗图 else if (c === 'funnel') { $.extend(true, option, { legend: { left: 'center', top: 'bottom' }, series: { minSize: '1%', maxSize: '90%' } }) } // 南丁格尔玫瑰图 else if (c === 'rose') { $.extend(true, option, { legend: { orient: 'vertical', left: 'right', top: 'middle' }, series: { radius: ['15%', '70%'], roseType: 'area' } }) } } // 结束:饼图、漏斗图、玫瑰图 // 雷达图 else if (c === 'radar') { const radar = [] let max = 0 for (i = 1; i < v.length; i++) { const value = v[i][1] value > max && (max = value) radar.push(value) } max = max.toString() max = (Number(max.charAt(0)) + 1) * Math.pow(10, max.length - 1) delete option.dataset $.extend(true, option, { toolbox: { feature: { restore: {}, saveAsImage: {} } }, tooltip: {}, radar: { scale: true, axisLine: { show: false }, splitArea: { show: false }, axisLabel: { showMinLabel: false }, indicator: x.map((item, index) => ({ name: item, max, axisLabel: { show: index === 0 } })) }, series: { name: '数据', type: 'radar', areaStyle: {}, data: [{ value: radar }] } }) } // errorbar图,传值:v={M:[],SD:[]} else if (c === 'errorbar') { // 计算标准差范围 json = [] for (i = 0; i < x.length; i++) { json.push([i, v.SD[i], v.M[i] - v.SD[i], v.M[i] + v.SD[i]]) } $.extend(true, option, { grid: { left: 5, right: 5, containLabel: true, bottom: 0 }, tooltip: { trigger: 'axis', backgroundColor: 'rgba(255,255,255,0.7)', borderWidth: 1, borderColor: '#48b', textStyle: { color: '#000' }, axisPointer: { type: 'shadow' } }, toolbox: { feature: { saveAsImage: {} } }, xAxis: { type: 'category', axisLabel: { rotate: r }, data: x }, yAxis: { type: 'value', scale: true, show: y }, series: [ { name: '平均值', type: 'bar', label: { show: l, position: 'insideBottom', formatter: function (params) { return params.value + '±' + v.SD[params.dataIndex] } }, data: v.M }, { name: '标准差', type: 'custom', data: json, itemStyle: { borderWidth: 1.5 }, encode: { x: 0, y: [2, 3], tooltip: 1 }, renderItem: function (params, api) { var xValue = api.value(0) var highPoint = api.coord([xValue, api.value(2)]) var lowPoint = api.coord([xValue, api.value(3)]) var halfWidth = api.size([1, 0])[0] * 0.1 var style = api.style({ stroke: api.visual('color'), fill: null }) return { type: 'group', children: [{ type: 'line', shape: { x1: highPoint[0] - halfWidth, y1: highPoint[1], x2: highPoint[0] + halfWidth, y2: highPoint[1] }, style: style }, { type: 'line', shape: { x1: highPoint[0], y1: highPoint[1], x2: lowPoint[0], y2: lowPoint[1] }, style: style }, { type: 'line', shape: { x1: lowPoint[0] - halfWidth, y1: lowPoint[1], x2: lowPoint[0] + halfWidth, y2: lowPoint[1] }, style: style }] } } } ] }) } // 其他类型图表:折线图(line)、平滑曲线图(spline)、柱状图(column)、条形图(bar)、阶梯折线(step) else { // 图表类型 var type = c if (c === 'column') { type = 'bar' } else if (c === 'spline' || c === 'step') { type = 'line' } $.extend(true, option, { grid: { left: 0, right: c === 'bar' ? 10 : 5, bottom: b, top: 35, containLabel: true }, tooltip: { trigger: 'axis', backgroundColor: 'rgba(255,255,255,0.7)', borderWidth: 1, borderColor: '#48b', textStyle: { color: '#000' } }, toolbox: { feature: { magicType: {}, restore: {}, saveAsImage: {} } } }) // 坐标轴 if (c === 'bar') { $.extend(true, option, { xAxis: { type: 'value' }, yAxis: { type: 'category', name: n, inverse: true } }) } else { $.extend(true, option, { xAxis: { type: 'category', axisLabel: { interval: 0, rotate: r } }, yAxis: { type: 'value', name: n, scale: true, show: y } }) } // 处理工具提示bar和column为shadow,默认为直线(line) if (c === 'bar' || c === 'column') { $.extend(true, option, { tooltip: { axisPointer: { type: 'shadow' } } }) } // 需要对每个序列(series)追加的属性 var ItemEX = { type: type, smooth: c === 'spline', step: c === 'step' ? 'middle' : false } // 处理堆叠、数据标签位置 if ((c === 'bar' || c === 'column') && s) { $.extend(true, ItemEX, { stack: '总量' }) } else if (c === 'bar' && l) { $.extend(true, ItemEX, { label: { position: 'right' } }) } else if (c === 'column' && l) { $.extend(true, ItemEX, { label: { position: 'top' } }) } // 是否显示数据标签,标最大值,标平均值 if (l) { $.extend(true, ItemEX, { label: { show: true } }) } if (m) { $.extend(true, ItemEX, { markPoint: { silent: true, data: [{ type: 'max' }] } }) } if (a) { $.extend(true, ItemEX, { markLine: { silent: true, label: { position: 'middle', formatter: '平均值:{c}' }, data: [{ type: 'average' }] } }) } // 生成序列 var series = [] var seriesLength = Object.prototype.toString.call(v) === '[object Array]' ? v[0].length - 1 : Object.keys(v).length - 1 for (i = 0; i < seriesLength; i++) { series.push(ItemEX) } // 合并选项 $.extend(true, option, { series: series }) // 处理底部边距和图例(单序列底部且x轴标签未旋转为0,多序列根据参数设置) if (seriesLength === 1 && r === 0) { $.extend(true, option, { grid: { bottom: 0 } }) } else { $.extend(true, option, { legend: { top: 'bottom' } }) } // 处理工具栏 if (c === 'bar' || c === 'column') { if (s) { $.extend(true, option, { toolbox: { feature: { magicType: { type: ['line', 'tiled'] } } } }) } else { $.extend(true, option, { toolbox: { feature: { magicType: { type: ['line', 'stack'] } } } }) } } else { if (s) { $.extend(true, option, { toolbox: { feature: { magicType: { type: ['bar', 'tiled'] } } } }) } else { $.extend(true, option, { toolbox: { feature: { magicType: { type: ['bar', 'stack'] } } } }) } } } // 结束:其它类型图表 // 处理主题 if ('|default|light|dark|'.indexOf('|' + z + '|') < 0) { if ($('body').attr('data-echarts-theme-' + z)) { drawchart() } else { $.ajax('/Scripts/echarts/theme/' + z + '.js', { dataType: 'script', cache: true, success: function () { $('body').attr('data-echarts-theme-' + z, true) drawchart() } }) } } else { drawchart() } // 绘制 function drawchart() { // 创建容器 if (e === '') { e = 'chart-' + Math.random().toString().substring(2, 8) $script.after('
') } // 如果容器没有高度,则设置为默认高度 else if ($('#' + e).height() === 0) { $('#' + e).height(h) } // 调试时可以观察到脚本src $('#' + e).attr('data-src', src) // 添加data以备导出Word处理 $('#' + e).data({ option: option, theme: z }) // 移除脚本 $script.remove() // 实例化 var mychart = echarts.init(document.getElementById(e), z) // 处理图表超出dom宽度的情况 var domWidth = $('#' + e).width() var chartWidth = mychart.getWidth() if (chartWidth > domWidth) { mychart.resize({ width: domWidth }) } if (d) { console.log(e + '-option:\n' + JSON.stringify(option, null, 2)) } // 渲染为图片 if (p) { $.extend(true, option, { animation: false, toolbox: { show: false }, tooltip: { show: false } }) mychart.setOption(option) $('#' + e).html('') } // 手机屏幕取消动画、隐藏工具栏 else if (window.screen.width <= 640) { $.extend(true, option, { animation: false, toolbox: { show: false } }) mychart.setOption(option) } else { mychart.setOption(option) } // 调试 // console.log(JSON.stringify(option, null, 2)) } })()