先看下效果图,并且数据每隔3秒自动变换一次
先看下后台返回的数据结构是什么样子的
[{"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
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 + 1this.updateChart()//启动定时器this.startInterval()},
updateChart方法
//更新图表updateChart() {//起始的位置const start = (this.currentPage - 1) * 5 //结束的位置//起始为0,所以展示1-5const 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.6console.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)//手动调用图表对象resizethis.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 = nullstatic 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 = falsethis.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.actionif (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 = 0this.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.actionif (action === 'getData') {let filePath = '../data/' + payload.chartName + '.json'// payload.chartName // trend seller map rank hot stockfilePath = path.join(__dirname, filePath)const ret = await fileUtils.getFileJsonData(filePath)// 需要在服务端获取到数据的基础之上, 增加一个data的字段// data所对应的值,就是某个json文件的内容payload.data = retclient.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
项目详情如下