echarts、dataV 数据可视化大屏

一、项目描述 (包含echarts中国地图、dataV科技炫酷边框等等)

  • 一个基于 Vue、Datav、Echart 框架的 " 数据大屏项目 ",通过 Vue 组件实现数据动态刷新渲染,内部图表可实现自由替换。部分图表使用 DataV 自带组件, 组件库基于Vue (React版) ,主要用于构建大屏(全屏)数据展示页面即数据可视化,具有多种类型组件可供使用。

  • 项目环境:Vue-cli、DataV、Echarts、node

友情链接:

  1. Vue 官 方文档
  2. Vue CLI
  3. DataV 官方文档
  4. echarts 实例echarts API 文档

项目展示

二、主要文件介绍

文件

作用/功能

main.js

主目录文件,引入 Echart/DataV 等文件

utils

工具函数与 mixins 函数等

components/ HomeWord.vue

项目主结构

assets

静态资源目录,放置 logo 与背景图片

三、项目注意点

引用的模块(先下载 install)

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import * as echarts from 'echarts'  //echarts 引用使用断言,否则可能报错
import ElementUI from 'element-ui'; 
import 'element-ui/lib/theme-chalk/index.css';  
import dataV from '@jiaminghi/data-view'
Vue.use(ElementUI); //部分图标使用 element-ui
Vue.use(dataV)   //全局启用 dataV

Vue.prototype.$echarts=echarts  //将echarts挂载到Vue原型上,全局可使用this.$echarts 调用
Vue.config.productionTip = false
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

封装组件渲染图表

所有的 ECharts 图表已经对数据和屏幕改动进行了监听,能够动态渲染图表数据。在监听窗口小大的模块。

中间部分中国地图json数据来源 (地址)

我这边直接复制内容存到本地 (把复制的链接在网页打开Ctrl+A Ctrl+C 全选复制粘贴): assets > json > china.json
在这里插入图片描述

渲染地图的子组件

有几个注意点:

  1. 需要地图json文件

  2. 需要监听数据变化

  3. tooltip: formatter 鼠标移入的提示信息框

复用图表组件(可以研究下)

更换边框 (炫酷的科技感动态边框)

边框是使用了 DataV 自带的组件,只需要去 views 目录下去寻找对应的位置去查找并替换就可以,具体的种类请去 DavaV 官网查看
如:

<dv-border-box-1> 内容撑开 </dv-border-box-1>
<dv-border-box-2> 内容撑开 </dv-border-box-2>
<dv-border-box-3> 内容撑开 </dv-border-box-3>

在这里插入图片描述

Mixins 解决自适应适配功能

使用 mixins 注入解决了界面大小变动图表自适应适配的功能,函数在 utils/resizeMixins.js 中,应用在 common/echart/index.vue 的封装渲染组件,主要是对 this.chart 进行了功能注入。

屏幕适配

