日历热力图,月度数据可视化图表(日活跃图、格子图)vue组件
日历热力图,月度数据可视化图表,vue组件
先看效果👇


在线体验https://www.guetzjb.cn/calanderViewGraph/
日历图简单划分为近一年时间,开始时间是 上一年的今天,例如2024/01/01 —— 2025/01/01,跨度刚好一年,依次从上到下看一排真好七个小方格,分别对应着 周日、周一、周二、周三、周四、周五、周六。
PC端、移动端支持良好
方法颜色支持自定义,可根据数据大小规定颜色深度。
实现方式简单易懂~用到element plus和moment,用前请安装到项目
yarn add element-plus moment
# or
npm i element-plus moment
show code
calanderViewGraph.vue
<script setup lang='ts'>
import moment from 'moment'
import 'moment/dist/locale/zh-cn'moment.locale('zh-cn')const props = withDefaults(defineProps<{size: number
}>(), {size: 10
})const ansRes: any = []
const date = new Date()
const today = {year: date.getFullYear(),month: date.getMonth(),day: date.getDate()
}
let endTime = moment(date, 'YYYY/MM/DD').format('L')
let startTime = moment((today.year - 1) + '/' + (today.month + 1) + '/' + today.day, 'YYYY/MM/DD').format('L')
const visibleList = ref<Record<string, boolean>>({})
let days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]function isLeapYear(year: number) {return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}function init() {//上一年开始遍历//下标从0开始for (let month = today.month; month <= 12 + today.month; month++) {let year = Math.floor(month / 12); //0->前一年 1->今年let m = month % 12;let remainDaylet weekif (month != today.month && month != 12 + today.month) {remainDay = days[m] + (isLeapYear(today.year - (year == 0 ? 1 : 0)) && m == 1 ? 1 : 0) // 闰年补1week = new Date(today.year - (year == 0 ? 1 : 0) + '/' + (m + 1) + '/1').getDay()} else {if (month == today.month) {remainDay = days[m] - today.day + 1 + (isLeapYear(today.year - (year == 0 ? 1 : 0)) && m == 1 ? 1 : 0) // 例如 1/1 ~ 1/2 日期相差2天,相减+1week = new Date(today.year - (year == 0 ? 1 : 0) + '/' + (m + 1) + '/' + today.day).getDay()} else {remainDay = today.day + (isLeapYear(today.year - (year == 0 ? 1 : 0)) && m == 1 ? 1 : 0)week = new Date(today.year - (year == 0 ? 1 : 0) + '/' + (m + 1) + '/1').getDay()}}ansRes.push({year,month: m,remainDay,week,rev: month == today.month // 第一个日期必须反转 例如2024/1/20 剩余11天,应该显示2024/1/20 ~ 2024/1/31})}
}const viewsList = ref<any>({totalCnt: 0,views: {// '2025/01/20': 10 //格式 —— (key:日期,value:数量)},colors: {// '2025/01/20': '#fff000' //格式 —— (key:日期,value:色值)}
})// 自定义颜色
const colorArr = ["#E0F8E0", "#C6E0C6", "#AEE6AE", "#96EA96", "#7EF07E", "#66F566", "#4EF94E", "#36FD36", "#1EFF1E", "#00CC00"]
function getColorFunc(value: number): string {let i = 0while (value > 10 && i < colorArr.length) {value -= 10i += 1}return colorArr[i]
}function generateData() {let startTimeStamp = new Date(startTime).getTime()let endTimeStamp = new Date(endTime).getTime()// 随机生成365个数据for (let i = 0; i < 365; i++) {let randomTimeStamp: number = (endTimeStamp - Math.random() * (endTimeStamp - startTimeStamp)) // 随机减一个随机时间戳,相当于在今天的时间戳基础上减let dateStr: string = moment(randomTimeStamp).format('YYYY/MM/DD')if (!viewsList.value.views[dateStr]) {viewsList.value.views[dateStr] = 0}let curCnt = Math.random() * 100 | 0 // |0去除小数点 viewsList.value.views[dateStr] += curCntviewsList.value.totalCnt += curCntviewsList.value.colors[dateStr] = getColorFunc(viewsList.value.views[dateStr])}
}const formatDate = (year: number, month: number, day: number) => {return moment(today.year + (year == 0 ? -1 : 0) + '/' + (month + 1) + '/' + (day + 1), 'YYYY/MM/DD').format('L')
}init()
onMounted(() => {generateData()
})</script><template><div class="calander_box"><p class="view_title">近一年共浏览<span style="font-weight: bold;padding: 0 5px;">{{ viewsList?.totalCnt != null ?viewsList?.totalCnt : '...' }}</span>次</p><el-scrollbar><div class="mobile_wrap"><div class="calander_view_g_wrap"><div class="views_wrap" v-for="month in ansRes" v-show="month.remainDay > 0"><!-- 一排 7个 加边距(20px) --><div class="views_month" :style="{ height: props.size * 7 + 20 + 'px' }"><!-- 伪装的格子 --><div class="views_day" :style="{width: props.size + 'px',height: props.size + 'px'}" v-for="_offset in month.week" style="background: transparent;cursor: auto;"></div><!-- 真正显示的格子 --><div v-for="(_day, index) in month.remainDay"><el-tooltip effect="dark" :visible="visibleList[formatDate(month.year, month.month, index)]":content="`${formatDate(month.year, month.month, !month.rev ? index : (days[month.month] - (month.remainDay - index)))} ${viewsList?.views[formatDate(month.year, month.month, index)] || 0}次浏览`"placement="top-start"><div class="views_day" @mouseenter="visibleList[formatDate(month.year, month.month, index)] = true"@mouseleave="visibleList[formatDate(month.year, month.month, index)] = false" :style="{background: viewsList?.colors[formatDate(month.year, month.month, index)],width: props.size + 'px',height: props.size + 'px'}"></div></el-tooltip></div></div><p style="color: #a2a2a2;">{{ month.month + 1 + '月' }}</p></div></div></div></el-scrollbar></div>
</template><style lang='scss' scoped>
.calander_box {width: 100%;padding: 20px;.view_title {font-size: 18px;padding-left: 10px;margin-bottom: 20px;}.mobile_wrap {width: fit-content;@media screen and (max-width:480px) {width: 800px;white-space: nowrap;overflow-anchor: auto;}.calander_view_g_wrap {display: flex;justify-content: space-between;.views_wrap {width: 100%;margin-right: 8px;margin-left: 8px;p {text-align: center;margin-top: 10px;}.views_month {width: calc(100% / 12);height: 90px;display: flex;flex-direction: column;flex-wrap: wrap;@media screen and (max-width:1200px) {height: 50px;}.views_day {margin: 0 2px 2px 0;border-radius: 2px;background: #F7F7F8;cursor: pointer;@media screen and (max-width:1200px) {width: 5px;height: 5px;}}}}}}}
</style>
使用方法:
传入size表示方格的宽度和高度,
如果不想要方形,可以自己改样式实现(注意调整外部div高度,必须一排七个,否则周(日、一……六)的顺序会错乱)
<calanderViewGraph :size="10"/>
相关文章:
日历热力图,月度数据可视化图表(日活跃图、格子图)vue组件
日历热力图,月度数据可视化图表,vue组件 先看效果👇 在线体验https://www.guetzjb.cn/calanderViewGraph/ 日历图简单划分为近一年时间,开始时间是 上一年的今天,例如2024/01/01 —— 2025/01/01,跨度刚…...
Vue 3中导航守卫(Navigation Guard)结合Axios实现token认证机制
在Vue 3中,导航守卫(Navigation Guard)用于拦截路由的变化,可以在用户访问页面前进行检查。结合Axios进行token认证机制时,我们可以通过导航守卫在路由跳转时,检查用户的认证状态,确保用户有有效…...
【爬虫】使用 Scrapy 框架爬取豆瓣电影 Top 250 数据的完整教程
前言 在大数据和网络爬虫领域,Scrapy 是一个功能强大且广泛使用的开源爬虫框架。它能够帮助我们快速地构建爬虫项目,并高效地从各种网站中提取数据。在本篇文章中,我将带大家从零开始使用 Scrapy 框架,构建一个简单的爬虫项目&am…...
一分钟学习数据安全——白盒加密及安当应用
白盒加密作为一种先进的加密技术,在数据安全、通信安全和信息隐私保护等多个关键领域都有应用。这次的一分钟,让您快速了解一下白盒加密的概念,以及安当产品中的白盒加密应用。 一、什么是白盒加密 简单来说,白盒加密是一种特殊…...
composer安装指定php版本, 忽略平台原因导致的报错
windows下 //composer安装指定php版本, 写出完整的php和composer.phar路径 D:\phpstudy_pro\Extensions\php\php8.1.11nts\php.exe D:\phpstudy_pro\Extensions\composer1.8.5\composer.phar install windows下一些扩展不支持, 如下图, 所以本地composer安装组件时可以忽略 …...
Java 前端详解
Java 前端详解 Java 前端开发主要涉及使用 Java 相关技术和框架来创建用户界面和处理用户交互。虽然 Java 原本是后端开发的主力语言,但它也提供了许多前端开发工具和框架。以下是 Java 前端开发的主要内容和技术栈。 一、Java 前端技术栈 Java Swing 和 AWT AWT (…...
鸿蒙安装HAP时提示“code:9568344 error: install parse profile prop check error” 问题现象
在启动调试或运行应用/服务时,安装HAP出现错误,提示“error: install parse profile prop check error”错误信息。 解决措施 该问题可能是由于应用使用了应用特权,但应用的签名文件发生变化后未将新的签名指纹重新配置到设备的特权管控白名…...
Javaweb之css
css的三种引入方式 1内行式 2.内嵌式 3.外部样式表 内行式和内嵌式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&quo…...
实施工程师:面试基础宝典
一.运维工程师和实施工程师的区别:工作内容不同、职能不同、工作形式不同 1.工作内容不同: 运维工程师要对公司硬件和软件进行维护。 硬件包括:机房、机柜、网线光纤、PDU、服 务器、网络设备、安全设备等。 实施工程师包括常用操作系统、应…...
react install
react 安装 React 是一个用于构建用户界面的 JavaScript 库。以下是安装 React 的步骤: 使用 Create React App Create React App 是一个官方支持的命令行工具,用于快速搭建 React 应用。 安装 Node.js 和 npm 确保你的计算机上安装了 Node.js 和 npm…...
ElasticSearch DSL查询之排序和分页
一、排序功能 1. 默认排序 在 Elasticsearch 中,默认情况下,查询结果是根据 相关度 评分(score)进行排序的。我们之前已经了解过,相关度评分是通过 Elasticsearch 根据查询条件与文档内容的匹配程度自动计算得出的。…...
uniapp封装websocket
WebSocket介绍 后端使用的是springbootnetty做websocket的服务端,参考我其他博文 项目使用场景 开发uniapp项目时,需要进行实时通信,比如聊天等。需要封装一个工具类,统一对socket进行管理。 uniapp websocket官方文档࿱…...
【Linux】18.Linux进程控制(2)
文章目录 3. 进程程序替换3.1 单进程版 -- 看看程序替换3.2 替换原理3.3 替换函数函数解释命名理解 3.4 多进程版 -- 验证各种程序替换接口3.5 自定义shell 3. 进程程序替换 3.1 单进程版 – 看看程序替换 makefile mycommand:mycommand.cgcc -o $ $^ -stdc99 .PHONY:clean …...
reactor框架使用时,数据流请求流程
1. 我们在Flux打开时,可以看到 public abstract class Flux<T> implements CorePublisher<T> { 2. public interface CorePublisher<T> extends Publisher<T> {void subscribe(CoreSubscriber<? super T> subscriber); } Publish…...
读西瓜书的数学准备
1,高等数学:会求偏导数就行 2,线性代数:会矩阵运算就行 参考:线性代数--矩阵基本计算(加减乘法)_矩阵运算-CSDN博客 3,概率论与数理统计:知道啥是随机变量就行...
摄像头模块如何应用在宠物产品领域
一、宠物监控类产品 家庭宠物远程监控摄像头 1.基本功能与原理:这类摄像头可以通过 Wi - Fi 连接到家庭网络,主人可以使用手机应用程序在任何有网络连接的地方查看宠物的实时画面。摄像头模块内置有图像传感器,能够捕捉光线并将其转换为数字…...
c++学习第七天
创作过程中难免有不足,若您发现本文内容有误,恳请不吝赐教。 提示:以下是本篇文章正文内容,下面案例可供参考。 一、const成员函数 //Date.h#pragma once#include<iostream> using namespace std;class Date { public:Date…...
Ubuntu 24.04 LTS 通过 docker 安装 nextcloud 搭建个人网盘
准备 Ubuntu 24.04 LTSUbuntu 空闲硬盘挂载Ubuntu 安装 Docker DesktopUbuntu 24.04 LTS 安装 tailscale [我的Ubuntu服务器折腾集](https://blog.csdn.net/jh1513/article/details/145222679。 安装 nextcloud 参考 Ubuntu24.04系统Docker安装NextcloudOnlyoffice _。 更…...
RabbitMQ1-消息队列
目录 MQ的相关概念 什么是MQ 为什么要用MQ MQ的分类 MQ的选择 RabbitMQ RabbitMQ的概念 四大核心概念 RabbitMQ的核心部分 各个名词介绍 MQ的相关概念 什么是MQ MQ(message queue),从字面意思上看,本质是个队列,FIFO 先入先出&am…...
Open3D计算点云粗糙度(方法一)【2025最新版】
目录 一、Roughness二、代码实现三、结果展示博客长期更新,本文最近更新时间为:2025年1月18日。 一、Roughness 通过菜单栏的Tools > Other > Roughness找到该功能。 这个工具可以估计点云的“粗糙度”。 选择一个或几个点云,然后启动这个工具。 CloudCompare只会询问…...
第二篇:KNX实战进阶|分模式开发+综合项目落地,手把手教你搞定
在上一篇博客中,我们已经掌握了KNX协议基础、开发环境搭建与协议栈移植,完成了“入门铺垫”。这一篇,我们将进入核心实战环节——聚焦KNX TP(楼宇主流)和KNX IP(远程控制)两种模式的开发&#x…...
从vim-plug到packer.nvim的终极迁移指南:3步实现无缝切换
从vim-plug到packer.nvim的终极迁移指南:3步实现无缝切换 【免费下载链接】packer.nvim A use-package inspired plugin manager for Neovim. Uses native packages, supports Luarocks dependencies, written in Lua, allows for expressive config 项目地址: ht…...
OpenClaw自动化测试实践:Qwen3.5-9B驱动日志分析与报告生成
OpenClaw自动化测试实践:Qwen3.5-9B驱动日志分析与报告生成 1. 为什么选择OpenClawQwen3.5做测试分析? 去年参与的一个物联网项目让我吃尽了测试日志的苦头——每天要手动分析近千条设备日志,从中筛选异常模式、统计错误类型、整理测试报告…...
OpenClaw汽车保养助手:Qwen2.5-VL-7B解析故障灯照片生成检修指南
OpenClaw汽车保养助手:Qwen2.5-VL-7B解析故障灯照片生成检修指南 1. 为什么需要汽车故障灯智能助手 上周我的车突然亮起了发动机故障灯,黄色警示图标在仪表盘上闪烁。作为一个非专业车主,我面临两个选择:要么花半天时间排队去4S…...
数字孪生简介
数字孪生简介摘要数字孪生(Digital Twin)作为连接物理世界与数字世界的核心技术,正在重塑全球产业格局。本报告系统梳理了数字孪生技术的概念演进、技术架构、行业应用及发展趋势,深入分析了其在智能制造、航空航天、智慧城市、医…...
LabVIEW调用VisionPro框架代码:VisionPro labview 2020版
LabVIEW调用VisionPro框架代码 VisionPro labview 2020 最近在折腾LabVIEW和VisionPro的联动开发,发现这俩工业视觉领域的老搭档配合起来确实能玩出不少花样。今天咱们就聊聊怎么在LabVIEW 2020里直接调用VisionPro框架的代码,手头有工控机的朋友可以直接…...
001、开篇:为什么是LangChain?大模型应用开发范式变革
001、开篇:为什么是LangChain?大模型应用开发范式变革 昨天深夜调试一个对话场景,被大模型的输出格式折腾得够呛。需求很简单:从用户消息里提取时间、地点、事件三个字段,返回结构化的JSON。我对着API文档写了二十多行…...
从内存寻址到游戏操控:CE逆向分析扫雷核心机制的完整实践
1. 逆向工程入门:为什么选择扫雷作为CE分析对象 逆向工程听起来高大上,但入门其实可以从经典小游戏开始。扫雷作为Windows系统自带游戏,结构简单但机制完整,是学习内存分析的绝佳标本。我第一次用Cheat Engine(CE&…...
4步打造专属《无人深空》体验:NomNom存档编辑器全功能指南
4步打造专属《无人深空》体验:NomNom存档编辑器全功能指南 【免费下载链接】NomNom NomNom is the most complete savegame editor for NMS but also shows additional information around the data youre about to change. You can also easily look up each item …...
3DMax烘焙贴图实战:从零到一整合建筑模型,优化Unity运行性能
1. 为什么需要烘焙贴图:从性能瓶颈到解决方案 第一次把复杂建筑模型导入Unity时,我盯着屏幕上龟速移动的视角和疯狂跳动的帧率数字,整个人都是懵的。检查资源管理器才发现,这个看似普通的五层楼模型竟然用了87张不同尺寸的贴图&am…...
