vue專案使用Echarts製作專案工期甘特圖

鵬多多發表於2021-06-28

1,前言


專案迭代過程中,碰上一個需求,要求用甘特圖的方式顯示專案的工期進度,開完會我趕緊搜尋一下甘特圖是啥東東,大概瞭解之後,做出瞭如下樣式

Echarts版本4.5.0

vue版本2.x
甘特圖

2,佈局和資料部分


HTML部分

<template>
	<div id="index">
		<div id="chart" />
	</div>
</template>

CSS部分

<style lang="less" scoped>
#chart{
	width: 100%;
	height: 500px;
  	margin: 50px auto;
}
</style>

data部分

data() {
	return {
		chart: null, // chart例項
	    chartData: [], // chart資料來源
	    startTime: '', // X軸起始時間
	    endTime: '', // X軸終末時間
	    yData: [], // Y軸專案類目
	    dayTime: 3600 * 24 * 1000, // 一天的毫秒,因為01.01日-01.01日,也算一天
	    initData: { // 可以認為是axios請求過來的資料res.data
	        startTime: '2020-12-01', // X軸起始時間
	        endTime: '2022-01-30', // X軸終末時間
	        value: [
	          {
	            itemName: '專案一', // 專案名
	            value: [
	              0, // 索引
	              '2021-06-01', // 專案開始時間
	              '2021-08-30', // 專案結束時間
	              '2021-07-01', // 專案實際開始時間
	              '2021-07-28' // 專案實際結束時間
	            ]
	          },
	          {
	            itemName: '專案二',
	            value: [
	              1,
	              '2021-06-21',
	              '2021-07-21',
	              '2021-07-18',
	              '2021-08-10'
	            ]
	          },
	          {
	            itemName: '專案三',
	            value: [
	              2,
	              '2021-06-01',
	              '2021-06-22',
	              '2021-06-01',
	              '2021-06-22'
	            ]
	          },
	          {
	            itemName: '專案四',
	            value: [
	              3,
	              '2021-06-22',
	              '2021-06-30',
	              '2021-06-22',
	              '2021-07-05'
	            ]
	          },
	          {
	            itemName: '專案五',
	            value: [
	              4,
	              '2021-06-21',
	              '2021-07-06',
	              '2021-07-01',
	              '2021-07-30'
	            ]
	          },
	          {
	            itemName: '專案六',
	            value: [
	              5,
	              '2021-07-01',
	              '2021-07-21',
	              '2021-07-02',
	              '2021-07-30'
	            ]
	          },
	          {
	            itemName: '專案七',
	            value: [
	              6,
	              '2021-06-18',
	              '2021-09-30',
	              '2021-06-30',
	              '2021-10-10'
	            ]
	          }
	        ]
	      }
		}
	}

3,製作甘特圖


由於是demo,所以用的自己的資料,首先給需要用到的變數賦值

getData() {
    this.chartData = this.initData.value // chart的資料
    const arr = []
    this.chartData.forEach(item => {
      arr.push(item.itemName)
    })
    this.yData = arr // Y軸的類目標題
    this.startTime = this.initData.startTime // X軸開始值
    this.endTime = this.initData.endTime // X軸結束值
    this.setData()
}

賦值之後,根據值,定義初始引數配置

