2
头图

Original address: Use Vue+DataV+Echarts to create a large screen of new crown pneumonia epidemic data (can be dynamically refreshed)

Source code

View: https://github.com/lanweihong/data-visualization-with-covid-19

Effect picture

Demo

only adapts to 1080P screen, after using the browser to access, press F11 to enter the full screen to see the best display effect.

  1. Real data demonstration address of the epidemic: demonstration address-real data
  2. Analog data demonstration address: demonstration address-analog data

效果图

Front-end framework and class library

Code

Create project

Use Vue Cli to create a Vue project. If there is no Vue Cli, use the following command to install:

npm install -g @vue/cli

Create project:

vue create datav-covid-19

Installation dependencies

# 安装 DataV
npm install @jiaminghi/data-view

# 安装 echarts
npm install echarts -S

# 安装 element-ui
npm i element-ui -S

# 安装 vue-router
npm install vue-router

# 安装 mockjs
npm install mockjs --save-dev

# 安装 axios
npm install axios

# 安装 echarts-liquidfill
npm i echarts-liquidfill

Introduce registration

Introduced in the project, edit main.js :

import Vue from 'vue'
import App from './App.vue'
import dataV from '@jiaminghi/data-view'
import * as echarts from 'echarts'
import 'element-ui/lib/theme-chalk/index.css';
import axios from 'axios'
// 引入 echarts 水球图
import 'echarts-liquidfill'
import VueRouter from 'vue-router'

import { 
  Icon, Row, Col,  Table, TableColumn, Button, Dialog, Link
} from 'element-ui';

// 注册 echarts
Vue.prototype.$echarts = echarts
Vue.config.productionTip = false

// 注册 axios
Vue.prototype.axios = axios

// 注册 dataV
Vue.use(dataV)

// 注册路由
Vue.use(VueRouter)

// 按需注册其他 element-ui 组件
Vue.use(Icon)
Vue.use(Row)
Vue.use(Col)
Vue.use(Table)
Vue.use(TableColumn)
Vue.use(Button)
Vue.use(Dialog)
Vue.use(Link)

new Vue({
  render: h => h(App),
}).$mount('#app')

Writing components

Due to limited space, for reading experience, here is the cumulative ranking component as an example. For other components, please see the code on Github.

Cumulative ranking component

renderings

累计排名组件效果图

The cumulative ranking component is displayed using ECharts's histogram, and the implementation code is as follows:

<template>
  <div
    ref="provinceRankingBarChart"
    style="width: 100%; height: 100%"
    />
</template>
<script>
import * as echarts from 'echarts'
let chart = null
export default {
  props: {
    data: {
      type: Object,
      default () {
        return {
          provinceList: [],
          valueList: []
        }
      }
    }
  },
  methods: {
    initChart () {
      if (null != chart && undefined != chart) {
        chart.dispose()
      }
      chart = this.$echarts.init(this.$refs.provinceRankingBarChart)
      this.setOptions()
    },
    setOptions() {
      var salvProValue = this.data.valueList;
      var salvProMax = [];
      for (let i = 0; i < salvProValue.length; i++) {
        salvProMax.push(salvProValue[0])
      }
      let option = {
        grid: {
          left: '2%',
          right: '2%',
          bottom: '2%',
          top: '2%',
          containLabel: true
        },
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'none'
          },
          formatter: function (params) {
            return params[0].name + ' : ' + params[0].value
          }
        },
        xAxis: {
          show: false,
          type: 'value'
        },
        yAxis: [{
          type: 'category',
          inverse: true,
          axisLabel: {
            show: true,
            textStyle: {
              color: '#fff'
            },
          },
          splitLine: {
            show: false
          },
          axisTick: {
            show: false
          },
          axisLine: {
            show: false
          },
          data: this.data.provinceList
        }, {
          type: 'category',
          inverse: true,
          axisTick: 'none',
          axisLine: 'none',
          show: true,
          axisLabel: {
            textStyle: {
              color: '#ffffff',
              fontSize: '12'
            },
          },
          data: salvProValue
        }],
        series: [{
          name: '值',
          type: 'bar',
          zlevel: 1,
          itemStyle: {
            normal: {
              barBorderRadius: 30,
              color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{
                offset: 0,
                color: 'rgb(2,163,254,1)'
              }, {
                offset: 1,
                color: 'rgb(125,64,255,1)'
              }]),
            },
          },
          barWidth: 20,
          data: salvProValue
        },
        {
          name: '背景',
          type: 'bar',
          barWidth: 20,
          barGap: '-100%',
          data: salvProMax,
          itemStyle: {
            normal: {
              color: 'rgba(24,31,68,1)',
              barBorderRadius: 30,
            }
          },
        },
        ]
      }
      chart.setOption(option)
    }
  },
  watch: {
    data: {
      handler(newList, oldList) {
        if (oldList != newList) {
          this.setOptions()
        }
      },
      deep: true
    }
  }
}
</script>

Introduce and use in the page:

<template>
  <div class="demo">
    <province-ranking-bar-chart
      ref="rankChart"
      :data="dataList"
      style="width: 100%; height: 380px"
    />
  </div>
</template>
<script>
// 引入组件
import ProvinceRankingBarChart from '../components/ProvinceRankingBarChart'
export default {
  components: {
    ProvinceRankingBarChart
  },
  data () {
    return {
      // 定义数据
      dataList: {
        provinceList: ['湖北', '台湾'],
        valueList: [68188, 15379]
      }
    }
  },
  mounted() {
    // 创建图表并初始化
    this.$refs.rankChart.initChart()
  }
}
</script>
<style>
.demo {
  width: 500px;
  height: 600px;
}
</style>

The code of other components is not written here, the complete code has been uploaded to Github, you can check it if you need it.

The complete component structure is as follows:

完整的组件结

Prepare simulation data

Two data provision methods are provided in the project. One is to request the real background address, and the returned data format refers data directory; the other is to use Mock locally to generate simulated data. Here only introduces the way of using Mock to generate simulated data.

Use Mock to generate simulation data

mock in the project root directory, create covid19.js and index.js respectively;

Write mock service

covid19.js code of 061837b4ae2867 is as follows, some Mock syntax is used in the code, please refer to the Mock documentation for specific usage.

// 从本地读取 json 数据
const provinceData = require('../data/covid19-province.json')
const dailyData = require('../data/covid19-daily-list.json')
// 引入 mockjs
const Mock = require('mockjs')
// 使用 mockjs 的 Random 生成随机数据
const Random = Mock.Random

module.exports = [
  {
    url: '/api/covid-19/overall',
    type: 'get',
    response: config => {
      return {
        success: true,
        code: 200,
        message: "操作成功",
        data: {
          confirmedCount: Random.integer(110000, 120000),
          confirmedIncr: 72,
          curedCount: Random.integer(100000, 110000),
          curedIncr: 173,
          currentConfirmedCount: Random.integer(3000, 4000),
          currentConfirmedIncr: -110,
          deadCount: Random.integer(4000, 6000),
          deadIncr: 12,
          importedCount: Random.integer(6000, 8000),
          importedIncr: 23,
          noInFectCount: Random.integer(400, 600),
          noInFectIncr: 8,
          suspectIncr: 0,
          suspectCount: 2,
          updateTime: "2021-07-15 20:39:11",
          curedRate: Random.float(90, 95, 0, 9),
          deadRate: Random.float(1, 5, 0, 9)
        }
      }
    }
  },
  {
    url: '/api/covid-19/area/latest/list',
    type: 'get',
    response: config => {
      return provinceData
    }
  },
  {
    url: '/api/covid-19/list',
    type: 'get',
    response: config => {
      return dailyData
    }
  }
]
Register the mock service

Edit index.js , here is mainly to register the mock service, call the method initMockData() complete the registration;

const Mock = require('mockjs')
// 引入写好的 mock 服务
const covid19 = require('./covid19')

const mocks = [
  ...covid19
]

function param2Obj(url) {
  const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
  if (!search) {
    return {}
  }
  const obj = {}
  const searchArr = search.split('&')
  searchArr.forEach(v => {
    const index = v.indexOf('=')
    if (index !== -1) {
      const name = v.substring(0, index)
      const val = v.substring(index + 1, v.length)
      obj[name] = val
    }
  })
  return obj
}

