本文为个人近期学习总结,若有错误之处,欢迎指出!
Echarts之柱图绘制
- 前言
- 需求
- 实现效果
- 大概思路
- 具体实现
- 实现思路
- 具体代码
- 1.父组件写法
- 2.子组件写法
- 附加
- 1.同坐标系下,并排柱图绘制
- 2.柱图下钻功能实现
前言
在前文页面布局、DataV 的使用、Echarts 的基础使用的基础上,开始绘制大屏中的基础柱图。
需求
柱图展示四类蔬菜的销售量,且销售量数据每隔十分钟自动刷新。
实现效果
大概思路
1.将这一模块作为一个组件抽取出来,简化代码量;
2.边框使用 DataV 里的 dv-border-Box-13 元素实现;
3.柱图用 Echarts 绘制(因为柱图的数据可能是实时刷新的,保险起见,选择 echarts)
具体实现
实现思路
1.子组件中写边框和柱图代码,供父组件调用;
2.父组件中引用子组件,同时向子组件传递柱图的标题和横纵坐标(这里是为了增加子组件的可复用性);
3.子组件的行为:
(1)子组件接收柱图标题,放在标题位置
(2)子组件接收横纵坐标
(3)由于横坐标不变,所以子组件监测接收数据中纵坐标值的变化,绘制柱图
具体代码
1.父组件写法
<template>
<bar-chart :bar-title="barTitle" :x-data="barX" :y-data="barY" />
</template>
<script>
import BarChart from '../components/BarChart.vue'
export default {
components:{
BarChart
},
data(){
return {
//向子组件传递三个数据
barTitle: '销售量',
barX: [],
barY: [],
}
},
mounted () {
this.InitBar()
setTimeout(() => { // 模拟数据实时刷新调用后端接口
this.barY = [1514, 1729, 1337, 1810]
}, 10000)
},
methods:{
InitBar () {
setTimeout(() => { // 模拟调用后端接口,进行初始化
this.barY = [514, 729, 337, 810]
this.barX = [
'萝卜',
'玉米',
'白菜',
'芹菜'
]
}, 3000)
}
}
}
</script>
2.子组件写法
代码较长,拆为三块:
(1)html
<template>
<div class="block" style="height:100%;">
<dv-border-box-13>
<div class="title" style="height:20%;">
{{ barTitle }}
</div>
<div ref="barArea" :style="{width: '100%',height: '80%'}" />
</dv-border-box-13>
</div>
</template>
注意: 这里样式高度采用百分比(非固定像素 px),是为了使子组件自适应父组件的高度。
(2)JavaScript
<script>
export default {
name: 'bar-chart',
//子组件用 props 接收父组件传递过来的三个数据
props: {
barTitle: {
type: String,
default: ''
},
xData: {
type: Array,
default: () => []
},
yData: {
type: Array,
default: () => []
}
},
// eslint-disable-next-line max-lines-per-function
data () {
return {
option: {
color: ['#2f89cf'], // 柱子的颜色
tooltip: { // 悬浮提示框
trigger: 'axis', // 坐标轴触发
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
},
backgroundColor: 'rgba(83,164,230,.6)',
borderColor: 'rgba(83,164,230,1)', // 边框颜色
textStyle: {// 设置文字样式
color: 'rgba(255,255,255,1)',
fontSize: 10
}
},
grid: { // 图表大小,直角坐标系绘图网格
left: '13%',
bottom: '14%',
top: '13%',
containLabel: false // grid 区域是否包含刻度标签
},
xAxis: [// x 轴
{
name: '种类', // 坐标轴名称
type: 'category',
boundaryGap: true, // 坐标轴两边留白(默认就是 true)
data: this.xData,
axisTick: { // 坐标轴刻度相关设置
alignWithLabel: true // 刻度线和标签对齐
},
axisLabel: { // 坐标轴刻度标签
color: 'rgba(255,255,255,.6)',
fontSize: '12'
},
axisLine: { // 坐标轴轴线
show: true // 不显示
}
}
],
yAxis: [// y 轴
{
name: '单位:斤', // 坐标轴名称
type: 'value', // 坐标轴类型,这里是 数值轴,适用于连续数据
nameTextStyle: { // 坐标轴名称文字样式
color: 'rgba(255,255,255,.6)',
fontSize: 12,
padding: [10, 50, 7, 10] // name 文字位置 对应 上右下左
},
axisLabel: {
color: 'rgba(255,255,255,.6)',
fontSize: '12'
},
axisLine: {
show: true,
lineStyle: {
color: 'rgba(255,255,255,.6)',
type: 'solid'
}
},
axisTick: {
show: true
},
splitLine: { // 坐标轴在 grid 区域中的分隔线(默认数值轴显示,类目轴不显示)
show: false
}
}
],
// 系列(只有一个系列,所以数组内只有一个对象元素)
series: [
{
name: '售出', // 系列名称,用于 tooltip 的显示
type: 'bar',
barWidth: '20%', // 柱子的宽度
tooltip: { // 提示框浮层内容格式器
valueFormatter (value) { // tooltip 中数值显示部分的格式化回调函数
return value + ' 个'
}
},
data: this.yData, // 系列中的数据内容数组。
itemStyle: { // 图形样式
borderRadius: 5 // 圆角
},
label: { // 柱子上方的文字
color: 'rgba(255,255,255,.6)',
fontSize: '12',
show: true,
position: 'top'
}
}
]
}
}
},
// 监测 yData 属性的变化
watch: {
yData: {
handler (newVal, oldVal) {
console.log('yChange', newVal, oldVal, this.xData)
// 将变化后的新值赋给柱图的纵坐标,再重新绘制图表
this.option.series[0].data = newVal
this.option.xAxis[0].data = this.xData
this.drawBar()
}
}
},
mounted () {
this.drawBar()
},
methods: {
drawBar () {
// 检测是否已经存在 echarts 实例
let myBar = this.$echarts.getInstanceByDom(this.$refs.barArea)
// 如果为空 则正常进行渲染 反之 不再进行初始化
if (myBar === undefined) {
myBar = this.$echarts.init(this.$refs.barArea)
}
myBar.setOption(this.option, true)
window.addEventListener('resize', () => {
myBar.resize()
})
}
}
}
</script>
注意:
这里 watch 监测 ydata 的变化,其中 ydata 至少变化了两次(这里只有俩次):
①由[]变为初始化后的数据[514, 729, 337, 810];
②再变为实时刷新后的模拟数据[1514, 1729, 1337, 1810]
因此,调用了两次 handler 函数。
(3)css
<style lang="less" scoped>
.block {
background-color: rgba(10, 30, 56);
.title {
display: flex;
align-items: center;
margin-left:15px;
color: rgb(250, 212, 0);
font-size: 13px;
}
}
</style>
附加
1.同坐标系下,并排柱图绘制
比如:实现以下柱图:
option的关键点:
(1)两个柱子的颜色
color:['rgb(84,112,198)', 'rgb(115,192,222)']
(2)图例
legend:{
data:['支出','收入'],
//其它图例属性配置
}
注意:若series系列的name属性已经有值,则这里的data可不配置。
(3)共用一个xAxis[0].data;
xAxis:[{
data:['胡萝卜', '蒜台', '土豆', '豆角']
}]
因此父组件给子组件传值时,传递的数据格式为:[‘胡萝卜’, ‘蒜台’, ‘土豆’, ‘豆角’]
(4)series里有两个系列,所以series数组里有两个对象。
series:[{
data:[20,49,70,232],
//其它系列属性配置
},
{
data:[26,59,90,244],
barGap:0.5,//柱子之间的间距
//其它系列属性配置
}]
因此父组件给子组件传值时,传递的数据格式为:[[20,49,70,232],[26,59,90,244]]
注意:两个柱子之间的间隔属性,写在最后一个柱子系列对象里,才会起作用。
2.柱图下钻功能实现
需求:点击某个菜品,查看五个市的某菜品售出详情。(以白菜为例)
实现:
(1)在柱图组件,画图函数的地方,加上事件处理函数
myBar.on('click', params => {
this.$emit('BarClick', params, 'hello')
})
点击柱子,就会触发BarClick事件,并向父组件传递2个参数,一个是params,一个是字符串hello。
(2)父组件使用柱图组件
html
<!-- 父组件第一次调用子组件 -->
<bar-chart :bar-title="barTitle" :x-data="barX" :y-data="barY" @BarClick="LookDetail" />
<!-- 柱图下钻后的弹框 -->
<el-dialog
:visible.sync="dialogVisible"
width="50%"
>
<div style="height:100%">
<div style="height:200px">
<!-- 父组件第二次调用子组件 -->
<bar-chart :bar-title="cityChild" :x-data="cityX" :y-data="cityY" />
</div>
</div>
</el-dialog>
js
<script>
export default{
data(){
return {
dialogVisible: false,
cityChild: '',
cityX: [],
cityY: []
}
},
methods:{
LookDetail (arg1, arg2) {
//arg1, arg2就是从子组件传递过来的数据
console.log('args', arg1, arg2)
this.dialogVisible = true
// 数据暂时为模拟假数据
// 这块可将arg1.name作为参数去调用接口拿后端数据
this.cityChild = arg1.name
this.cityX = [
'西安',
'咸阳',
'安康',
'铜川',
'榆林'
],
this.cityY = [35, 30, 63, 17, 28]
}
}
}
</script>
传递的两个参数,打开控制台可看到内容如下: