使用EChars实现较为复杂的甘特图,包含多任务组合,完整到mock好数据的代码

最近接到一个将多人任务进度做成甘特图的需求,本以为用echars很容易就实现了,没想到啊,断断续续搞的头大,大概就是使用EChars官网的实栗不足以打到需求目标,所以开始了我为期一周的修修补补,终于是完成了,写一下总结:

包含了缩放、横轴分组显示一行展示多条内容、以及数据之间的对比效果

首先效果图上个: 数据mock有点少,可自行添加,项目中数据量大,我添加了分页,可按需添加
在这里插入图片描述
点击效果:在这里插入图片描述
点击对比效果:
在这里插入图片描述
右边的点击效果这次需求赶还没有添加,后续更新。。。上代码啦

加粗!! 完整到带mock数据的代码~~

index.vue

<style scoped lang="scss" src="./_.scss" />
<style lang="scss" src="./index.scss" />
<template lang="pug" src="./_.pug" />
<script src="./_.js" />

_.js

/**
 * 工序多人模式甘特图
 * 甘特图官网:https://echarts.apache.org/zh/option.html#title
 */

import echarts from 'echarts';
import moment from 'moment';
import * as R from 'ramda';

import {
    
     MOCKData, employeesMOCK } from './constant';

// 注意echarts版本,高版本不支持透明度rgba,以下代码版本为3.8.5
export default {
    
    
  name: 'instructGanttCopy',

  data() {
    
    
    return {
    
    
      targetDate: moment().format('YYYY-MM-DD'),
      ganttChart: null,
      chartData: [],
      employeeIds: {
    
    },
      employeeIdsTable: [],
    };
  },

  computed: {
    
    
  },

  mounted() {
    
    
    window.addEventListener(
      'resize',
      window._.debounce(() => {
    
    
        this.resizeCharts();
      }, 200),
    );
    this.searchResult();
    // this.initChart();
  },

  methods: {
    
    

    searchResult() {
    
    
      this.chartData = (R.map(item => ({
    
    
        ...item,
        factStartTime: moment(item.factStartTime).format('YYYY-MM-DD') === '1971-01-01' ? '--' : item.factStartTime,
        factEndTime: moment(item.factEndTime).format('YYYY-MM-DD') === '1971-01-01' ? '--' : item.factEndTime,
      }), MOCKData)) || [];
      const setEmp = (!R.isEmpty(MOCKData) && [...new Set(MOCKData.map(x => x.employeeId))]) || [];
      const emp = (setEmp && !R.isEmpty(setEmp) && setEmp.map((item, index) => {
    
    
        const noPeopleEquipments = R.map(x => !R.isEmpty(x) && `${
      
      x.equipments},`, R.filter(employee => !R.isEmpty(employee) && employee.employeeId === 'no_people', MOCKData));
        return {
    
    
          [item]: {
    
    
            ...employeesMOCK[index],
            employeeOrEquipments: item === 'no_people' ? ((!R.isEmpty(noPeopleEquipments) && [...new Set(noPeopleEquipments)]) || '') : item,
          },
        };
      })) || {
    
    };
      this.employeeIds = emp && !R.isEmpty(emp) && R.reduce((cur, per) => Object.assign(cur, per), {
    
    }, emp);
      this.employeeIdsTable = Object.values(this.employeeIds);
      this.initChart();
    },


    // 构造y轴
    getYaxis() {
    
    
      const groupYaxis = R.groupWith((a, b) => a.groupId === b.groupId, this.chartData);
      const Yaxis = groupYaxis.map(x => x[0].viewName);
      return Yaxis;
    },

    // 构造图形
    getSeries() {
    
    
      const series = [];
      const self = this;
      // 分组
      const groups = R.groupWith((a, b) => a.groupId === b.groupId, this.chartData);
      const length = R.reduce((count, x) => x.length > count ? x.length : count, 0, groups);
      const dataList = [];
      for (let index = 0; index < length; index++) {
    
    
        const data = groups.map(x => x[index]);
        dataList.push(data);
      }

      const selectColor = (defaultColor, kind, params) => {
    
    
        let defColor = defaultColor;
        if (groups && !R.isEmpty(groups) && params && !R.isEmpty(params) && params.data && !R.isEmpty(params.data)) {
    
    
          const employee = R.filter(emp => emp && emp.employeeId === params.data.name, this.chartData.filter(groupColor => groupColor && params.data.groupId === groupColor.groupId));
          const employeeId = (!R.isEmpty(employee) && employee[0] && employee[0].employeeId) || '';
          //  颜色设置
          defColor = (employeeId && this.employeeIds[employeeId] && this.employeeIds[employeeId][kind]) || defaultColor;
        }
        return defColor;
      };

      dataList.map((group, index) => series.push({
    
    
        name: '调整时间:',
        type: 'bar',
        stack: '调整时间',
        label: {
    
    
          show: true,
          position: 'insideRight',
        },
        itemStyle: {
    
    
          normal: {
    
    
            color: 'rgba(0,0,0,0)',
          },
        },
        data: group.map(item => ({
    
    
          value: item && item.adjustStartTime,
          name: item && item.employeeId,
          groupId: item && item.groupId,
        }) || ''),
        showSymbol: false,
        hoverAnimation: false,
      },
        {
    
    
          name: '调整时间',
          type: 'bar',
          stack: '调整时间',
          label: {
    
    
            show: true,
            position: 'insideRight',
          },
          barWidth: 8, // 柱宽度
          itemStyle: {
    
    
            normal: {
    
    
              barBorderRadius: 4, // 柱圆角
              color(params) {
    
    
                const color = 'rgb(14,14,14)';
                return selectColor(color, 'adjust', params);
              },
            },
          },
          data: group.map(item => ({
    
    
            value: item && item.adjustEndTime,
            name: item && item.employeeId,
            groupId: item && item.groupId,
          }) || ''),
          showSymbol: false,
          hoverAnimation: false,
        },
        {
    
    
          name: '期望时间:',
          type: 'bar',
          stack: '期望时间',
          barWidth: 8, // 柱宽度
          itemStyle: {
    
    
            normal: {
    
    
              color: 'rgba(0,0,0,0)',
            },
          },
          data: group.map(item => ({
    
    
            value: item && item.expectStartTime,
            name: item && item.employeeId,
            groupId: item && item.groupId,
          }) || ''),
          showSymbol: false,
          hoverAnimation: false,
        },
        {
    
    
          name: '期望时间',
          type: 'bar',
          stack: '期望时间',
          barWidth: 8, // 柱宽度
          itemStyle: {
    
    
            normal: {
    
    
              barBorderRadius: 4, // 柱圆角
              color(params) {
    
    
                const color = 'rgba(14,14,14,0.7)';
                return selectColor(color, 'expect', params);
              },
            },
          },
          data: group.map(item => ({
    
    
            value: item && item.expectEndTime,
            name: item && item.employeeId,
            groupId: item && item.groupId,
          }) || ''),
          showSymbol: false,
          hoverAnimation: false,
        },
        {
    
    
          name: '实际时间:',
          type: 'bar',
          stack: '实际时间',
          itemStyle: {
    
    
            normal: {
    
    
              color: 'rgba(0,0,0,0)',
            },
          },
          data: group.map(item => ({
    
    
            value: item && item.factStartTime,
            name: item && item.employeeId,
            groupId: item && item.groupId,
          }) || ''),
          showSymbol: false,
          hoverAnimation: false,
        },
        {
    
    
          name: '实际时间',
          type: 'bar',
          stack: '实际时间',
          barWidth: 8, // 柱宽度
          itemStyle: {
    
    
            normal: {
    
    
              barBorderRadius: 4, // 柱圆角
              color(params) {
    
    
                const color = 'rgba(14,14,14,0.4)';
                return selectColor(color, 'fact', params);
              },
            },
          },

          data: group.map(item => ({
    
    
            value: item && item.factEndTime,
            name: item && item.employeeId,
            groupId: item && item.groupId,
          }) || ''),
          showSymbol: false, // 数据卡顿
          showAllSymbol: false,
        },
      ));
      return series;
    },

    async initChart() {
    
    
      const gantt = this.$refs.chart;
      if (gantt) {
    
    
        this.ganttChart = echarts.init(gantt);
        // const self = this;
        const chartOption = {
    
    
          title: {
    
    
            text: '任务进度表',
            left: 10,
          },
          grid: {
    
    
            containLabel: true,
            left: 10,
          },

          // 横轴
          xAxis: {
    
    
            type: 'time',
            min: '2020-05-23 00:00',
            max: '2020-05-23 23:59',
            // min: '00:00',
            // max: '23:59',
            maxInterval: 60 * 1000 * 30, // 自动计算的坐标轴最大间隔大小为半小时
            axisLabel: {
    
    
              formatter(params) {
    
    
                return (moment(params).format('HH:mm'));
              },
              rotate: 60,
            },
          },

          // 纵轴
          yAxis: {
    
    
            data: this.getYaxis(),
            inverse: true, // 设置反向坐标轴
            nameLocation: 'start',
            axisLabel: {
    
    
              formatter(params) {
    
    
                return params && params.replace(/.{9}(?!$)/g, a => `${
      
      a}\n`);
              },
              // margin: 10,
              lineHeight: 40,
              verticalAlign: 'middle',
              align: 'right',
            },
            axisLine: {
    
    
              show: true,
            },
            splitLine: {
    
    
              show: true,
            },
          },

          legend: {
    
    
            data: ['调整时间', '期望时间', '实际时间'],
          },

          series: this.getSeries(),

          // 提示框组件。
          tooltip: {
    
    
            trigger: 'axis',
            axisPointer: {
    
    
              type: 'shadow',
              animation: false,
            },
            formatter(params) {
    
    
              let str = `${
      
      params[0].name} </br>`;
              for (let i = 0; i < params.length; i++) {
    
    
                str += `${
      
      i % 2 === 0 ? params[i].seriesName : ''} ${
      
      params[i] && params[i].value && moment(params[i].value).format('HH:mm')}${
      
      i % 2 ? '</br>' : '~'}`;
              }
              return str;
            },

          },

          // 选中行置灰
          toolbox: {
    
    
            show: true,
            feature: {
    
    
              saveAsImage: {
    
    },
            },
          },

          // 缩放
          dataZoom: [{
    
    
            type: 'slider',
            filterMode: 'weakFilter',
            showDataShadow: false,
            bottom: 50,
            height: 10,
            borderColor: 'transparent',
            backgroundColor: '#e2e2e2',
            handleIcon: 'M10.7,11.9H9.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z', // jshint ignore:line
            handleSize: 20,
            zoomOnMouseWheel: false,
            moveOnMouseMove: false,
            handleStyle: {
    
    
              shadowBlur: 6,
              shadowOffsetX: 1,
              shadowOffsetY: 2,
              shadowColor: '#aaa',
            },
            labelFormatter: '',
          }],
        };
        // 第二个参数:true: 是否和之前设置的option进行合并,true为不合并,默认为false:合并
        this.ganttChart.setOption(chartOption);
        this.resizeCharts();
      } else {
    
    
        console.log('gantt', gantt);
      }
    },
    resizeCharts() {
    
    
      if (this.ganttChart) {
    
    
        this.ganttChart.resize();
      }
    },
    destroyed() {
    
    
      window.removeEventListener('resize', this.resizeCharts); // 销毁事件
    },
  },
};

