echarts+vue2实战(二)

目录

一、WebSocket【双向通信】的使用

1.1、前端

1.2、后端

二、前端组件的合并与优化

三、全屏切换

3.1、单页面切换

3.2、同页面多端联动

四、主题切换

4.1、单页面切换

4.2、同页面多端联动

一、WebSocket【双向通信】的使用

1.1、前端

在utils文件夹里创建socket_service.js,并在main.js里进行挂载

(1)、定义类SocketService,并定义成单例设计模式;

(2)、定义连接服务器的方法connect,(前端项目不用下载,直接window.socket即可);

(3)、监听事件:onopen、onclose、onmessage;

(4)、存储回调函数;

(5)、接收数据的处理;

(6)、定义发送数据的方法;

(7)、挂载SocketService对象到vue的原型对象上;

import SocketService from '@/utils/socket_service'
// 对服务端进行websocket的连接
SocketService.Instance.connect()
// 通过this.$socket.xxx获取所有方法
Vue.prototype.$socket = SocketService.Instance
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 = truethis.connectRetryCount = 0}// 连接连接服务端失败this.ws.onclose = () => {console.log('连接服务端失败');this.connected = falsethis.connectRetryCount++setTimeout(() => {this.connect()}, this.connectRetryCount * 500)}// 得到服务端发送过来的数据this.ws.onmessage = msg => {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)}}
}

1.2、后端

在service文件夹里创建web_socket_service.js,在app.js里引入

const WebSocketService = require('./service/web_socket_service')
// 开启服务端的监听,监听客户端的连接
// 当某一个客户端连接成功后,就会对这个客户端进行message事件的监听
WebSocketService.listen()

(1)、安装包 npm i ws -S;

(2)、创建对象;

(3)、监听事件;

(4)、发送数据;

const path = require('path')
const fileUtils = require('../utils/file_utils')
const WebSocket = require('ws')
const { log } = require('console')
// 创建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 => {let payload = JSON.parse(msg)console.log(action, '客户端发送数据给服务端了: ' + msg)const action = payload.actionif (action === 'getData') {let filePath = '../data/' + payload.chartName + '.json'filePath = 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 => {
//如果数据被转成了Buffer对象,故使用msg.toString()将Buffer转换为字符串,前端用JSON.parse(xxx)解析字符串为JSON对象即可client.send(msg.toString())})}})})
}
// 备注:前后端约定的字段
// let msg = {
//     "action": 'getData',//获取数据方法名
//     "socketType": "trendData",//前端响应函数的标识
//     "chartName": 'trend',
//     "value": '',
//     "data":'.json里的数据'
// }

二、前端组件的合并与优化

(1)、在组件创建完成后,进行回调函数的注册;在组件销毁时,进行回调函数的取消;

(2)、发送数据给服务器;

(3)、直接 getData(ret) 得到数据,不用再发请求了;

(4)、重发数据机制

让属性connected在onopen时设置为true,在onclose时设置为false,计算重发次数

(5)、断开重连机制

如果失败,则延时的时长随着尝试的次数而增加,如果成功,则将次数归0

以Trend.vue组件为例:

  created() {this.$socket.registerCallBack('trendData', this.getData);},destroyed() {this.$socket.unRegisterCallBack('trendData');},mounted() {......// this.getData();this.$socket.send({action: "getData",socketType: "trendData",chartName: "trend",value: "",});//发送数据给服务器,告诉它,该组件需要数据},methods:{// 旧版本// async getData() {//   const { data: ret } = await this.$http.get("trend");//   this.allData = ret;//   this.updateChart();// },// websocket版本getData(ret) {this.allData = ret;this.updateChart();},
}

三、全屏切换

以"热销商品占比图表"为例:

(1)、定义全屏状态的数据、样式;

(2)、在点击指定组件的放大图标时传递自己的属性名,在方法里根据对应的属性值切换对应的样式和图标,并调用组件自己的screenAdapter()

(3)、联动效果:

发送全屏数据给服务器,服务器在收到这个数据时,会转发给每一个处于连接状态的客户端,前端在created()时注册回调、在destroyed()时取消回调、在recvData()里接收到数据进行属性值处理。

3.1、单页面切换

3.2、同页面多端联动

四、主题切换

4.1、单页面切换