使用更流程通用的 css3:scale 缩放方案,通过 ref 指向 主页面的元素,基准尺寸是 1980px*1080px,所以支持同比例屏幕 100% 填充,如果非同比例则会自动计算比例居中填充,不足的部分则留白。实现代码在 `src/utils/drawMixin ,如果有其它的适配方案,欢迎交流。

src > utils > drawMixin.js

// 屏幕适配 mixin 函数
//需要先设置index.html meta 标签 user-scalable=no
//<meta name="viewport" content="width=device-width,initial-scale=1.0 ,user-scalable=no">
//并且需要绑定ref 

// * 默认缩放值
const scale = {
    width: '1',
    height: '1',
  }
  
  // * 设计稿尺寸(px)
  // 1920×1080
  const baseWidth =1920
  const baseHeight = 1080
  
  
  // * 需保持的比例(默认1.77778)
  const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
  
  export default {
    data() {
      return {
        // * 定时函数
        drawTiming: null
      }
    },
    mounted () {
      this.calcRate()
      window.addEventListener('resize', this.resize)
    },
    beforeDestroy () {
      window.removeEventListener('resize', this.resize)
    },
    methods: {
      calcRate () {
        const appRef = this.$refs["appRef"]
        if (!appRef) return 
        // 当前宽高比
        const currentRate = parseFloat((window.innerWidth / window.innerHeight).toFixed(5))
        if (appRef) {
          if (currentRate > baseProportion) {
            // 表示更宽
            scale.width = ((window.innerHeight * baseProportion) / baseWidth).toFixed(5)
            scale.height = (window.innerHeight / baseHeight).toFixed(5)
            appRef.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`
          } else {
            // 表示更高
            scale.height = ((window.innerWidth / baseProportion) / baseHeight).toFixed(5)
            scale.width = (window.innerWidth / baseWidth).toFixed(5)
            appRef.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`
          }
        }
      },
      resize () {
        clearTimeout(this.drawTiming)
        this.drawTiming = setTimeout(() => {
          this.calcRate()
        }, 200)
      }
    },
  }

homeword.vue

<template>
  <div id="index" ref="appRef">
  ...页面内容...
  </div>
</template>

<script>
import drawMixin from "../utils/drawMixin";
export default {
  components: {
   //.....
  },
  mixins: [drawMixin], //混入 (保持页面缩放比例)
 } 
 </script>

请求数据

使用 axios 进行数据请求,在 main.js 位置进行全局配置。

src > request 文件统一处理所有的请求

src > request > request.js 请求和响应拦截

// 封装axios实例的拦截器(请求, 响应)
import axios from 'axios';


// 1. 创建axios实例
const instance = axios.create({
    timeout: 15000, // 超时时间15s
    baseURL: '这里是你请求是ip地址', // ip+端口, 公用的前缀路径
});

// 重写实例请求前拦截器
instance.interceptors.request.use((config) => {

    return config;
}, (err) => {
    return Promise.reject(err);
})

// 重写实例响应后拦截器
instance.interceptors.response.use((result) => {
   
    return result.data;
}, (err) => {
    return Promise.reject(err);
})

// 导出axios实例
export default instance;

src > request > request.js 请求拼接地址

import request from './request';

// 主要指标 /mcpbd-data/data/mainIndex
export const getMainIndex= () => request.get('/mainIndex')  //这里是get请求,"/mainIndex" 是接口文档的请求拼接字段

//......

homeword.vue 引入,并发送请求数据

由于一个页面要发送多个请求,并且成功获取数据再渲染,我使用了promise.all

并发请求,但是发现请求很慢(不知道有什么方法可以优化)

<template>
    ...
</template>

<script>
import drawMixin from "../utils/drawMixin";

//接口
import { 
  getMainIndex,
} from "../request/httpApi";

export default {
  components: {
    //...
  },
  mixins: [drawMixin],
  name: "HelloWorld",
  props: {},
  data() {
    return {
      //渲染子组件,还未请求到数据的时候,需要放数据的所有元素不显示
      falg: false,
      loading: true,
      loadTimer: null,
      resd: [],
      realVal: 0,
      MainIndicators: {
        // 主要指标 默认数据
        userAllCnt: "",
        userNewCnt: "",
        userDailyActvCnt: "",
        userDailyActvRatio: "",
        userMonthlyActvCnt: "",
        userMonthlyActvRatio: "",
        mctAllCnt: "",
      },

      //累计注册用户数折线
     
      //....

      mapData: {
        //地图数据
        mapArr: [],
      },
    };
  },
  mounted() {
    this.cancelLoading();
  },
  filters: {
    //过滤数据
    numFilter(val) {
      return (parseFloat(val) * 100).toFixed(1);
    },
  },
  created() {
    this.getJ();
    this.getShishi();
    // 实时更新数据(隔一个小时请求数据)
    setInterval(() => {
      this.getJ();
    }, 3600000);
    //左下角数据实时更新(1分钟)
    setInterval(() => {
      this.getShishi();
    }, 60000);
  },
  methods: {
    getShishi() {
      //左下角数据 (因为这部分数据需要每分钟更新一次所以单独拎出来)
      getRealTimeIndex()  //发送请求
        .then((res) => {
          this.RealTimeIndex.newUserAllUserCnt = [];
          this.RealTimeIndex.newUserAllUserDate = [];
          this.resd = res;
          this.resd.forEach((item) => {
            this.RealTimeIndex.newUserAllUserCnt.push(
              Number(item.userAllCnt).toFixed()
            ),
              this.RealTimeIndex.newUserAllUserDate.push(
                item.calTime.substring(11)
              );
          });
        })
        .catch((err) => {
          return;
        });
    },
    sortData(attr) {
      return function (a, b) {
        return b[attr] - a[attr];
      };
    },
    getJ() {
      // 累计注册用户
      let p1 = new Promise((resolve, reject) => {
        getUserAllCnt()
          .then((res) => {
            resolve(res);
          })
          .catch((err) => {
            reject(err);
          });
      });

      // 用户
      let p2 = new Promise((resolve, reject) => {
       //....
      });
      // 累计用户数
      let p3 = new Promise((resolve, reject) => {
        //....
      });
      // 日活用户数
      let p4 = new Promise((resolve, reject) => {
         //....
      });

      // 新增用户数
      let p5 = new Promise((resolve, reject) => {
        //....
      });
      // 指标排行表
      let p6 = new Promise((resolve, reject) => {
        //....
      });

      // 主要指标
      let p7 = new Promise((resolve, reject) => {
        getMainIndex()
          .then((res) => {
            setTimeout(() => {
              resolve(res);
            }, 700);
          })
          .catch((err) => {
            reject(err);
          });
      });
      // 地图数据省份
      let p8 = new Promise((resolve, reject) => {
        //....
      });

      Promise.all([p1, p2, p3, p4, p5, p6, p7, p8])
        .then((res) => {
          // 累计注册用户
       	  //数据处理。。。

          // 日活
         //数据处理。。。

          // 累计用户数 (只展示前5条数据)
         //数据处理。。。

          // 日活用户数
       //数据处理。。。

          // 新增用户数
          //数据处理。。。

          // 指标排行表
          //数据处理。。。

          // 主要指标
          let mainIndex = res[6];
          this.MainIndicators.userAllCnt = Number(
            mainIndex.userAllCnt
          ).toLocaleString("en-US"); //使用千分符
          this.MainIndicators.userNewCnt = Number(
            mainIndex.userNewCnt
          ).toLocaleString("en-US");
          this.MainIndicators.userDailyActvCnt = Number(
            mainIndex.userDailyActvCnt
          ).toLocaleString("en-US");
          this.MainIndicators.userDailyActvRatio = mainIndex.userDailyActvRatio;
          this.MainIndicators.userMonthlyActvCnt = Number(
            mainIndex.userMonthlyActvCnt
          ).toLocaleString("en-US");
          this.MainIndicators.userMonthlyActvRatio =
            mainIndex.userMonthlyActvRatio;
          this.MainIndicators.mctAllCnt = Number(
            mainIndex.mctAllCnt
          ).toLocaleString("en-US");

          //  地图省份指标
          let FenUserMap = res[7];
          this.mapData.mapArr = FenUserMap.map((item) => ({
            name: item.regionName,
            value: Number(item.userAllCnt).toFixed(),
            ratio: (Number(item.userActvDailyRatio) * 100).toFixed(1),
          }));
          //由于暂无数据隐藏南海诸岛,不太好,后面想到既然可以隐藏也可以将提示信息改成暂无数据
          this.mapData.mapArr.push({  
            name: "南海诸岛",
            value: 0,
            itemStyle: { opacity: 0, label: { show: false } },
          });

          this.falg = true; //子组件渲染
        })
        .catch((err) => {});
    },

    cancelLoading() {
      if (!this.loadTimer) {
        this.loadTimer = setTimeout(() => {
          this.loading = false;
        }, 500);
      } else {
        clearTimeout(this.loadTimer);
      }
    },
   
  },
  destroyed() {},
};
</script>

先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

猜你喜欢

转载自blog.csdn.net/m0_67403188/article/details/126099512