echarts实现时间甘特图、时间切片、状态切片图效果

坐标轴固定0-24h

以时间甘特图的形式显示某天24小时状态切片图,空白时间轴使用其他背景颜色填充

效果如下:

完整代码如下

var data = [
  {
    "ROW": "4",
    "GROUPID": "0",
    "NAME": "Traffic_14",
    "STATUSDESC": "运行",
    "RUNTIME": "2023-11-27 01:00:00.000",
    "END_TIME": "2023-11-27 11:15:00.000",
    "DIFF_MILLISEC": "0"
}, {
    "ROW": "5",
    "GROUPID": "0",
    "NAME": "Traffic_14",
    "STATUSDESC": "故障",
    "RUNTIME": "2023-11-27 12:15:00.000",
    "END_TIME": "2023-11-27 15:35:00.000",
    "DIFF_MILLISEC": "0"
}, {
    "ROW": "6",
    "GROUPID": "0",
    "NAME": "Traffic_14",
    "STATUSDESC": "运行",
    "RUNTIME": "2023-11-27 15:35:00.000",
    "END_TIME": "2023-11-27 22:35:00.000",
    "DIFF_MILLISEC": "0"
},{
    "ROW": "4",
    "GROUPID": "1",
    "NAME": "Traffic_15",
    "STATUSDESC": "运行",
    "RUNTIME": "2023-11-27 03:00:00.000",
    "END_TIME": "2023-11-27 10:15:00.000",
    "DIFF_MILLISEC": "0"
}, {
    "ROW": "5",
    "GROUPID": "1",
    "NAME": "Traffic_15",
    "STATUSDESC": "故障",
    "RUNTIME": "2023-11-27 10:15:00.000",
    "END_TIME": "2023-11-27 13:35:00.000",
    "DIFF_MILLISEC": "0"
}, {
    "ROW": "6",
    "GROUPID": "1",
    "NAME": "Traffic_15",
    "STATUSDESC": "运行",
    "RUNTIME": "2023-11-27 14:35:00.000",
    "END_TIME": "2023-11-27 21:35:00.000",
    "DIFF_MILLISEC": "0"
}];      

var types = [
    { name: '运行', color: '#1bb57d' },
    { name: '故障', color: '#ff3374' },
    { name: '空闲', color: '#edb217' }
];
var startTime;
var startDate;
startTime = new Date(data[0].RUNTIME).getTime();
startDate = new Date(data[0].RUNTIME);
var datatemp = data.map(d => ({
  name: d.STATUSDESC,
  value: [
    parseInt(d.GROUPID),
    new Date(d.RUNTIME).getTime(),
    new Date(d.END_TIME).getTime()
  ],
  itemStyle: {
    normal: {
      color: types.filter(a => a.name == d.STATUSDESC)[0].color
    }
  }
}));

var groupedData = datatemp.reduce((acc, curr) => {
  if (!acc[curr.value[0]]) {
    acc[curr.value[0]] = [];
  }
  acc[curr.value[0]].push(curr);
  return acc;
}, {});
var gaps = [];

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, b) => 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_
    ]);
  }
});

console.log(gaps)
for (var i = 0; i < gaps.length; i++) {
  var date = new Date(gaps[i][1])
  var year = date.getFullYear();
  var month = (date.getMonth() + 1).toString().padStart(2, '0'); // Months are zero-indexed
  var day = date.getDate().toString().padStart(2, '0');
  var hours = date.getHours().toString().padStart(2, '0');
  var minutes = date.getMinutes().toString().padStart(2, '0');
  var seconds = date.getSeconds().toString().padStart(2, '0');
  
  var sdformattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  
  date = new Date(gaps[i][2])
  year = date.getFullYear();
  month = (date.getMonth() + 1).toString().padStart(2, '0'); // Months are zero-indexed
  day = date.getDate().toString().padStart(2, '0');
  hours = date.getHours().toString().padStart(2, '0');
  minutes = date.getMinutes().toString().padStart(2, '0');
  seconds = date.getSeconds().toString().padStart(2, '0');
  var edformattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  
  console.log("groupID: "  + gaps[i][0] + " st: " + sdformattedDate + " et: " + edformattedDate)
  
}
var categories = Array.from(new Set(data.map(item => item.NAME)));
function renderItem(params, api) {
  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;
}

