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.
- Real data demonstration address of the epidemic: demonstration address-real data
- 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
- Adopt componentized packaging of each display chart, which can better display and reuse the chart;
- 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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。