当前位置: 首页 > news >正文

在vue3后台项目中使用热力图,并给热力图增加点击选中事件

需要实现的效果如上图,主要几个要求点:
1.要求热力图的开始和结束自定义
2.要求热力图的最大值根据接口返回的数据值定义
3.要求初始选中当前日期前一天的日期,同时tooltip 会自动显示昨天的提交次数
4.要求点中那个选中那个日期,同时展示当日的提交次数
 

使用的组件是echart中的日历图:Examples - Apache ECharts

具体代码:

<template><v-chartref="chartRef":option="chartOption"autoresizetheme="macarons"@click="handleClick"></v-chart>
</template><script setup>
import * as echarts from 'echarts'
import { reactive, watch, ref, nextTick } from 'vue'const props = defineProps({calenderData: {type: Array,required: true}
})const chartRef = ref(null)const chartOption = reactive({tooltip: {formatter: function (p) {return `日期:${p.data[0]}<br/>提交次数:${p.data[1]}`}},visualMap: {min: 0,max: 100, // ✅ 会被动态覆盖type: 'piecewise',orient: 'horizontal',left: 'center',top: 20,text: ['多', '少'],inRange: {color: ['#d4dcf7','#5070dd']}},calendar: {top: 80,left: 30,right: 30,cellSize: ['auto', 13],range: [],itemStyle: { borderWidth: 0.5 },yearLabel: { show: true, color: '#333' },dayLabel: {firstDay: 1,nameMap: ['日', '一', '二', '三', '四', '五', '六']},monthLabel: {nameMap: ['一月','二月','三月','四月','五月','六月','七月','八月','九月','十月','十一月','十二月']}},series: [{type: 'heatmap',coordinateSystem: 'calendar',data: []},{type: 'scatter',coordinateSystem: 'calendar',symbolSize: 18,itemStyle: {color: 'red',borderColor: '#fff',borderWidth: 2},data: []}]
})function handleClick(params) {if (params.componentType === 'series' && params.seriesType === 'heatmap') {const date = params.data[0]chartOption.series[1].data = [[date]]chartRef.value?.dispatchAction({type: 'showTip',seriesIndex: 0,dataIndex: params.dataIndex})}
}watch(() => props.calenderData,async (newVal) => {if (!newVal || newVal.length === 0) returnconst start = newVal[0].dateconst end = newVal[newVal.length - 1].datechartOption.calendar.range = [start, end]chartOption.series[0].data = newVal.map(item => [item.date, item.count])// ✅ 动态设置 visualMap.maxchartOption.visualMap.max = Math.max(...newVal.map(item => item.count)) || 0// ✅ 默认选中昨天const yesterday = new Date()yesterday.setDate(yesterday.getDate() - 1)const yesterdayStr = yesterday.toISOString().slice(0, 10)const index = newVal.findIndex(item => item.date === yesterdayStr)if (index !== -1) {chartOption.series[1].data = [[yesterdayStr]]await nextTick()setTimeout(() => {chartRef.value?.dispatchAction({type: 'showTip',seriesIndex: 0,dataIndex: index})}, 200)}},{ immediate: true, deep: true }
)
</script>

这里 我的这个组件是需要引入父组件中  所以数据calenderData是由父组件传进来的

基本数据格式是:

 { date: '2025-08-18', count: 5 },{ date: '2025-08-19', count: 12 },{ date: '2025-08-20', count: 3 },{ date: '2025-08-21', count: 20 },

我这个使用的是一年的数据量比较大,就不展示出来了
核心步骤:

  • 日历范围设置

    chartOption.calendar.range = [start, end]

    根据接口返回的第一条数据和最后一条数据,动态生成热力图的范围。

  • 热力图数据赋值

    chartOption.series[0].data = newVal.map(item => [item.date, item.count])

    { date, count } 转换成 ECharts 所需的二维数组 [日期, 数值]

  • 动态最大值设置

    chartOption.visualMap.max = Math.max(...newVal.map(item => item.count)) || 0

    保证颜色深浅映射合理。

  • 默认选中昨天

    chartOption.series[1].data = [[yesterdayStr]]

    使用一个额外的 scatter 系列 高亮昨天,并通过 dispatchAction 主动触发 tooltip。

  • 点击选中功能
    handleClick 中获取点击日期,更新 scatter 高亮点,并用 dispatchAction 展示 tooltip。

其实实现这些功能的主要就是热历图中的配置项。

以下是对这段代码的详细注释,有需要的可以查看

<template> <!-- 模板区域:渲染一个基于 ECharts 的图表组件 --><v-chartref="chartRef"                <!-- 通过 ref 获取图表实例,用于后续调用 dispatchAction 等 API -->:option="chartOption"         <!-- 绑定 ECharts 配置项(响应式对象) -->autoresize                    <!-- 容器大小变化时自动触发图表自适应 -->theme="macarons"              <!-- 使用 ECharts 的 macarons 主题(需已注册/引入) -->@click="handleClick"          <!-- 监听图表上的点击事件(ECharts 事件) -->></v-chart>
</template><script setup>
import * as echarts from 'echarts'                 // 引入 ECharts 主库(本例主要通过 <v-chart> 使用,但类型/工具函数可能会用到)
import { reactive, watch, ref, nextTick } from 'vue' // 从 Vue 引入响应式工具与生命周期工具const props = defineProps({                          // 声明组件接收的 props(<script setup> 语法)calenderData: {                                    // 外部传入的日历数据数组type: Array,                                     // 类型为数组:[{ date: 'YYYY-MM-DD', count: number }, ...]required: true                                   // 必须传入,否则组件无法正常渲染}
})const chartRef = ref(null)                           // 定义对 <v-chart> 组件的引用,用于获得图表实例(chartRef.value)const chartOption = reactive({                       // 定义 ECharts 配置,并用 reactive 包裹使其响应式tooltip: {                                         // 提示框配置:悬浮/显示数据详情formatter: function (p) {                        // 自定义 formatter,p 为数据点参数return `日期:${p.data[0]}<br/>提交次数:${p.data[1]}` // 将 [日期, 数值] 格式化为多行 HTML}},visualMap: {                                       // 视觉映射:用颜色深浅映射数值大小min: 0,                                          // 颜色映射的最小值(固定为 0)max: 100, // ✅ 会被动态覆盖                     // 初始最大值先给个默认值,后续根据数据动态覆盖type: 'piecewise',                               // 分段型 visualMap(更清晰的档位显示)orient: 'horizontal',                            // 水平布局left: 'center',                                  // 居中放置top: 20,                                         // 距离容器顶部 20pxtext: ['多', '少'],                              // 文案:右边代表较多,左边代表较少inRange: {                                       // 数值处于 [min, max] 范围内的样式color: ['#d4dcf7','#5070dd']                   // 由浅到深的配色(可根据品牌色调整)}},calendar: {                                        // 日历坐标系配置top: 80,                                         // 日历距离容器顶部 80px(给 visualMap 腾位置)left: 30,                                        // 左边距right: 30,                                       // 右边距cellSize: ['auto', 13],                          // 单元格尺寸:宽自适应,高 13px(可按需求调)range: [],                                       // 显示的日期范围(将由数据动态设置为 [start, end])itemStyle: { borderWidth: 0.5 },                 // 每个日期单元格的边框样式yearLabel: { show: true, color: '#333' },        // 年份标签样式dayLabel: {                                      // 星期标签配置firstDay: 1,                                   // 一周起始日:1 代表周一nameMap: ['日', '一', '二', '三', '四', '五', '六'] // 自定义星期名称},monthLabel: {                                    // 月份标签配置nameMap: [                                     // 自定义月份中文名称'一月','二月','三月','四月','五月','六月','七月','八月','九月','十月','十一月','十二月']}},series: [                                          // 系列(图形)配置:这里使用两个系列叠加{type: 'heatmap',                               // 第一层:热力图(根据 count 显示颜色深浅)coordinateSystem: 'calendar',                  // 使用日历坐标系data: []                                       // 数据格式为 [[date, count], ...](将由 watch 动态赋值)},{type: 'scatter',                               // 第二层:散点图(用于高亮选中的某一天)coordinateSystem: 'calendar',                  // 同样使用日历坐标系,与热力图重叠symbolSize: 18,                                // 高亮点的尺寸itemStyle: {                                   // 高亮点样式color: 'red',                                // 点的填充色(明显一些)borderColor: '#fff',                         // 白色描边,增强突出感borderWidth: 2                               // 描边宽度},data: []                                       // 数据格式为 [[date]](单值散点,表示某天)}]
})function handleClick(params) {                       // 图表点击事件处理函数(来自 @click)if (params.componentType === 'series' &&           // 仅在点击的是系列图形(排除坐标轴等)params.seriesType === 'heatmap') {             // 并且是热力图的格子时生效const date = params.data[0]                      // 从被点击的数据中取出日期([date, count] 的第 0 项)chartOption.series[1].data = [[date]]            // 更新 scatter 系列为当前日期,实现“高亮选中”chartRef.value?.dispatchAction({                 // 主动触发 ECharts 的动作(显示 tooltip)type: 'showTip',                               // 动作类型:显示提示框seriesIndex: 0,                                // 指向第 0 个系列(heatmap),因为 tooltip 数据在热力图上dataIndex: params.dataIndex                    // 指定被点击的数据索引,从而展示对应的提交次数})}
}watch(                                               // 监听 calenderData 的变化(含初始化)() => props.calenderData,                          // 侦听源:父组件传入的数据async (newVal) => {                                // 回调:当新数据到来时更新图表if (!newVal || newVal.length === 0) return       // 容错:无数据则不处理const start = newVal[0].date                     // 假定数据已按时间升序:第一条为开始日期const end = newVal[newVal.length - 1].date       // 最后一条为结束日期chartOption.calendar.range = [start, end]        // 设置日历显示范围为 [start, end]chartOption.series[0].data = newVal.map(item => [item.date, item.count]) // 将数据转为 [[date, count]] 供热力图使用// ✅ 动态设置 visualMap.maxchartOption.visualMap.max = Math.max(...newVal.map(item => item.count)) || 0 // 计算 count 最大值,映射到颜色深浅;空数组时回退 0// ✅ 默认选中昨天const yesterday = new Date()                     // 获取当前本地时间yesterday.setDate(yesterday.getDate() - 1)       // 日期回退 1 天(得到“昨天”)const yesterdayStr = yesterday.toISOString().slice(0, 10) // 转为 YYYY-MM-DD(注意:toISOString 基于 UTC,跨时区可能早/晚一天)const index = newVal.findIndex(item => item.date === yesterdayStr) // 在数据中查找昨天对应的索引if (index !== -1) {                               // 如果数据里确实包含昨天chartOption.series[1].data = [[yesterdayStr]]   // 用 scatter 高亮昨天await nextTick()                                // 等待 DOM & 图表下一次更新完成,确保实例可用setTimeout(() => {                              // 再稍微延迟,避免立即触发导致 tooltip 不出现chartRef.value?.dispatchAction({              // 主动展示昨天的数据提示type: 'showTip',                            // 显示 tooltipseriesIndex: 0,                             // 指向热力图系列dataIndex: index                            // 指定昨天在数据中的索引})}, 200)                                         // 200ms 的经验延时(可按实际情况微调)}},{ immediate: true, deep: true }                     // 配置:立即执行一次(用于首屏渲染)+ 深度侦听(数组/对象内部变更)
)
</script>

