原文地址:使用Vue+DataV+Echarts打造新冠肺炎疫情数据大屏(可动态刷新)
源码
查看:https://github.com/lanweihong/data-visualization-with-covid-19
效果图
演示
仅适配 1080P 屏幕,使用浏览器访问后按 F11 进入全屏可看最佳显示效果。
前端框架和类库
代码实现
创建项目
使用 Vue Cli 创建 Vue 项目,没有 Vue Cli 的使用以下命令安装:
npm install -g @vue/cli
创建项目:
vue create datav-covid-19
安装依赖
# 安装 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
引入注册
在项目中引入,编辑 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')
编写组件
因为篇幅有限,为了阅读体验,这里以 累计排名 组件为例,其他的组件请看 Github 上的代码。
累计排名组件
效果图
累计排名组件采用 ECharts 的柱状图来显示,实现代码如下:
<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>
在页面中引入使用:
<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>
其他组件的代码就不在这里写了,完整代码已上传 Github ,需要的可以去查看。
完整的组件结构如下:
准备模拟数据
项目中提供了两种数据提供方式,一是请求真实后台地址,返回的数据格式参考 data
目录下的 json 文件;二是在本地使用 Mock 生成模拟数据。这里仅介绍使用 Mock 生成模拟数据方式。
使用 Mock 生成模拟数据
在项目根目录下创建文件夹 mock
,分别创建 covid19.js
和 index.js
;
编写 mock 服务
covid19.js
代码如下,代码中使用到一些 Mock 的语法,具体使用方法请查看 Mock 的文档。
// 从本地读取 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
}
}
]
注册 mock 服务
编辑 index.js
,这里主要是注册 mock 服务,调用方法 initMockData()
完成注册;
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
}
使用 mock 服务
在 main.js
中引入:
const { initMockData } = require('../mock')
// 完成注册
initMockData()
然后在页面中使用 request.get('/api/covid-19/list')
就能请求获取到数据,这里的 request.get()
是我用 axios
封装写的方法。
封装数据接口
封装 axios
项目中的数据请求都是使用 axios
为方便使用,我简单封装了一个工具类 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
代码如下:
let baseUrl = ''
if (process.env.NODE_ENV === 'development') {
// 修改你的 API 地址
baseUrl = ''
} else {
// 你的 API 地址
baseUrl = ''
}
export default {
baseUrl
}
封装数据接口
在项目根目录下新建文件夹 api
,用于保存编写数据接口,在该目录下新增文件 covid19.js
,用于封装请求获取数据:
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)
}
}
调用数据接口获取数据并更新图表展示
// 引入
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
})
项目结构
完整的项目结构如下:
├─build
├─data # 本地模拟数据目录
├─mock # mock 配置
├─public
└─src
├─api # 接口封装目录
├─assets
├─components # 组件目录
│ ├─About # 关于
│ ├─BasicDataItemLabel # 基本数据显示标签
│ ├─BasicProportionChart # 占比图表
│ ├─BasicTrendChart # 趋势图表
│ ├─ChartCard # 图表面板
│ ├─CuredAndDeadRateChart # 治愈率和死亡率图表
│ ├─CurrentConfirmedCompareBarChart # 最近一周累计治愈图表
│ ├─DataMap # 数据地图
│ └─ProvinceRankingBarChart # 累计排名图表
├─libs # 一些常用的配置
├─router # 路由配置
├─utils # 工具类
└─views # 视图
详细结构:
总结
- 采用组件化封装各个展示图表,能更好的图表展示及复用;
- 使用
axios
请求后台服务或本地 mock 服务获取数据,然后重新赋值图表中指定的数据;
项目源码:本项目源码已上传至 Github,在我的博客中可查看到地址:使用Vue+DataV+Echarts打造新冠肺炎疫情数据大屏(可动态刷新)
这个项目是个人学习作品,能力有限,难免会有 BUG 和错误,敬请谅解。如有更好的建议或想法,请指出,谢谢
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。