Flask+Vue前后端分离疫情可视化系统
基于Flask+Vue前后端分离的疫情数据可视化大屏开发
一、引言
在2020年初爆发的新型冠状病毒,是一种传染力极强的病毒。患者在感染了新型冠状病毒后,即使在潜伏期也很容易传染其他人,该病毒会导致人呼吸困难,严重的可能会导致死亡。时至今日,疫情仍未完全褪去,新冠疫情仍是全世界人民最关注的话题之一,特别是有些国家仍处于爆发期。因此,疫情的实时数据以及相关新闻报导受到了极大的关注。本系统利用数据爬虫技术,完成疫情数据以及疫苗接种数据的爬取经数据处理后形成数据集。利用数据可视化技术,对数据集进行可视化,实现疫情情况与疫苗接种情况的实时更新。帮助民众充分了解最新疫情数据、近期疫情发展趋势以及疫苗接种情况。
Flask 框架是一个轻量级的、便捷的、Python 所提供的 Web 框架,它更加的灵活、轻便、安全且容易上手,是目前主流的服务器框架,非常适合全队的分工 协作式开发,对一个小型团队而言,大大提升了开发的效率。利用 Flask框架,实现前后端的数据交互,其对应的前端 JinJa2的模板引擎,也极大地方便了前端的数据调用。
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用(SPA)提供驱动。简单小巧的核心,渐进式技术栈,足以应付任何规模的应用。
本系统则采用后端Flask+前端Vue的开发模型进行开发。
二、现有技术研究
目前,比较成熟的疫情数据可视化平台有:网易可视化平台、腾讯疫情数据可视化平台、 百度疫情数据可视化平台。这三家数据可视化平台内容分析,如图1所示。
现有技术的实现均是通过对大量的疫情数据进行 爬取、数据处理,利用可视化技术进行数据的可视化。 本系统集全国疫情数据于一个页面中,让用户可以更直观的观察到疫情信息。并且,本系统在疫情分布图上做出创新,在全国疫情分布图中点击各省份,直接可查看当前省份的疫情数据。用户可以由全国疫情分布图直接进入各省份疫情分布图,了解各省份的疫情分布。
三、系统架构和实现流程
四、系统运行流程
五、数据爬取与处理(网易接口)
键 | 说明 |
ChinaTotal | 中国疫情总数据 |
Today | 今日疫情数据 |
Total | 总计疫情数据 |
Confirm | 确证人数 |
Suspect | 疑似病例 |
Heal | 康复人数 |
Dead | 死亡人数 |
Input | 境外输入人数 |
NoSymptom | 无症状感染者人数 |
IncrNosymptom | 无症状感染者较昨日增加人数 |
curedCount | 治愈总人数 |
curedIncr | 较昨日治愈增长人数 |
六、部分代码展示
-
后端(三个接口):
from flask import Flask,jsonify
import requests,json,time
app=Flask(__name__)
@app.route('/overall')
def get_data_overall():
now = time.localtime()
nowt = time.strftime("%Y-%m-%d %H:%M:%S", now)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36',
'referer': 'https://news.qq.com/',
}
url='https://c.m.163.com/ug/api/wuhan/app/data/list-total?t=329222187209'
res=requests.get(url=url,headers=headers).json()
if res:
json_overall={
"success":True,
"code": 200,
"message": "操作成功",
"data":{
"confirmedCount": res.get('data').get('chinaTotal').get('total').get('confirm'),
"confirmedIncr": res.get('data').get('chinaTotal').get('today').get('confirm'),
"curedCount": res.get('data').get('chinaTotal').get('total').get('heal'),
"curedIncr": res.get('data').get('chinaTotal').get('today').get('heal'),
"currentConfirmedCount": res.get('data').get('chinaTotal').get('total').get('confirm')- res.get('data').get('chinaTotal').get('total').get('dead')-res.get('data').get('chinaTotal').get('total').get('heal'),
"currentConfirmedIncr": res.get('data').get('chinaTotal').get('today').get('storeConfirm'),
"deadCount": res.get('data').get('chinaTotal').get('total').get('dead'),
"deadIncr": 0,
"importedCount": res.get('data').get('chinaTotal').get('total').get('input'),
"importedIncr": res.get('data').get('chinaTotal').get('today').get('input'),
"noInFectCount": res.get('data').get('chinaTotal').get('extData').get('noSymptom'),
"noInFectIncr": res.get('data').get('chinaTotal').get('extData').get('incrNoSymptom'),
"suspectIncr": 0,
"suspectCount": res.get('data').get('chinaTotal').get('total').get('suspect'),
"updateTime": nowt,
"curedRate": 92.7,
"deadRate": 4.69
}
}
with open('data/covid19-overall.json','w') as f:
json.dump(json_overall,f)
print("json_overall文件加载完成!")
return json_overall
@app.route('/daily')
def get_daily():
now = time.localtime()
nowt = time.strftime("%Y-%m-%d %H:%M:%S", now)
today=time.strftime("%Y-%m-%d")
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36',
'referer': 'https://news.qq.com/',
}
url = 'https://c.m.163.com/ug/api/wuhan/app/data/list-total?t=329222187209'
res = requests.get(url=url, headers=headers).json()
importedIncrList=[]
curedCountList=[]
confirmedCountList=[]
importedCountList=[]
currentConfirmedIncrList=[]
noInFectCountList=[]
currentConfirmedCountList=[]
for i in res.get('data').get('chinaDayList'):
importedIncrList.append([i.get('date'),i.get('today').get('input')])
curedCountList.append([i.get('date'),i.get('today').get('confirm')])
confirmedCountList.append([i.get('date'),i.get('total').get('confirm')])
importedCountList.append([i.get('date'),i.get('total').get('input')])
currentConfirmedIncrList.append([i.get('date'),i.get('total').get('heal')])
# 无症状
noInFectCountList.append([today,res.get('data').get('chinaTotal').get('extData').get('noSymptom')])
if res:
json_daily={
"success":True,
"code": 200,
"message": "操作成功",
"data": {
# 境外输入
"importedIncrList": importedIncrList,
# 现有确诊
"curedCountList": curedCountList,
"confirmedCountList":confirmedCountList ,
"currentConfirmedIncrList": currentConfirmedIncrList,
"importedCountList": importedCountList,
# 无症状
"noInFectCountList":noInFectCountList ,
"currentConfirmedCountList":currentConfirmedCountList
}
}
with open('data/covid19-daily-list.json','w') as f:
json.dump(json_daily,f)
print("json_daily文件加载完成!")
return json_daily
@app.route('/province')
def get_province():
now = time.localtime()
nowt = time.strftime("%Y-%m-%d %H:%M:%S", now)
today = time.strftime("%Y-%m-%d")
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36',
'referer': 'https://news.qq.com/',
}
url = 'https://c.m.163.com/ug/api/wuhan/app/data/list-total?t=329222187209'
res = requests.get(url=url, headers=headers).json()
data=[]
province=res.get('data').get('areaTree')[2].get('children')
for i in province:
if i.get('total').get('dead')==0:
province_dict = {
"confirmedCount": i.get('total').get('confirm'),
"countryLabel": "中国",
"countryName": "China",
"curedCount": i.get('today').get('heal'),
"curedRate": i.get('today').get('heal') / i.get('total').get('heal'),
"currentConfirmedCount": i.get('today').get('confirm'),
"deadCount": i.get('today').get('dead'),
"deadRate": 0 ,
"provinceLabel": i.get('name'),
"provinceName": None,
"suspectCount": 0,
"todayConfirmedCount": 0,
"updateTime": None
}
else:
province_dict = {
"confirmedCount": i.get('total').get('confirm'),
"countryLabel": "中国",
"countryName": "China",
"curedCount": i.get('today').get('heal'),
"curedRate": i.get('today').get('heal') / i.get('total').get('heal'),
"currentConfirmedCount": i.get('today').get('confirm'),
"deadCount": i.get('today').get('dead')/i.get('total').get('dead'),
"deadRate": 0,
"provinceLabel": i.get('name'),
"provinceName": None,
"suspectCount": 0,
"todayConfirmedCount": 0,
"updateTime": None
}
data.append(province_dict)
if res:
json_province={
"success": True,
"code": 200,
"message": "操作成功",
"data":data
}
with open('data/covid19-province.json', 'w') as f:
json.dump(json_province, f)
print("json_province文件加载完成!")
return json_province
if __name__ == '__main__':
get_daily()
get_province()
get_data_overall()
app.run(port=8080)
#前后端分离 首先启动后端 爬取当日的数据 由于腾讯接口使用的人比较多 所以我们这次选取的是网易接口
#然后启动前端 npm run dev 因为采用的vue
-
前端(引用蓝伟洪先生模板)
<template> <div class="container"> <!-- 顶部 --> <div class="top-header"> <div class="title"> <h1>{{ title }}</h1> <div class="top-header-tip"> <div class="sub-title">此数据为实时真实数据,数据来源:网易新闻</div> <div class="last-update-time"> 更新时间:{{ basicData.updateTime }} </div> </div> </div> </div> <div class="main-content"> <el-row> <el-col :span="18"> <!-- 中间信息开始 --> <div class="statistics-content"> <!-- 中间左侧开始 --> <div class="main-inner"> <el-row> <el-col :span="7"> <chart-card title="累计排名(TOP 10)" :customClass="`chart-item-bottom-sep`" > <province-ranking-bar-chart ref="topConfirmedCountRankChart" :data="top10ProvinceData" style="width: 100%; height: 380px" /> </chart-card> <!-- 占比 --> <chart-card title="占比" :customClass="`chart-item-bottom-sep`" > <basic-proportion-chart ref="basicProportionChart" :data="basicData" style="width: 100%; height: 120px" /> </chart-card> <chart-card title="最近一周累计治愈"> <current-confirmed-compare-bar-chart ref="confirmSingleBarChart" :data="confirmSingleBarChartData" style="width: 100%; height: 310px" /> </chart-card> </el-col> <el-col :span="17"> <!-- 顶部基本统计信息开始 --> <div class="basic-header flex"> <!-- 顶部统计信息开始 --> <div class="top-basic-info"> <basic-data-item-label label="现有确诊" :config="defaultDataConfig.currentConfirmedCount" :inCrValue="basicData.currentConfirmedIncr" /> <basic-data-item-label label="累计确诊" :config="defaultDataConfig.confirmedCount" :inCrValue="basicData.confirmedIncr" /> <!-- 境外输入 --> <basic-data-item-label label="境外输入" :config="defaultDataConfig.importedCount" :inCrValue="basicData.importedIncr" /> <!-- 无症状感染者 --> <basic-data-item-label label="无症状感染者" :config="defaultDataConfig.noInFectCount" :inCrValue="basicData.noInFectIncr" /> <!-- 累计治愈 --> <basic-data-item-label label="累计治愈" :config="defaultDataConfig.curedCount" :inCrValue="basicData.curedIncr" /> <!-- 死亡人数 --> <!-- <basic-data-item-label--> <!-- label="累计死亡"--> <!-- :config="defaultDataConfig.deadCount"--> <!-- :inCrValue="basicData.deadIncr"--> <!-- />--> </div> <!-- 顶部统计信息结束 --> </div> <!-- 顶部基本统计信息开始 --> <div class="main-inner-map-wraper"> <!-- 地图 --> <div class="map"> <data-map ref="dataMap" title="" :list="mapDataList" style="width: 100%; height: 100%" /> </div> </div> </el-col> </el-row> </div> <!-- 中间左侧结束 --> </div> <!-- 中间信息结束 --> </el-col> <el-col :span="6"> <!-- 右侧区域开始 --> <div class="main-right"> <!-- 治愈率和死亡率 --> <chart-card title="治愈率和死亡率" :innerClass="`cure-and-dead-rate-chart`" :customClass="`chart-item-bottom-sep`" > <cured-and-dead-rate-chart ref="cureRateChart" :data="rate" title="治愈率" style="width: 280px; height: 130px" /> </chart-card> <chart-card title="新增趋势" :customClass="`chart-item-bottom-sep`"> <basic-trend-chart :data="basicIncrTrendData" ref="confirmedCountTrendChart" style="width: 100%; height: 320px" /> </chart-card> <chart-card title=""> <div slot="title" class="province-table-title flex"> 各省累计确诊 <el-link icon="el-icon-view" style="color: #bcbcbf; padding-left: 10px" :underline="false" @click="provinceTableDialogShowHandler" >详情</el-link > </div> <dv-scroll-board :config="provinceConfirmedCountBoardConfig" style="width: 100%; height: 360px" /> </chart-card> </div> <!-- 右侧区域结束 --> </el-col> </el-row> </div> <!-- 其他页面 --> <div class="province-data-table-wrapper"> <el-dialog :visible.sync="provinceTableDialogVisible" width="50%" :before-close="provinceTableDialogClose" > <div slot="title" class="province-data-modal-title"> <p>各省数据表</p> <span class="province-data-modal-update-time" >(更新时间:{{ basicData.updateTime }})</span > </div> <div class="area-data-table-wrapper"> <el-table class="area-data-table" :data="provinceDataList" style="width: 100%" > <el-table-column prop="provinceLabel" align="center" label="省份"> </el-table-column> <el-table-column prop="confirmedCount" align="center" label="累计确诊" > </el-table-column> <el-table-column prop="currentConfirmedCount" align="center" label="现有确诊" > </el-table-column> <el-table-column prop="curedCount" align="center" label="累计治愈"> </el-table-column> <el-table-column prop="deadCount" align="center" label="累计死亡"> </el-table-column> </el-table> </div> </el-dialog> <!-- 关于弹窗 --> <el-dialog title="关于" :visible.sync="aboutDialogVisible" width="30%" :before-close="aboutDialogClose" > <about /> </el-dialog> <!-- 关于图标 --> <div class="about-wraper"> <i class="el-icon-info" style="font-size: 30px" @click="aboutDialogShowHandler" ></i> </div> </div> </div> </template> <script> import ChartCard from '../components/ChartCard' import DataMap from '../components/DataMap' import CuredAndDeadRateChart from '../components/CuredAndDeadRateChart' import BasicDataItemLabel from '../components/BasicDataItemLabel' import BasicTrendChart from '../components/BasicTrendChart' import ProvinceRankingBarChart from '../components/ProvinceRankingBarChart' import CurrentConfirmedCompareBarChart from '../components/CurrentConfirmedCompareBarChart' import About from '../components/About' import BasicProportionChart from '../components/BasicProportionChart' import covid19Service from '../api/covid19' const formatter = (number) => { const numbers = number.toString().split('').reverse() const segs = [] while (numbers.length) segs.push(numbers.splice(0, 3).join('')) return segs.join(',').split('').reverse().join('') } // 数据样式 const getNumberStyle = (color = '#E8EAF6', fontSize = 30, fontWeight = 'bolder') => { return { fontSize: fontSize, fill: color, fontWeight: fontWeight } } const initBasicConfig = (data = null) => { let currentConfirmedCount = data ? [data.currentConfirmedCount] : 0 let confirmedCount = data ? [data.confirmedCount] : 0 let importedCount = data ? [data.importedCount] : 0 let noInFectCount = data ? [data.noInFectCount] : 0 let deadCount = data ? [data.deadCount] : 0 let curedCount = data ? [data.curedCount] : 0 return { confirmedCount: { number: [confirmedCount], content: '{nt}', formatter, style: getNumberStyle() }, currentConfirmedCount: { number: [currentConfirmedCount], content: '{nt}', formatter, style: getNumberStyle('#2E8EEA') }, importedCount: { number: [importedCount], content: '{nt}', formatter, style: getNumberStyle() }, noInFectCount: { number: [noInFectCount], content: '{nt}', formatter, style: getNumberStyle() }, deadCount: { number: [deadCount], content: '{nt}', formatter, style: getNumberStyle('#D32E58') }, curedCount: { number: [curedCount], content: '{nt}', formatter, style: getNumberStyle() } } } const initProvinceConfirmedCountBoardConfig = (resultList = []) => { return { header: ['省份', '累计确诊', '累计治愈', '累计死亡'], headerHeight: 30, data: resultList, align: ['center'], rowNum: 10, index: true, indexHeader: '排名', headerBGC: '', oddRowBGC: '', evenRowBGC: '', carousel: 'single' } } export default { components: { ChartCard, DataMap, CuredAndDeadRateChart, BasicDataItemLabel, BasicTrendChart, ProvinceRankingBarChart, CurrentConfirmedCompareBarChart, About, BasicProportionChart }, data () { return { title: '全国新冠肺炎疫情数据大屏', provinceTableDialogVisible: false, aboutDialogVisible: false, commonData: {}, basicData: { currentConfirmedCount: 0, currentConfirmedIncr: 0, confirmedCount: 0, confirmedIncr: 0, curedCount: 0, curedIncr: 0, deadCount: 0, deadIncr: 0, sure: 0, sureAdd: 0, importedCount: 0, importedIncr: 0, noInFectCount: 0, noInFectIncr: 0, suspectCount: 0, suspectIncr: 0, updateTime: '2022-3-1' }, defaultDataConfig: initBasicConfig(), cureRateConfig: { data: [0], shape: 'round' }, deadRateConfig: { data: [0], shape: 'round' }, countryRankingTakeTurnChartConfig: { data: [], waitTime: 2000, unit: '单位', valueFormatter ({ value }) { const reverseNumber = (value + '').split('').reverse() let valueStr = '' while (reverseNumber.length) { const seg = reverseNumber.splice(0, 3).join('') valueStr += seg if (seg.length === 3) valueStr += ',' } return valueStr.split('').reverse().join('') } }, provinceConfirmedCountBoardConfig: initProvinceConfirmedCountBoardConfig(), provinceDataList: [], trendDataList: [], confirmedCountList: [], top10ProvinceData: { provinceList: [], valueList: [] }, basicIncrTrendData: { dateList: [], importedIncrDataList: [], currentConfirmedIncrDataList: [] }, confirmSingleBarChartData: { dateList: [], currentConfirmedCountList: [], confirmedCountList: [] }, rate: { curedRate: 0, deadRate: 0 }, areaData: {}, mapDataList: [] } }, methods: { queryBasicData () { let self = this covid19Service.getOverall().then((res) => { if (!res.success) { console.log('错误:' + res.info) return } self.basicData = res.data self.setBasicData(res.data) }) }, queryProvinceDataList () { let self = this covid19Service.getProvinceDataList().then((res) => { if (!res.success) { // TODO 错误处理... console.log('错误:' + res.info) return } self.provinceDataList = res.data self.setAreaChartData(res.data) // 设置累计排名数据 self.setProvinceRankingData(res.data) // 设置各省累计确诊轮播表格数据 self.setProvinceComfirmedCountBoardData(res.data) // 设置地图数据 self.setMapData(res.data) }) }, queryTrendDataList () { let self = this covid19Service.getDailyList().then((res) => { if (!res.success) { // TODO 错误处理... console.log('错误:' + res.info) return } self.trendDataList = res.data // 重置图表数据 self.setBasicIncrTrendData(res.data) }) }, setProvinceRankingData (areaList) { let provinceList = [] let dataValueList = [] for (let i = 0; i < 10; i++) { provinceList.push(areaList[i].provinceLabel) dataValueList.push(areaList[i].confirmedCount) } let data = { provinceList: provinceList, valueList: dataValueList } this.top10ProvinceData = data }, setBasicIncrTrendData (data) { let dateList = [] let currentConfirmedIncrList = [] let importedIncrList = [] let sevenDayDateList = [] // 仅显示一周条数据 let confirmedCountList = [] let curedCountList = [] // 仅获取7条数据 let count = 7 let noInFectDataList = [] for (let i = data.currentConfirmedIncrList.length - 1; i >= 0; i--) { dateList.push(data.currentConfirmedIncrList[i][0]) currentConfirmedIncrList.push(data.currentConfirmedIncrList[i][1]) } for (let i = data.importedIncrList.length - 1; i >= 0; i--) { importedIncrList.push(data.importedIncrList[i][1]) } for (let i = data.noInFectCountList.length - 1; i >= 0; i--) { noInFectDataList.push(data.noInFectCountList[i][1]) } for (let i = count; i >= 0; i--) { if (confirmedCountList.length >= count) { break } sevenDayDateList.push(data.confirmedCountList[i][0]) confirmedCountList.push(data.confirmedCountList[i][1]) } for (let i = count; i >= 0; i--) { if (curedCountList.length >= count) { break } curedCountList.push(data.curedCountList[i][1]) } this.basicIncrTrendData = { dateList: dateList, importedIncrDataList: importedIncrList, currentConfirmedIncrDataList: currentConfirmedIncrList, noInFectDataList: noInFectDataList } this.confirmSingleBarChartData = { dateList: sevenDayDateList, curedCountList: curedCountList, confirmedCountList: confirmedCountList } }, setProvinceComfirmedCountBoardData (areaList) { let resultList = areaList.map(item => { return [item.provinceLabel, item.confirmedCount, item.curedCount, item.deadCount] }) // 重新生成,否则无法刷新状态 this.provinceConfirmedCountBoardConfig = initProvinceConfirmedCountBoardConfig(resultList) }, setAreaChartData (areaList) { let confirmedCountList = [] let provinceList = [] let curedCountList = [] let deadCountList = [] areaList.forEach(item => { provinceList.push(item.provinceLabel) confirmedCountList.push(item.confirmedCount) curedCountList.push(item.curedCount) deadCountList.push(item.deadCount) }) this.areaData = { provinceList: provinceList, confirmedCountList: confirmedCountList, curedCountList: curedCountList, deadCountList: deadCountList } }, setMapData (dataList) { let list = dataList.map(item => { return { name: item.provinceLabel, value: item.confirmedCount } }) this.mapDataList = list }, provinceTableDialogShowHandler () { this.provinceTableDialogVisible = true }, provinceTableDialogClose () { this.provinceTableDialogVisible = false }, aboutDialogShowHandler () { this.aboutDialogVisible = true }, aboutDialogClose () { this.aboutDialogVisible = false }, setBasicData (data) { // 重新生成,否则视图不更新 let config = initBasicConfig(data) this.defaultDataConfig = config // 处理治愈率和死亡率 this.rate = { curedRate: data.curedRate / 100, deadRate: data.deadRate / 100 } }, startQueryData () { this.queryBasicData() this.queryProvinceDataList() this.queryTrendDataList() }, initAllChart() { this.$refs.dataMap.initChart() this.$refs.cureRateChart.initChart() this.$refs.confirmedCountTrendChart.initChart() this.$refs.topConfirmedCountRankChart.initChart() this.$refs.confirmSingleBarChart.initChart() this.$refs.basicProportionChart.initChart() } }, mounted () { this.initAllChart() this.startQueryData() let self = this // 定义定时器,隔 5 秒刷新一次 this.timer = setInterval(() => { self.startQueryData() }, 5000) }, beforeDestroy() { clearInterval(this.timer) } } </script> <style> .container { position: relative; } h1 { font-size: 35px; font-weight: bold; padding: 20px; } .flex { display: flex; } .top-header { position: relative; margin-bottom: 10px; } .top-header-tip { font-size: 14px; position: absolute; padding: 20px 20px; text-align: right; top: 0; right: 0; } .title { min-width: 350px; } .chart-card { background: #0f142b; border-radius: 10px; margin: 0 20px; } .main-inner-map-wraper { height: 100%; margin: 0 20px; } .map { width: 99%; height: 840px; padding: 20px 10px 10px 10px; } .province-scroll-board-wrapper { padding-top: 10px; padding-bottom: 10px; } .chart-item-bottom-sep { margin-bottom: 20px; } .province-table-title { color: #fff; font-size: 16px; font-weight: bold; padding: 10px 10px 10px 20px; text-align: left; } .basic-incr-trend-chart-wrapper { padding-bottom: 10px; } .sub-title, .last-update-time, .province-scroll-board-wrapper ::v-deep .dv-scroll-board .header, .province-scroll-board-wrapper ::v-deep .dv-scroll-board .rows { font-size: 14px; } .province-scroll-board-wrapper ::v-deep .dv-scroll-board .rows { color: #bcbcbf; } .province-data-modal-title { color: #000; font-size: 20px; font-weight: bold; } .province-data-modal-update-time { color: #58585a; font-size: 16px; font-weight: normal; padding-top: 10px; display: block; } .cure-and-dead-rate-chart { display: flex; justify-content: space-around; } .top-basic-info { display: flex; } .dv-scroll-ranking-board .ranking-column .inside-column { background: linear-gradient(90deg, #29bfff, #a231ff, #0deccd, #29bfff); } .chart-item-container { margin: 0 10px; } .about-wraper { position: fixed; bottom: 20px; right: 20px; } </style>
七、成品展示
结束语
截至到 2022年 3 月,疫情仍未完全褪去,民众最为关心的是疫苗接种情况。本系统基于疫情数据显 示的基础上,增加疫苗数据显示功能。利用数据爬虫 技术完成数据的获取与下载,经数据处理后形成数据 集,数据可视化阶段利用 Python 语言的 Flask 框架、 Vue、ECharts可视化等可视化技术,对疫情数据以及疫苗数据进行数据可视化。经过对现有技术的分析,并在可 视化内容上做出创新。集可视化内容于一个页面中, 疫情数据与疫苗数据一目了然,用户可以从多方面,多角度,全方位直观的观察到疫情数据、疫情发展 趋势,以及疫苗接种情况。本系统的开发是用来帮助 民众充分了解全国各地的疫情情况、近期的疫情发展 趋势以及疫苗接种情况。
更多推荐
所有评论(0)