_.pug
该文件为html的另一种表现形式,具体转换参考:http://html2jade.org/

.content-wrapper
  .demo
    .chartTitle
      p.title 员工/设备 & 色系:
      el-table(
        :data='employeeIdsTable'
        border
        size='mini'
      )
        el-table-column(label='员工' prop='employeeOrEquipments' align='center' width='100px')
        el-table-column(label='色系' align='center' width='100px')
          template(slot-scope="scope")
            .opera-panel
              span.employeeItem(:style='{backgroundColor:scope.row.adjust}')
              span.employeeItem(:style='{backgroundColor:scope.row.expect}')
              span.employeeItem(:style='{backgroundColor:scope.row.fact}')
    .gantt-chart(ref="chart")

_.scss



.demo{
    
    
    position: relative;
    width: 100%;
    overflow: scroll;

    .chartTitle {
    
    
        position: absolute;
        right: 0;
        right: -200px;
        top: 5%;
        // top: 0;
        transform: translateX(-50%);
        z-index: 999;
        .employee {
    
    
            display: inline-block;
            display: flex;
            border-radius: 5px;
            vertical-align: center;
            margin: 5px 0;
            padding-left: 5px;
            padding-right: 5px;
            text-align: center;
            justify-content: center;
            // background-color:rgba(205,120,0,1);
        }
        .employeeItem{
    
    
            width: 30px;
            height: 14px;
        }
        .title{
    
    
            font-size: 14px;
            font-weight: 500;
            padding-bottom: 5px;
        }
        .employeeName{
    
    
            width:100px ;
            text-align: end;
            word-wrap:break-word ;
        }
    }
    .gantt-chart {
    
    
        margin: 1em auto;
        height: 500px; // 高度必须添加,不然甘特图无法渲染
        max-width: 80%;
    }
}

