【辰兮要努力】: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!!!
📚愿我们奔赴在各自的热爱里!我们未来见……