1.背景:设备状态监控图表,监控不同状态的时间段,可以使用甘特图来展示效果
鼠标经过时的数据提示框
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([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 startDate;
startDate = new Date(data[0]?.RUNTIME);
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 = [];
var startTime_ = new Date(
startDate.getFullYear(),
startDate.getMonth(),
startDate.getDate()
).getTime(); // 返回时间戳
var endTime_ = startTime_ + 24 * 60 * 60 * 1000; // 24小时后的时间
Object.keys(groupedData).forEach((groupID) => {
var groupData = groupedData[groupID];
// 按开始时间排序
groupData.sort((a:any, b:any) => a.value[1] - b.value[1]);
var lastEndTime = startTime_;
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 < endTime_) {
gaps.push([parseInt(groupID), lastEndTime, endTime_]);
}
});
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-19 01:23:48”, // 结束时间
“RUNTIME”: “2024-08-19 00:22:56”, // 开始时间
“STATUSDESC”: “1” // 状态值
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:24:01”,
“RUNTIME”: “2024-08-19 15:23:48”,
“STATUSDESC”: “2”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:24:54”,
“RUNTIME”: “2024-08-19 15:24:01”,
“STATUSDESC”: “2”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:25:07”,
“RUNTIME”: “2024-08-19 15:24:54”,
“STATUSDESC”: “2”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:25:58”,
“RUNTIME”: “2024-08-19 15:25:07”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:26:14”,
“RUNTIME”: “2024-08-19 15:25:58”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:27:05”,
“RUNTIME”: “2024-08-19 15:26:14”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:27:16”,
“RUNTIME”: “2024-08-19 15:27:05”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:28:14”,
“RUNTIME”: “2024-08-19 15:27:16”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:28:24”,
“RUNTIME”: “2024-08-19 15:28:14”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:29:16”,
“RUNTIME”: “2024-08-19 15:28:24”,
“STATUSDESC”: “2”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:29:32”,
“RUNTIME”: “2024-08-19 15:29:16”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:30:26”,
“RUNTIME”: “2024-08-19 15:29:32”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:30:35”,
“RUNTIME”: “2024-08-19 15:30:26”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:31:30”,
“RUNTIME”: “2024-08-19 15:30:35”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:31:43”,
“RUNTIME”: “2024-08-19 15:31:30”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:32:36”,
“RUNTIME”: “2024-08-19 15:31:43”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:32:48”,
“RUNTIME”: “2024-08-19 15:32:36”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:33:45”,
“RUNTIME”: “2024-08-19 15:32:48”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:33:54”,
“RUNTIME”: “2024-08-19 15:33:45”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:34:49”,
“RUNTIME”: “2024-08-19 15:33:54”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:35:04”,
“RUNTIME”: “2024-08-19 15:34:49”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:35:55”,
“RUNTIME”: “2024-08-19 15:35:04”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:36:04”,
“RUNTIME”: “2024-08-19 15:35:55”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:36:59”,
“RUNTIME”: “2024-08-19 15:36:04”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:37:12”,
“RUNTIME”: “2024-08-19 15:36:59”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:38:04”,
“RUNTIME”: “2024-08-19 15:37:12”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:38:16”,
“RUNTIME”: “2024-08-19 15:38:04”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:39:12”,
“RUNTIME”: “2024-08-19 15:38:16”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:39:23”,
“RUNTIME”: “2024-08-19 15:39:12”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:40:19”,
“RUNTIME”: “2024-08-19 15:39:23”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:40:32”,
“RUNTIME”: “2024-08-19 15:40:19”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:41:24”,
“RUNTIME”: “2024-08-19 15:40:32”,
“STATUSDESC”: “1”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:41:36”,
“RUNTIME”: “2024-08-19 15:41:24”,
“STATUSDESC”: “3”
},
{
“GROUPID”: “0”,
“END_TIME”: “2024-08-19 15:41:36”,
“RUNTIME”: “2024-08-19 15:47:24”,
“STATUSDESC”: “3”
}]