设备状态图表-甘特图

打印 上一主题 下一主题

主题 522|帖子 522|积分 1566

1.背景:设备状态监控图表,监控差异状态的时间段,可以利用甘特图来展示效果

鼠标经过期的数据提示框


2、代码实现
  1. <template>
  2.   <div
  3.     ref="ganttChartRefs"
  4.     :style="{ height: '6.2rem', width: '100%' }"
  5.     class="bg_screen"
  6.   ></div>
  7. </template>
  8. <script lang="ts" setup>
  9. import { onMounted, ref, watch, nextTick,onBeforeUnmount } from "vue";
  10. import * as echarts from "echarts";
  11. // import { GaugeChart } from "echarts/charts";
  12. import { useDebounceFn, useResizeObserver } from "@vueuse/core";
  13. // echarts.use([GaugeChart]); // 引入仪表盘 看版本 echart5以上不需要引入
  14. const chartInstance = ref<any>(null);
  15. const ganttChartRefs = ref<any>(null);
  16. const myChart = ref<any>(null);
  17. const props = defineProps({
  18.   chartData: {
  19.     type: Array,
  20.     default: () => [],
  21.   },
  22. });
  23. function setChartOptions(data:any) {
  24.   myChart.value = echarts.init(ganttChartRefs.value);
  25.   var types:any = [
  26.     { value: "1", color: "#91cc75", label: '运行'},
  27.     { value: "2", color: "#ccc", label: '停机'},
  28.     { value: "3", color: "#fd666d", label: '故障'},
  29.   ];
  30.   var datatemp = data.map((d:any) => ({  // 数据集处理
  31.     name: types.filter((a:any) => a.value == d.STATUSDESC)[0].label,  // 状态名称
  32.     value: [
  33.       parseInt(d.GROUPID),
  34.       new Date(d.RUNTIME).getTime(),  // 处理成时间辍的格式
  35.       new Date(d.END_TIME).getTime(),
  36.     ],
  37.     itemStyle: {
  38.       normal: {
  39.         color: types.filter((a:any) => a.value == d.STATUSDESC)[0].color, // 状态颜色的绑定
  40.       },
  41.     },
  42.   }));
  43.   var groupedData = datatemp.reduce((acc:any, curr:any) => {
  44.     if (!acc[curr.value[0]]) {
  45.       acc[curr.value[0]] = [];
  46.     }
  47.     acc[curr.value[0]].push(curr);
  48.     return acc;
  49.   }, {});  // 状态值组合成一组 以便后面排序
  50.   var gaps:any = [];
  51.   const now = new Date();
  52.   // 更新起始时间为当前时间前24小时
  53. const twentyFourHoursAgo = now.getTime() - 24 * 60 * 60 * 1000;
  54. // 获取第一条数据的开始时间
  55. const firstRecordTime = new Date(data[0]?.RUNTIME).getTime();
  56. // 检查是否有空隙(即不足24小时时)
  57. if (firstRecordTime > twentyFourHoursAgo) {
  58.   // 添加到 gaps 数组
  59.   gaps.unshift([parseInt(data[0]?.GROUPID), twentyFourHoursAgo, firstRecordTime]);
  60. }
  61.   Object.keys(groupedData).forEach((groupID) => {
  62.     var groupData = groupedData[groupID];
  63.     // 按开始时间排序
  64.     groupData.sort((a:any, b:any) => a.value[1] - b.value[1]);
  65.     let lastEndTime = twentyFourHoursAgo; // 起始时间为当前时间前24小时
  66.     for (var i = 0; i < groupData.length; i++) {
  67.       var currentStartTime = groupData[i].value[1];
  68.       var currentEndTime = groupData[i].value[2];
  69.       if (currentStartTime > lastEndTime) {
  70.         gaps.push([parseInt(groupID), lastEndTime, currentStartTime]);
  71.       }
  72.       lastEndTime = Math.max(lastEndTime, currentEndTime);
  73.     }
  74.     // 检查最后一个时间段结束时间与24小时结束时间之间的空隙
  75.     if (lastEndTime < now.getTime()) {
  76.       gaps.push([parseInt(groupID), lastEndTime,now.getTime()]);
  77.     }
  78.   });
  79.   function renderItem(params:any, api: any) {
  80.     var categoryIndex = api.value(0);
  81.     var start = api.coord([api.value(1), categoryIndex]);
  82.     var end = api.coord([api.value(2), categoryIndex]);
  83.     var height = api.size([0, 1])[1] * 0.6;
  84.     var rectShape = echarts.graphic.clipRectByRect(
  85.       {
  86.         x: start[0],
  87.         y: start[1] - height / 2,
  88.         width: end[0] - start[0],
  89.         height: height,
  90.       },
  91.       {
  92.         x: params.coordSys.x,
  93.         y: params.coordSys.y,
  94.         width: params.coordSys.width,
  95.         height: params.coordSys.height,
  96.       }
  97.     );
  98.     var shapes;
  99.     shapes = rectShape && {
  100.       type: "rect",
  101.       transition: ["shape"],
  102.       shape: rectShape,
  103.       style: api.style(),
  104.     };
  105.     return shapes;
  106.   }
  107.   const series = [
  108.     {
  109.       type: "custom",
  110.       renderItem: renderItem,
  111.       encode: {
  112.         x: [1, 2],
  113.         y: 0,
  114.       },
  115.       data: datatemp,
  116.     },
  117.     {
  118.       type: "custom",
  119.       renderItem: renderItem,
  120.       z: -1, // 放在最底层
  121.       data: gaps,
  122.     },
  123.   ];
  124.   const option = {
  125.     grid: {
  126.       left: "1%",
  127.       right: "1%",
  128.       top: "1%",
  129.       bottom: 2,
  130.       height: "28%",
  131.       width: "98%",
  132.       containLabel: true,
  133.     },
  134.     tooltip: {
  135.       show: true,
  136.       textStyle: {
  137.         fontSize: 10,
  138.       },
  139.       position: function (point:any, size:any) {
  140.         var mouseX = point[0];
  141.         var mouseY = point[1];
  142.         // 获取容器的宽度和 tooltip 的宽度
  143.         var containerWidth = size.viewSize&&size.viewSize[0];
  144.         var tooltipWidth =  size.contentSize&&size.contentSize[0];
  145.         // 调整 tooltip 的位置
  146.         var offsetX = 10; // x 方向偏移量
  147.         var offsetY = 1; // y 方向偏移量
  148.         // 如果 tooltip 超出容器的右侧,将其显示在鼠标的左侧
  149.         if (mouseX + tooltipWidth + offsetX > containerWidth) {
  150.           // 新的位置坐标
  151.           var newX = mouseX - tooltipWidth - offsetX;
  152.           var newY = mouseY + offsetY;
  153.           // 返回新的位置坐标
  154.           return [newX, newY];
  155.         } else {
  156.           // tooltip 显示在鼠标的下方
  157.           var newX:number = mouseX + offsetX;
  158.           var newY:any = mouseY + offsetY;
  159.           // 返回新的位置坐标
  160.           return [newX, newY];
  161.         }
  162.       },
  163.       formatter: function (params:any) {
  164.         // 参数校验
  165.         if (!Array.isArray(params.value) || params.value.length < 2) {
  166.           return "";
  167.         }
  168.         try {
  169.           const startTime = params.value[1];
  170.           const endTime = params.value[2];
  171.           if (endTime < startTime) {
  172.             return "";
  173.           }
  174.           const duration = endTime - startTime;
  175.           const hours = Math.floor(duration / (1000 * 60 * 60));
  176.           const minutes = Math.floor(
  177.             (duration % (1000 * 60 * 60)) / (1000 * 60)
  178.           );
  179.           const seconds = Math.floor((duration % (1000 * 60)) / 1000);
  180.           // 获取带前导0的小时、分钟和秒
  181.           const formatTimeUnit = (unit:any) =>
  182.             unit < 10 ? "0" + unit : unit.toString();
  183.           const formattedStartTimeHours = formatTimeUnit(
  184.             new Date(startTime).getHours()
  185.           );
  186.           const formattedStartTimeMinutes = formatTimeUnit(
  187.             new Date(startTime).getMinutes()
  188.           );
  189.           const formattedStartTimeSeconds = formatTimeUnit(
  190.             new Date(startTime).getSeconds()
  191.           );
  192.           const formattedEndTimeHours = formatTimeUnit(
  193.             new Date(endTime).getHours()
  194.           );
  195.           const formattedEndTimeMinutes = formatTimeUnit(
  196.             new Date(endTime).getMinutes()
  197.           );
  198.           const formattedEndTimeSeconds = formatTimeUnit(
  199.             new Date(endTime).getSeconds()
  200.           );
  201.           const formattedTimeSeconds = formatTimeUnit(seconds);
  202.           let formattedDuration = `${hours}小时${minutes}分${formattedTimeSeconds}秒`;
  203.          // output 鼠标经过的时候展示的文本
  204.           let output = `${params.marker}${params.name}${
  205.             params.seriesIndex === 1
  206.               ? `空闲时长:`
  207.               : `时长:`
  208.           }${formattedDuration}<br/>`;
  209.           output += `${params.marker}时间区间:${formattedStartTimeHours}:${formattedStartTimeMinutes}:${formattedStartTimeSeconds} - ${formattedEndTimeHours}:${formattedEndTimeMinutes}:${formattedEndTimeSeconds}`;
  210.           return output;
  211.         } catch (error) {
  212.           return ""; // 根据实际情况考虑是否返回错误信息或特定的格式化失败字符串
  213.         }
  214.       },
  215.     },
  216.     dataZoom: [
  217.       {
  218.         type: "inside",
  219.         filterMode: "none",
  220.         showDataShadow: false,
  221.         show: true,
  222.       },
  223.     ],
  224.     xAxis: {
  225.       type: "time",
  226.       min: new Date().getTime() - 24 * 60 * 60 * 1000, // 24小时前
  227.       max: new Date().getTime(), // 当前时间
  228.       axisLabel: {
  229.         formatter: function (value:any) {
  230.           var date = new Date(value);
  231.           var hours = date.getHours();
  232.           var minutes = date.getMinutes();
  233.           var seconds = date.getSeconds();
  234.           // 如果分钟和秒都是0,则只显示小时(例如:00)
  235.           if (minutes === 0 && seconds === 0) {
  236.             return hours === 0
  237.               ? "00:00"
  238.               : echarts.format.formatTime("hh:mm", value);
  239.           }
  240.           // 其他情况显示小时、分钟和秒(例如:01:30:45)
  241.           return echarts.format.formatTime("hh:mm:ss", value);
  242.         },
  243.         // 标签与轴线紧挨在一起
  244.         padding: [0, 0, 0, 0], // 标签的内边距(可根据实际情况调整)
  245.         // rotate: 30, // 旋转角度,避免标签重叠(可选)
  246.       },
  247.       splitNumber: 24,
  248.       splitLine: {
  249.         show: false,
  250.       },
  251.       axisLine: {
  252.         show: false, // 隐藏坐标轴线
  253.       },
  254.       axisTick: {
  255.         show: false, // 隐藏刻度线
  256.       },
  257.     },
  258.     yAxis: [
  259.       {
  260.         yAxisIndex: 0,
  261.         type: "category",
  262.         data: [""],
  263.         axisLine: {
  264.           show: false,
  265.         },
  266.         axisTick: {
  267.           show: false,
  268.         },
  269.         axisLabel: {
  270.           show: false,
  271.         },
  272.       },
  273.     ],
  274.     series: series,
  275.   };
  276.   useResizeObserver(ganttChartRefs.value, resizeChart);
  277.   myChart.value.setOption(option);
  278. }
  279. // 窗口自适应并开启过渡动画
  280. const resize = () => {
  281.   if (chartInstance.value) {
  282.     chartInstance.value.resize({ animation: { duration: 300 } });
  283.   }
  284. };
  285. // 重绘图表函数
  286. const resizeChart = useDebounceFn(() => {
  287.   myChart.value?.resize();
  288. }, 300);
  289. const debouncedResize = useDebounceFn(resize, 500, { maxWait: 800 });
  290. watch(
  291.   () => props.chartData,
  292.   () => {
  293.     nextTick(() => {
  294.       setChartOptions(props.chartData);
  295.     });
  296.   },
  297.   {
  298.     immediate: true,
  299.   }
  300. );
  301. onMounted(() => {
  302.   window.addEventListener("resize", debouncedResize);
  303. });
  304. onBeforeUnmount(() => {
  305.   myChart.value?.dispose();
  306.   myChart.value = null;
  307.   window.removeEventListener("resize", debouncedResize);
  308. });
  309. </script>
