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 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"
- }
- ]
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |