Echarts实现数据下钻
某医疗机构搭建的数据系统需要对其可视化模块新增1项数据下钻的功能,原有的数据展示如下:
【项目要求】
现在要实现的下钻功能如下:
1.单击其中一根数据柱后,数据下钻到下一个层级
2.与传统的下钻不同,下钻至下一个层级后,数据的X轴将发生变化,即X轴由原始的时段变成初始选定的日期区间,Y轴展示的则为时段h在对应各个日期区间内的值。
3.添加1个返回上一级按钮,实现上钻与下钻的自主可选。
4.其余原始的可选框保持不变,仍然可选
5.保障在各个类型数据和各个层级之间切换的页面流畅性
【项目分析】
要实现该项目的要求需要解决几个主要问题
1.下钻1级后要阻止其再次点击柱形条时再次触发下钻
2.X轴变化在上下钻过程中实时切换
3.如何保障页面流畅性,即不要过度加载底层数据源
4.不同类型之间切换时会访问不同的底层数据源,如何进行统一协调
5.实时监控当前图表的层级
【核心代码块/算法】
1.构造日期列表,以01-01的方式展示日期使下钻的X轴变得更加美观/简约
//写一个获取时间列表函数,用于下钻使用,假定时间传入格式为文本格式 function getDateList(begin_date,end_date){ function addZero(num){ if (parseInt(num)<10){ num='0'+num } return num } var dateBox={'strDate':[],'axisDate':[]} let b_date=Date.parse(begin_date.replace(/\-/g,'/')) //利用正则将字符串格式转换统一标准格式,并將字符串格式日期转化为时间戳 let e_date=Date.parse(end_date.replace(/\-/g,'/')) //若结束日期小于开始日期,进行初始/结束日期互换 if (e_date<b_date){ m_date=b_date b_date=e_date e_date=m_date } let dayGap=(e_date - b_date)/86400000 //计算两个日期的天数差 // console.log(dayGap) //设定观测日期范围,介于3-60日期间 if (dayGap>60|dayGap<3){ alert('观测日期的间隔设定需介于3-60日之间!') } else{ for (i =0;i<=dayGap;i++){ date_s=new Date(b_date+i*86400000) dateBox.strDate.push(b_date+i*86400000) //date_s.getFullYear()+'/'+(date_s.getMonth()+1)+'/'+date_s.getDate() dateBox.axisDate.push(addZero((date_s.getMonth()+1))+'-'+addZero(date_s.getDate())) } } return dateBox }
复制
2.初始化一个全局参数使得在不同类型间切换时部分底层数据源无需二次加载实现功能的流畅性,并在每次切换类型前验证是否已经加载该类型所需的数据
var loadedData={'rep_consume':{'data':0,'status':0},'rep_dialogue':{'data':0,'status':0},'rep_key_words_statistics':{'data':0,'status':0},'rep_api_account':{'data':0,'status':0},"rep_appointment":{'data':0,'status':0},'rep_arrival':{'data':0,'status':0}} async function checkDataStatus(tbName){ let url="DataSource/"+tbName+".json" //此处为项目初测阶段未连接服务器,使用本地数据进行测试 let response = await fetch(url) return await response.json(); } //写一个获取不同层级的x/y轴数据的函数 function getData(myArr,typeValue,begin_date,end_date,level,hourNum){ var outputData={'xdata':0,'ydata':0} var axis_list=[] var oriDic={} var ydata=[] if (level==0){ for (i =0;i<24;i++){ oriDic[i.toString()]=0; axis_list.push(i.toString()); } finalDic=f(myArr,oriDic,typeValue,0,'0') //获取最终各个时段对应的消费或其它类型数据 for (i =0;i<24;i++){ ydata[i]=finalDic[i.toString()].toFixed(2) } } else { dateBox=getDateList(begin_date,end_date) for (i=0;i<dateBox.strDate.length;i++){ oriDic[dateBox.strDate[i]]=0; axis_list.push(dateBox.axisDate[i]); } finalDic=f(myArr,oriDic,typeValue,level,hourNum) for (i=0;i<dateBox.strDate.length;i++){ ydata[i]=finalDic[dateBox.strDate[i]].toFixed(2) } } function f(myArr,dataDic,datatype,level,hourNum){ if (level==0){ for (i=0;i<myArr['RECORDS'].length;i++){ dateTime=Date.parse(myArr['RECORDS'][i]['start_time']) // console.log(dateTime) if (dateTime>=bgTime && dateTime<=edTime && !isNaN(parseFloat(myArr['RECORDS'][i][datatype]))){ dataDic[myArr['RECORDS'][i]['start_hour']]+=parseFloat(myArr['RECORDS'][i][datatype]) } } } else { for (i=0;i<myArr['RECORDS'].length;i++){ strDate=myArr['RECORDS'][i]['start_time'] // console.log(strDate.length) if (strDate.length>10){ fmtDate=(/\d{4}\/\d{1,2}\/\d{1,2}/g.exec(myArr['RECORDS'][i]['start_time'])).replace(/\-/g,'/') dateTime=Date.parse(fmtDate) }else{ dateTime=Date.parse(myArr['RECORDS'][i]['start_time']) } if (dataDic.hasOwnProperty(dateTime) && myArr['RECORDS'][i]['start_hour']==hourNum && !isNaN(parseFloat(myArr['RECORDS'][i][datatype]))){ dataDic[dateTime]+=parseFloat(myArr['RECORDS'][i][datatype]) } } } return dataDic } outputData.xdata=axis_list outputData.ydata=ydata return outputData }
复制
3.画图,根据传入的层级参数指定不同的格式
function plotData(xdata,ydata,typeValue){ //<!--开始绘制图表,初始化一个Echarts实例对象--> var MyEcharts=echarts.init(document.querySelector('div')) //,'shine' var option={ animationDuration:2000, backgroundColor: 'white',//'#f5faff', xAxis:{ type:"category", name:(oriParam.level==0)?'时段':'日期', nameLocation:'center', nameGap:40, nameTextStyle:{ color:(oriParam.level==0)?'DodgerBlue':'red', fontSize:(oriParam.level==0)?16:14, fontFamily:'黑体', // fontWeight:'bold' }, data:xdata, axisLabel:{ rotate:(oriParam.level==0)?0:30, } }, yAxis:{ name:(synList[typeValue]==1)?typeList[typeValue]+'(%)':typeList[typeValue], type:"value", nameLocation:'center', nameGap:60, scale:true, nameTextStyle:{ color:(oriParam.level==0)?'DodgerBlue':'red', fontSize:(oriParam.level==0)?15:14, fontFamily:'黑体', // fontWeight:'bold' }, }, // backgroundColor:green, title:{ text:(oriParam.level==0)?'全时段分析-'+typeList[typeValue]:'时段【'+oriParam.hourNum+'】'+typeList[typeValue]+'分析', //+"-"+typeList[typeValue] textStyle:{ color:(oriParam.level==0)?'SlateBlue':'Tomato', fontSize:(oriParam.level==0)?28:24, fontFamily:'楷体' //楷体 }, // borderWidth:2, // borderColor:'dark', // borderRadius:8, left:'center', top:'top', bottom:'auto' }, tooltip:{ trigger:'item', triggerOn:'mousemove|click', axisPointer:{ type:'cross', }, formatter:function(arg){ arg.value=(synList[typeValue]==1)?arg.value+'%':arg.value if (arg.componentType=='markPoint'|arg.componentType=='markLine'){ return typeList[typeValue]+'\n'+arg.name+" : "+arg.value//` ${typeValue}${arg.name}:${arg.value}` "The "+arg.name+ " Of "+typeValue+" During TimeInterval Is : "+arg.value } else { if (oriParam.level==0){ return "时段 : "+arg.name+' / '+typeList[typeValue]+" : "+arg.value//"The Value of "+typeValue+" During " + arg.name + " O'Clock Is : "+arg.value }else{ return "日期 : "+arg.name+' / '+typeList[typeValue]+" : "+arg.value } } } }, toolbox:{ feature:{ saveAsImage:{}, // restore:{}, dataView:{}, magicType:{ type:['line','bar','tiled'], }, dataZoom:{ }, } }, series:[{ type:'line', itemStyle:{ color:(oriParam.level==0)?'DodgerBlue':'red'//'DodgerBlue' 'white' }, lineStyle:{ type:(oriParam.level==0)?'dashed':'dotted' }, smooth:true, markPoint: { data: [ { type: 'max', name: 'Max_Value' }, { type: 'min', name: 'Min_Value' } ] }, markLine: { data: [ { type: 'average', name: 'Avg_Value' } ] }, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, (oriParam.level==0)?[{offset: 0,color: 'rgb(128, 255, 165)'},{offset: 1,color: 'rgb(1, 191, 236)'}]:[{offset: 0,color: 'rgb(255, 158, 68)'},{offset: 1,color: 'rgb(255, 70, 131)'}]) }, data:ydata} ] } //step05:将配置项设置给echarts实例 MyEcharts.setOption(option) MyEcharts.on('click',itemClick) //点击图表触发下钻 }
复制
4.单击图表元素触发下钻的点击函数
//单击图表元素触发下钻的点击函数 function itemClick(e){ if (parseInt(e.name).toString()!=='NaN' && oriParam.level==0){ oriParam.hourNum=e.name oriParam.level+=1 //防止进一步触发下钻 drawPlot(loadedData,oriParam.typeValue,begin_date,end_date,oriParam.level,e.name) } }
复制
5.返回上级的函数
function backToLast(){ if (oriParam.level==1){ oriParam.level=0 drawPlot(loadedData,oriParam.typeValue,begin_date,end_date,level,0) }else{ alert('提示:当前页面已是第1级视图') } }
复制
【效果展示】
B站/数据下钻/视频