当前位置: 首页 > news >正文

vue前端案例教学:动态获取最新疫情数据展示(代码详解)

【辰兮要努力】:hello你好我是辰兮,很高兴你能来阅读,昵称是希望自己能不断精进,向着优秀程序员前行!

博客来源于项目以及编程中遇到的问题总结,偶尔会有读书分享,我会陆续更新Java前端、后台、数据库、项目案例等相关知识点总结,感谢你的阅读和关注,希望我的博客能帮助到更多的人,分享获取新知,大家一起进步!

吾等采石之人,应怀大教堂之心,愿我们奔赴在各自的热爱里…

一、案例介绍

在这里插入图片描述

分享原因:最近看到这样的实时显示的系统觉得很有趣,分享给vue前端初学者实践学习 具体的效果如图所示

数据分析:数据来源于第三方官方提供的API接口,我们调用即可获取对应接口,拿到显示出来即实时展示

百度一下有很多可以直接调用的第三方接口

实时最新疫情数据接口

https://c.m.163.com/ug/api/wuhan/app/data/list-total

实时播报:时间线的对应接口

https://ent.163.com/special/00035080/virus_report_data.js

在这里插入图片描述


二、后端代码

后端使用HttpClient创建对象,执行对应的请求即可~

@Slf4j
@RestController
@RequestMapping("/demo")
public class DemoController {

    @ApiOperation("/HttpClient使用代码案例")
    @GetMapping("/getData")
    public Result getJsonTypeData(@RequestParam(name = "url", required = true) String url) throws Exception {
        Result result = new Result();
        //创建HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //设置请求传输超时时间
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000)
                .setConnectionRequestTimeout(5000)
                .setSocketTimeout(5000)
                .setRedirectsEnabled(true)
                .build();
        //创建get请求
        HttpGet httpGet = new HttpGet(url);
        //设置配置
        httpGet.setConfig(requestConfig);
        // 执行请求
        HttpResponse  httpResponse = httpClient.execute(httpGet);
        //判断响应信息是否正确
        if (httpResponse.getStatusLine().getStatusCode() == 200) {
            String jsonResult = EntityUtils.toString(httpResponse.getEntity());
            JSONObject jsonObject = JSONObject.parseObject(jsonResult);
            result.setData(jsonObject);
            result.setCode(200);
        }
        return result;
    }

    @ApiOperation("/实时播报:时间线的对应接口:此处仅给右下角显示使用")
    @GetMapping("/getTimeLine")
    public Result getNoJsonType(@RequestParam(name = "url", required = true) String url) throws IOException {
        Result result = new Result();
        //创建HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000)
                .setConnectionRequestTimeout(5000)
                .setSocketTimeout(5000)
                .setRedirectsEnabled(true)
                .build();
        HttpGet httpGet = new HttpGet(url);
        httpGet.setConfig(requestConfig);
        HttpResponse httpResponse = httpClient.execute(httpGet);
        if (httpResponse.getStatusLine().getStatusCode() == 200) {
            String jsonResult = EntityUtils.toString(httpResponse.getEntity());
            result.setData(jsonResult);
            result.setCode(200);
        }
        return result;
    }
}


三、前端代码

主页面代码以及效果

在这里插入图片描述

<template>
    <div class="container">
        <el-card class="box-card1">
            <div slot="header" class="title">全国疫情数据(含港澳台)</div>
            <div class="list-total">
                <div class="item cover_input">
                    <h4>境外输入</h4>
                    <div class="number">{{ total.input }}</div>
                    <p class="added">
                        较昨日
                        <span>+{{ today.input }}</span>
                    </p>
                </div>
                <div class="item cover_nosymptom">
                    <h4>无症状感染者</h4>
                    <div class="number">{{ extData.noSymptom }}</div>
                    <p class="added">
                        较昨日
                        <span>+{{ extData.incrNoSymptom }}</span>
                    </p>
                </div>
                <div class="item cover_today_confirm">
                    <h4>现有确诊</h4>
                    <div class="number">
                        {{ total.confirm - total.dead - total.heal }}
                    </div>
                    <p class="added">
                        较昨日
                        <span>+{{ today.storeConfirm }}</span>
                    </p>
                </div>
                <div class="item cover_confirm">
                    <h4>累计确诊</h4>
                    <div class="number">{{ total.confirm }}</div>
                    <p class="added">
                        较昨日
                        <span>+{{ today.confirm }}</span>
                    </p>
                </div>
                <div class="item cover_dead">
                    <h4>累计死亡</h4>
                    <div class="number">{{ total.dead }}</div>
                    <p class="added">
                        较昨日
                        <span>+{{ today.dead }}</span>
                    </p>
                </div>
                <div class="item cover_heal">
                    <h4>累计治愈</h4>
                    <div class="number">{{ total.heal }}</div>
                    <p class="added">
                        较昨日
                        <span>+{{ today.heal }}</span>
                    </p>
                </div>
            </div>
            <div class="cover_time">截至{{ lastUpdateTime }}</div>
        </el-card>
        <china-map></china-map>
        <line-chart id="line-chart"></line-chart>
        <time-line id="time-line"></time-line>
    </div>
