当前位置:   article > 正文

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

较昨日治愈增长人数


六、部分代码展示

  • 后端(三个接口):

  1. from flask import Flask,jsonify
  2. import requests,json,time
  3. app=Flask(__name__)
  4. @app.route('/overall')
  5. def get_data_overall():
  6. now = time.localtime()
  7. nowt = time.strftime("%Y-%m-%d %H:%M:%S", now)
  8. headers = {
  9. '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',
  10. 'referer': 'https://news.qq.com/',
  11. }
  12. url='https://c.m.163.com/ug/api/wuhan/app/data/list-total?t=329222187209'
  13. res=requests.get(url=url,headers=headers).json()
  14. if res:
  15. json_overall={
  16. "success":True,
  17. "code": 200,
  18. "message": "操作成功",
  19. "data":{
  20. "confirmedCount": res.get('data').get('chinaTotal').get('total').get('confirm'),
  21. "confirmedIncr": res.get('data').get('chinaTotal').get('today').get('confirm'),
  22. "curedCount": res.get('data').get('chinaTotal').get('total').get('heal'),
  23. "curedIncr": res.get('data').get('chinaTotal').get('today').get('heal'),
  24. "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'),
  25. "currentConfirmedIncr": res.get('data').get('chinaTotal').get('today').get('storeConfirm'),
  26. "deadCount": res.get('data').get('chinaTotal').get('total').get('dead'),
  27. "deadIncr": 0,
  28. "importedCount": res.get('data').get('chinaTotal').get('total').get('input'),
  29. "importedIncr": res.get('data').get('chinaTotal').get('today').get('input'),
  30. "noInFectCount": res.get('data').get('chinaTotal').get('extData').get('noSymptom'),
  31. "noInFectIncr": res.get('data').get('chinaTotal').get('extData').get('incrNoSymptom'),
  32. "suspectIncr": 0,
  33. "suspectCount": res.get('data').get('chinaTotal').get('total').get('suspect'),
  34. "updateTime": nowt,
  35. "curedRate": 92.7,
  36. "deadRate": 4.69
  37. }
  38. }
  39. with open('data/covid19-overall.json','w') as f:
  40. json.dump(json_overall,f)
  41. print("json_overall文件加载完成!")
  42. return json_overall
  43. @app.route('/daily')
  44. def get_daily():
  45. now = time.localtime()
  46. nowt = time.strftime("%Y-%m-%d %H:%M:%S", now)
  47. today=time.strftime("%Y-%m-%d")
  48. headers = {
  49. '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',
  50. 'referer': 'https://news.qq.com/',
  51. }
  52. url = 'https://c.m.163.com/ug/api/wuhan/app/data/list-total?t=329222187209'
  53. res = requests.get(url=url, headers=headers).json()
  54. importedIncrList=[]
  55. curedCountList=[]
  56. confirmedCountList=[]
  57. importedCountList=[]
  58. currentConfirmedIncrList=[]
  59. noInFectCountList=[]
  60. currentConfirmedCountList=[]
  61. for i in res.get('data').get('chinaDayList'):
  62. importedIncrList.append([i.get('date'),i.get('today').get('input')])
  63. curedCountList.append([i.get('date'),i.get('today').get('confirm')])
  64. confirmedCountList.append([i.get('date'),i.get('total').get('confirm')])
  65. importedCountList.append([i.get('date'),i.get('total').get('input')])
  66. currentConfirmedIncrList.append([i.get('date'),i.get('total').get('heal')])
  67. # 无症状
  68. noInFectCountList.append([today,res.get('data').get('chinaTotal').get('extData').get('noSymptom')])
  69. if res:
  70. json_daily={
  71. "success":True,
  72. "code": 200,
  73. "message": "操作成功",
  74. "data": {
  75. # 境外输入
  76. "importedIncrList": importedIncrList,
  77. # 现有确诊
  78. "curedCountList": curedCountList,
  79. "confirmedCountList":confirmedCountList ,
  80. "currentConfirmedIncrList": currentConfirmedIncrList,
  81. "importedCountList": importedCountList,
  82. # 无症状
  83. "noInFectCountList":noInFectCountList ,
  84. "currentConfirmedCountList":currentConfirmedCountList
  85. }
  86. }
  87. with open('data/covid19-daily-list.json','w') as f:
  88. json.dump(json_daily,f)
  89. print("json_daily文件加载完成!")
  90. return json_daily
  91. @app.route('/province')
  92. def get_province():
  93. now = time.localtime()
  94. nowt = time.strftime("%Y-%m-%d %H:%M:%S", now)
  95. today = time.strftime("%Y-%m-%d")
  96. headers = {
  97. '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',
  98. 'referer': 'https://news.qq.com/',
  99. }
  100. url = 'https://c.m.163.com/ug/api/wuhan/app/data/list-total?t=329222187209'
  101. res = requests.get(url=url, headers=headers).json()
  102. data=[]
  103. province=res.get('data').get('areaTree')[2].get('children')
  104. for i in province:
  105. if i.get('total').get('dead')==0:
  106. province_dict = {
  107. "confirmedCount": i.get('total').get('confirm'),
  108. "countryLabel": "中国",
  109. "countryName": "China",
  110. "curedCount": i.get('today').get('heal'),
  111. "curedRate": i.get('today').get('heal') / i.get('total').get('heal'),
  112. "currentConfirmedCount": i.get('today').get('confirm'),
  113. "deadCount": i.get('today').get('dead'),
  114. "deadRate": 0 ,
  115. "provinceLabel": i.get('name'),
  116. "provinceName": None,
  117. "suspectCount": 0,
  118. "todayConfirmedCount": 0,
  119. "updateTime": None
  120. }
  121. else:
  122. province_dict = {
  123. "confirmedCount": i.get('total').get('confirm'),
  124. "countryLabel": "中国",
  125. "countryName": "China",
  126. "curedCount": i.get('today').get('heal'),
  127. "curedRate": i.get('today').get('heal') / i.get('total').get('heal'),
  128. "currentConfirmedCount": i.get('today').get('confirm'),
  129. "deadCount": i.get('today').get('dead')/i.get('total').get('dead'),
  130. "deadRate": 0,
  131. "provinceLabel": i.get('name'),
  132. "provinceName": None,
  133. "suspectCount": 0,
  134. "todayConfirmedCount": 0,
  135. "updateTime": None
  136. }
  137. data.append(province_dict)
  138. if res:
  139. json_province={
  140. "success": True,
  141. "code": 200,
  142. "message": "操作成功",
  143. "data":data
  144. }
  145. with open('data/covid19-province.json', 'w') as f:
  146. json.dump(json_province, f)
  147. print("json_province文件加载完成!")
  148. return json_province
  149. if __name__ == '__main__':
  150. get_daily()
  151. get_province()
  152. get_data_overall()
  153. app.run(port=8080)
  154. #前后端分离 首先启动后端 爬取当日的数据 由于腾讯接口使用的人比较多 所以我们这次选取的是网易接口
  155. #然后启动前端 npm run dev 因为采用的vue
  •  前端(引用蓝伟洪先生​​​​​​​模板)

    1. <template>
    2. <div class="container">
    3. <!-- 顶部 -->
    4. <div class="top-header">
    5. <div class="title">
    6. <h1>{{ title }}</h1>
    7. <div class="top-header-tip">
    8. <div class="sub-title">此数据为实时真实数据,数据来源:网易新闻</div>
    9. <div class="last-update-time">
    10. 更新时间:{{ basicData.updateTime }}
    11. </div>
    12. </div>
    13. </div>
    14. </div>
    15. <div class="main-content">
    16. <el-row>
    17. <el-col :span="18">
    18. <!-- 中间信息开始 -->
    19. <div class="statistics-content">
    20. <!-- 中间左侧开始 -->
    21. <div class="main-inner">
    22. <el-row>
    23. <el-col :span="7">
    24. <chart-card
    25. title="累计排名(TOP 10)"
    26. :customClass="`chart-item-bottom-sep`"
    27. >
    28. <province-ranking-bar-chart
    29. ref="topConfirmedCountRankChart"
    30. :data="top10ProvinceData"
    31. style="width: 100%; height: 380px"
    32. />
    33. </chart-card>
    34. <!-- 占比 -->
    35. <chart-card
    36. title="占比"
    37. :customClass="`chart-item-bottom-sep`"
    38. >
    39. <basic-proportion-chart
    40. ref="basicProportionChart"
    41. :data="basicData"
    42. style="width: 100%; height: 120px"
    43. />
    44. </chart-card>
    45. <chart-card title="最近一周累计治愈">
    46. <current-confirmed-compare-bar-chart
    47. ref="confirmSingleBarChart"
    48. :data="confirmSingleBarChartData"
    49. style="width: 100%; height: 310px"
    50. />
    51. </chart-card>
    52. </el-col>
    53. <el-col :span="17">
    54. <!-- 顶部基本统计信息开始 -->
    55. <div class="basic-header flex">
    56. <!-- 顶部统计信息开始 -->
    57. <div class="top-basic-info">
    58. <basic-data-item-label
    59. label="现有确诊"
    60. :config="defaultDataConfig.currentConfirmedCount"
    61. :inCrValue="basicData.currentConfirmedIncr"
    62. />
    63. <basic-data-item-label
    64. label="累计确诊"
    65. :config="defaultDataConfig.confirmedCount"
    66. :inCrValue="basicData.confirmedIncr"
    67. />
    68. <!-- 境外输入 -->
    69. <basic-data-item-label
    70. label="境外输入"
    71. :config="defaultDataConfig.importedCount"
    72. :inCrValue="basicData.importedIncr"
    73. />
    74. <!-- 无症状感染者 -->
    75. <basic-data-item-label
    76. label="无症状感染者"
    77. :config="defaultDataConfig.noInFectCount"
    78. :inCrValue="basicData.noInFectIncr"
    79. />
    80. <!-- 累计治愈 -->
    81. <basic-data-item-label
    82. label="累计治愈"
    83. :config="defaultDataConfig.curedCount"
    84. :inCrValue="basicData.curedIncr"
    85. />
    86. <!-- 死亡人数 -->
    87. <!-- <basic-data-item-label-->
    88. <!-- label="累计死亡"-->
    89. <!-- :config="defaultDataConfig.deadCount"-->
    90. <!-- :inCrValue="basicData.deadIncr"-->
    91. <!-- />-->
    92. </div>
    93. <!-- 顶部统计信息结束 -->
    94. </div>
    95. <!-- 顶部基本统计信息开始 -->
    96. <div class="main-inner-map-wraper">
    97. <!-- 地图 -->
    98. <div class="map">
    99. <data-map
    100. ref="dataMap"
    101. title=""
    102. :list="mapDataList"
    103. style="width: 100%; height: 100%"
    104. />
    105. </div>
    106. </div>
    107. </el-col>
    108. </el-row>
    109. </div>
    110. <!-- 中间左侧结束 -->
    111. </div>
    112. <!-- 中间信息结束 -->
    113. </el-col>
    114. <el-col :span="6">
    115. <!-- 右侧区域开始 -->
    116. <div class="main-right">
    117. <!-- 治愈率和死亡率 -->
    118. <chart-card
    119. title="治愈率和死亡率"
    120. :innerClass="`cure-and-dead-rate-chart`"
    121. :customClass="`chart-item-bottom-sep`"
    122. >
    123. <cured-and-dead-rate-chart
    124. ref="cureRateChart"
    125. :data="rate"
    126. title="治愈率"
    127. style="width: 280px; height: 130px"
    128. />
    129. </chart-card>
    130. <chart-card title="新增趋势" :customClass="`chart-item-bottom-sep`">
    131. <basic-trend-chart
    132. :data="basicIncrTrendData"
    133. ref="confirmedCountTrendChart"
    134. style="width: 100%; height: 320px"
    135. />
    136. </chart-card>
    137. <chart-card title="">
    138. <div slot="title" class="province-table-title flex">
    139. 各省累计确诊
    140. <el-link
    141. icon="el-icon-view"
    142. style="color: #bcbcbf; padding-left: 10px"
    143. :underline="false"
    144. @click="provinceTableDialogShowHandler"
    145. >详情</el-link
    146. >
    147. </div>
    148. <dv-scroll-board
    149. :config="provinceConfirmedCountBoardConfig"
    150. style="width: 100%; height: 360px"
    151. />
    152. </chart-card>
    153. </div>
    154. <!-- 右侧区域结束 -->
    155. </el-col>
    156. </el-row>
    157. </div>
    158. <!-- 其他页面 -->
    159. <div class="province-data-table-wrapper">
    160. <el-dialog
    161. :visible.sync="provinceTableDialogVisible"
    162. width="50%"
    163. :before-close="provinceTableDialogClose"
    164. >
    165. <div slot="title" class="province-data-modal-title">
    166. <p>各省数据表</p>
    167. <span class="province-data-modal-update-time"
    168. >(更新时间:{{ basicData.updateTime }})</span
    169. >
    170. </div>
    171. <div class="area-data-table-wrapper">
    172. <el-table
    173. class="area-data-table"
    174. :data="provinceDataList"
    175. style="width: 100%"
    176. >
    177. <el-table-column prop="provinceLabel" align="center" label="省份">
    178. </el-table-column>
    179. <el-table-column
    180. prop="confirmedCount"
    181. align="center"
    182. label="累计确诊"
    183. >
    184. </el-table-column>
    185. <el-table-column
    186. prop="currentConfirmedCount"
    187. align="center"
    188. label="现有确诊"
    189. >
    190. </el-table-column>
    191. <el-table-column prop="curedCount" align="center" label="累计治愈">
    192. </el-table-column>
    193. <el-table-column prop="deadCount" align="center" label="累计死亡">
    194. </el-table-column>
    195. </el-table>
    196. </div>
    197. </el-dialog>
    198. <!-- 关于弹窗 -->
    199. <el-dialog
    200. title="关于"
    201. :visible.sync="aboutDialogVisible"
    202. width="30%"
    203. :before-close="aboutDialogClose"
    204. >
    205. <about />
    206. </el-dialog>
    207. <!-- 关于图标 -->
    208. <div class="about-wraper">
    209. <i
    210. class="el-icon-info"
    211. style="font-size: 30px"
    212. @click="aboutDialogShowHandler"
    213. ></i>
    214. </div>
    215. </div>
    216. </div>
    217. </template>
    218. <script>
    219. import ChartCard from '../components/ChartCard'
    220. import DataMap from '../components/DataMap'
    221. import CuredAndDeadRateChart from '../components/CuredAndDeadRateChart'
    222. import BasicDataItemLabel from '../components/BasicDataItemLabel'
    223. import BasicTrendChart from '../components/BasicTrendChart'
    224. import ProvinceRankingBarChart from '../components/ProvinceRankingBarChart'
    225. import CurrentConfirmedCompareBarChart from '../components/CurrentConfirmedCompareBarChart'
    226. import About from '../components/About'
    227. import BasicProportionChart from '../components/BasicProportionChart'
    228. import covid19Service from '../api/covid19'
    229. const formatter = (number) => {
    230. const numbers = number.toString().split('').reverse()
    231. const segs = []
    232. while (numbers.length) segs.push(numbers.splice(0, 3).join(''))
    233. return segs.join(',').split('').reverse().join('')
    234. }
    235. // 数据样式
    236. const getNumberStyle = (color = '#E8EAF6', fontSize = 30, fontWeight = 'bolder') => {
    237. return {
    238. fontSize: fontSize,
    239. fill: color,
    240. fontWeight: fontWeight
    241. }
    242. }
    243. const initBasicConfig = (data = null) => {
    244. let currentConfirmedCount = data ? [data.currentConfirmedCount] : 0
    245. let confirmedCount = data ? [data.confirmedCount] : 0
    246. let importedCount = data ? [data.importedCount] : 0
    247. let noInFectCount = data ? [data.noInFectCount] : 0
    248. let deadCount = data ? [data.deadCount] : 0
    249. let curedCount = data ? [data.curedCount] : 0
    250. return {
    251. confirmedCount: {
    252. number: [confirmedCount],
    253. content: '{nt}',
    254. formatter,
    255. style: getNumberStyle()
    256. },
    257. currentConfirmedCount: {
    258. number: [currentConfirmedCount],
    259. content: '{nt}',
    260. formatter,
    261. style: getNumberStyle('#2E8EEA')
    262. },
    263. importedCount: {
    264. number: [importedCount],
    265. content: '{nt}',
    266. formatter,
    267. style: getNumberStyle()
    268. },
    269. noInFectCount: {
    270. number: [noInFectCount],
    271. content: '{nt}',
    272. formatter,
    273. style: getNumberStyle()
    274. },
    275. deadCount: {
    276. number: [deadCount],
    277. content: '{nt}',
    278. formatter,
    279. style: getNumberStyle('#D32E58')
    280. },
    281. curedCount: {
    282. number: [curedCount],
    283. content: '{nt}',
    284. formatter,
    285. style: getNumberStyle()
    286. }
    287. }
    288. }
    289. const initProvinceConfirmedCountBoardConfig = (resultList = []) => {
    290. return {
    291. header: ['省份', '累计确诊', '累计治愈', '累计死亡'],
    292. headerHeight: 30,
    293. data: resultList,
    294. align: ['center'],
    295. rowNum: 10,
    296. index: true,
    297. indexHeader: '排名',
    298. headerBGC: '',
    299. oddRowBGC: '',
    300. evenRowBGC: '',
    301. carousel: 'single'
    302. }
    303. }
    304. export default {
    305. components: {
    306. ChartCard,
    307. DataMap,
    308. CuredAndDeadRateChart,
    309. BasicDataItemLabel,
    310. BasicTrendChart,
    311. ProvinceRankingBarChart,
    312. CurrentConfirmedCompareBarChart,
    313. About,
    314. BasicProportionChart
    315. },
    316. data () {
    317. return {
    318. title: '全国新冠肺炎疫情数据大屏',
    319. provinceTableDialogVisible: false,
    320. aboutDialogVisible: false,
    321. commonData: {},
    322. basicData: {
    323. currentConfirmedCount: 0,
    324. currentConfirmedIncr: 0,
    325. confirmedCount: 0,
    326. confirmedIncr: 0,
    327. curedCount: 0,
    328. curedIncr: 0,
    329. deadCount: 0,
    330. deadIncr: 0,
    331. sure: 0,
    332. sureAdd: 0,
    333. importedCount: 0,
    334. importedIncr: 0,
    335. noInFectCount: 0,
    336. noInFectIncr: 0,
    337. suspectCount: 0,
    338. suspectIncr: 0,
    339. updateTime: '2022-3-1'
    340. },
    341. defaultDataConfig: initBasicConfig(),
    342. cureRateConfig: {
    343. data: [0],
    344. shape: 'round'
    345. },
    346. deadRateConfig: {
    347. data: [0],
    348. shape: 'round'
    349. },
    350. countryRankingTakeTurnChartConfig: {
    351. data: [],
    352. waitTime: 2000,
    353. unit: '单位',
    354. valueFormatter ({ value }) {
    355. const reverseNumber = (value + '').split('').reverse()
    356. let valueStr = ''
    357. while (reverseNumber.length) {
    358. const seg = reverseNumber.splice(0, 3).join('')
    359. valueStr += seg
    360. if (seg.length === 3) valueStr += ','
    361. }
    362. return valueStr.split('').reverse().join('')
    363. }
    364. },
    365. provinceConfirmedCountBoardConfig: initProvinceConfirmedCountBoardConfig(),
    366. provinceDataList: [],
    367. trendDataList: [],
    368. confirmedCountList: [],
    369. top10ProvinceData: {
    370. provinceList: [],
    371. valueList: []
    372. },
    373. basicIncrTrendData: {
    374. dateList: [],
    375. importedIncrDataList: [],
    376. currentConfirmedIncrDataList: []
    377. },
    378. confirmSingleBarChartData: {
    379. dateList: [],
    380. currentConfirmedCountList: [],
    381. confirmedCountList: []
    382. },
    383. rate: {
    384. curedRate: 0,
    385. deadRate: 0
    386. },
    387. areaData: {},
    388. mapDataList: []
    389. }
    390. },
    391. methods: {
    392. queryBasicData () {
    393. let self = this
    394. covid19Service.getOverall().then((res) => {
    395. if (!res.success) {
    396. console.log('错误:' + res.info)
    397. return
    398. }
    399. self.basicData = res.data
    400. self.setBasicData(res.data)
    401. })
    402. },
    403. queryProvinceDataList () {
    404. let self = this
    405. covid19Service.getProvinceDataList().then((res) => {
    406. if (!res.success) {
    407. // TODO 错误处理...
    408. console.log('错误:' + res.info)
    409. return
    410. }
    411. self.provinceDataList = res.data
    412. self.setAreaChartData(res.data)
    413. // 设置累计排名数据
    414. self.setProvinceRankingData(res.data)
    415. // 设置各省累计确诊轮播表格数据
    416. self.setProvinceComfirmedCountBoardData(res.data)
    417. // 设置地图数据
    418. self.setMapData(res.data)
    419. })
    420. },
    421. queryTrendDataList () {
    422. let self = this
    423. covid19Service.getDailyList().then((res) => {
    424. if (!res.success) {
    425. // TODO 错误处理...
    426. console.log('错误:' + res.info)
    427. return
    428. }
    429. self.trendDataList = res.data
    430. // 重置图表数据
    431. self.setBasicIncrTrendData(res.data)
    432. })
    433. },
    434. setProvinceRankingData (areaList) {
    435. let provinceList = []
    436. let dataValueList = []
    437. for (let i = 0; i < 10; i++) {
    438. provinceList.push(areaList[i].provinceLabel)
    439. dataValueList.push(areaList[i].confirmedCount)
    440. }
    441. let data = {
    442. provinceList: provinceList,
    443. valueList: dataValueList
    444. }
    445. this.top10ProvinceData = data
    446. },
    447. setBasicIncrTrendData (data) {
    448. let dateList = []
    449. let currentConfirmedIncrList = []
    450. let importedIncrList = []
    451. let sevenDayDateList = []
    452. // 仅显示一周条数据
    453. let confirmedCountList = []
    454. let curedCountList = []
    455. // 仅获取7条数据
    456. let count = 7
    457. let noInFectDataList = []
    458. for (let i = data.currentConfirmedIncrList.length - 1; i >= 0; i--) {
    459. dateList.push(data.currentConfirmedIncrList[i][0])
    460. currentConfirmedIncrList.push(data.currentConfirmedIncrList[i][1])
    461. }
    462. for (let i = data.importedIncrList.length - 1; i >= 0; i--) {
    463. importedIncrList.push(data.importedIncrList[i][1])
    464. }
    465. for (let i = data.noInFectCountList.length - 1; i >= 0; i--) {
    466. noInFectDataList.push(data.noInFectCountList[i][1])
    467. }
    468. for (let i = count; i >= 0; i--) {
    469. if (confirmedCountList.length >= count) {
    470. break
    471. }
    472. sevenDayDateList.push(data.confirmedCountList[i][0])
    473. confirmedCountList.push(data.confirmedCountList[i][1])
    474. }
    475. for (let i = count; i >= 0; i--) {
    476. if (curedCountList.length >= count) {
    477. break
    478. }
    479. curedCountList.push(data.curedCountList[i][1])
    480. }
    481. this.basicIncrTrendData = {
    482. dateList: dateList,
    483. importedIncrDataList: importedIncrList,
    484. currentConfirmedIncrDataList: currentConfirmedIncrList,
    485. noInFectDataList: noInFectDataList
    486. }
    487. this.confirmSingleBarChartData = {
    488. dateList: sevenDayDateList,
    489. curedCountList: curedCountList,
    490. confirmedCountList: confirmedCountList
    491. }
    492. },
    493. setProvinceComfirmedCountBoardData (areaList) {
    494. let resultList = areaList.map(item => {
    495. return [item.provinceLabel, item.confirmedCount, item.curedCount, item.deadCount]
    496. })
    497. // 重新生成,否则无法刷新状态
    498. this.provinceConfirmedCountBoardConfig = initProvinceConfirmedCountBoardConfig(resultList)
    499. },
    500. setAreaChartData (areaList) {
    501. let confirmedCountList = []
    502. let provinceList = []
    503. let curedCountList = []
    504. let deadCountList = []
    505. areaList.forEach(item => {
    506. provinceList.push(item.provinceLabel)
    507. confirmedCountList.push(item.confirmedCount)
    508. curedCountList.push(item.curedCount)
    509. deadCountList.push(item.deadCount)
    510. })
    511. this.areaData = {
    512. provinceList: provinceList,
    513. confirmedCountList: confirmedCountList,
    514. curedCountList: curedCountList,
    515. deadCountList: deadCountList
    516. }
    517. },
    518. setMapData (dataList) {
    519. let list = dataList.map(item => {
    520. return {
    521. name: item.provinceLabel,
    522. value: item.confirmedCount
    523. }
    524. })
    525. this.mapDataList = list
    526. },
    527. provinceTableDialogShowHandler () {
    528. this.provinceTableDialogVisible = true
    529. },
    530. provinceTableDialogClose () {
    531. this.provinceTableDialogVisible = false
    532. },
    533. aboutDialogShowHandler () {
    534. this.aboutDialogVisible = true
    535. },
    536. aboutDialogClose () {
    537. this.aboutDialogVisible = false
    538. },
    539. setBasicData (data) {
    540. // 重新生成,否则视图不更新
    541. let config = initBasicConfig(data)
    542. this.defaultDataConfig = config
    543. // 处理治愈率和死亡率
    544. this.rate = {
    545. curedRate: data.curedRate / 100,
    546. deadRate: data.deadRate / 100
    547. }
    548. },
    549. startQueryData () {
    550. this.queryBasicData()
    551. this.queryProvinceDataList()
    552. this.queryTrendDataList()
    553. },
    554. initAllChart() {
    555. this.$refs.dataMap.initChart()
    556. this.$refs.cureRateChart.initChart()
    557. this.$refs.confirmedCountTrendChart.initChart()
    558. this.$refs.topConfirmedCountRankChart.initChart()
    559. this.$refs.confirmSingleBarChart.initChart()
    560. this.$refs.basicProportionChart.initChart()
    561. }
    562. },
    563. mounted () {
    564. this.initAllChart()
    565. this.startQueryData()
    566. let self = this
    567. // 定义定时器,隔 5 秒刷新一次
    568. this.timer = setInterval(() => {
    569. self.startQueryData()
    570. }, 5000)
    571. },
    572. beforeDestroy() {
    573. clearInterval(this.timer)
    574. }
    575. }
    576. </script>
    577. <style>
    578. .container {
    579. position: relative;
    580. }
    581. h1 {
    582. font-size: 35px;
    583. font-weight: bold;
    584. padding: 20px;
    585. }
    586. .flex {
    587. display: flex;
    588. }
    589. .top-header {
    590. position: relative;
    591. margin-bottom: 10px;
    592. }
    593. .top-header-tip {
    594. font-size: 14px;
    595. position: absolute;
    596. padding: 20px 20px;
    597. text-align: right;
    598. top: 0;
    599. right: 0;
    600. }
    601. .title {
    602. min-width: 350px;
    603. }
    604. .chart-card {
    605. background: #0f142b;
    606. border-radius: 10px;
    607. margin: 0 20px;
    608. }
    609. .main-inner-map-wraper {
    610. height: 100%;
    611. margin: 0 20px;
    612. }
    613. .map {
    614. width: 99%;
    615. height: 840px;
    616. padding: 20px 10px 10px 10px;
    617. }
    618. .province-scroll-board-wrapper {
    619. padding-top: 10px;
    620. padding-bottom: 10px;
    621. }
    622. .chart-item-bottom-sep {
    623. margin-bottom: 20px;
    624. }
    625. .province-table-title {
    626. color: #fff;
    627. font-size: 16px;
    628. font-weight: bold;
    629. padding: 10px 10px 10px 20px;
    630. text-align: left;
    631. }
    632. .basic-incr-trend-chart-wrapper {
    633. padding-bottom: 10px;
    634. }
    635. .sub-title,
    636. .last-update-time,
    637. .province-scroll-board-wrapper ::v-deep .dv-scroll-board .header,
    638. .province-scroll-board-wrapper ::v-deep .dv-scroll-board .rows {
    639. font-size: 14px;
    640. }
    641. .province-scroll-board-wrapper ::v-deep .dv-scroll-board .rows {
    642. color: #bcbcbf;
    643. }
    644. .province-data-modal-title {
    645. color: #000;
    646. font-size: 20px;
    647. font-weight: bold;
    648. }
    649. .province-data-modal-update-time {
    650. color: #58585a;
    651. font-size: 16px;
    652. font-weight: normal;
    653. padding-top: 10px;
    654. display: block;
    655. }
    656. .cure-and-dead-rate-chart {
    657. display: flex;
    658. justify-content: space-around;
    659. }
    660. .top-basic-info {
    661. display: flex;
    662. }
    663. .dv-scroll-ranking-board .ranking-column .inside-column {
    664. background: linear-gradient(90deg, #29bfff, #a231ff, #0deccd, #29bfff);
    665. }
    666. .chart-item-container {
    667. margin: 0 10px;
    668. }
    669. .about-wraper {
    670. position: fixed;
    671. bottom: 20px;
    672. right: 20px;
    673. }
    674. </style>

    七、成品展示

 

 

 

 

 结束语

截至到 2022年 3 月,疫情仍未完全褪去,民众最为关心的是疫苗接种情况。本系统基于疫情数据显 示的基础上,增加疫苗数据显示功能。利用数据爬虫 技术完成数据的获取与下载,经数据处理后形成数据 集,数据可视化阶段利用 Python 语言的 Flask 框架、 Vue、ECharts可视化等可视化技术,对疫情数据以及疫苗数据进行数据可视化。经过对现有技术的分析,并在可 视化内容上做出创新。集可视化内容于一个页面中, 疫情数据与疫苗数据一目了然,用户可以从多方面,多角度,全方位直观的观察到疫情数据、疫情发展 趋势,以及疫苗接种情况。本系统的开发是用来帮助 民众充分了解全国各地的疫情情况、近期的疫情发展 趋势以及疫苗接种情况。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/195216
推荐阅读
相关标签
  

闽ICP备14008679号