Vue使用Echarts实现横向柱状图,并通过WebSocket即时通讯更新

先看下效果图

并且数据每隔3秒自动变换一次

Vue使用Echarts实现横向柱状图,并通过WebSocket即时通讯更新

先看下后台返回的数据结构是什么样子的

[
  {
    "name":"商家1",
    "value":"99"
  },
  {
    "name":"商家2",
    "value":"199"
  },
  {
    "name":"商家3",
    "value":"222"
  },
  {
    "name":"商家4",
    "value":"99"
  },
  {
    "name":"商家5",
    "value":"499"
  },
  {
    "name":"商家6",
    "value":"252"
  },
  {
    "name":"商家7",
    "value":"199"
  },
  {
    "name":"商家8",
    "value":"29"
  },
  {
    "name":"商家9",
    "value":"232"
  },{
    "name":"商家10",
    "value":"99"
  },
  {
    "name":"商家11",
    "value":"77"
  },
  {
    "name":"商家12",
    "value":"82"
  },
  {
    "name":"商家13",
    "value":"99"
  },
  {
    "name":"商家14",
    "value":"19"
  },
  {
    "name":"商家15",
    "value":"22"
  },
  {
    "name":"商家16",
    "value":"522"
  }
]

开始实现前端的代码

html

<div class="com-page">
<div class="com-container">
  <div class="com-chart" ref="seller_ref"></div>
</div>
</div>

css

