“我正在参加「掘金·启航计划」”
最终效果如下;一开始准备自己使用div排版这种结构,后来发现实在太麻烦了,然后在echart官网,发现热点图,可以实现这种效果。每周使用了四个echart图,分为四个不同时间段。上面的点根据在线的时长越长,颜色越深。
vue中如何安装eachart 在这里我就不再做深层说明了,网上应该有很多博主都写过很详细的安装说明。
首先我们来看下我的数据结构,每一个data为一个周期。
{
"code": 0,
"data": {
"y": [
{
"data": [
{ "week": 1, "date": "21:05", "value": 100 }, //week 周一 在线时长 100小时
{ "week": 5, "value": 9, "date": "06:53" } //week周五 在线时长9小时
]
},
{
"data": [
{ "value": 22, "week": 7, "date": "04:16" },
{ "week": 3, "date": "17:35", "value": 92 },
{ "week": 6, "date": "14:26", "value": 11 },
{ "week": 2, "value": 68, "date": "21:20" },
{ "week": 5, "date": "01:28", "value": 83 }
]
},
{
"data": [
{ "value": 38, "date": "18:57", "week": 4 },
{ "week": 4, "value": 35, "date": "11:02" }
]
},
{ "data": [{ "week": 2, "date": "20:06", "value": 68 }] },
{
"data": [
{ "value": 74, "week": 5, "date": "11:20" },
{ "date": "01:32", "week": 5, "value": 13 },
{ "date": "19:54", "value": 39, "week": 5 },
{ "date": "17:23", "week": 4, "value": 73 },
{ "date": "14:36", "value": 75, "week": 5 }
]
},
{
"data": [
{ "value": 15, "date": "07:36", "week": 5 },
{ "value": 80, "date": "06:55", "week": 1 },
{ "value": 93, "date": "07:51", "week": 2 },
{ "week": 6, "value": 79, "date": "23:31" }
]
},
{ "data": [{ "week": 7, "value": 39, "date": "03:01" }] },
{
"data": [
{ "week": 4, "value": 50, "date": "19:49" },
{ "value": 12, "date": "07:21", "week": 2 },
{ "value": 10, "week": 2, "date": "06:38" },
{ "date": "00:50", "value": 47, "week": 7 },
{ "date": "21:39", "week": 4, "value": 12 }
]
},
{
"data": [
{ "value": 40, "week": 2, "date": "08:28" },
{ "value": 42, "date": "09:06", "week": 7 }
]
},
{
"data": [
{ "value": 96, "week": 7, "date": "05:43" },
{ "week": 1, "value": 50, "date": "15:14" },
{ "date": "22:55", "week": 1, "value": 16 },
{ "date": "11:54", "week": 4, "value": 93 }
]
},
{
"data": [
{ "date": "08:17", "week": 1, "value": 26 },
{ "date": "17:43", "week": 7, "value": 3 },
{ "week": 6, "value": 22, "date": "07:58" },
{ "value": 32, "date": "19:35", "week": 6 },
{ "week": 3, "date": "01:36", "value": 7 }
]
},
{
"data": [
{ "week": 6, "value": 19, "date": "20:51" },
{ "week": 2, "date": "09:24", "value": 79 },
{ "week": 3, "value": 66, "date": "09:05" },
{ "date": "00:37", "value": 13, "week": 6 },
{ "date": "06:43", "week": 4, "value": 33 }
]
}
]
}
}
timeEchart.vue
组件库
我这里可能封装的不太灵活,主要还是为了实现了这个效果。没有去做太多的灵活代码优化。大家如果有时间有更好的想法欢迎更新。
每一周期,需要有四个热点图,使用heatmap
组件,没有做过多的封装,主要就是把echarts.apache.org/examples/zh… 这个封装了一个公用的库,方便四个重复使用。
<div class="time-box" v-if="time && time.length > 0">
<a-row class="grid-demo" :wrap="false">
<a-col flex="60px">
<div class="time-label">00-05</div>
<div class="time-label">06-11</div>
<div class="time-label">12-17</div>
<div class="time-label">18-23</div>
</a-col>
<a-col flex="auto" style="width:0">
<a-row class="grid-demo" style="width:100%;overflow:auto;" :wrap="false">
<a-col flex="86px" v-for="(item, index) in time" :key="index">
<div class="heatmp-li">
<div class="heatmap">
<heatmap :y="['00','01','02','03','04','05']" :x="['1','2','3','4','5','6','7']" :data= "time[index].heat"/>
</div>
<div class="heatmap">
<heatmap :y="['06','07','08','09','10','11']" :x="['1','2','3','4','5','6','7']" :data= "time[index].heat"/>
</div>
<div class="heatmap">
<heatmap :y="['12','13','14','15','16','17']" :x="['1','2','3','4','5','6','7']" :data= "time[index].heat"/>
</div>
<div class="heatmap">
<heatmap :y="['18','19','20','21','22','23']" :x="['1','2','3','4','5','6','7']" :data= "time[index].heat"/>
</div>
<p>{{index + 1}}周</p>
</div>
</a-col>
</a-row>
</a-col>
</a-row>
</div>
<script lang="ts" setup>
import { ref, provide } from 'vue'
import useLoading from '@/hooks/loading'
import { onBeforeRouteLeave } from 'vue-router'
import { useAppStore } from '@/stores'
import Heatmap from '@/views/components/heatmap.vue'
import {getPianhaoTime} from '@/axios/api'
import { Notification } from '@arco-design/web-vue';
const {loading,setLoading} = useLoading(false)
const renderList = ref([])
const cacheStore = useAppStore()
const x = ref(['1周','2周','2周'])
const time = ref()
const api_getPianhaoTime = ()=>{
setLoading(true)
getPianhaoTime().then((res)=>{
if(res.data.code == 0){
time.value = res.data.data.y
time.value.forEach((e,index) => {
e.heat = []
e.data.forEach((d,i)=>{
if( Number(d.date.split(':')[0]) >= 0 && Number(d.date.split(':')[0]) <= 5){
e.heat.push([d.date.split(':')[0],String(d.week),d.value])
}else if( Number(d.date.split(':')[0]) >= 6 && Number(d.date.split(':')[0]) <= 11){
e.heat.push([d.date.split(':')[0],String(d.week),d.value])
}else if( Number(d.date.split(':')[0]) >= 12 && Number(d.date.split(':')[0]) <=17){
e.heat.push([d.date.split(':')[0],String(d.week),d.value])
}else if( Number(d.date.split(':')[0]) >= 18 && Number(d.date.split(':')[0]) <= 23){
e.heat.push([d.date.split(':')[0],String(d.week),d.value])
}
})
});
}else{
Notification.error(res.data.msg)
}
setLoading(false)
}).catch((err)=>{
setLoading(false)
Notification.error(err)
})
}
api_getPianhaoTime()
console.log(time)
</script>
heatmap.vue 组件
<template>
<Chart :option="chartOption"/>
</template>
<script setup lang="ts">
import { ref, defineProps } from 'vue';
import type { ToolTipFormatterParams } from '@/types/echarts';
import useChartOption from '@/hooks/chart-option';
const tooltipItemsHtmlString = (items: ToolTipFormatterParams[]) => {
return items
.map(
(el) => `<div class="content-panel">
<p>
<span style="background-color: ${el.color}" class="tooltip-item-icon"></span>
<span>${el.seriesName}</span>
</p>
<span class="tooltip-value">
${el.value}
</span>
</div>`
)
.join('');
};
const props = defineProps({
data:{
type:Array,
default:()=>{
return []
}
},
y:{
type:Array,
default:()=>{
return []
}
},
x:{
type:Array,
default:()=>{
return []
}
},
color:{
type:String,
default:''
},
xname:{
type:String,
default:''
},
yname:{
type:String,
default:''
},
axisLabel:{
type:String,
default:''
},
type:{
type:String,
default:'bar'
},
series:{
type:Object,
default:()=>{
return {}
}
},
grid:{
type:Object,
default:()=>{
return {
left: '0',
right: 0,
top: '0',
bottom: '0',
}
}
}
})
const xAxis = ref<string[]>([]);
const textChartsData = ref<number[]>([]);
// prettier-ignore
const data = props.data
.map(function (item) {
return [item[1], item[0], item[2] || '-'];
});
const { chartOption } = useChartOption((isDark:any) => {
return {
// tooltip: {
// position: 'top'
// },
grid:props.grid,
xAxis: {
type: 'category',
data: props.x,
axisLabel:{
show:false
},
axisLine:{
show:false
},
splitLine: {
show: true,
lineStyle:{
color:isDark ? '#232324': '#fff',
width:2
}
}
},
yAxis: {
type: 'category',
data: props.y,
inverse:true,
axisLabel:{
show:false
},
axisLine:{
show:false
},
splitLine: {
show: true,
lineStyle:{
color:isDark ? '#232324': '#fff',
width:2
}
}
},
visualMap: {
show:false,
min: 0,
max: 100,
calculable: false,
orient: 'horizontal',
inRange: {
color: ['rgb(232, 243, 255)','rgb(0, 13, 77)'],
},
left: 'center',
bottom: '15%'
},
series: [
{
name: 'Punch Card',
type: 'heatmap',
data: data,
label: {
show: false
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
});
</script>
总结
最后我把我的代码已经上传到了gitcode.net/u013491704/… 欢迎大家一起讨论