Vue3.0+ElemenPlus+Echarts+百度疫情数据分析

206 阅读1分钟

直接上代码

  • 项目地址

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>