设备状态图表-甘特图

1.背景:设备状态监控图表,监控不同状态的时间段,可以使用甘特图来展示效果
在这里插入图片描述
鼠标经过时的数据提示框
在这里插入图片描述
在这里插入图片描述
2、代码实现

<template><divref="ganttChartRefs":style="{ height: '6.2rem', width: '100%' }"class="bg_screen"></div>
</template>
<script lang="ts" setup>
import { onMounted, ref, watch, nextTick,onBeforeUnmount } from "vue";
import * as echarts from "echarts";
// import { GaugeChart } from "echarts/charts";
import { useDebounceFn, useResizeObserver } from "@vueuse/core";
// echarts.use([GaugeChart]); // 引入仪表盘 看版本 echart5以上不需要引入
const chartInstance = ref<any>(null);
const ganttChartRefs = ref<any>(null);
const myChart = ref<any>(null);
const props = defineProps({chartData: {type: Array,default: () => [],},
});
function setChartOptions(data:any) {myChart.value = echarts.init(ganttChartRefs.value);var types:any = [{ value: "1", color: "#91cc75", label: '运行'},{ value: "2", color: "#ccc", label: '停机'},{ value: "3", color: "#fd666d", label: '故障'},];var datatemp = data.map((d:any) => ({  // 数据集处理name: types.filter((a:any) => a.value == d.STATUSDESC)[0].label,  // 状态名称value: [parseInt(d.GROUPID),new Date(d.RUNTIME).getTime(),  // 处理成时间辍的格式new Date(d.END_TIME).getTime(),],itemStyle: {normal: {color: types.filter((a:any) => a.value == d.STATUSDESC)[0].color, // 状态颜色的绑定},},}));var groupedData = datatemp.reduce((acc:any, curr:any) => {if (!acc[curr.value[0]]) {acc[curr.value[0]] = [];}acc[curr.value[0]].push(curr);return acc;}, {});  // 状态值组合成一组 以便后面排序var gaps:any = [];const now = new Date();// 更新起始时间为当前时间前24小时
const twentyFourHoursAgo = now.getTime() - 24 * 60 * 60 * 1000;
// 获取第一条数据的开始时间
const firstRecordTime = new Date(data[0]?.RUNTIME).getTime();
// 检查是否有空隙(即不足24小时时)
if (firstRecordTime > twentyFourHoursAgo) {// 添加到 gaps 数组gaps.unshift([parseInt(data[0]?.GROUPID), twentyFourHoursAgo, firstRecordTime]);
}Object.keys(groupedData).forEach((groupID) => {var groupData = groupedData[groupID];// 按开始时间排序groupData.sort((a:any, b:any) => a.value[1] - b.value[1]);let lastEndTime = twentyFourHoursAgo; // 起始时间为当前时间前24小时for (var i = 0; i < groupData.length; i++) {var currentStartTime = groupData[i].value[1];var currentEndTime = groupData[i].value[2];if (currentStartTime > lastEndTime) {gaps.push([parseInt(groupID), lastEndTime, currentStartTime]);}lastEndTime = Math.max(lastEndTime, currentEndTime);}// 检查最后一个时间段结束时间与24小时结束时间之间的空隙if (lastEndTime < now.getTime()) {gaps.push([parseInt(groupID), lastEndTime,now.getTime()]);}});function renderItem(params:any, api: any) {var categoryIndex = api.value(0);var start = api.coord([api.value(1), categoryIndex]);var end = api.coord([api.value(2), categoryIndex]);var height = api.size([0, 1])[1] * 0.6;var rectShape = echarts.graphic.clipRectByRect({x: start[0],y: start[1] - height / 2,width: end[0] - start[0],height: height,},{x: params.coordSys.x,y: params.coordSys.y,width: params.coordSys.width,height: params.coordSys.height,});var shapes;shapes = rectShape && {type: "rect",transition: ["shape"],shape: rectShape,style: api.style(),};return shapes;}const series = [{type: "custom",renderItem: renderItem,encode: {x: [1, 2],y: 0,},data: datatemp,},{type: "custom",renderItem: renderItem,z: -1, // 放在最底层data: gaps,},];const option = {grid: {left: "1%",right: "1%",top: "1%",bottom: 2,height: "28%",width: "98%",containLabel: true,},tooltip: {show: true,textStyle: {fontSize: 10,},position: function (point:any, size:any) {var mouseX = point[0];var mouseY = point[1];// 获取容器的宽度和 tooltip 的宽度var containerWidth = size.viewSize&&size.viewSize[0];var tooltipWidth =  size.contentSize&&size.contentSize[0];// 调整 tooltip 的位置var offsetX = 10; // x 方向偏移量var offsetY = 1; // y 方向偏移量// 如果 tooltip 超出容器的右侧,将其显示在鼠标的左侧if (mouseX + tooltipWidth + offsetX > containerWidth) {// 新的位置坐标var newX = mouseX - tooltipWidth - offsetX;var newY = mouseY + offsetY;// 返回新的位置坐标return [newX, newY];} else {// tooltip 显示在鼠标的下方var newX:number = mouseX + offsetX;var newY:any = mouseY + offsetY;// 返回新的位置坐标return [newX, newY];}},formatter: function (params:any) {// 参数校验if (!Array.isArray(params.value) || params.value.length < 2) {return "";}try {const startTime = params.value[1];const endTime = params.value[2];if (endTime < startTime) {return "";}const duration = endTime - startTime;const hours = Math.floor(duration / (1000 * 60 * 60));const minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60));const seconds = Math.floor((duration % (1000 * 60)) / 1000);// 获取带前导0的小时、分钟和秒const formatTimeUnit = (unit:any) =>unit < 10 ? "0" + unit : unit.toString();const formattedStartTimeHours = formatTimeUnit(new Date(startTime).getHours());const formattedStartTimeMinutes = formatTimeUnit(new Date(startTime).getMinutes());const formattedStartTimeSeconds = formatTimeUnit(new Date(startTime).getSeconds());const formattedEndTimeHours = formatTimeUnit(new Date(endTime).getHours());const formattedEndTimeMinutes = formatTimeUnit(new Date(endTime).getMinutes());const formattedEndTimeSeconds = formatTimeUnit(new Date(endTime).getSeconds());const formattedTimeSeconds = formatTimeUnit(seconds);let formattedDuration = `${hours}小时${minutes}分${formattedTimeSeconds}秒`;// output 鼠标经过的时候展示的文本let output = `${params.marker}${params.name}${params.seriesIndex === 1? `空闲时长:`: `时长:`}${formattedDuration}<br/>`;output += `${params.marker}时间区间:${formattedStartTimeHours}:${formattedStartTimeMinutes}:${formattedStartTimeSeconds} - ${formattedEndTimeHours}:${formattedEndTimeMinutes}:${formattedEndTimeSeconds}`;return output;} catch (error) {return ""; // 根据实际情况考虑是否返回错误信息或特定的格式化失败字符串}},},dataZoom: [{type: "inside",filterMode: "none",showDataShadow: false,show: true,},],xAxis: {type: "time",min: new Date().getTime() - 24 * 60 * 60 * 1000, // 24小时前max: new Date().getTime(), // 当前时间axisLabel: {formatter: function (value:any) {var date = new Date(value);var hours = date.getHours();var minutes = date.getMinutes();var seconds = date.getSeconds();// 如果分钟和秒都是0,则只显示小时(例如:00)if (minutes === 0 && seconds === 0) {return hours === 0? "00:00": echarts.format.formatTime("hh:mm", value);}// 其他情况显示小时、分钟和秒(例如:01:30:45)return echarts.format.formatTime("hh:mm:ss", value);},// 标签与轴线紧挨在一起padding: [0, 0, 0, 0], // 标签的内边距(可根据实际情况调整)// rotate: 30, // 旋转角度,避免标签重叠(可选)},splitNumber: 24,splitLine: {show: false,},axisLine: {show: false, // 隐藏坐标轴线},axisTick: {show: false, // 隐藏刻度线},},yAxis: [{yAxisIndex: 0,type: "category",data: [""],axisLine: {show: false,},axisTick: {show: false,},axisLabel: {show: false,},},],series: series,};useResizeObserver(ganttChartRefs.value, resizeChart);myChart.value.setOption(option);
}
// 窗口自适应并开启过渡动画
const resize = () => {if (chartInstance.value) {chartInstance.value.resize({ animation: { duration: 300 } });}
};
// 重绘图表函数
const resizeChart = useDebounceFn(() => {myChart.value?.resize();
}, 300);
const debouncedResize = useDebounceFn(resize, 500, { maxWait: 800 });
watch(() => props.chartData,() => {nextTick(() => {setChartOptions(props.chartData);});},{immediate: true,}
);
onMounted(() => {window.addEventListener("resize", debouncedResize);
});
onBeforeUnmount(() => {myChart.value?.dispose();myChart.value = null;window.removeEventListener("resize", debouncedResize);
});
</script>

3.数据源:组件传过来 props.chartData
格式如下:

var data = [{"GROUPID": "0", // 组别 因为考虑到有多种设备情况 若只有一种 默认0就好"END_TIME": "2024-08-25 18:35:31",  // 结束时间"RUNTIME": "2024-08-25 18:25:07",   // 开始时间"STATUSDESC": "1" // 状态值},{"GROUPID": "0","END_TIME": "2024-08-26 00:25:49","RUNTIME": "2024-08-25 18:05:31","STATUSDESC": "2"},{"GROUPID": "0","END_TIME": "2024-08-26 01:26:29","RUNTIME": "2024-08-26 00:25:49","STATUSDESC": "1"},{"GROUPID": "0","END_TIME": "2024-08-26 04:31:38","RUNTIME": "2024-08-26 01:26:29","STATUSDESC": "1"},{"GROUPID": "0","END_TIME": "2024-08-26 04:37:25","RUNTIME": "2024-08-26 11:31:38","STATUSDESC": "2"},{"GROUPID": "0","END_TIME": "2024-08-26 06:52:45","RUNTIME": "2024-08-26 04:37:25","STATUSDESC": "3"},{"GROUPID": "0","END_TIME": "2024-08-26 06:54:42","RUNTIME": "2024-08-26 06:52:45","STATUSDESC": "2"},{"GROUPID": "0","END_TIME": "2024-08-26 08:01:53","RUNTIME": "2024-08-26 06:54:42","STATUSDESC": "1"},{"GROUPID": "0","END_TIME": "2024-08-26 08:40:18","RUNTIME": "2024-08-26 08:01:53","STATUSDESC": "3"},{"GROUPID": "0","END_TIME": "2024-08-26 08:53:59","RUNTIME": "2024-08-26 08:40:18","STATUSDESC": "1"},{"GROUPID": "0","END_TIME": "2024-08-26 08:03:59","RUNTIME": "2024-08-26 08:53:59","STATUSDESC": "1"},{"GROUPID": "0","END_TIME": "2024-08-26 09:29:19","RUNTIME": "2024-08-26 09:03:59","STATUSDESC": "1"},{"GROUPID": "0","END_TIME": "2024-08-26 09:51:48","RUNTIME": "2024-08-26 09:29:19","STATUSDESC": "1"},{"GROUPID": "0","END_TIME": "2024-08-26 12:01:42","RUNTIME": "2024-08-26 09:51:48","STATUSDESC": "1"},{"GROUPID": "0","END_TIME": "2024-08-26 14:42:49","RUNTIME": "2024-08-26 12:01:42","STATUSDESC": "1"}]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/878147.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java Boss直聘爬虫数据分析

摘要 本报告利用Java和Selenium爬虫技术获取数据&#xff0c;并使用ECharts库对薪资数据进行可视化分析&#xff0c;旨在探究不同经验和学历的薪资分布情况。 数据来源 数据来源于Boss直聘&#xff0c;使用Java结合Selenium库进行数据抓取。 数据总数&#xff1a;约2000家企…

如何设置Winfrom中dataGridView中的内容换行并行高自适应

如何设置Winfrom中dataGridView行高 在 Windows Forms (WinForms) 应用程序中&#xff0c;DataGridView 控件用于显示和编辑数据的表格形式。如果你想要设置 DataGridView 控件中行的高度&#xff0c;可以通过以下几种方式来实现&#xff1a; 1. 通过属性设置行高 你可以直接…

Vue:组件化开发

我们为什么要组件化开发&#xff1f; 在之前的vue中&#xff0c;我们要么是通过本地引入vue.js进行开发&#xff0c;要么就是通过CLI&#xff08;脚手架&#xff09;来创建环境然后单独在App.vue中进行开发&#xff0c;这样的开发模式当然没有问题&#xff0c;但是当项目庞大起…

3 Python开发工具:VSCode+插件

本文是 Python 系列教程第 3 篇&#xff0c;完整系列请查看 Python 专栏。 Visual Studio Code的安装非常简单&#xff0c;就不放这里增加文章篇幅了。 相比PyCharm&#xff0c;VSCode更加轻量&#xff0c;启动速度快。并且搭配Python插件就能实现和Pycharm一样的代码提示、高…

摄影曝光:曝光模式认知

写在前面 学习整理《摄影曝光&#xff1a;拍出好照片的49个关键技法》读书笔记博文内容涉及曝光模式简单认知适合小白认知理解不足小伙伴帮忙指正 &#x1f603;,生活加油 99%的焦虑都来自于虚度时间和没有好好做事&#xff0c;所以唯一的解决办法就是行动起来&#xff0c;认真…

PyTorch深度学习模型训练流程:(二、回归)

回归的流程与分类基本一致&#xff0c;只需要把评估指标改动一下就行。回归输出的是损失曲线、R^2曲线、训练集预测值与真实值折线图、测试集预测值散点图与真实值折线图。输出效果如下&#xff1a; 注意&#xff1a;预测值与真实值图像处理为按真实值排序&#xff0c;图中呈现…

【JS】使用MessageChannel实现深度克隆

前言 通常使用简便快捷的JSON 序列化与反序列化实现深克隆&#xff0c;也可以递归实现或者直接使用lodash。 但 JSON 序列化与反序列化 无法处理如下的循环引用&#xff1a; 实现 MessageChannel 内部使用了浏览器内置的结构化克隆算法&#xff0c;该算法可以在不同的浏览器上…

Qt WebAssembly 警告:构建套件中未设置编译器

目录 Qt WebAssembly 警告:构建套件中未设置编译器问题解决方法 参考资料 Qt WebAssembly 警告:构建套件中未设置编译器 问题 安装好QT之后构建套件中出现黄色感叹号Qt WebAssembly 警告:构建套件中未设置编译器。 原因是现在你只安装了qt for webassembly的qt的库&#xff…

Task-Embedded Control Networks for Few-Shot Imitation Learning

发表时间&#xff1a;CoRL 2018 论文链接&#xff1a;https://readpaper.com/pdf-annotate/note?pdfId4500197057754718210&noteId2424798567891365120 作者单位&#xff1a;Imperial College London Motivation&#xff1a;就像人类一样&#xff0c;机器人应该能够利用来…

JVM上篇:内存与垃圾回收篇-07-方法区

笔记来源&#xff1a;尚硅谷 JVM 全套教程&#xff0c;百万播放&#xff0c;全网巅峰&#xff08;宋红康详解 java 虚拟机&#xff09; 文章目录 7. 方法区7.1. 栈、堆、方法区的交互关系7.2. 方法区的理解7.2.1. 方法区在哪里&#xff1f;7.2.2. 方法区的基本理解7.2.3. HotSp…

无人机之基本结构篇

无人机&#xff08;Unmanned Aerial Vehicle, UAV&#xff09;作为一种无人驾驶的飞行器&#xff0c;其基本结构涵盖了多个关键组件&#xff0c;这些组件共同协作以实现无人机的自主飞行和执行各种任务。以下是无人机基本结构的详细解析&#xff1a; 一、飞机平台系统 机身&am…

vue2表单校验:添加自定义el-form表单校验规则

前言 在vue2表单校验&#xff1a;el-form表单绑定数组并使用rules进行校验_vue2 rules校验-CSDN博客中&#xff0c;使用form原生的rules对表单中每个控件的必填、格式等做了校验。但是保存时&#xff0c;除了验证每一个控件的输入合乎要求外&#xff0c;还需要验证控件之间的数…

SpringBoot集成kafka-生产者发送消息

springboot集成kafka发送消息 1、kafkaTemplate.send()方法1.1、springboot集成kafka发送消息Message对象消息1.2、springboot集成kafka发送ProducerRecord对象消息1.3、springboot集成kafka发送指定分区消息 2、kafkaTemplate.sendDefault()方法3、kafkaTemplate.send(...)和k…

WIN/MAC 图像处理软件Adobe Photoshop PS2024软件下载安装

目录 一、软件概述 1.1 基本信息 1.2 主要功能 二、系统要求 2.1 Windows 系统要求 2.2 macOS 系统要求 三、下载 四、使用教程 4.1 基本界面介绍 4.2 常用工具使用 4.3 进阶操作 一、软件概述 1.1 基本信息 Adobe Photoshop&#xff08;简称PS&#xff09;是一款…

springboot嵌入式数据库实践-H2内嵌数据库(文件、内存)

本文章记录笔者的嵌入式数据库简单实现&#xff0c; 记录简要的配置过程。自用文章&#xff0c;仅作参考。 目录 本文章记录笔者的嵌入式数据库简单实现&#xff0c; 记录简要的配置过程。自用文章&#xff0c;仅作参考。 嵌入式数据库 -------------------------------具…

16岁激活交学费银行卡需要本人实名电话卡,线下营业厅不给办,怎么办?

16岁激活交学费银行卡需要本人实名电话卡&#xff0c;线下营业厅不给办&#xff0c;怎么办&#xff1f; 话卡办理规定&#xff1a; 根据《民法典》和《电话用户真实身份信息登记规定》的相关要求&#xff0c;未满16周岁的用户通常需要在监护人的陪同下办理电话卡&#xff0c;并…

uniapp微信小程序 分享功能

uniapp https://zh.uniapp.dcloud.io/api/plugins/share.html#onshareappmessage export default {onShareAppMessage(res) {if (res.from button) {// 来自页面内分享按钮console.log(res.target)}return {title: 自定义分享标题,path: /pages/test/test?id123}} }需要再真机…

衡石科技BI的API如何授权文档解析

授权说明​ 授权模式​ 使用凭证式&#xff08;client credentials&#xff09;授权模式。 授权模式流程说明​ 第一步&#xff0c;A 应用在命令行向 B 发出请求。 第二步&#xff0c;B 网站验证通过以后&#xff0c;直接返回令牌。 授权模式结构说明​ 接口说明​ 获取a…

【贪心 决策包容性 】757. 设置交集大小至少为2

本文涉及知识点 贪心 决策包容性 LeetCode757. 设置交集大小至少为2 给你一个二维整数数组 intervals &#xff0c;其中 intervals[i] [starti, endi] 表示从 starti 到 endi 的所有整数&#xff0c;包括 starti 和 endi 。 包含集合 是一个名为 nums 的数组&#xff0c;并…

Quasar V2.16.4 新版发布,基于 Vue 3 的前端开发框架,一套代码发布到多端

Quasar 又发布新版本了&#xff0c;性能优秀的 Vue 组件开发框架&#xff0c;时隔3年再次推荐给大家。 早在2021年&#xff0c;我就写了一篇简单的文章向大家推荐了 Quasar 这款 Vue.js 开发框架&#xff0c;如今3年过去了&#xff0c;Quasar 发展得很好&#xff0c;更新频率依…