constant.js

// mock 数据
export const MOCKData = [{
    
    
  shopCode: '100023003',
  shopName: '双营路天畅园1号楼店',
  viewId: 2068456,
  viewName: '任务一',
  viewDesc: '',
  employeeId: '员工-1',
  groupId: 29,
  equipments: '[]',
  expectStartTime: '2020-05-23 09:00:00',
  expectEndTime: '2020-05-23 09:30:00',
  adjustStartTime: '2020-05-23 09:00:00',
  adjustEndTime: '2020-05-23 09:40:00',
  factStartTime: '2020-05-23 09:00:00',
  factEndTime: '2020-05-23 09:30:00',
}, {
    
    
  shopCode: '100023003',
  shopName: '双营路天畅园1号楼店',
  viewId: 2068456,
  viewName: '任务一',
  viewDesc: '',
  employeeId: '员工-1',
  groupId: 29,
  equipments: '[]',
  expectStartTime: '2020-05-23 13:30:00',
  expectEndTime: '2020-05-23 15:30:00',
  adjustStartTime: '2020-05-23 14:00:00',
  adjustEndTime: '2020-05-23 15:40:00',
  factStartTime: '2020-05-23 14:40:00',
  factEndTime: '2020-05-23 14:00:00',
}, {
    
    
  shopCode: '100023003',
  shopName: '双营路天畅园1号楼店',
  viewId: 2068457,
  viewName: '任务二',
  viewDesc: '',
  employeeId: '员工-1',
  groupId: 21,
  equipments: '[{"276":"万能蒸烤箱_AWE- 0623DXB_750 * 630 * 665"}]',
  expectStartTime: '2020-05-23 09:00:00',
  expectEndTime: '2020-05-23 09:30:00',
  adjustStartTime: '2020-05-23 09:00:00',
  adjustEndTime: '2020-05-23 09:40:00',
  factStartTime: '2020-05-23 09:00:00',
  factEndTime: '2020-05-23 09:30:00',
},
{
    
    
  shopCode: '100023003',
  shopName: '双营路天畅园1号楼店',
  viewId: 2068457,
  viewName: '任务二',
  viewDesc: '',
  employeeId: '员工-2',
  groupId: 21,
  equipments: '[{"276":"万能蒸烤箱_AWE- 0623DXB_750 * 630 * 665"}]',
  expectStartTime: '2020-05-23 10:30:00',
  expectEndTime: '2020-05-23 14:30:00',
  adjustStartTime: '2020-05-23 11:00:00',
  adjustEndTime: '2020-05-23 15:40:00',
  factStartTime: '2020-05-23 11:40:00',
  factEndTime: '2020-05-23 16:00:00',
},
{
    
    
  shopCode: '100023003',
  shopName: '双营路天畅园1号楼店',
  viewId: 2068458,
  viewName: '任务三',
  viewDesc: '',
  employeeId: '员工-2',
  groupId: 27,
  equipments: '[]',
  expectStartTime: '2020-05-23 16:13:00',
  expectEndTime: '2020-05-23 16:50:00',
  adjustStartTime: '2020-05-23 16:13:00',
  adjustEndTime: '2020-05-23 17:13:00',
  factStartTime: '1971-01-01 16:13:00',
  factEndTime: '1971-01-01 17:13:00',
},
{
    
    
  shopCode: '100023003',
  shopName: '双营路天畅园1号楼店',
  viewId: 2068458,
  viewName: '任务四',
  viewDesc: '',
  employeeId: '员工-1',
  groupId: 20,
  equipments: '[]',
  expectStartTime: '2020-05-23 17:13:00',
  expectEndTime: '2020-05-23 17:50:00',
  adjustStartTime: '2020-05-23 17:13:00',
  adjustEndTime: '2020-05-23 18:13:00',
  factStartTime: '1971-01-01 17:13:00',
  factEndTime: '1971-01-01 18:30:00',
}];


export const employeesMOCK = [{
    
    
  adjust: 'rgb(14,14,14)',
  expect: 'rgba(14,14,14,0.7)',
  fact: 'rgba(14,14,14,0.4)',
}, {
    
    
  adjust: 'rgb(208,33,37)',
  expect: 'rgba(208,33,37,0.7)',
  fact: 'rgba(208,33,37,0.4)',
}, {
    
    
  adjust: 'rgb(80,144,152)',
  expect: 'rgba(80,144,152,0.7)',
  fact: 'rgba(80,144,152,0.4)',
}, {
    
    
  adjust: 'rgb(10,144,100)',
  expect: 'rgba(10,144,100,0.7)',
  fact: 'rgba(10,144,100,0.4)',
},
];

猜你喜欢

转载自blog.csdn.net/khadijiah/article/details/106301707
今日推荐