</template>

<script>
    import request from "@/utils/request";
    import LineChart from './components/lineChart.vue'
    import ChinaMap from './components/map.vue'
    import TimeLine from './components/timeLine.vue'

    export default {
        name: 'Statistics',
        components: {
            LineChart,
            ChinaMap,
            TimeLine,
        },
        data() {
            return {
                total: {},
                today: {},
                extData: {},
                lastUpdateTime: '',
            }
        },
        created() {
            this.getInfo()
        },
        mounted() {
        },
        methods: {
            getInfo() {
                request.get("/demo/getData", {
                    params: {
                        url: 'https://c.m.163.com/ug/api/wuhan/app/data/list-total',
                    }
                }).then(data => {
                    this.today = data.data.data.chinaTotal.today
                    this.total = data.data.data.chinaTotal.total
                    this.extData = data.data.data.chinaTotal.extData
                    this.lastUpdateTime = data.data.data.lastUpdateTime
                })
            },
        },
    }
</script>
<style lang="scss" scoped>
    .box-card1 {
        width: 440px;
        float: left;
        margin-left: 20px;
    }

    .list-total {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-around;
    }

    .title {
        color: #333;
        font-size: 24px;
    }

    .item {
        width: 120px;
        height: 100px;
        margin-bottom: 15px;
        border: 1px dashed #ccc;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-content: space-around;
    }

    h4 {
        text-align: center;
        color: #333;
    }

    .number {
        text-align: center;
        color: #ffa352;
        font-weight: 700;
        font-size: 26px;
    }

    .added {
        text-align: center;
        color: #999;
    }

    .added span {
        color: #ffa352;
    }

    .cover_nosymptom .added span {
        color: #791618;
    }

    .cover_nosymptom .number {
        color: #791618;
    }

    .cover_today_confirm .number {
        color: #e44a3d;
    }

    .cover_today_confirm .added span {
        color: #e44a3d;
    }

    .cover_confirm .number {
        color: #a31d13;
    }

    .cover_confirm .added span {
        color: #a31d13;
    }

    .cover_dead .number {
        color: #333;
    }

    .cover_dead .added span {
        color: #333;
    }

    .cover_heal .number {
        color: #34aa70;
    }

    .cover_heal .added span {
        color: #34aa70;
    }

    .cover_time {
        color: #a9a9a9;
    }
</style>

疫情地图显示效果
在这里插入图片描述

对应前端代码

<template>
  <el-card class="box-card">
    <div id="map"></div>
  </el-card>
</template>

