学习效率比较低,花了很长时间才弄明白如何使用echarts,因此还是记录一下,免得以后再后头来看时又不记得怎么改了。
设想
方案就是做一个如下的图表,默认提供的查询是一周内的统计,直接在时间线上点选即可;至于超出一周的将提供一个自定义日期查询
1.x轴显示问题
echarts提供了一个旋转的方式来显示x轴文字,但是显然不适合我的要求(难道要歪着脖子看x轴?度娘的东西果然不可靠
很快找到了另一篇文章:《echarts x轴文字显示不全》,很容易就改好了,但是马上就发现有问题:即使改成了竖排,x轴上的产品名称比预想的还长,导致显示不完全,直接溢出了。不过该文章提供了双行显示的方法,效果如下:
不过……既然已经是竖排显示了,为什么不干脆改得完整一点呢?每一行还是从左往右啊,不能忍……我想要的是这样的效果
那么,只能自己写代码实现了,反正已经知道了可以自定义显示效果(添加一个option.xAxis.axisLabel.formatter
即可),直接po代码:
function(val) { //val就是要显示在x轴上的字符串了
var rows = 2 // 这个参数表示x轴的品名需要显示为几行
if (val.length % rows) { //字数不够,空格来凑,否则错乱
val += ' '.repeat(rows - val.length % rows) // 必须是全角字的空格,半角字的会被忽略掉,导致无效
}
var count = val.length / rows
var formattedText = []
for (var i = 0; i < count; i++) {
formattedText[i] = ''
for (var j = 0; j < rows; j++) {
formattedText[i] += val[j * count + i]
}
}
return formattedText.join('\n')
}
option
与formatter
的层级关系如下(因为timeline组件的配置必须(?)写在baseOption
里面,所以多了一层baseOption
):
但是!!
我忍不住再想,固定死了显示为2行真的好吗?一定要让客户自己选择,于是把rows与data
里的变量绑定:
data() {
return {
//其他变量
xAxisRows: 2,
//其他变量
option: {
baseOption: {
xAxis: {
formatter: function(val) {
var rows = this.xAxisRows // 绑定参数
//下面的代码是一样的
}
}
}
}
}
}
然后在页面里添加一个下拉框改变rows的数值(这里使用了element-UI的样式):
<el-select v-model="xAxisRows" style="width:60px;">
<el-option value="1"></el-option>
<el-option value="2"></el-option>
<el-option value="3"></el-option>
<el-option value="4"></el-option>
</el-select>
2.vue中data中的变量互相调用
运行……报错:xAxisRows未定义,很明显,是data中的变量发生了互相调用的问题。
参考《vue中data数据之间的调用》做了修改,添加了mounted
mounted(){
this.option.baseOption.xAxis.axisLabel.formatter = function(val) {
var rows = this.xAxisRows // 绑定参数
//下面的代码是一样的
}
}
运行……还是报错,错误仍然是xAxisRows未定义
。嗯……别人的情况跟我的还是略有不同的,所以不能照搬代码。
我也不知道为什么会报错,不过也懒得去想,总之把formatter
的动态绑定改到method
里总不会报错了吧。handleChangexAxis绑定到一个按钮上去,点击才会生效
method:{
//修改x轴文字的显示行数
handleChangexAxis() {
this.option.baseOption.xAxis.axisLabel.formatter = (val) => {
var len = this.xAxisRows // 这个参数表示x轴的品名需要显示为几行,只能在这里修改
if (val.length % len) {
val += ' '.repeat(len - val.length % len)
}
var count = val.length / len
var formattedText = []
for (var i = 0; i < count; i++) {
formattedText[i] = ''
for (var j = 0; j < len; j++) {
formattedText[i] += val[j * count + i]
}
}
return formattedText.join('\n')
}
this.chart.setOption(this.option)
}
}
页面里写个按钮,点击就调用handleChangexAxis方法去修改formatter:
<el-select v-model="xAxisRows" style="width:60px;">
<el-option value="1"></el-option>
<el-option value="2"></el-option>
<el-option value="3"></el-option>
<el-option value="4"></el-option>
</el-select>
<el-button type="primary" @click="handleChangexAxis">修改X轴文字行数</el-button>
OK,这次没问题了。
3.时间线与异步加载的数据
配置
在官方提供的示例中,所有的数据都是在已完全加载完毕的情况下才使用setOption
加载图表的,实际应用时肯定不能这样,必须异步加载——用户点击查询了某个日期我才加载相应的数据,否则不加载。
为了做到这一点,我先给初始数据置空(不记得不初始化会不会有问题了,貌似会不显示图表?)。这里主要是时间线麻烦一点,其他的代码就省略了。
option: {
baseOption: {
timeline: {
axisType: 'category',
top: 50,
lineStyle: {
color: ['#409EFF']
},
autoPlay: false, // 不允许自动播放
currentIndex: 5,
playInterval: 1000,
controlStyle: { showPlayBtn: false },
data: [],
label: {
formatter: function(s) {
return s.substr(5, 6).replace('月', '-')
}
}
},
xAxis: {
type: 'category',
axisLabel: {
interval: 0,
formatter: function(val) {
var len = 2 // vue渲染页面时还是需要一个方法来显示x轴的,所以这里还是保留了一个formatter
// method中的那个formatter才是用了动态修改x轴显示的
//此处省略重复代码
}
}
},
series: [ // 必须加入此项,否则会报错。虽然我的series只有一个元素
{ name: '单品出货量', type: 'bar' }
]
// baseOption中的其他配置项
},
// options置空,需要加载数据时再从数据库拿,然后填充options中的各个元素
options: [{}, {}, {}, {}, {}, {}, {}]
注意以下几点(正确性就懒得验证了,反正按照这个办法去做不会错):
- timeline的配置项放在baseOption中
- 因为有了baseOption,异步加载的数据就只能放在options中了
- 注意option和options不是同一个变量。
option:{baseOption:{...}, options:[...] }
加载数据
不说废话,直接贴method
里对应的代码
// 处理查询的数据,data是从服务器拿到的数据,
// index表示options: [{}, {}, {}, {}, {}, {}, {}]中的下标,因为默认是查询一周,所以有7个空对象
proccessData(data, title, index) {
var sum = 0
var dataArr = new Array(this.option.baseOption.xAxis.data.length)
for (var i = 0; i < data.length; i++) {
if (data[i].grassCount) {
sum += data[i].grassCount
// 插入数据时,数据的位置与x轴的产品名位置对应
dataArr[this.option.baseOption.xAxis.data.indexOf(data[i].grassName)] = data[i].grassCount
} else {
dataArr[this.option.baseOption.xAxis.data.indexOf(data[i].grassName)] = 0
}
}
this.option.options[index] = {
title: { text: title, subtext: '总出货量' + sum },
series: [{ data: dataArr }]
}
//修改时间线上的小红点的位置
this.option.baseOption.timeline.currentIndex = index
//应用所做的更改
this.chart.setOption(this.option)
}
绑定时间线的点击事件,
this.chart.setOption(this.option) // 将配置应用于图表
this.chart.on('timelinechanged', this.onChange) // 绑定时间线点击事件
事件方法,params
参数是由echarts传递过来的,默认有几个属性,但是我只用到了它的currentIndex属性
。
// 时间线选择发生变化时
onChange(params) {
this.option.baseOption.timeline.currentIndex = params.currentIndex
var len = Object.keys(this.option.options[params.currentIndex]).length
if (len === 0) { // 该日期数据为空,则获取
getStaticsByDate({ date: this.option.baseOption.timeline.data[params.currentIndex] }).then(res => {
var title = this.option.baseOption.timeline.data[params.currentIndex]
title = title.replace(/[年月]/g, '-') + '单日出货量'
this.proccessData(res.data, title, params.currentIndex)
})
}
}
值得注意的是:
为了减少向数据库的请求,在加载选中日期的数据前,会先判断options
对应下标里面有无数据,没有(即options[index]是{ }
)才请求数据并加载。
这里判断数据是否为空用了Object.keys
,当然用JSON.stringify
也是可以的。
Object.keys
是es6中的写法,可以用来检测对象是否为空,在vue中一定要像上面那样拆开来写,写在一起,如:
if (Object.keys(this.option.options[4]).length===0 ) {
// code
}
这样是会出错的,vue会自动把Object.keys
改成_Object$keys
,运行时会被识别为undefined
,然后每一次都会执行到if
括号里面的代码了(即使上次已经加载过这个数据了),造成重复的数据请求。所以这里分开写,没有出错。
JSON.stringify
也不能连用,必须拆开:
// 错误写法 实际运行时会变成 if(undefined === '{}'),这样就永远都不会加载数据了
if(JSON.stringify(this.option.options[params.currentIndex]) === '{}') {
// 加载数据
}
// 正确写法
var str = JSON.stringify(this.option.options[params.currentIndex])
if (str === '{}') {
// 加载数据
}
- 初始化选中一个日期。
用户进入这个页面时,还是应该默认加载一个日期的数据并显示出来,否则只能看到一个空的图表。
既然已经把时间线的点击事件写完了,那么手动触发事件就行了。
一般情况下是echarts产生onChange
里的params参数
的,但是这里是我手动触发这个事件,所以就自己创建一个带有currentIndex属性
的对象传进去
mounted() { // 不能用created,会报错的
this.onChange({ currentIndex: 0 }) //加载options下标为0处的数据
}
4.其他配置
为了减少不必要的数据请求,这里已经将数据请求设置为点击才加载,那么时间线的自动播放事件也要给它停了,配置涉及到autoPlay
,controlStyle
,lineStyle
这些属性,修改柱形图的颜色则直接在option.baseOption.color
中添加即可。
更多信息建议直接阅读echarts官方文档。