如何使用 ECharts 展示项目历程

156 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情

一、前言

由于项目需求,想要开发一个展示项目历程的功能,一开始想到的是用 element ui 的时间线来实现,但是我发现 element ui 已经让我审美疲劳了,我不想再用了,机缘巧合下我发现 ECharts 有一个关系图,似乎能用这个方式来实现,抱着试一试的态度,在一顿猛如虎的操作下,还真别说真的能实现,所以想给大家也安利一下,有需要优化的地方,欢迎大家提出来。不废话了,上代码。

二、ECharts 实现项目历程

代码如下:

<template>
  <div class="app-container">
    <!-- 图表容器 -->
    <div id="routeCanvas" style="width: 100%; height: 800px; background-color: black;" />
  </div>
</template>

<script>
import { EleResize } from '@/utils/esresize' // 图表自适应
export default {
  name: 'App',
  data() {
    return {
      // 图片
      symbols: [
        require('../../assets/test/start.png'),
        require('../../assets/test/through.png'),
        require('../../assets/test/check.png'),
        require('../../assets/test/update.png'),
        require('../../assets/test/check2.png')
      ],
      // 颜色
      colors: ['pink', 'skyblue', 'green', 'orange', 'white'],
      // 连线标签
      linkLabels: [
        '1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
        '11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
        '21', '22', '23', '24', '25', '26', '27', '28', '29', '30',
        '31', '32', '33', '34', '35', '36', '37', '38', '39', '40'
      ],
      // 步骤
      stepList: ['提交合同', '审核合同', '调整合同', '生成合同'],
      // 关系图数据
      jsonData: {
        '1': [{
          'finished_time': '2022-07-18 16:16:22',
          'finished_step_id': 0,
          'id': 378,
          'operator': '巨蟹座不吃鱼'
        },
        {
          'finished_time': '2022-07-18 16:16:45',
          'finished_step_id': 1,
          'id': 379,
          'operator': '巨蟹座不吃鱼'
        },
        {
          'finished_time': '2022-07-18 16:16:48',
          'finished_step_id': 2,
          'id': 380,
          'operator': '巨蟹座不吃鱼'
        },
        {
          'finished_time': '2022-07-18 16:46:53',
          'finished_step_id': 3,
          'id': 381,
          'operator': '巨蟹座不吃鱼'
        }],
        '2': [{
          'finished_time': '2022-07-18 17:02:55',
          'finished_step_id': 1,
          'id': 382,
          'operator': '巨蟹座不吃鱼'
        },
        {
          'finished_time': '2022-07-18 17:02:58',
          'finished_step_id': 2,
          'id': 383,
          'operator': '巨蟹座不吃鱼'
        },
        {
          'finished_time': '2022-07-19 09:35:44',
          'finished_step_id': 3,
          'id': 384,
          'operator': '巨蟹座不吃鱼'
        }],
        '3': [{
          'finished_time': '2022-07-19 09:36:38',
          'finished_step_id': 2,
          'id': 385,
          'operator': '巨蟹座不吃鱼'
        },
        {
          'finished_time': '2022-07-20 11:14:05',
          'finished_step_id': 3,
          'id': 387,
          'operator': '巨蟹座不吃鱼'
        }],
        '4': [{
          'finished_time': '2022-07-20 11:14:18',
          'finished_step_id': 1,
          'id': 388,
          'operator': '巨蟹座不吃鱼'
        },
        {
          'finished_time': '2022-07-20 11:14:21',
          'finished_step_id': 2,
          'id': 389,
          'operator': '巨蟹座不吃鱼'
        },
        {
          'finished_time': '2022-07-22 14:06:22',
          'finished_step_id': 3,
          'id': 390,
          'operator': '巨蟹座不吃鱼'
        }],
        '5': [{
          'finished_time': '2022-07-22 14:59:01',
          'finished_step_id': 1,
          'id': 391,
          'operator': '巨蟹座不吃鱼'
        },
        {
          'finished_time': '2022-07-22 14:59:04',
          'finished_step_id': 2,
          'id': 392,
          'operator': '巨蟹座不吃鱼'
        },
        {
          'finished_time': '2022-07-22 16:01:01',
          'finished_step_id': 3,
          'id': 393,
          'operator': '巨蟹座不吃鱼'
        },
        {
          'finished_time': '2022-07-22 16:16:53',
          'finished_step_id': 4,
          'id': 394,
          'operator': '巨蟹座不吃鱼'
        }]
      }
    }
  },
  
  created() {
    //初始化调用
    this.formateChartsData()
  },
  
  methods: {
    // 数据处理
    formateChartsData() {
      // 初始化
      var chartData = []
      var chartLink = []
      var x = 400
      var y = 0

      // 单独拼接开始
      chartData.push({
        name: '开始' + '\n' + this.jsonData['1'][0]['finished_time'], // 显示的标签
        x: x, // x位置
        y: 0, // y位置
        symbol: 'image://' + this.symbols[0], // 开始图片
        symbolSize: 40, // 图片大小
        value: this.jsonData['1'][0]['operator'], // 提示框中的展示的名字
        label: { // 标签样式
          color: 'red',
          fontWeight: 'bold'
        }
      })

      // 拼接其他轮次
      if (this.jsonData['1'].length > 1) {
        for (var iter_num in this.jsonData) { // iter_item:轮次
          // 1.拼接轮次
          y += 400
          chartData.push({
            name: '第' + iter_num + '轮:',
            x: 0,
            y: y,
            symbol: 'rect',
            symbolSize: 0,
            label: {
              color: this.colors[Number(iter_num) - 1],
              fontSize: '16px',
              fontWeight: 'bold'
            }
          })

          // 2.拼接每轮次的步骤
          for (var node_num in this.jsonData[iter_num]) { // node_num:步骤
            if (this.jsonData[iter_num][node_num]['finished_step_id'] === 0) { // 跳过开始
              continue
            } else {
              chartData.push({
                name: this.stepList[this.jsonData[iter_num][node_num]['finished_step_id'] - 1] + '\n' + this.jsonData[iter_num][node_num]['finished_time'],
                x: iter_num === '1' ? (x + 700 * (node_num - 1)) : (x + 700 * node_num), // 计算每个步骤的x位置
                y: y,
                symbol: 'image://' + this.symbols[this.jsonData[iter_num][node_num]['finished_step_id']],
                symbolSize: 40,
                value: this.jsonData[iter_num][node_num]['operator'],
                label: {
                  color: this.colors[Number(iter_num) - 1],
                  fontWeight: 'bold'
                }
              })
            }
          }
        }

        // 3.拼接每个步骤之间的连线chartLink
        for (var i = 0; i < chartData.length - 1; i++) {
          if (chartData[i]['name'].indexOf('第') === -1) {
            chartLink.push({
              source: i,
              target: chartData[i + 1]['name'].indexOf('第') === -1 && chartData[i]['name'] !== '开始' ? i + 1 : i + 2,
              symbolSize: [2, 10],
              lineStyle: {
                width: 1,
                curveness: 0
              },
              label: {
                show: false, // 连线标签是否展示
                formatter: this.linkLabels[i]
              }
            })
          }
        }
      }

      // 画图
      this.$nextTick(() => {
        this.initCharts(document.getElementById('routeCanvas'), '项目历程示例', chartData, chartLink)
      })
    },
    
    // 画图方法
    initCharts(domName, projectName, chartData, chartLink) {
      var myChart
      if (myChart !== null && myChart !== '' && myChart !== undefined) {
        myChart.dispose() // 图表销毁
      }

      myChart = this.$echarts.init(domName)

      // 图表自适应
      var listener = function() {
        myChart.resize()
      }
      EleResize.on(domName, listener)

      var option = {
        title: { // 标题
          text: projectName.split('*#*')[0],
          top: 10,
          left: 'center',
          textStyle: {
            fontSize: 30,
            color: '#fff'
          }
        },
        tooltip: { // 提示框
          trigger: 'item',
          backgroundColor: 'rgba(255, 255, 255, 0.9)'
        },
        animationDurationUpdate: 1500,
        animationEasingUpdate: 'quinticInOut',
        series: [// 数据
          {
            type: 'graph',
            layout: 'none',
            roam: true,
            label: {
              show: true,
              position: 'bottom'
            },
            edgeSymbol: ['circle', 'arrow'],
            edgeSymbolSize: [4, 10],
            edgeLabel: {
              fontSize: 20
            },
            animationDelay: function(idx) {
              return idx * 200
            },
            data: chartData,
            links: chartLink,
            lineStyle: {
              opacity: 0.9,
              width: 2,
              curveness: 0
            }
          }
        ]
      }

      // 清除图表
      myChart.clear()

      // 绘制图表
      option && myChart.setOption(option)
    }

  }
}
</script>

页面渲染效果如下:

image.png