tsx81428 发表于 2024-8-29 02:40:16

设备状态图表-甘特图

1.背景:设备状态监控图表,监控差异状态的时间段,可以利用甘特图来展示效果
https://i-blog.csdnimg.cn/direct/18d4b389f2e342a5a33a977213166f50.png
鼠标经过期的数据提示框
https://i-blog.csdnimg.cn/direct/b3a377a76397407eb7b229c3e8b45557.png
https://i-blog.csdnimg.cn/direct/e5379a6b489148e7a6ab922955020dfe.png
2、代码实现
<template>
<div
    ref="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(); // 引入仪表盘 看版本 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).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).color, // 状态颜色的绑定
      },
    },
}));
var groupedData = datatemp.reduce((acc:any, curr:any) => {
    if (!acc]) {
      acc] = [];
    }
    acc].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?.RUNTIME).getTime();
// 检查是否有空隙(即不足24小时时)
if (firstRecordTime > twentyFourHoursAgo) {
// 添加到 gaps 数组
gaps.unshift(?.GROUPID), twentyFourHoursAgo, firstRecordTime]);
}
Object.keys(groupedData).forEach((groupID) => {
    var groupData = groupedData;
    // 按开始时间排序
    groupData.sort((a:any, b:any) => a.value - b.value);
    let lastEndTime = twentyFourHoursAgo; // 起始时间为当前时间前24小时
    for (var i = 0; i < groupData.length; i++) {
      var currentStartTime = groupData.value;
      var currentEndTime = groupData.value;
      if (currentStartTime > lastEndTime) {
      gaps.push();
      }
      lastEndTime = Math.max(lastEndTime, currentEndTime);
    }
    // 检查最后一个时间段结束时间与24小时结束时间之间的空隙
    if (lastEndTime < now.getTime()) {
      gaps.push();
    }
});
function renderItem(params:any, api: any) {
    var categoryIndex = api.value(0);
    var start = api.coord();
    var end = api.coord();
    var height = api.size() * 0.6;
    var rectShape = echarts.graphic.clipRectByRect(
      {
      x: start,
      y: start - height / 2,
      width: end - start,
      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: ,
      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;
      var mouseY = point;
      // 获取容器的宽度和 tooltip 的宽度
      var containerWidth = size.viewSize&&size.viewSize;
      var tooltipWidth =size.contentSize&&size.contentSize;

      // 调整 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 ;
      } else {
          // tooltip 显示在鼠标的下方
          var newX:number = mouseX + offsetX;
          var newY:any = mouseY + offsetY;
          // 返回新的位置坐标
          return ;
      }
      },
      formatter: function (params:any) {
      // 参数校验
      if (!Array.isArray(params.value) || params.value.length < 2) {
          return "";
      }
      try {
          const startTime = params.value;
          const endTime = params.value;
          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: , // 标签的内边距(可根据实际情况调整)
      // 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"
            }
      ]

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 设备状态图表-甘特图