You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Vue-echarts创建单行展示事件的交互式图表?

使用Vue-Echarts创建时间轴堆叠事件图表方案

一、数据预处理

首先需要把原始时间数据转换成Echarts可识别的时间戳,同时处理end为null的未结束事件(可设为当日24点或当前时间,按需选择)。

示例处理函数:

const processEventData = (events) => {
  if (!events.length) return [];
  const startTime = new Date(events[0].start).getTime();
  // 当日24点作为未结束事件的默认结束时间
  const endOfDay = new Date(startTime).setHours(24, 0, 0, 0);
  
  return events.map(event => {
    const start = new Date(event.start).getTime();
    const end = event.end ? new Date(event.end).getTime() : endOfDay;
    // 计算相对于起始时间的偏移和时长(单位:秒)
    const offset = (start - startTime) / 1000;
    const duration = (end - start) / 1000;
    
    return {
      offset,
      duration,
      original: event // 保留原始数据用于交互逻辑
    };
  });
};

二、Vue-Echarts组件完整实现

采用横向堆叠柱状图适配需求,将X轴设为时间类型,Y轴仅保留单个分类(隐藏冗余标签),同时添加交互和响应式配置:

<template>
  <v-chart :option="chartOption" :autoresize="true" @click="handleEventClick" />
</template>

<script setup>
import { ref, computed, onMounted } from 'vue';
import VChart from 'vue-echarts';
import { use } from 'echarts/core';
import { BarChart } from 'echarts/charts';
import {
  TooltipComponent,
  GridComponent,
  DataZoomComponent
} from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';

// 注册Echarts所需模块
use([
  BarChart,
  TooltipComponent,
  GridComponent,
  DataZoomComponent
]);

const rawEvents = ref([]); // 存储API获取的原始数据
const processedData = computed(() => processEventData(rawEvents.value));

// 图表核心配置
const chartOption = computed(() => {
  if (!rawEvents.value.length) return {};
  
  const startTime = new Date(rawEvents.value[0].start);
  const endOfDay = new Date(startTime).setHours(24, 0, 0, 0);
  
  return {
    tooltip: {
      trigger: 'item',
      formatter: (params) => {
        const event = params.data.original;
        const startStr = new Date(event.start).toLocaleString();
        const endStr = event.end ? new Date(event.end).toLocaleString() : '进行中';
        return `
          <div>
            <p>开始时间:${startStr}</p>
            <p>结束时间:${endStr}</p>
            <p>时长:${event.duration}分钟</p>
          </div>
        `;
      }
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '15%',
      containLabel: true
    },
    xAxis: {
      type: 'time',
      min: startTime.getTime(),
      max: endOfDay,
      axisLabel: {
        formatter: (value) => new Date(value).toLocaleString(),
        rotate: 45, // 旋转标签避免重叠
        interval: 'auto' // 自动调整标签密度
      },
      axisLine: { onZero: false }
    },
    yAxis: {
      type: 'category',
      data: ['事件时间线'],
      axisTick: { show: false },
      axisLabel: { show: false } // 隐藏Y轴冗余标签
    },
    dataZoom: [
      {
        type: 'inside',
        xAxisIndex: 0,
        start: 0,
        end: 100
      },
      {
        show: true,
        xAxisIndex: 0,
        type: 'slider',
        bottom: '5%',
        start: 0,
        end: 100
      }
    ],
    series: [
      {
        type: 'bar',
        stack: 'total',
        data: processedData.value.map(item => ({
          value: [
            item.offset * 1000 + startTime.getTime(),
            item.duration * 1000 + item.offset * 1000 + startTime.getTime()
          ],
          original: item.original
        })),
        itemStyle: {
          borderRadius: 4,
          color: (params) => {
            // 用固定色组区分不同事件,避免随机色混乱
            const colors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de'];
            return colors[params.dataIndex % colors.length];
          }
        },
        label: {
          show: true,
          formatter: (params) => {
            // 短事件隐藏标签,避免重叠
            return params.data.original.duration > 5 ? `${params.data.original.duration}min` : '';
          }
        }
      }
    ]
  };
});

// 事件点击交互逻辑
const handleEventClick = (params) => {
  console.log('选中事件详情:', params.data.original);
  // 可扩展弹窗、跳转详情页等功能
};

// 模拟API数据请求
onMounted(() => {
  rawEvents.value = [
    {
      start: "2023-11-01T01:47:09.930Z",
      end: "2023-11-01T02:47:09.930Z",
      duration: 60
    },
    {
      start: "2023-11-01T02:47:09.930Z",
      end: "2023-11-01T02:57:09.930Z",
      duration: 10
    },
    {
      start: "2023-11-01T02:57:09.930Z",
      end: "2023-11-01T03:27:09.930Z",
      duration: 30
    },
    {
      start: "2023-11-01T03:27:09.930Z",
      end: null,
      duration: 30
    }
  ];
});

// 数据处理函数
const processEventData = (events) => {
  if (!events.length) return [];
  const startTime = new Date(events[0].start).getTime();
  const endOfDay = new Date(startTime).setHours(24, 0, 0, 0);
  
  return events.map(event => {
    const start = new Date(event.start).getTime();
    const end = event.end ? new Date(event.end).getTime() : endOfDay;
    const offset = (start - startTime) / 1000;
    const duration = (end - start) / 1000;
    
    return {
      offset,
      duration,
      original: event
    };
  });
};
</script>

三、核心优化说明

  • 时间轴适配:X轴用time类型自动处理刻度,避免手动计算偏移导致的标签混乱
  • 交互支持:内置tooltip展示事件详情,dataZoom组件支持缩放查看短事件细节
  • 响应式适配:开启autoresize属性,结合百分比网格配置,自动适配手机、平板屏幕
  • 大数据兼容:通过时间轴刻度自动优化和标签显示规则,支持数百条事件数据不重叠
  • 未结束事件处理:统一将end为null的事件设为当日24点,保证图表完整性

四、替代工具建议

如果后续需要更复杂的时间轴交互,可考虑:

  • TimelineJS:专注时间序列展示,配置简单
  • D3.js:高度自定义,但开发成本较高
    不过基于项目已使用Vue-Echarts的现状,优先扩展现有组件更高效

内容的提问来源于stack exchange,提问作者Diego Felipe Florença

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 05:33:13