setData() {
   const _this = this
   const param = {
     title: {
       text: '專案執行情況',
       left: 'center'
     },
     tooltip: {
       // 自定義提示資訊
       // params為當前點選圖形元素的資料資訊的物件
       formatter(params) {
         // 計劃開始時間
         let planStartDate = params[0].value[1]
         // 計劃結束時間
         let planEndDate = params[0].value[2]
         // 實際開始時間
         let practiceStartDate = params[0].value[3]
         // 實際結束時間
         let practiceEndDate = params[0].value[4]
         // 專案週期(毫秒值):計劃結束日期 - 計劃開始日期
         // eslint-disable-next-line
         let projectCycle_millisecond = +Echarts.number.parseDate(params[0].value[2]) - +Echarts.number.parseDate(params[0].value[1])
         // 專案週期(天數)
         let projectCycle_days = projectCycle_millisecond / _this.dayTime + 1
         return params[0].name + '<br/>'
           + '計劃開始時間:' + planStartDate + '<br/>'
           + '計劃結束時間:' + planEndDate + '<br/>'
           + '專案週期:' + projectCycle_days + '天<br/>'
           + '實際開始時間:' + practiceStartDate + '<br/>'
           + '實際結束時間:' + practiceEndDate
       }
     },
     dataZoom: [
       {
         // 區域縮放元件的型別為滑塊,預設作用在x軸上
         type: 'slider',
         // 區域縮放元件的過濾模式,weakFilter:在進行區域縮放時,允許圖形的一部分在座標系上(可見),另一部分在座標系外(隱藏)
         filterMode: 'weakFilter',
         showDataShadow: false,
         top: 450,
         height: 10,
         // 區域縮放元件邊框顏色
         borderColor: 'transparent',
         // 區域縮放元件邊框背景
         backgroundColor: '#e2e2e2',
         // 區域縮放元件上的手柄的樣式
         // eslint-disable-next-line
         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',
         // 手柄大小
         handleSize: 20,
         // 為手柄設定陰影效果
         handleStyle: {
           shadowBlur: 6,
           shadowOffsetX: 1,
           shadowOffsetY: 2,
           shadowColor: '#aaa'
         },
         labelFormatter: ''
       },
       {
         // 區域縮放元件的型別為內建在座標系中,預設作用在x軸的座標系中
         type: 'inside',
         // 區域縮放元件的過濾模式,weakFilter:在進行區域縮放時,允許圖形的一部分在座標系上(可見),另一部分在座標系外(隱藏)
         filterMode: 'weakFilter'
       }
     ],
     // 圖表底板
     grid: {
       height: 330,
       tooltip: {
         trigger: 'axis',
         axisPointer: {
           type: 'shadow'
         }
       }
     },
     xAxis: {
       // x軸型別為時間軸
       type: 'time',
       // 最小值
       min: _this.startTime,
       // 最大值
       max: _this.endTime,
       axisLabel: {
         // 強制顯示所有標籤
         interval: 0
       }
     },
     yAxis: {
       data: _this.yData
     },
     legend: {
       selectedMode: false,
       left: '70%',
       top: 10,
       data: ['計劃工期', '實際工期']
     },
     series: [
       {
         type: 'custom',
         // 使用自定義的圖形元素
         renderItem: _this.renderItem,
         name: '計劃工期',
         itemStyle: {
           opacity: 0.7,
           color: '#409EFF'
         },
         encode: {
           // 將維度1和維度2的資料對映到x軸
           x: [1, 2],
           // 將維度0的資料對映到y軸
           y: 0
         },
         data: _this.chartData
       },
       // 沒有給它設定data,只是為了通過這個系列,顯示圖例(legend)而已
       {
         type: 'custom',
         name: '實際工期',
         itemStyle: {
           color: '#F56C6C'
         }
       }
     ]
   }
   this.init(param)
 }

上述引數配置中,還需定義一下自定義的圖形繪製方法