<script>
  import * as echarts from 'echarts'
  import china from 'echarts/map/js/china'
  import request from "@/utils/request";
  let option = {
    title: {
      text: '中国疫情图',
    },

    series: [
      {
        name: '现有确诊', //控制鼠标hover上去显示的固定文本
        type: 'map', //告诉echarts需要渲染一个地图
        mapType: 'china', //告诉echarts要渲染注册的china地图
        label: {
          show: true, //控制是否显示省份的名称
          color: '#333', // 设置显示每个省份的字体颜色
        },
        itemStyle: {
          borderColor: '#e8e8e8', //每个省份的边界的颜色
        },
        emphasis: {
          //控制鼠标移入的版块的颜色
          color: '#fff', //移入该模块的字体颜色
          itemStyle: {
            areaColor: '#83b5e7', //鼠标hover到模块上的背景色
          },
        },
        data: [], //每个板块的数据
      },
    ],
    visualMap: [
      {
        type: 'piecewise', //左下角的分段显示
        show: true,
        pieces: [
          {
            min: 10000,
            max: 1000000,
            label: '≥ 10000人',
            color: '#7f1100',
          },
          {
            min: 1000,
            max: 9999,
            label: '1000 - 9999人',
            color: '#bd1316',
          },
          {
            min: 500,
            max: 999,
            label: '500 - 999人',
            color: '#e64b45',
          },
          {
            min: 100,
            max: 499,
            label: '100 - 499人',
            color: '#ff8c71',
          },
          {
            min: 10,
            max: 99,
            label: '10 - 99人',
            color: '#fdd2a0',
          },
          {
            min: 1,
            max: 9,
            label: '1 - 9人',
            color: '#fff2cf',
          },
          {
            min: 0,
            max: 0,
            label: '0',
            color: '#ffffff',
          },
        ],
        color: ['#fafafa', '#7f1100'],
      },
    ],
    tooltip: {
      //控制鼠标hover上去显示信息
      trigger: 'item',
      formatter: function (params) {
        //自定义悬浮窗的显示内容
        return params.name + '<br/>' + params.seriesName + ':' + params.value
      },
    },
  }
  export default {
    // 组件名称
    name: 'ChinaMap',
    // 组件参数 接收来自父组件的数据
    props: {},
    // 组件状态值
    data() {
      return {}
    },
    // 计算属性
    computed: {},
    // 侦听器
    watch: {},
    // 以下是生命周期钩子
    /**
     * 组件实例创建完成,属性已绑定,但DOM还未生成,$ el属性还不存在
     */
    created() {},
    /**
     * el 被新创建的 vm.$ el 替换,并挂载到实例上去之后调用该钩子。
     * 如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$ el 也在文档内。
     */
    mounted() {
      this.getData()
      this.myEcharts = echarts.init(document.getElementById('map'))
      this.myEcharts.setOption(option)
    },
    // 组件方法
    methods: {
       getData() {
         request.get("/demo/getData", {
           params: {
             url: 'https://c.m.163.com/ug/api/wuhan/app/data/list-total',
           }
         }).then(data => {
           let list = data.data.data.areaTree[2].children.map((item) => ({
             name: item.name,
             value: item.total.confirm - item.total.heal - item.total.dead,
           }))
           option.series[0].data = list
           this.myEcharts.setOption(option)
         })
      },
    },
  }
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<!--使用了scoped属性之后,父组件的style样式将不会渗透到子组件中,-->
<!--然而子组件的根节点元素会同时被设置了scoped的父css样式和设置了scoped的子css样式影响,-->
<!--这么设计的目的是父组件可以对子组件根元素进行布局。-->
<style scoped>
  #map {
    width: 100%;
    height: 400px;
    background-color: #f3f3f3;
  }
  .box-card {
    width: 750px;
    float: left;
    margin-left: 20px;
  }
</style>

折线图显示效果

在这里插入图片描述

折线图对应代码

<template>
    <el-card class="card-linechart">
        <div id="linechart"></div>
    </el-card>
</template>

<script>
    import * as echarts from 'echarts'
    import request from "@/utils/request";

    var option = {
        title: {
            text: '全国疫情新增趋势',
        },
        tooltip: {
            trigger: 'axis',
        },
        legend: {
            right: '20px',
            data: ['确诊', '疑似'],
        },
        xAxis: {
            type: 'category',
            boundaryGap: false,
            data: [],
        },
        yAxis: {
            type: 'value',
            data: ['0', '20', '40', '60', '80', '100', '120', '140'],
        },
        series: [
            {
                name: '确诊',
                type: 'line',
                data: [],
            },
            {
                name: '疑似',
                type: 'line',
                data: [],
            },
        ],
    }
    export default {
        // 组件名称
        name: 'LineChart',
        // 组件参数 接收来自父组件的数据
        props: {},
        // 组件状态值
        data() {
            return {
                chinaDayList: [],
            }
        },
        // 计算属性
        computed: {},
        // 侦听器
        watch: {},
        // 以下是生命周期钩子
        /**
         * 组件实例创建完成,属性已绑定,但DOM还未生成,$ el属性还不存在
         */
        created() {
        },
        /**
         * el 被新创建的 vm.$ el 替换,并挂载到实例上去之后调用该钩子。
         * 如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$ el 也在文档内。
         */
        mounted() {
            this.getData()
            this.myChart = echarts.init(document.getElementById('linechart'))
            this.myChart.setOption(option)
        },
        // 组件方法
        methods: {
            getData() {
                request.get("/demo/getData", {
                    params: {
                        url: 'https://c.m.163.com/ug/api/wuhan/app/data/list-total',
                    }
                }).then(data => {
                    this.chinaDayList = data.data.data.chinaDayList
                    option.series[0].data = []
                    option.series[1].data = []
                    option.xAxis.data = []
                    this.chinaDayList.forEach((item, index) => {
                        option.xAxis.data.push(item.date)
                        option.series[0].data.push(item.today.confirm)
                        option.series[1].data.push(item.today.suspect)
                    })
                    this.myChart.setOption(option)
                })

            },
        },
    }
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<!--使用了scoped属性之后,父组件的style样式将不会渗透到子组件中,-->
<!--然而子组件的根节点元素会同时被设置了scoped的父css样式和设置了scoped的子css样式影响,-->
<!--这么设计的目的是父组件可以对子组件根元素进行布局。-->
<style scoped>
    .card-linechart {
        float: left;
        width: 500px;
    }

    #linechart {
        width: 100%;
        height: 400px;
    }
