项目执行进度跟踪

描述:当前是关于Echarts图表中的 折线图 示例。
 
            const HEIGHT_RATIO = 0.6;
const DIM_CATEGORY_INDEX = 0;
const DIM_TIME_ARRIVAL = 1;
const DIM_TIME_DEPARTURE = 2;
const ACTUAL_TIME_ARRIVAL = 3;
const _cartesianXBounds = [];
const _cartesianYBounds = [];
const rowData = {
        flight: {
          data: [
            [7, '2020-1-1', '2020-1-2', '2020-1-9', 'Project Kick Off'],
            [6, '2020-1-1', '2020-1-31', '2020-1-24', 'Requirement confirm'],
            [5, '2020-2-1', '2020-4-30', '2020-4-30', 'Development'],
            [4, '2020-5-1', '2020-5-31', '2020-6-30', 'SIT'],
            [3, '2020-6-1', '2020-6-30', '2020-7-15', 'UAT'],
            [2, '2020-7-1', '2020-7-15', '2020-7-23', 'Go live'],
            [1, '2020-7-16', '2020-7-31', '', 'Training'],
            [0, '2020-8-1', '2020-8-31', '', 'Payment'],
          ],
          dimensions: ['Parking Apron Index', '开始时间', '结束时间', '实际结束时间'],
        },
        parkingApron: {
          dimensions: ['Name', 'Type'],
          data: [
            ['Task01', 'Project Kick Off'],
            ['Task02', 'Requirement confirm'],
            ['Task03', 'Development'],
            ['Task04', 'SIT'],
            ['Task05', 'UAT'],
            ['Task06', 'Go live'],
            ['Task07', 'Training'],
            ['Task08', 'Payment'],
          ],
        },
      }

 function clipRectByRect ( params,rect){
    return echarts.graphic.clipRectByRect(rect, {
      x: params.coordSys.x,
      y: params.coordSys.y,
      width: params.coordSys.width,
      height: params.coordSys.height,
    })
  }

  function renderGanttItem (params,api) {
    const categoryIndex = api.value(DIM_CATEGORY_INDEX);
    const timeStart = api.coord([api.value(DIM_TIME_ARRIVAL), categoryIndex]);
    const timeEnd = api.coord([api.value(DIM_TIME_DEPARTURE), categoryIndex]);
    const actualTimeArrival = api.coord([api.value(ACTUAL_TIME_ARRIVAL), categoryIndex]);
    const { coordSys } = params;
    _cartesianXBounds[0] = coordSys.x;
    _cartesianXBounds[1] = coordSys.x + coordSys.width;
    _cartesianYBounds[0] = coordSys.y;
    _cartesianYBounds[1] = coordSys.y + coordSys.height;
    const barLength1 = timeEnd[0] - timeStart[0];
    const barLength2 = timeEnd[0] - actualTimeArrival[0];
    const barLength3 = actualTimeArrival[0] - timeEnd[0];
    // Get the heigth corresponds to length 1 on y axis.
    const barHeight = api.size([0, 1])[1] * HEIGHT_RATIO;
    const rectNormal = clipRectByRect(params, {
      x: timeStart[0],
      y: timeStart[1] - barHeight,
      width: barLength1,
      height: barHeight,
    });
    const rectBefore = clipRectByRect(params, {
      x: actualTimeArrival[0],
      y: actualTimeArrival[1] - barHeight,
      width: barLength2,
      height: barHeight,
    });
    const rectAfter = clipRectByRect(params, {
      x: timeEnd[0],
      y: timeEnd[1] - barHeight,
      width: barLength3,
      height: barHeight,
    });
    return {
      type: 'group',
      children: [
        {
          type: 'rect',
          ignore: !rectNormal,
          shape: rectNormal,
          style: api.style({ fill: '#fac758' }), // 黄色区域-开始时间到结束时间
        },
        {
          type: 'rect',
          ignore: !rectBefore,
          shape: rectBefore,
          style: api.style({ fill: '#3ba272' }), // 绿色区域 - 提前结束
        },
        {
          type: 'rect',
          ignore: !rectAfter,
          shape: rectAfter,
          style: api.style({ fill: '#ee6666' }), // 红色区域-延迟
        },
      ],
    };
  }
  
  function renderAxisLabelItem (params, api){
    const y = api.coord([0, api.value(0)])[1];
    if (y < params.coordSys.y + 5) {
      return;
    }
    // eslint-disable-next-line consistent-return
    return {
      type: 'group',
      position: [10, y],
      children: [
        {
          type: 'path', // task区域
          shape: {
            d: 'M0,0 L0,-20 L30,-20 C42,-20 38,-1 50,-1 L70,-1 L70,0 Z',
            x: 0,
            y: -20,
            width: 90,
            height: 20,
            layout: 'cover',
          },
          style: {
            fill: '#368c6c',
          },
        },
        {
          type: 'text', // task字体
          style: {
            x: 24,
            y: -3,
            text: api.value(1),
            textVerticalAlign: 'bottom',
            textAlign: 'center',
            textFill: '#fff',
          },
        },
        {
          type: 'text', // 项目名称
          style: {
            x: 60,
            y: -2,
            textVerticalAlign: 'bottom',
            textAlign: 'left',
            text: api.value(2),
            textFill: '#000',
          },
        },
      ],
    };
  }
  
   function makeOption() {
      return {
        tooltip: {
          trigger: 'item',
          show: true,
          axisPointer: {
            type: 'line',
          },
          formatter(params) {
            if (params && params.length > 0) {
              return '';
            }
            const { data, value } = params;
            const end = value[2] ? new Date(value[2]).getTime() : 0;
            const actual = value[3] ? new Date(value[3]).getTime() : 0;
            let color = '#ddb30b';
            if (actual - end > 0) {
              color = '#ee6666';
            } else if (actual - end < 0) {
              color = '#3ba272';
            }
            const marker = `<span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:${color};"></span>`;
            return `${data[4]} <br/>${marker}开始时间:${value[1]}<br/>${marker}结束时间:${value[2]}<br/>${marker}实际结束时间:${value[3]}`;
          },
        },
        animation: false,
        grid: {
          show: true,
          top: 70,
          bottom: 20,
          left: 200,
          right: 20,
          backgroundColor: '#fff',
          borderWidth: 0,
        },
        dataZoom: [
          {
            type: 'slider',
            xAxisIndex: 0,
            filterMode: 'weakFilter',
            height: 20,
            bottom: 0,
            start: 0,
            end: 100,
            handleIcon:
              'path://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.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
            handleSize: '80%',
            showDetail: false,
          },
          {
            type: 'inside',
            id: 'insideX',
            xAxisIndex: 0,
            filterMode: 'weakFilter',
            start: 0,
            end: 100,
            zoomOnMouseWheel: false,
            moveOnMouseMove: true,
          },
          {
            type: 'slider',
            yAxisIndex: 0,
            zoomLock: true,
            width: 10,
            right: 10,
            top: 70,
            bottom: 20,
            start: 100,
            end: 0,
            handleSize: 0,
            showDetail: false,
          },
          {
            type: 'inside',
            id: 'insideY',
            yAxisIndex: 0,
            start: 95,
            end: 100,
            zoomOnMouseWheel: false,
            moveOnMouseMove: true,
            moveOnMouseWheel: true,
          },
        ],
        xAxis: {
          type: 'time',
          position: 'top',
          splitLine: {
            alignWithLabel: true,
            show: true,
            lineStyle: {
              color: ['#E9EDFF'],
            },
          },
          axisLine: {
            show: false,
          },
          axisTick: {
            show: false,
            lineStyle: {
              color: '#929ABA',
            },
          },
          axisLabel: {
            color: '#929ABA',
            inside: false,
            align: 'center',
            formatter: (param) => {
              const formatDate = new Date(param);
              return `${formatDate.getFullYear()}-${
                formatDate.getMonth() + 1
              }-${formatDate.getDate()}`;
            },
          },
          axisPointer: {
            show: true,
            label: {
              backgroundColor: '#004f53',
              margin: -20,
            },
            lineStyle: {
              color: '#9fbfcd',
              type: 'solid',
              width: 1.5,
            },
          },
        },
        yAxis: {
          axisTick: { show: false },
          splitLine: { show: false },
          axisLine: { show: false },
          axisLabel: { show: false },
          min: 0,
          axisPointer: {
            show: false,
          },
          max: rowData.parkingApron.data.length,
        },
        legend: {
          show: true,
          data: [
            {
              name: '提前完成',
              itemStyle: { color: '#3ba272' },
            },
            {
              name: '延期完成',
              itemStyle: { color: '#ee6666' },
            },
          ],
        },
        series: [
          {
            id: 'flightData1',
            type: 'custom',
            name: '提前完成',
            renderItem: renderGanttItem,
            dimensions: rowData.flight.dimensions,
            encode: {
              x: [DIM_TIME_ARRIVAL, DIM_TIME_DEPARTURE, ACTUAL_TIME_ARRIVAL],
              y: DIM_CATEGORY_INDEX,
              tooltip: [DIM_TIME_ARRIVAL, DIM_TIME_DEPARTURE, ACTUAL_TIME_ARRIVAL],
            },
            data: rowData.flight.data,
          },
          {
            id: 'flightData2',
            type: 'custom',
            name: '延期完成',
            renderItem: renderGanttItem,
            dimensions: rowData.flight.dimensions,
            encode: {
              x: [DIM_TIME_ARRIVAL, DIM_TIME_DEPARTURE, ACTUAL_TIME_ARRIVAL],
              y: DIM_CATEGORY_INDEX,
              tooltip: [DIM_TIME_ARRIVAL, DIM_TIME_DEPARTURE, ACTUAL_TIME_ARRIVAL],
            },
            data: rowData.flight.data,
          },
          {
            type: 'custom',
            renderItem: renderAxisLabelItem,
            dimensions: rowData.parkingApron.dimensions,
            encode: {
              x: -1,
              y: 0,
            },
            data: rowData.parkingApron.data.map((item, index) => {
              return [rowData.parkingApron.data.length - 1 - index].concat(item);
            }),
          },
        ],
      };
    }
  
option = makeOption()