http://www.dtcms.com/a/350649.html

相关文章:

  • Java中删除字符串首字符
  • 【51单片机】【protues仿真】基于51单片机数码管温度报警器系统
  • AR眼镜赋能水利智能巡检的创新实践
  • 算法题打卡力扣第167题:两数之和——输入有序数组(mid)
  • VASP计算层错能(SFE)全攻略2
  • python自学笔记12 NumPy 常见运算
  • QT(1)
  • 独立显卡接口操作指南
  • 小程序开发指南(四)(UI 框架整合)
  • Linux系统网络管理
  • UE5 UI遮罩
  • 人形机器人产业风口下,低延迟音视频传输如何成为核心竞争力
  • Linux笔记9——shell编程基础-3
  • OpenFeign的原理解析
  • FMS回顾和总结
  • C++ 中 `std::map` 的 `insert` 函数
  • 【机器学习项目 心脏病预测】
  • 【广告系列】流量归因模型
  • centos 用 docker 方式安装 dufs
  • 【C++11】auto关键字:自动类型推导
  • Python爬虫实战: 爬虫常用到的技术及方案详解
  • Leetcode top100之链表排序
  • Swift 解法详解 LeetCode 362:敲击计数器,让数据统计更高效
  • 【猿人学】web第一届 第16题 js-逆向 windows蜜罐 / webpack初体验
  • 通过C#上位机串口写入和读取浮点数到stm32实战5(通过串口读取bmp280气压计的数值并在上位机显示)
  • java 并发编程八股-多线程篇
  • 【已解决】统信UOS安装后没有有线网络,缺少Chengdu Haiguang IC Design Co., Ltd. 10 Gb Ethernet网卡驱动
  • 支付宝直连商户,自动处理支付交易投诉,支持多支付宝应用
  • 【VS2022】背景设置详细教程(背景透明)
  • AI 时代“驯导师”职业发展方向探究