var now = new Date(); // 获取当前时间
var todayStart = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); // 获取今天的起始时间
var todayEnd = new Date(todayStart.getTime() + 24 * 60 * 60 * 1000+1); // 获取今天的结束时间
var todayEnd_ = new Date(todayStart.getTime() + 24 * 60 * 60 * 1000); // 获取今天的结束时间

const series = [
  {
    type: 'custom',
    renderItem: renderItem,
    encode: {
      x: [1, 2],
      y: 0
    },
    data: datatemp
  },
  {
    type: 'custom',
    renderItem: renderItem,
    z: -1, // 放在最底层
    data: gaps,
  }
];

// 手动添加最后一个数据点,放在当天的最后一秒
option = {
  grid: {
    left: '6%',
    right: '4%',
    bottom: '3%',
    
    containLabel: true,
  },
  tooltip: {
    show: true,
    textStyle: {
      fontSize: 10
    },
    
    position: function (point, params, dom, rect, size) {
       
    var mouseX = point[0];
    var mouseY = point[1];

    // 获取容器的宽度和 tooltip 的宽度
    var containerWidth = size.viewSize[0];
    var tooltipWidth = size.contentSize[0];

    // 调整 tooltip 的位置
    var offsetX = 10; // x 方向偏移量
    var offsetY = 10; // y 方向偏移量
    // 如果 tooltip 超出容器的右侧,将其显示在鼠标的左侧
    if (mouseX + tooltipWidth + offsetX > containerWidth) {
      // 新的位置坐标
      var newX = mouseX - tooltipWidth - offsetX;
      var newY = mouseY + offsetY;

      // 返回新的位置坐标
      return [newX, newY];
    } else {
      // tooltip 显示在鼠标的下方
      var newX = mouseX + offsetX;
      var newY = mouseY + offsetY;

      // 返回新的位置坐标
      return [newX, newY];
    }
  },
    formatter: function (params) {
      let duration = params.value[2] - params.value[1];
      // 将毫秒数转换为小时和分钟
      let hours = Math.floor(duration / (1000 * 60 * 60));
      let s_d = new Date(params.value[1])
      let s_hours = s_d.getHours()
      let s_hours_ = s_hours < 10 ? '0' + s_hours : s_hours;
  
      let s_mins = s_d.getMinutes()
      let s_mins_ = s_mins < 10 ? '0' + s_mins : s_mins;
  
      let e_d = new Date(params.value[2])
      let e_hours = e_d.getHours()
      let e_hours_ = e_hours < 10 ? '0' + e_hours : e_hours;
      //console.log("s_d: " + s_d + " e_d : " + e_d)
      //console.log("params.value[1]" + params.value[1] + " params.value[2] : " + params.value[2])
      let e_mins = e_d.getMinutes()
      let e_mins_ = e_mins < 10 ? '0' + e_mins : e_mins;
      let minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60));
      let minutes_ = minutes < 10 ? '0' + minutes : minutes
  
      if (params.seriesIndex === 1) {
        return params.marker + params.name + '空白时长:'+ hours + '小时' + minutes + '分钟' + '<br/>'
      + params.marker + '时间区间:' + s_hours_ + ':' + s_mins_ + ' - ' + e_hours_ + ':' + e_mins_;
      }
  
      return params.marker + params.name + '时长:'+ hours + '小时' + minutes + '分钟' + '<br/>'
      + params.marker + '时间区间:' + s_hours_ + ':' + s_mins_ + ' - ' + e_hours_ + ':' + e_mins_;
    }
  },     
  dataZoom: [
          {
            type: 'inside',
            filterMode: 'none',
            showDataShadow: false,
            show: true,
          }
        ],    
    xAxis: {   
            type: 'time',
            min: todayStart, // 设置最小值为今天的起始时间
            max: todayEnd,
            //min: new Date(startTime).setHours(0, 0, 0, 0),
            //max: new Date(startTime + 24 * 60 * 60 * 1000+1),
            axisLabel: {
              formatter: function (value) {
                  var date = new Date(value);
                  var hours = date.getHours();
                  var minutes = date.getMinutes();
                  //console.log(date)
                  if(date.getTime() == todayEnd_.getTime())
                  {
                    return '24h'
                  }
                  return `${hours}h`;
              },  // 标签与轴线紧挨在一起
              padding: [0, 0, 0, 0], // 标签的内边距(可根据实际情况调整)
            },
            splitNumber: 6,
            splitLine: {
              show: false
            },
            axisLine: {
              show: true  // 隐藏坐标轴线
            },
            axisTick: {
              show: true  // 隐藏刻度线
            },
        },
    yAxis: [
        {
            yAxisIndex: 0,
            type: 'category',
            data: categories,
            axisLine: {
                show: true
            },
            axisTick: {
                show: true
            },
            axisLabel: {
                show: true,
            }
        },
    ],
    series: series
};

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现多层甘特图可以通过使用echarts的gantt组件,结合vue的组件化实现。 首先,在vue项目中安装echarts: ``` npm install echarts --save ``` 然后,在vue组件中引入echarts: ```javascript import echarts from 'echarts' ``` 接着,在vue组件中定义一个gantt组件,并在其中使用echarts: ```vue <template> <div class="gantt-chart"></div> </template> <script> export default { name: 'GanttChart', props: { data: { type: Array, required: true }, height: { type: String, default: '500px' } }, mounted() { this.initChart() }, methods: { initChart() { const chart = echarts.init(document.querySelector('.gantt-chart')) chart.setOption({ tooltip: { formatter: function (params) { return params.marker + params.name + ': ' + new Date(params.start).toLocaleDateString() + ' - ' + new Date(params.end).toLocaleDateString() } }, grid: { top: 10, left: 100, bottom: 30, right: 30 }, xAxis: { min: new Date(2021, 0, 1), max: new Date(2022, 0, 1) }, yAxis: { type: 'category', data: ['Layer 1', 'Layer 2', 'Layer 3'] }, series: [{ type: 'gantt', data: this.data, barWidth: 20, label: { show: true, formatter: function (params) { return params.name } } }] }) } } } </script> ``` 在上述代码中,我们定义了一个名为GanttChart的vue组件,该组件接收两个props:data和height,其中data是一个数组,包含了多层甘特图的数据,height是表的高度,默认为500px。 在mounted方法中,我们调用了initChart方法,该方法使用echarts.init初始化了一个echarts实例,并使用setOption方法设置了表的各种配置项,包括tooltip、grid、xAxis、yAxis和series。 其中,series中的type属性设置为gantt,表示使用gantt组件,data属性设置为props中传入的data数组,barWidth属性设置为20,表示一个甘特图的宽度为20像素,label的formatter属性设置为params.name,表示在每个甘特图上显示其名称。 最后,在vue组件中使用该组件: ```vue <template> <div class="gantt-chart-wrapper"> <gantt-chart :data="ganttData" :height="'600px'" /> </div> </template> <script> import GanttChart from './GanttChart.vue' export default { name: 'App', components: { GanttChart }, data() { return { ganttData: [ { name: 'Task 1', start: new Date(2021, 0, 1), end: new Date(2021, 3, 1), y: 0 }, { name: 'Task 2', start: new Date(2021, 3, 1), end: new Date(2021, 6, 1), y: 0 }, { name: 'Task 3', start: new Date(2021, 6, 1), end: new Date(2021, 9, 1), y: 1 }, { name: 'Task 4', start: new Date(2021, 9, 1), end: new Date(2022, 0, 1), y: 2 } ] } } } </script> ``` 在上述代码中,我们在App组件中使用GanttChart组件,并传入了ganttData作为props的data属性,该数组包含了4个甘特图,分别属于三个不同的层级。其中,y属性表示当前甘特图所属的层级,从0开始计数。我们还为GanttChart组件设置了一个高度为600px。 通过上述代码,我们就可以在vue中实现多层甘特图了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值