直接上代码
- 项目地址
gitee github-没有
-
看注释
-
说明
import anime from "animejs"; //动画插件
import numeral from "numeral"; //数据格式化插件
//echarts配置
import pieConfig from "../assets/pie.config";
import mapChinaConfig from "../assets/map.china.config";
import lineConfig from "../assets/line.config";
<template>
<div class="wrapper">
<div class="total">
<div class="title blod"><span>总计</span></div>
<div class="flex-start-box">
<div>
<div class="title">
<span>国内</span>
</div>
<div class="flex-start-box">
<div>
<div
class="total-details"
v-for="(item, index) of totalTitle"
:key="index"
>
<span>{{ item }}:</span>
<span :ref="'number-china' + index"> </span>
</div>
</div>
<div>
<div class="china">
<div class="china-pie" ref="china"></div>
</div>
</div>
</div>
</div>
<div>
<div class="title">
<span>国外</span>
</div>
<div class="flex-start-box">
<div>
<div
class="total-details"
v-for="(item, index) of totalTitle"
:key="index"
>
<span>{{ item }}:</span>
<span :ref="'number-anthor' + index">0</span>
</div>
</div>
<div class="china">
<div class="china-pie" ref="anthor-region"></div>
</div>
</div>
</div>
</div>
</div>
<div class="line-charts margin-top-30px">
<div class="title"><span>国内疫情统计图</span></div>
<el-select v-model="currentRegion" size="mini">
<el-option
v-for="(item, index) of options"
:key="index"
:label="item.name"
:value="item.name"
>
</el-option>
</el-select>
<span class="margin-left-30px">
<el-button type="default" size="mini" @click="selectLineCharts"
>折线图</el-button
>
<el-button type="default" size="mini" @click="selectBarCharts"
>柱状图</el-button
>
</span>
<div class="wrapper-charts">
<div id="line-china" ref="line-region">
<div class="nodata">请选择地区</div>
</div>
</div>
<div class="title"><span>国外疫情统计图</span></div>
<el-select v-model="currentRegionAnthor" size="mini">
<el-option
v-for="(item, index) of optionsAnthor"
:key="index"
:label="item.name"
:value="item.name"
>
</el-option>
</el-select>
<span class="margin-left-30px">
<el-button type="default" size="mini" @click="selectLineChartsAnthor"
>折线图</el-button
>
<el-button type="default" size="mini" @click="selectBarChartsAnthor"
>柱状图</el-button
>
</span>
<div class="wrapper-charts">
<div id="line-china" ref="line-region-anthor">
<div class="nodata">请选择地区</div>
</div>
</div>
</div>
<div class="map-charts">
<div class="title blod">
<span>地图</span>
</div>
<div class="flex-start-box-wrap">
<div class="map">
<div id="map" ref="map-china-0"></div>
</div>
<div class="map">
<div id="map" ref="map-china-1"></div>
</div>
<div class="map">
<div id="map" ref="map-china-2"></div>
</div>
<div class="map">
<div id="map" ref="map-china-3"></div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
/***
* 本来想着学习vue3.0+typescript
* 结果一大部分逻辑用的是vue2.0的
* typescript + vue3.0网站的教程也很多,但是貌似方法和使用js版本一样
* 所以这里sorry了
* 不过这个项目让我对echarts熟悉了很多,也不是没用
*/
import { Vue } from "vue-class-component";
import anime from "animejs";
import numeral from "numeral";
import * as echarts from "echarts";
import "echarts/map/js/china.js";
import { Watch } from "vue-property-decorator";
import Api from "../api";
import pieConfig from "../assets/pie.config";
import mapChinaConfig from "../assets/map.china.config";
import lineConfig from "../assets/line.config";
import { ElMessage } from "element-plus";
import { h } from "vue";
export default class Home extends Vue {
// 当前国内地区名称
private currentRegion: string = "";
// 国内所有地区
private options: any = [{}];
// 疫情统计国内数据
private chinaData: Array<any> = [];
// 接口直接返回的数据-国内
private chinaOriginData: Array<any> = [];
// 接口直接返回的数据-国外
private anthorOriginData: Array<any> = [];
// 疫情统计国外数据
private anthorData: Array<any> = [];
// 情况分类
private totalTitle: Array<string> = ["确诊", "治愈", "新增确诊", "死亡"];
// 折线图echart实例
private lineChartsView: any = null;
private lineChartsViewAnthor: any = null;
private currentValueRegion: any = null;
private currentRegionValueAnthor: any = null;
private currentRegionAnthor: string = "";
private optionsAnthor: any = [{}];
// 监听地区变化
@Watch("currentRegion")
public watchCurrentRegion(newVal: any, oldVal: any) {
const currentValue = this.chinaOriginData.find((ele: any) => {
return ele.name === newVal;
});
if (this.lineChartsView) {
this.lineChartsView.dispose();
}
this.currentValueRegion = currentValue;
this.createLineCharts(currentValue, "line-region");
}
@Watch("currentRegionAnthor")
public watchCurrentRegionAnthor(newVal: any, oldVal: any) {
const currentValue = this.anthorOriginData.find((ele: any) => {
return ele.name === newVal;
});
if (this.lineChartsViewAnthor) {
this.lineChartsViewAnthor.dispose();
}
this.currentRegionValueAnthor = currentValue;
this.createLineCharts(currentValue, "line-region-anthor");
}
public selectLineChartsAnthor() {
if (this.lineChartsViewAnthor) {
this.lineChartsViewAnthor.dispose();
}
if (this.currentRegionValueAnthor) {
this.createLineCharts(
this.currentRegionValueAnthor,
"line-region-anthor"
);
} else {
ElMessage({
type: "error",
message: "请选择地区",
});
}
}
public selectBarChartsAnthor() {
if (this.lineChartsViewAnthor) {
this.lineChartsViewAnthor.dispose();
}
if (this.currentRegionValueAnthor) {
this.createBarCharts(this.currentRegionValueAnthor, "line-region-anthor");
} else {
ElMessage({
type: "error",
message: "请选择地区",
});
}
}
// 切换折线图
public selectLineCharts() {
if (this.lineChartsView) {
this.lineChartsView.dispose();
}
if (this.currentValueRegion) {
this.createLineCharts(this.currentValueRegion, "line-region");
} else {
ElMessage({
type: "error",
message: "请选择地区",
});
}
}
//切换柱状图
public selectBarCharts() {
if (this.lineChartsView) {
this.lineChartsView.dispose();
}
if (this.currentValueRegion) {
this.createBarCharts(this.currentValueRegion, "line-region");
} else {
ElMessage({
type: "error",
message: "请选择地区",
});
}
}
// 创建柱状图
public createBarCharts(data: any, refName: string) {
const config: any = lineConfig;
config.xAxis.data = data.trend.updateDate;
const legend: Array<any> = [];
config.series = data.trend.list.map((item: any) => {
legend.push(item.name);
return {
data: item.data,
type: "bar",
smooth: true,
name: item.name,
};
});
config.legend.data = legend;
this.lineChartsView = echarts
.init(this.$refs[refName] as HTMLCanvasElement)
.setOption(config);
}
// 创建折线图
public createLineCharts(data: any, refName: string) {
const config: any = lineConfig;
config.xAxis.data = data.trend.updateDate;
const legend: Array<any> = [];
config.series = data.trend.list.map((item: any) => {
legend.push(item.name);
return {
data: item.data,
type: "line",
smooth: true,
name: item.name,
};
});
config.legend.data = legend;
this.lineChartsView = echarts
.init(this.$refs[refName] as HTMLCanvasElement)
.setOption(config);
}
// 生命周期
public mounted() {
this.getData();
}
// 创建疫情地图
private getChinaMap(
config: any,
type: string,
data: any,
element: HTMLCanvasElement
) {
const total = data.map((ele: any) => {
const flag = ele.details.find((item: any) => {
return item.name === type;
});
const value = flag.value;
return {
name: ele.region,
value: value[value.length - 1].value,
};
});
config.series[0].name = type + "人数";
config.title.text = "中国疫情地图" + type + "汇总";
config.series[0].data = total;
echarts.init(element).setOption(config);
}
// 获取疫情数据
private getData() {
Api.getChinaData((err: any, res: any) => {
this.chinaOriginData = res.data;
this.options = this.chinaOriginData;
this.chinaData = this.handlerData(err, res);
const legend: Array<any> = this.totalTitle;
const data: Array<any> = [];
this.totalTitle.forEach((type: string, index: number) => {
this.createAnimate(
this.todayDeatils(this.chinaData, type),
this.$refs["number-china" + index] as HTMLCanvasElement
);
data.push({
name: type,
value: this.todayDeatils(this.chinaData, type),
});
});
this.createPieCharts(
this.$refs["china"] as HTMLCanvasElement,
legend,
data
);
const chinaConfig = mapChinaConfig;
this.totalTitle.forEach((ele: any, index: number) => {
this.getChinaMap(
chinaConfig,
ele,
this.chinaData,
this.$refs["map-china-" + index] as HTMLCanvasElement
);
});
});
Api.getAnthorData((err: any, res: any) => {
this.anthorOriginData = res.data;
this.optionsAnthor = this.anthorOriginData;
this.anthorData = this.handlerData(err, res);
const legend: Array<any> = this.totalTitle;
const data: Array<any> = [];
this.totalTitle.forEach((type: string, index: number) => {
this.createAnimate(
this.todayDeatils(this.anthorData, type),
this.$refs["number-anthor" + index] as HTMLElement
);
data.push({
name: type,
value: this.todayDeatils(this.anthorData, type),
});
});
this.createPieCharts(
this.$refs["anthor-region"] as HTMLCanvasElement,
legend,
data
);
});
}
// 获取各个省市数据的总和
public todayDeatils(data: any, type: string) {
const yesterdayList = data.map((ele: any) => {
const list = ele.details.map((item: any) => {
const len = item.value.length;
return {
...item,
value: item.value[len - 1],
};
});
return {
...ele,
yesterday: list,
};
});
const yesterdayAllData = yesterdayList.map((total: any) => {
return total.yesterday;
});
const queZhengTotal = yesterdayAllData
.map((ele: any) => {
const queZheng = ele.filter((sitem: any) => {
if (sitem.name === type) {
return {
...sitem.value,
};
}
});
const totalYeaterday = { ...queZheng[0] }.value.value;
return totalYeaterday !== undefined ? totalYeaterday : 0;
})
.reduce((total: number, current: number) => {
return (total += current);
}, 0);
return queZhengTotal;
}
// 处理请求回来的新冠肺炎数据
// 让日期和数据匹配
public handlerData(err: any, res: any) {
const { status, data } = res;
if (status === 0) {
return data.map((ele: any) => {
const dateArray: [] = ele.trend.updateDate;
const valueArray: [] = ele.trend.list;
const value = valueArray.map((item: any) => {
const list = item.data;
const dateForValue = dateArray.map((date: string, sindex: number) => {
return {
date: date,
value: list[sindex],
};
});
return {
name: item.name,
value: dateForValue,
};
});
return {
region: ele.name,
details: value,
};
});
}
}
// 创建数字滚动动画
public createAnimate(total: number, ref: HTMLElement) {
const num = numeral(total).format("0,0");
anime({
targets: ref,
innerHTML: ["0", num],
easing: "linear",
round: 1,
});
}
// 创建饼图
public createPieCharts(
element: HTMLCanvasElement,
legend: Array<any>,
data: Array<any>
) {
const config: any = pieConfig;
config.legend.data = legend;
config.series[0].data = data;
echarts.init(element).setOption(config);
}
}
</script>
<style lang="less" scoped>
.wrapper-charts {
padding: 10px;
}
.nodata {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.wrapper {
width: 1170px;
height: 100%;
margin: 0 auto;
.china-pie {
width: 400px;
height: 300px;
}
.map {
width: 1170px;
height: 400px;
margin-top: 50px;
#map {
width: 100%;
height: 100%;
}
}
}
#line-china {
width: 1170px;
height: 400px;
}
.title {
padding: 10px;
}
.total {
width: 100vw;
.total-details {
padding: 10px;
&:first-child {
color: #ffd04b;
}
&:nth-child(2) {
color: #42b983;
}
&:nth-child(3) {
color: #f00;
}
&:last-child {
color: #fff;
}
}
}
</style>