复制代码
3.数据源:组件传过来 props.chartData
格式如下:
  1. var data = [
  2.             {
  3.                 "GROUPID": "0", // 组别 因为考虑到有多种设备情况 若只有一种 默认0就好
  4.                 "END_TIME": "2024-08-25 18:35:31",  // 结束时间
  5.                 "RUNTIME": "2024-08-25 18:25:07",   // 开始时间
  6.                 "STATUSDESC": "1" // 状态值
  7.             },
  8.             {
  9.                 "GROUPID": "0",
  10.                 "END_TIME": "2024-08-26 00:25:49",
  11.                 "RUNTIME": "2024-08-25 18:05:31",
  12.                 "STATUSDESC": "2"
  13.             },
  14.             {
  15.                 "GROUPID": "0",
  16.                 "END_TIME": "2024-08-26 01:26:29",
  17.                 "RUNTIME": "2024-08-26 00:25:49",
  18.                 "STATUSDESC": "1"
  19.             },
  20.             {
  21.                 "GROUPID": "0",
  22.                 "END_TIME": "2024-08-26 04:31:38",
  23.                 "RUNTIME": "2024-08-26 01:26:29",
  24.                 "STATUSDESC": "1"
  25.             },
  26.             {
  27.                 "GROUPID": "0",
  28.                 "END_TIME": "2024-08-26 04:37:25",
  29.                 "RUNTIME": "2024-08-26 11:31:38",
  30.                 "STATUSDESC": "2"
  31.             },
  32.             {
  33.                 "GROUPID": "0",
  34.                 "END_TIME": "2024-08-26 06:52:45",
  35.                 "RUNTIME": "2024-08-26 04:37:25",
  36.                 "STATUSDESC": "3"
  37.             },
  38.             {
  39.                 "GROUPID": "0",
  40.                 "END_TIME": "2024-08-26 06:54:42",
  41.                 "RUNTIME": "2024-08-26 06:52:45",
  42.                 "STATUSDESC": "2"
  43.             },
  44.             {
  45.                 "GROUPID": "0",
  46.                 "END_TIME": "2024-08-26 08:01:53",
  47.                 "RUNTIME": "2024-08-26 06:54:42",
  48.                 "STATUSDESC": "1"
  49.             },
  50.             {
  51.                 "GROUPID": "0",
  52.                 "END_TIME": "2024-08-26 08:40:18",
  53.                 "RUNTIME": "2024-08-26 08:01:53",
  54.                 "STATUSDESC": "3"
  55.             },
  56.             {
  57.                 "GROUPID": "0",
  58.                 "END_TIME": "2024-08-26 08:53:59",
  59.                 "RUNTIME": "2024-08-26 08:40:18",
  60.                 "STATUSDESC": "1"
  61.             },
  62.             {
  63.                 "GROUPID": "0",
  64.                 "END_TIME": "2024-08-26 08:03:59",
  65.                 "RUNTIME": "2024-08-26 08:53:59",
  66.                 "STATUSDESC": "1"
  67.             },
  68.             {
  69.                 "GROUPID": "0",
  70.                 "END_TIME": "2024-08-26 09:29:19",
  71.                 "RUNTIME": "2024-08-26 09:03:59",
  72.                 "STATUSDESC": "1"
  73.             },
  74.             {
  75.                 "GROUPID": "0",
  76.                 "END_TIME": "2024-08-26 09:51:48",
  77.                 "RUNTIME": "2024-08-26 09:29:19",
  78.                 "STATUSDESC": "1"
  79.             },
  80.             {
  81.                 "GROUPID": "0",
  82.                 "END_TIME": "2024-08-26 12:01:42",
  83.                 "RUNTIME": "2024-08-26 09:51:48",
  84.                 "STATUSDESC": "1"
  85.             },
  86.             {
  87.                 "GROUPID": "0",
  88.                 "END_TIME": "2024-08-26 14:42:49",
  89.                 "RUNTIME": "2024-08-26 12:01:42",
  90.                 "STATUSDESC": "1"
  91.             }
  92.         ]
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

tsx81428

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表