(1)、自己在echarts的"主题编辑器"里选择要切换的主题.json文件;

(2)、点击切换按钮,修改vuex中的theme数据(通过mutations修改state里的数据);

(3)、在每个组件里监听theme的变化,用xx.dispose()销毁当前图表,再重新渲染(在registerTheme()和init()函数里改为变化的theme)

(4)、HTML样式随之改变:

utils文件夹的theme_utils.js内容

const theme = {chalk: {backgroundColor: '#161522',titleColor: '#fff',logoSrc: 'logo_dark.png',//左上角logo图标路径themeSrc: 'qiehuan_dark.png',//切换主题按钮的图片路径headerBorderSrc: 'header_border_dark.png'//页面顶部的边框图片},vintage: {backgroundColor: '#fff',titleColor: '#000',logoSrc: 'logo_light2.png',themeSrc: 'qiehuan_light.png',headerBorderSrc: 'header_border_light.png'}
}
export function getThemeValue(themeName) {return theme[themeName]
}

store文件夹里的index内容

state: {theme: 'chalk',},
mutations: {changeTheme(state) {if (state.theme == 'chalk') {state.theme = 'vintage'} else {state.theme = 'chalk'}},},

以Hot.vue组件为例:

<template><div class="com-container"><div class="com-chart" ref="hot_ref"></div><!-- 左右箭头 --><span class="iconfont arr-left" @click="toLeft" :style="comStyle">&#xe6ef;</span><span class="iconfont arr-right" @click="toRight" :style="comStyle">&#xe6ed;</span><!-- 一级标题 --><span class="cat-name" :style="comStyle">{{ catName }}系列</span></div>
</template>
<script>
import * as ets from "echarts";
import { mapState } from "vuex";
import chalk from "../../../../public/static/theme/chalk.json"; //自己下载
import vintage from "../../../../public/static/theme/vintage.json"; //自己下载
import { getThemeValue } from "@/utils/theme_utils.js";
export default {data() {return {isStyle: chalk,};},computed: {...mapState(["theme"]),comStyle() {return {fontSize: `${this.titleFontSize}px`,color: getThemeValue(this.theme).titleColor,//HTML字体颜色改变};},},watch: {theme() {if (this.theme == "chalk") {this.isStyle = chalk;} else {this.isStyle = vintage;}this.chartInstance.dispose(); //销毁当前图表// 重新初始化图表、完成屏幕适配、更新图表展示this.initChart();this.screenAdapter();this.updateChart();},},methods: {// 初始化echartsInstance对象initChart() {// ets.registerTheme("chalk", chalk);// this.chartInstance = ets.init(this.$refs.hot_ref, "chalk");ets.registerTheme("isStyle", this.isStyle);this.chartInstance = ets.init(this.$refs.hot_ref, "isStyle");},},
};
</script>

4.2、同页面多端联动

可参考全屏切换模块

echartsPage.vue页面(整合所有组件)代码:

<template><div class="screen-container" :style="containStyle"><header class="screen-header"><div><img :src="headerSrc" alt="" /></div><span class="logo"><img :src="logoSrc" alt="" /></span><span class="title">电商平台实时监控系统</span><div class="title-right"><img :src="themeSrc" class="qiehuan" @click="handleChangeTheme" /><span class="datetime">2049-01-01 00:00:00</span></div></header><div class="screen-body"><section class="screen-left"><divid="left-top":class="[fullScreenStatus.trend ? 'fullscreen' : '']"><!-- 销量趋势图表 --><Trend ref="trend"></Trend><div class="resize"><span@click="changeSize('trend')":class="['iconfont',fullScreenStatus.trend? 'icon-compress-alt': 'icon-expand-alt',]"></span></div></div><divid="left-bottom":class="[fullScreenStatus.seller ? 'fullscreen' : '']"><!-- 商家销售金额图表 --><Seller ref="seller"></Seller><div class="resize"><span@click="changeSize('seller')":class="['iconfont',fullScreenStatus.seller? 'icon-compress-alt': 'icon-expand-alt',]"></span></div></div></section><section class="screen-middle"><divid="middle-top":class="[fullScreenStatus.map ? 'fullscreen' : '']"><!-- 商家分布图表 --><Map ref="map"></Map><div class="resize"><span@click="changeSize('map')":class="['iconfont',fullScreenStatus.map ? 'icon-compress-alt' : 'icon-expand-alt',]"></span></div></div><divid="middle-bottom":class="[fullScreenStatus.rank ? 'fullscreen' : '']"><!-- 地区销量排行图表 --><Rank ref="rank"></Rank><div class="resize"><span@click="changeSize('rank')":class="['iconfont',fullScreenStatus.rank ? 'icon-compress-alt' : 'icon-expand-alt',]"></span></div></div></section><section class="screen-right"><div id="right-top" :class="[fullScreenStatus.hot ? 'fullscreen' : '']"><!-- 热销商品占比图表 --><Hot ref="hot"></Hot><div class="resize"><span@click="changeSize('hot')":class="['iconfont',fullScreenStatus.hot ? 'icon-compress-alt' : 'icon-expand-alt',]"></span></div></div><divid="right-bottom":class="[fullScreenStatus.stock ? 'fullscreen' : '']"><!-- 库存销量分析图表 --><Stock ref="stock"></Stock><div class="resize"><span@click="changeSize('stock')":class="['iconfont',fullScreenStatus.stock? 'icon-compress-alt': 'icon-expand-alt',]"></span></div></div></section></div></div>
</template><script>
import Hot from "./components/Hot.vue";
import Map from "./components/Map.vue";
import Rank from "./components/Rank.vue";
import Seller from "./components/Seller.vue";
import Stock from "./components/Stock.vue";
import Trend from "./components/Trend.vue";
import { mapState } from "vuex";
import { getThemeValue } from "@/utils/theme_utils.js";
export default {data() {return {// 定义每一个图表的全屏状态fullScreenStatus: {trend: false,seller: false,map: false,rank: false,hot: false,stock: false,},};},created() {this.$socket.registerCallBack("fullScreen", this.recvData);this.$socket.registerCallBack("themeChange", this.recvThemeChange);},destroyed() {this.$socket.unRegisterCallBack("fullScreen");this.$socket.unRegisterCallBack("themeChange");},components: {Hot,Map,Rank,Seller,Stock,Trend,},computed: {...mapState(["theme"]),headerSrc() {return "/static/img/" + getThemeValue(this.theme).headerBorderSrc;},logoSrc() {return "/static/img/" + getThemeValue(this.theme).logoSrc;},themeSrc() {return "/static/img/" + getThemeValue(this.theme).themeSrc;},containStyle() {return {backgroundColor: getThemeValue(this.theme).backgroundColor,color: getThemeValue(this.theme).titleColor,};},},methods: {changeSize(chartName) {// 全屏切换单页面写法// 改变fullScreenStatus中组件对应的属性值// this.fullScreenStatus[chartName] = !this.fullScreenStatus[chartName];// 调用每个组件里的屏幕适配方法// this.$nextTick(() => {//   this.$refs[chartName].screenAdapter();// });// 同页面多端联动切换写法const targetValue = !this.fullScreenStatus[chartName];this.$socket.send({action: "fullScreen",socketType: "fullScreen",chartName: chartName,value: targetValue,});},// 处理接收到的全屏数据recvData(data) {const chartName = data.chartName;const targetValue = data.value;this.fullScreenStatus[chartName] = targetValue;this.$nextTick(() => {this.$refs[chartName].screenAdapter();});},handleChangeTheme() {// this.$store.commit("changeTheme"); //修改VueX里的数据this.$socket.send({action: "themeChange",socketType: "themeChange",chartName: "",value: "",});},recvThemeChange() {this.$store.commit("changeTheme");},},
};
</script>
<style lang="less" scoped>
// 全屏样式的定义
.fullscreen {position: fixed !important;top: 0 !important;left: 0 !important;width: 100% !important;height: 100% !important;margin: 0 !important;z-index: 100;
}
.screen-container {width: 100%;height: 100%;padding: 0 20px;background-color: #161522;color: #fff;box-sizing: border-box;
}
.screen-header {width: 100%;height: 64px;font-size: 20px;position: relative;> div {img {width: 100%;}}.title {position: absolute;left: 50%;top: 50%;font-size: 20px;transform: translate(-50%, -50%);}.title-right {display: flex;align-items: center;position: absolute;right: 0px;top: 50%;transform: translateY(-80%);}.qiehuan {width: 28px;height: 21px;cursor: pointer;}.datetime {font-size: 15px;margin-left: 10px;}.logo {position: absolute;left: 0px;top: 50%;transform: translateY(-80%);img {height: 35px;width: 128px;}}
}
.screen-body {width: 100%;height: 100%;display: flex;margin-top: 10px;.screen-left {height: 100%;width: 27.6%;#left-top {height: 53%;position: relative;}#left-bottom {height: 31%;margin-top: 25px;position: relative;}}.screen-middle {height: 100%;width: 41.5%;margin-left: 1.6%;margin-right: 1.6%;#middle-top {width: 100%;height: 56%;position: relative;}#middle-bottom {margin-top: 25px;width: 100%;height: 28%;position: relative;}}.screen-right {height: 100%;width: 27.6%;#right-top {height: 46%;position: relative;}#right-bottom {height: 38%;margin-top: 25px;position: relative;}}
}
.resize {position: absolute;right: 20px;top: 20px;cursor: pointer;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/34771.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ArkUI开发学习随机——得物卡片,京东登录界面

案例一&#xff1a;得物卡片 代码&#xff1a; Column(){Column(){Image($r("app.media.mihoyo")).width(200).height(200)Row(){Text("今晚玩这个 | 每日游戏打卡").fontWeight(700).fontSize(16).padding(4)}.width(200)Text("No.12").fontWe…

盲盒小程序开发:解锁未知,探索无限惊喜

一、开启新篇章 在追求独特与新颖的时代&#xff0c;盲盒以其神秘感与未知性&#xff0c;成为了年轻人热衷的购物新方式。为了满足这一市场需求&#xff0c;我们精心打造了一款全新的盲盒小程序&#xff0c;带您步入一个充满未知与惊喜的购物新领域。 二、产品亮点 精选商品&…

【机器学习】K-Means算法详解:从原理到实践

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 K-Means算法详解&#xff1a;从原理到实践引言1. 基本原理1.1 簇与距离度量1.2 …

JLPT历年真题刷题小程序:Navi日语社全新升级,更新至2024年真题!

Navi日语社小程序专为日语能力考试设计&#xff0c;提供全网最全的JLPT备考真题资源&#xff0c;包括日语N1-N5等级考试的历年真题&#xff0c;2024年真题将在7月底更新。无论你是日语新手准备参加N3考试练练手&#xff0c;还是准备冲刺N1最高等级&#xff0c;都能在这个小程序…

基于Java微信小程序火锅店点餐系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

动态规划数字三角形模型——AcWing 275. 传纸条

动态规划数字三角形模型 定义 动态规划数字三角形模型是在一个三角形的数阵中&#xff0c;通过一定规则找到从顶部到底部的最优路径或最优值。 运用情况 通常用于解决具有递推关系、需要在不同路径中做出选择以达到最优结果的问题。比如计算最短路径、最大和等。 计算其他…

惯性级惯导的定位漂移估算

一般来说&#xff0c;惯性级陀螺仪指的是0.01度/小时的零偏稳定性&#xff08;是否可以作为等效常值漂移呢&#xff1f;&#xff09;&#xff0c;其定位误差大约为1海里每小时&#xff0c;其具体估算方法可见秦永元老师的《惯性导航》一书中静基座下系统误差传播特性分析一节内…

【龙晰 离线安装openssl-devel】openssl-devel rpm 离线安装 需要下载哪些安rpm 包

进入龙晰镜像源地址下载 http://mirrors.openanolis.cn/anolis/8/BaseOS/x86_64/os/Packages/(base) [rootAI lib64]# yum install openssl-devel Last metadata expiration check: 14:03:32 ago on Fri 21 Jun 2024 07:26:56 AM CST. Dependencies resolved. Package …

公交车安全监控的智能化革新:4G车载视频监控与GPS卫星定位技术的融合

随着科技的快速发展&#xff0c;智能化监控技术在交通管理领域的应用日益广泛。特别是对于公交车这类公共交通工具&#xff0c;其安全监控的智能化、实时化、全面化显得尤为重要。综合运用最新的4G车载视频监控技术及GPS卫星定位技术&#xff0c;对公交车进行全方位、立体化、智…

正则表达式以及文本三剑客grep、sed、awk

正则表达式匹配的是文本内容&#xff0c;文本三剑客都是针对文本内容。 grep&#xff1a;过滤文本内容 sed&#xff1a;针对文本内容进行增删改查 awk&#xff1a;按行取列 一、grep grep的作用使用正则表达式来匹配文本内容 1、grep选项 -m&#xff1a;匹配几次之后停止…

【论文解读】通过多标记预测建立更好更快的大型语言模型

Meta 的这篇多标记预测论文显示,与当前的下一标记预测器相比,多头预测器内存效率高、性能更好、训练速度更快。 https://arxiv.org/pdf/2404.19737 主要收获: 多标记预测是对 LLM 训练的一种简单而强大的修改,可提高样本效率和各种任务的性能。这种方法在大规模应用中尤为…

从 Hadoop 迁移,无需淘汰和替换

我们仍然惊讶于有如此多的客户来找我们&#xff0c;希望从HDFS迁移到现代对象存储&#xff0c;如MinIO。我们现在以为每个人都已经完成了过渡&#xff0c;但每周&#xff0c;我们都会与一个决定进行过渡的主要、高技术性组织交谈。 很多时候&#xff0c;在这些讨论中&#xff…

Mac环境 aab包转apks,并安装apks

一、下载下载bundletool工具 Releases google/bundletool GitHub 二、将下载bundletool.jar包、aab、keystore文件全部放到同一个目录下 例如我全部放到download目录下 转换命令行&#xff1a; java -jar bundletool-all-1.16.0.jar build-apks --modeuniversal --bundle…

java运维交接项目逆向工程

​ 背景 有承接过Java项目运维的团队估计都处理过的一件事情&#xff0c;就是同步生产代码跟本地代码&#xff0c;条件再差些甚至要直接基于生产部署包逆向本地源码工程。而哪怕是原运维团队交接了源码&#xff0c;往往也会历史久远的原因&#xff0c;给了一份不太可靠的源码…

MySQL连接

MySQL工具包 MySQL实现简单链接 一 引入工具包 JBDCUtils&#xff0c;无需更改&#xff0c;直接使用即可。 import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties;public class JDBCUtil {private static String URL;p…

基于PHP+MySQL组合开发的在线客服小程序源码系统 带完整的安装代码包以及搭建教程

系统概述 源码系统是专门为满足企业在线客服需求而设计的&#xff0c;它集成了多种功能&#xff0c;能够帮助企业实现与用户的实时沟通、问题解答、信息反馈等。通过该系统&#xff0c;企业可以更好地了解用户需求&#xff0c;提升用户体验&#xff0c;增强用户对企业的信任感…

Linux[高级管理]——Squid代理服务器的部署和应用(传统模式详解)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f468;‍&#x1f4bb;Linux高级管理专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月24日11点11分 &#x1f004;️文章质量&#xff1a;95分 目录 ————前言———— Squid功能 Squ…

【JavaEE精炼宝库】多线程进阶(1)常见锁策略 | CAS | ABA问题

目录 一、常见的锁策略&#xff1a; 1.1 悲观锁 | 乐观锁&#xff1a; 1.2 重量级锁 | 轻量级锁&#xff1a; 1.3 自旋锁 | 挂起等待锁&#xff1a; 1.4 公平锁 | 非公平锁&#xff1a; 1.5 可重入锁 | 不可重入锁&#xff1a; 1.6 互斥锁 | 读写锁&#xff1a; 1.7 面…

Spring Boot 集成 MinIO 实现文件上传

Spring Boot 集成 MinIO 实现文件上传 一、 Minio 服务准备 MinIO的搭建过程参考 Docker 搭建 MinIO 对象存储。 登录MinIO控制台&#xff0c;新建一个 Bucket&#xff0c;修改 Bucket 权限为公开。 二、MinIO 集成 添加 MinIO 依赖 <!-- https://mvnrepository.com/ar…

【工具测评】ONLYOFFICE——你的下一款桌面编辑器

文章目录 前言一、安装1.1 跳转官网下载安装包1.2 安装步骤 二、功能介绍2.1 功能全面的 PDF 编辑器2.2 PDF 表单2.3 文本文档编辑器的更新2.4 电子表格编辑器的更新2.5 演示文稿编辑器有哪些更新2.6 所有编辑器中的改进内容2.7 从右至左显示 & 新的本地化选项2.8 可用性提…