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 鼠标移入的提示信息框
<template>
  <div
    id="map"
    :style="{
      width: '700px',
      height: '730px',
      marginTop: '10px',
    }"
  ></div>
</template>

<script>
import china from "../assets/json/china.json"; //引入地图json文件
export default {
  mounted() {
    this.initChart();
  },

  props: {
    mapData: {  //接收父组件传过来的值
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      options: {},
      chart: null,
    };
  
  methods: {
    // 地图
    initChart() {
      this.$echarts.registerMap("china", china); //echarts的map需要注册,根据引入的json文件名,并定义map的名称
      this.chart = this.$echarts.init(document.getElementById("map"), null, {
        renderer: "svg",
      }); //init()挂载在某个元素,所以还需要在mounted调用, { renderer: "svg" } 将原来的canvas绘图改为svg 清晰度更高
      this.drawMap();
    },
    drawMap() {
      this.chart.setOption(
        {
          title: {
            text: "平台运营实时数据",
            textStyle: {
              color: "#fff",
              fontSize: 28,
            },
          },

          tooltip: { //鼠标移入的提示信息框
            show: true,
            trigger: "item",
            formatter: function (a, b) {
              // 将人数改千分位
              let a2 = "";
              let olda = Number(a["data"].value);

              if (olda >= 0) {
                a2 = olda;
                if (olda > 999) {
                  let parts = olda.toString().split(".");
                  parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
                  a2 = parts.join(".");
                }
              }
              // 日活兼容
              let a3 = a["data"].ratio ? a["data"].ratio : 0;
              let a1 = a["name"];
              if (a1 == "台湾省" && !a2) { // 台湾省显示暂无数据
                return `${a1}<br/> 暂无数据`;
              }

              return `${a1}<br/>累计注册: ${a2}  <br/>日活: ${a3} %`;
            },
          },

          series: [
            {
              type: "map", 
              map: "china", //对应registerMap() 对应的名称
              top: "150",  //调整地图在页面的位置
              zoom: 1.3,  //缩放比例
              emphasis: {
                label: { show: false },
                itemStyle: {
                  areaColor: "rgba(136, 132, 216)",
                },
              },
              itemStyle: {
                borderColor: "#fff",
              },
              data: this.mapData.mapArr, //有数据才显示颜色
            },
          ],

          visualMap: {
            show: true,
            type: "continuous",
            calculable: true,
            orient: "horizontal",  
            textStyle: {
              color: "#fff",
            },
            min: 0,
            max: 10000000,
            text: ["累计注册/人", ""],
            color: ["#df3d20", "#fff"],
            inRange: {
              // color: [ "#fff","#44effb", "#3399ff","#2b8afe", "#006699"],
              color: ["#44effb", "#3399ff", "#2b8afe", "#006699"],  //地图颜色
            },
          },
        },
        true
      );
    },
  },

  watch: {
    // handler  监听数据发生变化需要具体执行的方法
    // deep  需要监听的数据的深度,一般用来监听对象中某个属性的变化
    mapData: {
      handler() {
        this.drawMap();
      },
      deep: true, 
    },
  },
};
</script>

<style></style>

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

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

边框是使用了 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/drawMixin.js 中,应用在页面中,实现适配效果。

屏幕适配

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

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>

  • 26
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的内容,项目调度可视化大屏主要包括以下几个步骤: 1. 项目分析:对项目需求进行分析,确定需要显示的地图和资源信息。 2. 引入库:引入相关的库和插件,用于地图的展示和功能实现。 3. 项目开发:根据需求进行项目开发,包括地图容器构建、筛选和返回按钮事件、企业筛选功能、百度地图封装等。 4. 核心企业标注:在地图上标注核心企业的位置。 5. 周边资源标注和弹窗:在核心企业周围的5公里范围内标注其他资源的位置,并实现弹窗功能。 6. 路线规划:根据需要计算路径,包括时间和距离。 7. 总结:对项目开发进行总结和评估。 根据引用\[3\]的内容,可以将地图map定义为全局函数,方便在实际开发过程中进行参数的传递。通过创建地图对象、设置地图中心点和缩放级别、添加控件等操作,实现地图的展示和交互功能。 综上所述,项目调度可视化大屏是通过地图展示企业和资源的位置信息,并提供路径计算功能的项目。 #### 引用[.reference_title] - *1* *2* *3* [数据可视化大屏应急管理综合指挥调度系统完整案例详解(PHP-API、Echarts、百度地图)](https://blog.csdn.net/weixin_41290949/article/details/128436277)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值