const initMockData = () => {

  Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
  Mock.XHR.prototype.send = function() {
    if (this.custom.xhr) {
      this.custom.xhr.withCredentials = this.withCredentials || false

      if (this.responseType) {
        this.custom.xhr.responseType = this.responseType
      }
    }
    this.proxy_send(...arguments)
  }

  function XHR2ExpressReqWrap(respond) {
    return function(options) {
      let result = null
      if (respond instanceof Function) {
        const { body, type, url } = options
        result = respond({
          method: type,
          body: JSON.parse(body),
          query: param2Obj(url)
        })
      } else {
        result = respond
      }
      return Mock.mock(result)
    }
  }

  for (const i of mocks) {
    Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response))
  }
}

module.exports = {
  mocks,
  initMockData
}
Use mock service

Introduced in main.js

const { initMockData } = require('../mock')
// 完成注册
initMockData()

request.get('/api/covid-19/list') in the page to request the data. The request.get() here is the method I wrote with the axios

Package data interface

Package axios

The data requests in the project are all using axios for ease of use, I simply encapsulated a tool class request.js :

import axios from "axios"
import sysConst from '../libs/const'
const fetch = (method = 'GET', url, param = '') => {
  // 处理 url
  url = `${sysConst.baseUrl}${url}`
  return new Promise((resolve, reject) => {
    axios({
      method: method,
      url: url,
      changeOrigin: true,
      data: JSON.stringify(param)
    }).then((res) => {
      resolve(res.data)
    }, error => {
      reject(error)
    }).catch((error) => {
      reject(error)
    })
  })
}

const get = (url) => {
  return fetch('GET', url)
}

const post = (url, data) => {
  return fetch('POST', url, data)
}

const put = (url, data) => {
  return fetch('PUT', url, data)
}

const remove = (url, data) => {
  return fetch('DELETE', url, data)
}

export {
  get,
  post,
  put,
  remove
}

const.js code introduced here is as follows:

let baseUrl = ''
if (process.env.NODE_ENV === 'development') {
  // 修改你的 API 地址
  baseUrl = ''
} else {
  // 你的 API 地址
  baseUrl = ''
}

export default {
  baseUrl
}

Package data interface

api in the project root directory to save the writing data interface, and add a new file covid19.js this directory to encapsulate the request for data:

import * as request from '@/utils/request'

/**
 * 接口封装
 */
export default {
  getOverall() {
    let url = `/api/covid-19/overall?_=${Math.random()}`
    return request.get(url)
  },
  getProvinceDataList() {
    let url = `/api/covid-19/area/latest/list?_=${Math.random()}`
    return request.get(url)
  },
  getDailyList() {
    let url = `/api/covid-19/list?t=${Math.random()}`
    return request.get(url)
  }
}

Call the data interface to get data and update the chart display

// 引入
import covid19Service from '../api/covid19'

// 使用
let self = this
covid19Service.getOverall().then((res) => {
    if (!res.success) {
        console.log('错误:' + res.info)
        return
    }
    // 修改数据,图表组件检测到数据变化会触发 setOptions() 方法更新显示( setOptions() 在图表组件中已定义好)
    self.basicData = res.data
})

Project structure

The complete project structure is as follows:

├─build
├─data                                   # 本地模拟数据目录
├─mock                                   # mock 配置
├─public
└─src
    ├─api                                # 接口封装目录
    ├─assets
    ├─components                         # 组件目录
    │  ├─About                           # 关于
    │  ├─BasicDataItemLabel              # 基本数据显示标签
    │  ├─BasicProportionChart            # 占比图表
    │  ├─BasicTrendChart                 # 趋势图表
    │  ├─ChartCard                       # 图表面板
    │  ├─CuredAndDeadRateChart           # 治愈率和死亡率图表
    │  ├─CurrentConfirmedCompareBarChart # 最近一周累计治愈图表
    │  ├─DataMap                         # 数据地图
    │  └─ProvinceRankingBarChart         # 累计排名图表
    ├─libs                               # 一些常用的配置
    ├─router                             # 路由配置
    ├─utils                              # 工具类
    └─views                              # 视图

detailed structure:

完整的项目结构

Summarize

  1. Adopt componentized packaging of each display chart, which can better display and reuse the chart;
  2. Use axios request background service or local mock service to obtain data, and then re-assign the data specified in the chart;

Project source code: The source code of this project has been uploaded to Github, and the address can be viewed in my blog: Use Vue+DataV+Echarts to create a large screen of new crown pneumonia epidemic data (dynamic refresh)

This project is a personal study work, with limited ability, it is inevitable that there will be bugs and errors, please understand. If you have better suggestions or ideas, please point them out, thank you


lanweihong
255 声望5 粉丝