// params為data中的資料項的資訊物件 api是可呼叫的方法集合,可以對data中的資料項進行操作
renderItem(params, api) {
      // 取出data中資料項的第一個維度的值
      let categoryIndex = api.value(0)
      // ===============計劃工期進度條
      // 計劃開始日期(在螢幕上的畫素值)
      // 將資料項中的數值對應的座標系上的點,轉換為螢幕上的畫素值
      // 座標系上的點:是資料項對映到座標系的x軸和y軸後,對應的位置
      // 螢幕上的畫素值:是座標系上的點,在螢幕上的位置
      let planStartDate = api.coord([api.value(1), categoryIndex])
      // 計劃結束日期(在螢幕上的畫素值)
      let planEndDate = api.coord([api.value(2), categoryIndex])
      // 由於data.value中維度1和維度2的資料會被對映到x軸,而x軸的type為time,即時間軸,
      // 所以api.value(1)和api.value(2)獲取到的值是將日期轉換後的毫秒值
      // 設定圖形的高度
      // 獲得Y軸上數值範圍為1的一段所對應的畫素長度;這是官方文件的註釋,對於api.size()方法,目前我還不是很理解;先做個標記??? 以後再說
      let height = api.size([0, 1])[1] * 0.4
      let width = planEndDate[0] - planStartDate[0]
      if (width <= 10) {
        width = 3
      }
      // 使用graphic圖形元素元件,繪製矩形
      // clipRectByRect方法,在繪製矩形時,如果矩形大小超出了當前座標系的包圍盒,則裁剪這個矩形
      let rectShape1 = Echarts.graphic.clipRectByRect({
        // 矩形的位置
        x: planStartDate[0],
        y: planStartDate[1],
        // 矩形的寬高
        width,
        height
      },
      {
        // 當前座標系的包圍盒
        x: params.coordSys.x,
        y: params.coordSys.y,
        width: params.coordSys.width,
        height: params.coordSys.height
      })
      // ===============實際工期進度條
      let rectShape2 = null
      // 判斷實際開始日期和結束日期是否為空
      if (api.value(3) !== '' && api.value(4) !== '') {
        // 實際開始日期(在螢幕上的畫素值)
        let practiceStartDate = api.coord([api.value(3), categoryIndex])
        let practiceEndDate = api.coord([api.value(4), categoryIndex])
        let widthNum = practiceEndDate[0] - practiceStartDate[0]
        if (widthNum <= 5) {
          widthNum = 3
        }
        // 使用graphic圖形元素元件,繪製矩形
        // clipRectByRect方法,在繪製矩形時,如果矩形大小超出了當前座標系的包圍盒,則裁剪這個矩形
        rectShape2 = Echarts.graphic.clipRectByRect({
          // 矩形的位置
          x: practiceStartDate[0],
          y: practiceStartDate[1],
          // 矩形的寬高
          width: widthNum,
          height
        }, {
          // 當前座標系的包圍盒
          x: params.coordSys.x,
          y: params.coordSys.y,
          width: params.coordSys.width,
          height: params.coordSys.height
        })
      }
      let lineObj = {}
      // 如果專案還沒開始,那麼只渲染計劃工期的進度條
      if (rectShape2 === null) {
        // 設定繪製的矩形的元素定義
        lineObj = rectShape1 && {
          type: 'group',
          children: [
            {
              // 型別為矩形
              type: 'rect',
              // 具體形狀
              shape: rectShape1,
              // 樣式
              style: api.style({
                fill: '#409EFF'
              })
            }
          ]
        }
      } else {
        // 渲染計劃工期和實際工期
        // 設定繪製的矩形的元素定義
        lineObj = rectShape1 && rectShape2 && {
          type: 'group',
          children: [
            {
              // 型別為矩形
              type: 'rect',
              // 具體形狀
              shape: rectShape1,
              // 樣式
              style: api.style({
                fill: '#409EFF'
              })
            },
            {
              // 型別為矩形
              type: 'rect',
              // 具體形狀
              shape: rectShape2,
              // 樣式
              style: api.style({
                fill: '#F56C6C'
              })
            }
          ]
        }
      }
      return lineObj
    }

這些都ok,我們就可以初始化chart了

init(param) {
    this.chart = Echarts.init(document.getElementById('chart'))
 	this.chart.setOption(param)
 }

大功告成

如果看了覺得有幫助的,我是@鵬多多,歡迎 點贊 關注 評論;
END

面向百度程式設計

往期文章

個人主頁

相關文章