</style>

时间线实现效果
在这里插入图片描述

时间线实现前端代码

<template>
    <el-card class="box-timeline">
        <div slot="header" class="bobao">实时播报</div>
        <el-timeline class="time-container">
            <el-timeline-item
                    v-for="(item, index) in timeLineList"
                    :key="index"
                    :timestamp="item.time"
                    placement="top"
            >
                <el-card class="content">
                    <div class="tit">{{ item.title }}</div>
                    <a :href="item.link" target="_blank" class="detail">查看详细报道</a>
                </el-card>
            </el-timeline-item>
        </el-timeline>
    </el-card>
</template>

<script>
    import request from "@/utils/request";

    export default {
        // 组件名称
        name: 'TimeLine',
        // 组件参数 接收来自父组件的数据
        props: {},
        // 组件状态值
        data() {
            return {
                timeLineList: [],
            }
        },
        // 计算属性
        computed: {},
        // 侦听器
        watch: {},
        // 以下是生命周期钩子
        /**
         * 组件实例创建完成,属性已绑定,但DOM还未生成,$ el属性还不存在
         */
        created() {
            this.getData()
        },
        /**
         * el 被新创建的 vm.$ el 替换,并挂载到实例上去之后调用该钩子。
         * 如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$ el 也在文档内。
         */
        mounted() {
        },
        // 组件方法
        methods: {
            getData() {
                request.get("/demo/getTimeLine", {
                    params: {
                        url: 'https://ent.163.com/special/00035080/virus_report_data.js',
                    }
                }).then(data => {
                    let str = data.data
                    this.timeLineList = eval(
                        '(' + str.slice(str.indexOf('(') + 1, -1) + ')'
                    ).list.slice(0, 10)
                })

            },
        },
    }
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<!--使用了scoped属性之后,父组件的style样式将不会渗透到子组件中,-->
<!--然而子组件的根节点元素会同时被设置了scoped的父css样式和设置了scoped的子css样式影响,-->
<!--这么设计的目的是父组件可以对子组件根元素进行布局。-->
<style scoped>
    .box-timeline {
        width: 500px;
        float: left;
        margin-left: 20px;
    }

    .bobao {
        height: 20px;
        font-weight: 700;
        font-size: 18px;
    }

    .time-container {
        /* width: 100%; */
        height: 400px;
        overflow: auto;
    }

    .content {
        width: 340px;
        background-color: #f4f4f4;
        border-radius: 8px;
    }

    .tit {
        font-weight: 600;
        font-size: 18px;
    }

    .detail {
        color: #999;
        margin-top: 20px;
        display: block;
        position: relative;
    }

    .detail::before {
        position: absolute;
        content: '';
        right: 200px;
        top: 3px;
        width: 12px;
        height: 12px;
        background: url('./../../images/arrow.png') no-repeat;
        background-size: 100% 100%;
    }
</style>

本来很早就写好了,发的比较晚,当自己学习了

好了,未来争取输出更多干货视文章……


📣非常感谢你阅读到这里,如果这篇文章对你有帮助,希望能留下你的点赞👍 关注❤️ 分享👥 留言💬thanks!!!

📚愿我们奔赴在各自的热爱里!我们未来见……

相关文章:

  • Part9:RPA核心技术之抓屏技术
  • 关于缓存与数据双写一致性问题(清晰易懂)
  • 2022 年时间序列分析最顶流的 Python 库
  • Typora入门教程
  • CSC7720
  • 编译Nginx源码(windows10及centos7)
  • Elasticsearch入门、API操作
  • Linux--进程控制
  • Python——字典
  • 机器学习必会面试知识点
  • C#基于ASP.NET的人事薪资管理系统
  • 上传项目代码到Github|Gitee
  • 中小企业OA系统的设计与实现
  • 直播弹幕系统(二)- 整合RabbitMQ进行消息广播和异步处理
  • NR RedCap UE关键技术与标准化进展
  • CARIS11.4基本使用流程及其bug
  • 我用了几行代码就实现了界面变灰效果
  • [附源码]Python计算机毕业设计Django教育企业网站
  • App 黑白化技术实践
  • 网络安全常用的工具有哪些(二)
  • 中信银行一季度净利195.09亿增1.66%,不良率持平
  • 北大深圳研究生院成立科学智能学院:培养交叉复合型人才
  • 上海科创的三种品格
  • 10台核电新机组获核准,上海核电厂商独揽超500亿元订单
  • 准85后青海海北州副州长、州公安局局长李贤荣赴山东临沂挂职
  • 屋顶上的阳光与火光:战争如何改变了加沙的能源格局