html,body,#app{
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  overflow: hidden;
}
.com-page {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.com-container {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.com-chart {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

data

data() {
    return {
      chartInstance: null,  //初始化echartInstance对象
      allData: null,    //接收的后台数据
      currentPage: 1, //当前显示的页数
      totalPage: 0, //一共有多少页
      timerId: null //定时器标识
    }
  },

methods

initChart方法

initChart() {
     //初始化echartInstance对象
     //chalk是我们定义的主题,echarts官方有案例,怎么使用可以百度一下,不喜欢可以直接删掉
      this.chartInstance = this.$echarts.init(this.$refs.seller_ref, 'chalk')
      //对图表初始化配置对控制
      const initOption = {
        title: {
          text: '▎商家销售统计',
          left: 20,
          top: 20
        },
        grid: {
          top: '20%',
          left: '3%',
          right: '6%',
          bottom: '3%',
          containLabel: true // 距离是包含坐标轴上的文字
        },
        xAxis: {
          type: 'value'
        },
        yAxis: {
          type: 'category'
        },
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'line',
            z: 0,
            lineStyle: {
              color: '#2D3443'
            }
          }
        },
        series: [{
          type: 'bar',
          label: {
            show: true,
            position: 'right',
            textStyle: {
              color: 'white'
            }
          },
          itemStyle: {
            // 指明颜色渐变的方向
            // 指明不同百分比之下颜色的值
            color: new this.$echarts.graphic.LinearGradient(0, 0, 1, 0, [
              // 百分之0状态之下的颜色值
              {
                offset: 0,
                color: '#5052EE'
              },
              // 百分之100状态之下的颜色值
              {
                offset: 1,
                color: '#AB6EE5'
              }
            ])
          }
        }]
      }
      this.chartInstance.setOption(initOption)
      //对图表对象进行鼠标事件监听
      //鼠标移入,定时器停止
      this.chartInstance.on('mouseover', () => {
        clearInterval(this.timerId)
      })
      //鼠标移出,定时器开始
      this.chartInstance.on('mouseout', () => {
        this.startInterval()
      })
    },

getData方法

这里还是用http请求获取的数据,后面我再讲怎么用WebSocket获取我们的数据

 async getData() {
      const {
        data: res
      } = await this.$http.get('seller')
      this.allData = res
      //对数据进行排序
      this.allData.sort((a, b) => {
        return a.value - b.value //从小到大排序
      })
      //每五个元素显示一页
      this.totalPage = this.allData.length % 5 === 0 ? this.allData.length / 5 : this.allData.length / 5 + 1
      this.updateChart()
      //启动定时器
      this.startInterval()
    },

updateChart方法

//更新图表
    updateChart() {
      //起始的位置
      const start = (this.currentPage - 1) * 5 
      //结束的位置
      //起始为0,所以展示1-5
      const end = this.currentPage * 5 
      const showData = this.allData.slice(start, end)
      const sellerNames = showData.map((item) => {
        return item.name
      })
      const sellerValue = showData.map((item) => {
        return item.value
      })
      const dataOption = {
        yAxis: {
          data: sellerNames
        },
        series: [{
          data: sellerValue
        }]
      }
      this.chartInstance.setOption(dataOption)
    },

startInterval方法

//定时器,数据每3秒更新一次
 startInterval() {
      if (this.timerId) {
        clearInterval(this.timerId)
      }
      this.timerId = setInterval(() => {
        this.currentPage++
        if (this.currentPage > this.totalPage) {
          this.currentPage = 1
        }
        this.updateChart()
      }, 3000)
    },

screenAdapter方法

 //屏幕适配
    screenAdapter() {
      const titleFontSize = this.$refs.seller_ref.offsetWidth / 100 * 3.6
      console.log(titleFontSize)
      const adapterOption = {
        title: {
          textStyle: {
            fontSize: titleFontSize
          }
        },
        tooltip: {
          axisPointer: {
            lineStyle: {
              width: titleFontSize
            }
          }
        },
        series: [{
          barWidth: titleFontSize,
          //圆角大小
          itemStyle: {
            barBorderRadius: [0, titleFontSize / 2, titleFontSize / 2, 0],
          }
        }]
      }
      this.chartInstance.setOption(adapterOption)
      //手动调用图表对象resize
      this.chartInstance.resize()
    }

mounted

  mounted() {
    this.initChart()
    this.getData()
    window.addEventListener('resize', this.screenAdapter)
    this.screenAdapter()
  },

destroyed

  destroyed() {
    clearInterval(this.timerId)
    window.removeEventListener('resize', this.screenAdapter)
  },

好了,完事

下面我把如何用WebSocket获取数据说一下

封装了一个WebSocket

export default class SocketService {
  /**
   * 单例
   */
  static instance = null
  static get Instance() {
    if (!this.instance) {
      this.instance = new SocketService()
    }
    return this.instance
  }

  // 和服务端连接的socket对象
  ws = null

  // 存储回调函数
  callBackMapping = {}

  // 标识是否连接成功
  connected = false

  // 记录重试的次数
  sendRetryCount = 0

  // 重新连接尝试的次数
  connectRetryCount = 0

  //  定义连接服务器的方法
  connect() {
    // 连接服务器
    if (!window.WebSocket) {
      return console.log('您的浏览器不支持WebSocket')
    }
    this.ws = new WebSocket('ws://localhost:9998')

    // 连接成功的事件
    this.ws.onopen = () => {
      console.log('连接服务端成功了')
      this.connected = true
      // 重置重新连接的次数
      this.connectRetryCount = 0
    }
    // 1.连接服务端失败
    // 2.当连接成功之后, 服务器关闭的情况
    this.ws.onclose = () => {
      console.log('连接服务端失败')
      this.connected = false
      this.connectRetryCount++
      setTimeout(() => {
        this.connect()
      }, 500 * this.connectRetryCount)
    }
    // 得到服务端发送过来的数据
    this.ws.onmessage = msg => {
      console.log('从服务端获取到了数据')
      // 真正服务端发送过来的原始数据时在msg中的data字段
      // console.log(msg.data)
      const recvData = JSON.parse(msg.data)
      const socketType = recvData.socketType
      // 判断回调函数是否存在
      if (this.callBackMapping[socketType]) {
        const action = recvData.action
        if (action === 'getData') {
          const realData = JSON.parse(recvData.data)
          this.callBackMapping[socketType].call(this, realData)
        } else if (action === 'fullScreen') {
          this.callBackMapping[socketType].call(this, recvData)
        } else if (action === 'themeChange') {
          this.callBackMapping[socketType].call(this, recvData)
        }
      }
    }
  }

  // 回调函数的注册
  registerCallBack (socketType, callBack) {
    this.callBackMapping[socketType] = callBack
  }

  // 取消某一个回调函数
  unRegisterCallBack (socketType) {
    this.callBackMapping[socketType] = null
  }

  // 发送数据的方法
  send (data) {
    // 判断此时此刻有没有连接成功
    if (this.connected) {
      this.sendRetryCount = 0
      this.ws.send(JSON.stringify(data))
    } else {
      this.sendRetryCount++
      setTimeout(() => {
        this.send(data)
      }, this.sendRetryCount * 500)
    }
  }
}

在main.js中进行连接,挂载原型

//对服务端进行连接
import SocketService from '../utils/socket_service'
SocketService.Instance.connect()
// 其他的组件  this.$socket
Vue.prototype.$socket = SocketService.Instance

然后在组件中

  created() {
    //在组件创建完成之后进行回调函数注册
    this.$socket.registerCallBack('trendData',this.getData)
  },
  
   mounted() {
    this.initChart();
    //发送数据给服务器,告诉服务器,我现在需要数据
    this.$socket.send({
      action:'getData',
      socketType:'trendData',
      chartName:'trend',
      value:''
    })
    window.addEventListener("resize", this.screenAdapter);
    this.screenAdapter();
  },
  
  destroyed() {
    window.removeEventListener("resize", this.screenAdapter);
    //取消
    this.$socket.unRegisterCallBack('trendData')
  },
  methods:{
  //res就是服务端发送给客户端的图表数据
    getData(res) {
      this.allData = res;
      this.updateChart();
    },
    }

这样就实现了后端发生变化,前端即时更新视图

至于为什么WebSocket这样封装,因为后台定了规则

const path = require('path')
const fileUtils = require('../utils/file_utils')
const WebSocket = require('ws')
// 创建WebSocket服务端的对象, 绑定的端口号是9998
const wss = new WebSocket.Server({
  port: 9998
})
// 服务端开启了监听
module.exports.listen = () => {
  // 对客户端的连接事件进行监听
  // client:代表的是客户端的连接socket对象
  wss.on('connection', client => {
    console.log('有客户端连接成功了...')
    // 对客户端的连接对象进行message事件的监听
    // msg: 由客户端发给服务端的数据
    client.on('message',async msg => {
      console.log('客户端发送数据给服务端了: ' + msg)
      let payload = JSON.parse(msg)
      const action = payload.action
      if (action === 'getData') {
        let filePath = '../data/' + payload.chartName + '.json'
        // payload.chartName // trend seller map rank hot stock
        filePath = path.join(__dirname, filePath)
        const ret = await fileUtils.getFileJsonData(filePath)
        // 需要在服务端获取到数据的基础之上, 增加一个data的字段
        // data所对应的值,就是某个json文件的内容
        payload.data = ret
        client.send(JSON.stringify(payload))
      } else {
        // 原封不动的将所接收到的数据转发给每一个处于连接状态的客户端
        // wss.clients // 所有客户端的连接
        wss.clients.forEach(client => {
          client.send(msg)
        })
      }
      // 由服务端往客户端发送数据
      // client.send('hello socket from backend')
    })
  })
}

有不懂的可以去我的github查看源代码,前后端都有,后端必须启动,前端才有显示,WebSocket我只配了Trend组件,其他全部一样的操作

github项目地址https://github.com/lsh555/Echarts

项目详情如下

Vue使用Echarts实现横向柱状图,并通过WebSocket即时通讯更新

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文地址:https://blog.csdn.net/weixin_45389051/article/details/108885861