数据产品之热力表格图开发详解
原型如图
经分析,表格左侧和底部标题都为维度,文章数量及对应文章标题为数值部分,所以需要两个维度,两个数值。为了容易控制,打算用div+table的方式来渲染该图。
html部分
<div ref="chart" :style="chartStyle" style="height: auto; padding: 10px 0;font-size:1rem">
<ul class="legendbox"><li v-for="(legend,i) in legendList" :key="legend"><span :style="{background:legendColor[i%23]}"></span>{{legend}}</li></ul>
<section class="hotbox" style="max-height:100%; overflow:auto;">
<table class="hotTable" style="font-size:12px">
<thead>
<tr><th>{{legendTitle}}</th><th :colspan="hotData.hotFoot.length" align="center">{{hotData.hotTitle}}</th></tr>
</thead>
<tfoot>
<tr>
<td>{{footTitle}}</td><td v-for="item in hotData.hotFoot" :key="item">{{item}}</td>
</tr>
</tfoot>
<tbody>
<tr v-for="(legend,i) in legendList" :key="legend">
<td :style="{borderColor:legendColor[i%23]}">{{legend}}</td>
<template v-for="(col,index) in hotData.hotList[i]">
<td :key="index" :style="{borderColor:col.value == '-'?'transparent':legendColor[i%23], background:col.value == '-'? 'rgba(239,239,239,0.4)':toRGB(legendColor[i%23]).replace('rgb', 'rgba').replace(')', ',.4)'),cursor:col.value == '-'?'default':'pointer'}"
:class="(col.category == hotActive.category && col.project == hotActive.project)?'yuansu-active':''"
:data-category="col.category" :data-project="col.project"
@click="overShow(col.category,col.project)"
>
<div class="area" v-if="col.value != '-'"> {{col.value}}</div>
<div class="hot-tooltip" v-if="col.value != '-'">
<div class="hot-tooltip-close" @click="outHide($event)"></div>
<div class="hot-tooltip-conbox">
<div class="hot-tooltip-title">{{col.project}} {{col.category}}</div>
<!-- <div class="hot-tooltip-content">{{col.name}}:{{col.value}}</div> -->
</div>
<ul class="hot-tooltip-tipbox" v-if="col.tips && col.tips.data">
<li v-for="(tip,k) in col.tips.data" :key="tip.count">
<a :href="tip.url" target="_blank">{{k+1}}.{{tip.title}}</a><span>{{tip.count}}</span>
</li>
</ul>
</div>
</td>
</template>
</tr>
</tbody>
</table>
</section>
</div>
css部分
.hotTable td,.hotTable th{min-width: 220px; height:60px; padding: 2px;}
.hotTable td{border-left: 2px solid; text-align: center; position: relative;}
.hotTable tfoot td{border-left: 2px solid transparent;}
.hot-tooltip{display:none; position: absolute; bottom: 20px;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
width: 300px;
padding: 8px 10px;
line-height: 24px;
background: rgba(0,0,0,0.6);
color: #fff;
border-radius: 3px;
text-align: center;
font-style: normal;
z-index: 999;
.hot-tooltip-close{position: absolute;
right: 10px;
top: 10px;
height: 16px;
width: 16px;
cursor: pointer;}
.hot-tooltip-close::before{content: '';
display: block;
position: absolute;
height: 1px;
width: 100%;
background: #fff;
top: 50%;
transform: rotate(45deg);}
.hot-tooltip-close::after{
content: '';
display: block;
position: absolute;
height: 1px;
width: 100%;
background: #fff;
top: 50%;
transform: rotate(-45deg);
}
.hot-tooltip-conbox{display: flex; justify-content: space-around; border-bottom: 1px solid #8c8c8c;padding-bottom: 5px;
.hot-tooltip-tipbox{margin-top:10px}
.hot-tooltip-tipbox a:hover{text-decoration: underline;}
}
.yuansu-active .hot-tooltip{display: block;}
//.hotTable td .area{width: 60px;}
.legendbox{width: 100%; display: flex; justify-content: flex-end; align-items: center; flex-wrap: wrap; overflow-y: auto; max-height: 40px;}
.legendbox li{font-size: 14px; height: 50px; line-height: 50px; padding-right: 10px;}
.legendbox li span{display: inline-block; width: 26px; height: 14px; border-radius: 3px; margin-right: 3px; vertical-align: middle;}
js部分
import {defaultColor,toRGB} from '@/utils/chart/base/color'
export default {
data() {
return {
hotData:{
hotTitle:'',
hotFoot:[],
hotList:[]
},
legendList:[],
legendTitle:'',
footTitle:'',
showActive:false,
hotActive:{category:'',project:''},
legendColor:[]
}
},
mounted() {
this.renderChart(this.data)
},
methods: {
toRGB(color){
return toRGB(color)
},
renderChart(data) {
if (!this.$refs.chart) return
if (data.length == 0) {
if (!this.chart) {
return
}
}
let hotTitle = ''
let hotList = []
let hotFoot = []
let seriesObj ={}
let legendList = [] //左侧名称数组
let tipsList = [] //有提示框的时候,提示框的数据
const dimensions = this.schema.filter(schema => schema.asxAxis) //维度
const dataSchema = this.schema.filter(schema => !schema.asxAxis) //数值
this.legendTitle = (dimensions && dimensions.length>=2)?dimensions[1].detail.alias:''
this.footTitle = (dimensions && dimensions.length>=1)?dimensions[0].detail.alias:''
data.forEach((item, i) => {
legendList.push(item[dimensions[1].name])
})
legendList = Array.from(new Set(legendList))
dataSchema.forEach((schema, index) => {
seriesObj[schema.name] = {
name: schema.label || schema.detail.alias,
term: schema.name,
projectList:[]}
})
data.forEach((item,index) =>{
hotFoot.push(item[dimensions[0].name])
hotList.push([])
dataSchema.forEach((schema, i) => {
legendList.forEach((lege, m) => {
var itlt = {
name: schema.detail.alias,
project:lege,
category: item[dimensions[0].name],
value: item[schema.name]
}
if(item[dimensions[1].name] == lege){
seriesObj[schema.name].projectList.push(itlt)
}
})
})
})
let newHotList = Object.values(seriesObj);
let newLegendList = []
hotFoot = Array.from(new Set(hotFoot))
let selectList = []
if(this.dispose.barQuota == ''){
hotTitle = newHotList[0].name;
selectList = newHotList[0].projectList
}else{
newHotList.forEach((hot,i) => {
if (this.dispose.barQuota !='' && hot.term == this.dispose.barQuota){
hotTitle = hot.name;
selectList = hot.projectList
}
})
}
if(this.dispose.chooseIndex){
newHotList.forEach((hot,i) => {
if (hot.term == this.dispose.chooseIndex){
tipsList = hot.projectList
}
})
}
legendList.forEach((lege, m) => {
let newArr=[]
selectList.forEach((item,n) => {
if(lege === item.project){
if(this.dispose.chooseIndex){
item['tips'] = JSON.parse(tipsList[n].value)
}
// console.log(item,'item')
newArr.push(item)
}
})
newLegendList.push(newArr) //首页将数据组装到newLegendList二维数组
})
let dataArr=[]
let hotFoot1 = JSON.parse(JSON.stringify(hotFoot))
newLegendList.forEach((item)=>{
dataArr.push(hotFoot1)
})
let hotListArray = JSON.parse(JSON.stringify(dataArr))
for(let i = 0; i< newLegendList.length;i++){ //使用两个for循环遍历矩阵
for(let j = 0; j < hotFoot.length; j++){
let flag = newLegendList[i].find((item) => {return item.category == hotFoot[j]})
if(flag){
hotListArray[i][j] = {
name:flag.name,
category: flag.category,
project:flag.project,
value:flag.value,
tips:flag.tips? flag.tips:null
}
}
else{
hotListArray[i][j] = {
name:newLegendList[i][0].name,
category: hotFoot[j],
project:legendList[i],
value:'-'}
}
}
}
this.hotData.hotTitle = hotTitle
this.hotData.hotFoot = hotFoot
this.hotData.hotList = hotListArray
this.legendList =legendList
this.legendColor = defaultColor
},
overShow(a,b){
this.hotActive.category = a
this.hotActive.project = b
},
outHide(e){
let that = this;
that.hotActive.category = '',
that.hotActive.project = '',
e.stopPropagation()
},
}
}