最近寫了一個echarts堆疊柱狀圖頁面,因為經驗不是很多,寫個博客記錄一下
先貼代碼
<template>
<div class="container-box">
<div class="item-box">
<div class="box" ref="clintBox">
<div :id="chartsCode" :style="`width: ${width}px; height:400px;`"></div>
<div class="tablebox">
<table border="1px solid #000" class="mytable">
<tr v-for="(item,index) in tableData" :key="index">
<td width="100px" class="header" v-if="index === 0">
<i class="poinit" style="background-color: #ffbb00"></i>
{{'>' + '3年'}}
</td>
<td width="100px" class="header" v-if="index == 1">
<i class="poinit" style="background-color: #a5a5a5"></i>
{{'2-3年'}}
</td>
<td width="100px" class="header" v-if="index == 2">
<i class="poinit" style="background-color: #ed7d31"></i>
{{'1-2年'}}
</td>
<td width="100px" class="header" v-if="index == 3">
<i class="poinit" style="background-color: #5b9bd5"></i>
{{'0-1年'}}
</td>
<td v-for="(t) in item" :key="t" :width="filterWidth" class="mytd">{{t}}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'chartsCard',
components: {
},
data () {
return {
option: [],
roadNameList: [],
tableData: [],
width: 0,
}
},
props: {
data: {
required: true,
},
code: {
required: true,
}
},
computed: {
// 過濾路線名稱
filterRoadName () {
let arr = []
this.data.forEach(item => {
arr.push(item.roadCode)
})
this.roadNameList = arr
return this.roadNameList
},
// 根據傳過來的數據長度動態計算表格的寬的
filterWidth () {
return (this.width - 300) / this.filterRoadName.length
},
// 獲取圖表容器的寬度
chartsWith () {
return this.$refs.clintBox.clientWidth
},
// 動態圖表id
chartsCode () {
return 'chart' + this.code
}
},
watch: {
data: function () {
this.init()
}
},
created () {
},
mounted () {
this.$nextTick(() => {
this.width = this.chartsWith
setTimeout(() => {
this.init()
}, 200)
})
},
methods: {
init () {
this.initChart()
this.initTableData()
},
// 初始化echatrs
initChart () {
this.option = {
tooltip: {
trigger: "axis",
formatter: '{b0}<br />{a0} : {c0}%<br/>{a1} : {c1}%<br/>{a2} : {c2}%<br/>{a3} : {c3}%',
},
grid: {
// width: this.chartsWith * 0.8 + 'px',
left: '150px',
right: '150px',
bottom: '25px',
},
xAxis: {
show: true,
data: this.filterRoadName,
splitLine: {
show: false,
},
axisTick: {
show:true,
length: 25, // 豎線的長度
// interval:0
}
},
yAxis: {
max: 100,// 設置最大值是多少
splitNumber: 10,// 設置分幾段顯示
splitLine: {
show: true,
},
axisTick: {
show: false
},
axisLabel: {
show: true,
interval: 'auto',
formatter: '{value} %',
},
},
series: [{
name: '0-1年',
type: 'bar',
stack: '使用情況',//相同的stack開啟堆疊
// data: [60, 20, 36, 10, 10, 20],
data: this.initData('year01Length'),
barWidth: 50,//柱子寬度
barGap: '0%',/*多個並排柱子設置柱子之間的間距*/
barCategoryGap: '0%',/*多個並排柱子設置柱子之間的間距*/
itemStyle: {
normal: { color: "#009587" },
}
}, {
name: '1-2年',
type: 'bar',
stack: '使用情況',//相同的stack開啟堆疊
data: this.initData('year12Length'),
barWidth: 50,//柱子寬度
itemStyle: {
normal: { color: "#2196f3" },
}
}, {
name: '2-3年',
type: 'bar',
stack: '使用情況',//相同的stack開啟堆疊
data: this.initData('year23Length'),
barWidth: 50,//柱子寬度
itemStyle: {
normal: { color: "#ffeb3b" },
}
}, {
name: '3-4年',
type: 'bar',
stack: '使用情況',//相同的stack開啟堆疊
data: this.initData('year34Length'),
barWidth: 50,//柱子寬度
itemStyle: {
normal: { color: "#9c27b0" },
}
}]
}
// 基於准備好的dom,初始化echarts實例
let myChart = this.$echarts.init(document.getElementById(this.chartsCode))
// 使用剛指定的配置項和數據顯示圖表。
myChart.setOption(this.option, true)
// myChart.resize()
},
// 初始化柱狀圖數據 計算每一個val在數據中和totalLength的百分比
initData (val) {
var serie = []
this.data.forEach((item, index) => {
let num = item[val]
let total = 0
let arr = [item.year01Length, item.year12Length, item.year23Length, item.year34Length]
let arr1 = arr.filter(item => {
if (item !== 'null') {
return item
}
})
arr1.forEach(item => {
total += parseFloat(item)
})
// // 計算占比
var numcount = this.Percentage(num, parseFloat(total.toFixed(3)))
serie.push(numcount)
})
return serie
},
//計算兩者占比方法
Percentage (num, total) {
return (Math.round(num / total * 10000) / 100.00)// 小數點后兩位百分比
},
// 初始化表格數據
initTableData () {
let tableArr = []
console.log(this.data)
for (let i = 0; i <= 3; i++) {
let arr = []
let str = ('year' + i) + (i + 1 + 'Length')
this.data.forEach((item, index) => {
arr.push(item[str])
})
tableArr.unshift(arr)
}
this.tableData = tableArr
},
},
}
</script>
<style scoped>
.el-carousel__item h3 {
color: #475669;
font-size: 18px;
opacity: 0.75;
line-height: 400px;
margin: 0;
}
.el-carousel__item:nth-child(2n) {
background-color: #f5f7fa;
}
.el-carousel__item:nth-child(2n + 1) {
background-color: #f5f7fa;
}
.item-box {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: start;
}
.box {
width: 100%;
position: relative;
}
.tablebox {
width: 100%;
height: 300px;
}
.mytable {
border-collapse: collapse;
margin-left: 50px;
}
.mytable td {
padding: 5px 10px;
text-align: center;
}
.mytable .header {
text-align: left;
}
.mytd {
height: 25px !important;
}
.poinit {
display: inline-block;
width: 10px;
height: 10px;
vertical-align: middle;
margin-right: 5px;
}
</style>
在echarts里option中的series的數組數據中我們可以通過stack屬性控制是否堆疊,當我們的兩個數據需要堆疊顯示時,我們只需要給他兩個的stack設置相同名稱就可以了!
實現等高的堆疊圖,需要對數據進行處理
- 參考 https://blog.csdn.net/qq_16416993/article/details/108118443
this.initData('year12Length') 返回一個數組 里面的每個元素是數據里每個對象的year12Length的值相對於>3年以下總和的百分比
修改 tooltip 顯示百分比
echarts提示框顯示百分比 數據已經轉換好了 只需在后面加%號
tooltip: {
trigger: "axis",
formatter: '{b0}<br />{a0} : {c0}%<br/>{a1} : {c1}%<br/>{a2} : {c2}%<br/>{a3} : {c3}%',
},
實現底部表格參考
因為實現起來難度較大 所以采用的是elementui的table組件 通過給定option配置項grid:{left:'100px',right:'100px'}此時圖表距離canvas畫布左右均有100px的空間
canvas的寬度是100% 所以我們等通過this.$refs.fatherBox.clientWidth的方式獲取到canvas父盒子的寬度 然后減去左右空白距離(200)除以柱子個數計算屬性動態的計算出x軸分隔的寬度同時指定給表格的寬度結合定位調整表格的位置,大體上能實現這種效果
因為element-ui的表格不好實現 所以手寫的table表格 表格頂部與圖表對齊實現方式:圖表配置項里 grid:{left:'100px',right:'100px',bottom:'25px'}指定的是圖表相對於canvas的上下左右的距離 底部25px為x軸豎線的長度 表格的父容器和圖表的父容器寬度都是100% 給表格和圖表相同的左邊距即可
td的寬度通過計算屬性拿到圖表父容器的寬度 然后(父寬度 - 圖表的左右邊距)/ 柱狀圖的個數
碰到的小坑
子組件渲染echarts時父組件還沒有獲取到數據就渲染了子組件 這是傳過來的是空值 子組件也就無法顯示
解決方法
:v-if="tableData.length !== 0" 判斷一下
watch里面調用methods方法不能使用箭頭函數 否則會報錯