当前位置: 首页 > news >正文

【系统开发】WebSocket + SpringBoot + Vue 搭建简易网页聊天室

文章目录

  • 一、数据库搭建
  • 二、后端搭建
    • 2.1 引入关键依赖
    • 2.2 WebSocket配置类
    • 2.3 配置跨域
    • 2.4 发送消息的控制类
  • 三、前端搭建
    • 3.1 自定义文件websocket.js
    • 3.2 main.js中全局引入websocket
    • 3.3 App.vue中声明websocket对象
    • 3.4 聊天室界面.vue
    • 3.5 最终效果


一、数据库搭建

很简单的一个user表,加两个用户admin和wskh

image-20220125134925596

二、后端搭建

2.1 引入关键依赖

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>

2.2 WebSocket配置类

WebSocketConfig的作用是:开启WebSocket监听

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** @Author:WSKH* @ClassName:WebSocketConfig* @ClassType:配置类* @Description:WebSocket配置类* @Date:2022/1/25/12:21* @Email:1187560563@qq.com* @Blog:https://blog.csdn.net/weixin_51545953?type=blog*/
@Configuration
public class WebSocketConfig {/*** 开启webSocket* @return*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

WebSocketServer里写了一些事件,如发送消息事件,建立连接事件,关闭连接事件等

import com.wskh.chatroom.util.FastJsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.EOFException;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;@ServerEndpoint("/websocket/{sid}")
@Component
public class WebSocketServer {private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);private static int onlineCount = 0;private static ConcurrentHashMap<String,WebSocketServer> webSocketServerMap = new ConcurrentHashMap<>();private Session session;private String sid;@OnOpenpublic void onOpen(Session session, @PathParam("sid") String sid) {this.sid = sid;this.session = session;webSocketServerMap.put(sid, this);addOnlineCount();log.info("有新窗口开始监听:"+sid+",当前在线人数为" + getOnlineCount());try {sendInfo("openSuccess:"+webSocketServerMap.keySet());} catch (IOException e) {e.printStackTrace();}}@OnClosepublic void onClose() {webSocketServerMap.remove(sid);subOnlineCount();log.info("有一连接关闭!当前在线人数为" + getOnlineCount());try {sendInfo("openSuccess:"+webSocketServerMap.keySet());} catch (IOException e) {e.printStackTrace();}}@OnMessagepublic void onMessage(String message) throws IOException {if("ping".equals(message)) {sendInfo(sid, "pong");}if(message.contains(":")) {String[] split = message.split(":");sendInfo(split[0], "receivedMessage:"+sid+":"+split[1]);}}@OnErrorpublic void onError(Session session, Throwable error) {if(error instanceof EOFException) {return;}if(error instanceof IOException && error.getMessage().contains("已建立的连接")) {return;}log.error("发生错误", error);}/*** 实现服务器主动推送*/public void sendMessage(String message) throws IOException {synchronized (session) {this.session.getBasicRemote().sendText(message);}}public static void sendObject(Object obj) throws IOException {sendInfo(FastJsonUtils.convertObjectToJSON(obj));}public static void sendInfo(String sid,String message) throws IOException {WebSocketServer socketServer = webSocketServerMap.get(sid);if(socketServer != null) {socketServer.sendMessage(message);}}public static void sendInfo(String message) throws IOException {for(String sid : webSocketServerMap.keySet()) {webSocketServerMap.get(sid).sendMessage(message);}}public static void sendInfoByUserId(Long userId,Object message) throws IOException {for(String sid : webSocketServerMap.keySet()) {String[] sids =  sid.split("id");if(sids.length == 2) {String id = sids[1];if(userId.equals(Long.parseLong(id))) {webSocketServerMap.get(sid).sendMessage(FastJsonUtils.convertObjectToJSON(message));}}}}public static Session getWebSocketSession(String sid) {if(webSocketServerMap.containsKey(sid)) {return webSocketServerMap.get(sid).session;}return null;}public static synchronized void addOnlineCount() {onlineCount++;}public static synchronized void subOnlineCount() {onlineCount--;}public static synchronized int getOnlineCount() {return onlineCount;}
}

2.3 配置跨域

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {@Override// 跨域配置public void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("*").allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE").maxAge(3600).allowCredentials(true);}}

2.4 发送消息的控制类

/*** @Author:WSKH* @ClassName:MsgController* @ClassType:控制类* @Description:信息控制类* @Date:2022/1/25/12:47* @Email:1187560563@qq.com* @Blog:https://blog.csdn.net/weixin_51545953?type=blog*/
@ApiModel("信息控制类")
@RestController
@RequestMapping("/chatroom/msg")
public class MsgController {@ApiOperation("发送信息方法")@PostMapping("/sendMsg")public R sendMsg(String msg) throws IOException {WebSocketServer.sendInfo(msg);return R.ok().message("发送成功");}
}

至此,后端部分大体配置完毕。


三、前端搭建

本文使用vue-admin-template-master模板进行聊天室的前端搭建

3.1 自定义文件websocket.js

将下面文件放在api文件夹下

image-20220125135840238

//websocket.js
import Vue from 'vue'// 1、用于保存WebSocket 实例对象
export const WebSocketHandle = undefined// 2、外部根据具体登录地址实例化WebSocket 然后回传保存WebSocket
export const WebsocketINI = function(websocketinstance) {this.WebSocketHandle = websocketinstancethis.WebSocketHandle.onmessage = OnMessage
}// 3、为实例化的WebSocket绑定消息接收事件:同时用于回调外部各个vue页面绑定的消息事件
// 主要使用WebSocket.WebSocketOnMsgEvent_CallBack才能访问  this.WebSocketOnMsgEvent_CallBack 无法访问很诡异
const OnMessage = function(msg) {// 1、消息打印// console.log('收到消息:', msg)// 2、如果外部回调函数未绑定 结束操作if (!WebSocket.WebSocketOnMsgEvent_CallBack) {console.log(WebSocket.WebSocketOnMsgEvent_CallBack)return}// 3、调用外部函数WebSocket.WebSocketOnMsgEvent_CallBack(msg)
}// 4、全局存放外部页面绑定onmessage消息回调函数:注意使用的是var
export const WebSocketOnMsgEvent_CallBack = undefined// 5、外部通过此绑定方法 来传入的onmessage消息回调函数
export const WebSocketBandMsgReceivedEvent = function(receiveevent) {WebSocket.WebSocketOnMsgEvent_CallBack = receiveevent
}// 6、封装一个直接发送消息的方法:
export const Send = function(msg) {if (!this.WebSocketHandle || this.WebSocketHandle.readyState !== 1) {// 未创建连接 或者连接断开 无法发送消息return}this.WebSocketHandle.send(msg)// 发送消息
}// 7、导出配置
const WebSocket = {WebSocketHandle,WebsocketINI,WebSocketBandMsgReceivedEvent,Send,WebSocketOnMsgEvent_CallBack
}// 8、全局绑定WebSocket
Vue.prototype.$WebSocket = WebSocket

3.2 main.js中全局引入websocket

import '@/utils/websocket' // 全局引入 WebSocket 通讯组件

3.3 App.vue中声明websocket对象

App.vue

<template><div id="app"><router-view /></div>
</template><script>import {getInfo} from './api/login.js';import {getToken} from './utils/auth.js'export default {name: 'App',mounted() {// 每3秒检测一次websocket连接状态 未连接 则尝试连接 尽量保证网站启动的时候 WebSocket都能正常长连接setInterval(this.WebSocket_StatusCheck, 3000)// 绑定消息回调事件this.$WebSocket.WebSocketBandMsgReceivedEvent(this.WebSocket_OnMesage)// 初始化当前用户信息this.token = getToken()getInfo(this.token).then((rep)=>{console.log(rep)this.userName = rep.data.name}).catch((error)=>{console.log(error)})},data(){return{}},methods: {// 实际消息回调事件WebSocket_OnMesage(msg) {console.log('收到服务器消息:', msg.data)console.log(msg)let chatDiv = document.getElementById("chatDiv")let newH3 = document.createElement("div")if(msg.data.indexOf('openSuccess')>=0){// 忽略连接成功消息提示}else{if(msg.data.indexOf(this.userName)==0){// 说明是自己发的消息,应该靠右边悬浮newH3.innerHTML = "<div style='width:100%;text-align: right;'><h3 style=''>"+msg.data+"</h3></div>"}else{newH3.innerHTML = "<div style='width:100%;text-align: left;'><h3 style=''>"+msg.data+"</h3></div>"}}chatDiv.appendChild(newH3)},// 1、WebSocket连接状态检测:WebSocket_StatusCheck() {if (!this.$WebSocket.WebSocketHandle || this.$WebSocket.WebSocketHandle.readyState !== 1) {console.log('Websocket连接中断,尝试重新连接:')this.WebSocketINI()}},// 2、WebSocket初始化:async WebSocketINI() {// 1、浏览器是否支持WebSocket检测if (!('WebSocket' in window)) {console.log('您的浏览器不支持WebSocket!')return}let DEFAULT_URL = "ws://" + '127.0.0.1:8002' + '/websocket/' + new Date().getTime()// 3、创建Websocket连接const tmpWebsocket = new WebSocket(DEFAULT_URL)// 4、全局保存WebSocket操作句柄:main.js 全局引用this.$WebSocket.WebsocketINI(tmpWebsocket)// 5、WebSocket连接成功提示tmpWebsocket.onopen = function(e) {console.log('webcoket连接成功')}//6、连接失败提示tmpWebsocket.onclose = function(e) {console.log('webcoket连接关闭:', e)}}}}
</script>

3.4 聊天室界面.vue

<template><div><div style="margin-top: 1vh;margin-bottom: 1vh;font-weight: bold;">聊天内容:</div><div style="background-color: #c8c8c8;width: 100%;height: 80vh;" id="chatDiv"></div><div style="margin-top: 1vh;margin-bottom: 1vh;font-weight: bold;">聊天输入框:</div><el-input v-model="text"></el-input><el-button @click="sendMsg">点击发送</el-button></div>
</template><script>import {getInfo} from '../../api/login.js';import {getToken} from '../../utils/auth.js'import msgApi from '../../api/msg.js'export default {mounted() {//this.token = getToken()getInfo(this.token).then((rep)=>{console.log(rep)this.userName = rep.data.name}).catch((error)=>{console.log(error)})},data() {return {text: "",token:"",userName:"",}},methods: {sendMsg(){let msg = this.userName+":"+this.textmsgApi.sendMsg(msg).then((rep)=>{}).catch((error)=>{})this.text = ""}}}
</script><style scoped="true">.selfMsg{float: right;}
</style>

3.5 最终效果

用两个不同的浏览器,分别登录admin账号和wskh账号进行聊天测试,效果如下(左边为admin):

image-20220125134213142

相关文章:

【系统开发】WebSocket + SpringBoot + Vue 搭建简易网页聊天室

文章目录一、数据库搭建二、后端搭建2.1 引入关键依赖2.2 WebSocket配置类2.3 配置跨域2.4 发送消息的控制类三、前端搭建3.1 自定义文件websocket.js3.2 main.js中全局引入websocket3.3 App.vue中声明websocket对象3.4 聊天室界面.vue3.5 最终效果一、数据库搭建 很简单的一个…...

Learning C++ No.14【STL No.4】

引言&#xff1a; 北京时间&#xff1a;2023/3/9/12:58&#xff0c;下午两点有课&#xff0c;现在先把引言给搞定&#xff0c;这样就能激励我更早的把这篇博客给写完了&#xff0c;万事开头难这句话还是很有道理的&#xff0c;刚好利用现在昏昏欲睡的时候&#xff0c;把这个没…...

高速PCB设计指南(八)

七、产品内部的电磁兼容性设计 1 印刷电路板设计中的电磁兼容性 1.1 印刷线路板中的公共阻抗耦合问题 数字地与模拟地分开&#xff0c;地线加宽。 1.2 印刷线路板的布局 ※对高速、中速和低速混用时&#xff0c;注意不同的布局区域。 ※对低模拟电路和数字逻辑要分离。…...

什么是腾讯云关系型数据库(MySQL/SQL Server/MariaDB/PostgreSQL详解)

什么是腾讯云关系型数据库&#xff1f;腾讯云关系型数据库提供 MySQL、SQL Server、MariaDB、PostgreSQL详细介绍。腾讯云关系型数据库让您在云中轻松部署、管理和扩展的关系型数据库&#xff0c;提供安全可靠、伸缩灵活的按需云数据库服务。腾讯云关系型数据库提供 MySQL、SQL…...

进程通信相关概念

一、概念 1.1 通信方式有哪些 管道&#xff1a;水管&#xff0c;男纸条放入水管&#xff0c;女看了拿走不回复 消息队列&#xff1a;大盒子&#xff0c;男放入纸条&#xff0c;女看了不拿走&#xff0c;男女都可放 共享内存&#xff1a;直接桌子&#xff0c;男放桌上&#…...

05.Java的运算符

1.运算符计算机的最基本的用途之一就是执行数学运算&#xff0c;比如&#xff1a;int a 10;int b 20;a b;a < b;上述 和 < 等就是运算符&#xff0c;即&#xff1a;对操作数进行操作时的符号&#xff0c;不同运算符操作的含义不同。作为一门计算机语言&#xff0c;Ja…...

轮转数组(力扣189)

轮转数组 题目描述&#xff1a; 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例1&#xff1a; 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7…...

主流的“对象转换工具”使用示例大全以及性能的对比

目录 前言 源码地址 代码示例 引入依赖 先定两个实体用于转换 定义一个接口让所有转换器都集成 Apache BeanUtils BeanCopier bean-mapping bean-mapping-asm Dozer 自己写get/set JMapper json2json MapStruct&#xff08;推荐&#xff09; ModelMapper OriK…...

分享10个不错的C语言开源项目

今天跟大家分享10个重量级的C语言开源项目&#xff0c;C语言确实经得住考验&#xff1a; Redis&#xff1a;Redis是一个开源的高性能的键值对数据库。它以C语言编写&#xff0c;具有极高的性能和可靠性。 Nginx&#xff1a;Nginx是一个高性能的HTTP和反向代理服务器&#xff0…...

【阅读笔记】JavaScript设计模式与开发实践2--闭包与单例、策略模式

目录闭包与高阶函数Function 扩展函数柯里化函数单例模式透明的单例模式惰性单例策略模式策略模式发展策略模式实现闭包与高阶函数 Array.prototype.sort 接受一个函数当作参数&#xff0c;用户可以自行在该函数内指定排序方式 // 由小到大排序 let res [1, 4, 2].sort((a, …...

设计模式(二十)----行为型模式之责任链模式

1、概述 在现实生活中&#xff0c;常常会出现这样的事例&#xff1a;一个请求有多个对象可以处理&#xff0c;但每个对象的处理条件或权限不同。例如&#xff0c;公司员工请假&#xff0c;可批假的领导有部门负责人、副总经理、总经理等&#xff0c;但每个领导能批准的天数不同…...

数据持久化层--冷热分离

业务场景 有一个系统的主要功能是这样的:它会对接客户的邮件服务器,自动收取发到几个特定客服邮箱的邮件,每收到一封客服邮件,就自动生成一个工单。之后系统就会根据一些规则将工单分派给不同的客服专员处理。 这家媒体集团客户两年多产生了近2000万的工单,工单的操作记…...

Ubuntu16.04系统 VSCode中python开发插件的安装

VSCode中python开发插件的安装 1. python python插件提供了代码分析&#xff0c;高亮&#xff0c;规范化等很多基本功能 2. Python for vscode 3. Python Preview 实时可视化你的代码结果。如果你Leedcode等题时&#xff0c;可以安装这个插件。能为VSCode切换各种主题皮肤…...

buuctf-pwn write-ups (12)

文章目录buu093-wustctf2020_easyfastbuu094-ciscn_2019_es_1buu095-wdb2018_guessbuu096-gyctf_2020_some_thing_excetingbuu097-axb_2019_heapbuu098-oneshot_tjctf_2016buu099-护网杯_2018_gettingstartbuu100-wustctf2020_number_gamebuu101-zctf2016_note2buu093-wustctf2…...

Linux- 系统随你玩之--网络上的黑客帝国

文章目录1、前言2、TCPDump介绍2.1、问题来了&#xff1a; 所有用户都可以采用该命令吗&#xff1f;2.2、抓包原理2.3、特点2.3.1、参数化支持2.2.2、 TCP功能3、 服务器安装Tcpdump3.1、安装3.2、检查安装是否正常。4、tcpdump 命令4.1、常用功能选项4.2、输出内容5、实操5.1、…...

Python每日一练(20230312)

目录 1. 提示用户输入的简单菜单 ★ 2. 字母异位词分组 ★★ 3. 俄罗斯套娃信封问题 ★★★ &#x1f31f; 每日一练刷题专栏 C/C 每日一练 ​专栏 Python 每日一练 专栏 1. 提示用户输入的简单菜单 如果用户选择菜单选项1&#xff0c;提示用户输入1到10之间的整数&a…...

人生又有几个四年

机缘 不知不觉&#xff0c;已经来 csdn 创作四周年啦~ 我是在刚工作不到一年的时候接触 csdn 的&#xff0c;当时在学习 node&#xff0c;对 node 的文件相关的几个 api 总是搞混&#xff0c;本来还想着在传统的纸质笔记本上记一下&#xff0c;但是想想我大学记了好久的笔记本…...

第九章:Java集合

第九章&#xff1a;Java集合 9.1&#xff1a;Java集合框架概述 数组、集合都是对多个数据进行存储(内存层面&#xff0c;不涉及持久化)操作的结构&#xff0c;简称Java容器。 数组存储多个数据方面的特点 一旦初始化以后&#xff0c;其长度就确定了。数组一旦定义好&#xff…...

嵌入式学习笔记——STM32的USART通信概述

文章目录前言常用通信协议分类及其特征介绍通信协议通信协议分类1.同步异步通信2.全双工/半双工/单工3.现场总线/板级总线4. 串行/并行通信5. 有线通信、无线通信STM32通信协议的配置方式使用通信协议控制器实现使用IO口模拟的方式实现STM32串口通信概述什么是串口通信STM32F40…...

MySQL性能优化

MySQL性能调优 存储数据类型优化 尽量避免使用 NULL尽量使用可以的最小数据类型。但也要确保没有低估需要存储的范围整型比字符串操作代价更低使用 MySQL 内建的数据类型&#xff08;比如date、time、datetime&#xff09;&#xff0c;比用字符串更快 基本数据类型 数字 整数…...

C语言/动态通讯录

本文使用了malloc、realloc、calloc等和内存开辟有关的函数。 文章目录 前言 二、头文件 三、主界面 四、通讯录功能函数 1.全代码 2.增加联系人 3.删除联系人 4.查找联系人 5.修改联系人 6.展示联系人 7.清空联系人 8.退出通讯录 总结 前言 为了使用通讯录时&#xff0c;可以…...

我用Compose做了一个地图轮子OmniMap

一、前言 半年前&#xff0c;我发布过一篇介绍&#xff1a;Compose里面如何使用地图&#xff0c;比如高德地图 的文章&#xff0c;原本是没有想造什么轮子的✍️ 闲来无事&#xff0c;有一天看到了评论区留言让我把源码地址分享出来&#xff0c;我感觉我太懒了&#xff0c;后来…...

STM32之SPI

SPISPI介绍SPI是串行外设接口(Serial Peripherallnterface)的缩写&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并且在芯片的管脚上只占用四根线&#xff0c;节约了芯片的管脚&#xff0c;同时为PCB的布局上节省空间&#xff0c;提供方便…...

02 深度学习环境搭建

1、查看对应版本关系 详细见&#xff1a;https://blog.csdn.net/qq_41946216/article/details/129476095?spm1001.2014.3001.5501此案例环境使用 CUDA 11.7、Pytouch1.12.1、Miniconda3_py38(含Python3.8) 2. 安装Anaconda 或 Miniconda 本案例重点一为Miniconda准 2.1 安…...

PHP导入大量CSV数据的方法分享

/** * @description 迭代器读取csv文件 * @param $strCsvPath * @return \Generator */ public static function readPathCsvFile($strCsvPath) { if ($handle = fopen($strCsvPath, r)) { while (!feof($handle)) { yield fgetcsv($handle); } …...

代码看不懂?ChatGPT 帮你解释,详细到爆!

偷个懒&#xff0c;用ChatGPT 帮我写段生物信息代码如果 ChatGPT 给出的的代码不太完善&#xff0c;如何请他一步步改好&#xff1f;网上看到一段代码&#xff0c;不知道是什么含义&#xff1f;输入 ChatGPT 帮我们解释下。生信宝典 1: 下面是一段 Linux 代码&#xff0c;请帮…...

【MyBatis】篇三.自定义映射resultMap和动态SQL

MyBatis整理 篇一.MyBatis环境搭建与增删改查 篇二.MyBatis查询与特殊SQL 篇三.自定义映射resultMap和动态SQL 篇四.MyBatis缓存和逆向工程 文章目录1、自定义映射P1:测试数据准备P2:字段和属性的映射关系P3:多对一的映射关系P4:一对多的映射关系2、动态SQL2.1 IF标签2.2 w…...

什么是API?(详细解说)

编程资料时经常会看到API这个名词&#xff0c;网上各种高大上的解释估计放倒了一批初学者。初学者看到下面这一段话可能就有点头痛了。 API&#xff08;Application Programming Interface,应用程序编程接口&#xff09;是一些预先定义的函数&#xff0c;目的是提供应用程序与开…...

比cat更好用的命令!

大家好&#xff0c;我是良许。 作为程序员&#xff0c;大家一定对 cat 这个命令不陌生。它主要的功能就是用来显示文本文件的具体内容。 但 cat 命令两个很重大的缺陷&#xff1a;1. 不能语法高亮输出&#xff1b;2. 文本太长的话无法翻页输出。正是这两个不足&#xff0c;使…...

MySQL、HBase、ElasticSearch三者对比

1、概念介绍 MySQL&#xff1a;关系型数据库&#xff0c;主要面向OLTP&#xff0c;支持事务&#xff0c;支持二级索引&#xff0c;支持sql&#xff0c;支持主从、Group Replication架构模型&#xff08;本文全部以Innodb为例&#xff0c;不涉及别的存储引擎&#xff09;。 HBas…...

招全国代理经销/台州seo网站排名优化

第一类&#xff1a;属性值为true可falseandroid:layout_centerHrizontal 水平居中 android:layout_centerVertical 垂直居中 android:layout_centerInparent 相对于父元素完全居中 android:layout_alignParentBottom 贴紧父元素的下边缘 android:la…...

wordpress支付配置/seo文章外包

问题&#xff1a;jsonsql中文排序不支持首先需要判断传入字符串是否含有中文&#xff0c;正则啥的&#xff0c;大家随便上吧然后需要对中文增加一条localeCompare的规则紧接着&#xff0c;高潮~需要把中文转换成拼音(要不你说怎么搞&#xff1f;)用拼音做排序&#xff0c;这里需…...

信息技术用C 做登录界面网站 csdn/建网站建设

影调&#xff1a; 对摄影作品而言&#xff0c;“影调”&#xff0c;又称为照片的基调或调子。指画面的明暗层次、虚实对比和色彩的色相明暗等之间的关系。通过这些关系&#xff0c;使欣赏者感到光的流动与变化。摄影画面中的线条、形状、色彩等元素是由影调来体现的&#xff0…...

有什么网站可以做外贸/开一个网站需要多少钱

LIVE MINI ESP32开发板教程系列&#xff08;一&#xff09;配置Arduino IDE环境支持ESP32模块安装开发板WIN10驱动Arduino IDE支持LIVE MINI ESP32第一步&#xff1a;添加扩展板json第二步&#xff1a;添加ESP32系列开发板演示例程---网页控制板载等亮灭效果图这块小板和Wemos …...

商务网站建设实训/小学培训机构

现在市面上免费的项目管理工具多如牛毛&#xff0c;然而在工作中一旦用错了工具&#xff0c;就不是免费不免费的事了&#xff0c;如果对项目整体进度造成影响&#xff0c;就得不偿失了。本着让项目管理者省钱又省力的目标&#xff0c;我们今天来测评一下那些热门的项目管理工具…...

wordpress 登录 不同/百度搜索排名与点击有关吗

我们经常碰到的需求是希望页面某个元素固定在浏览器的一个位置&#xff0c;无论如何滚动滚动条&#xff0c;位置不变&#xff0c;就例如经常看到的弹出广告。方法一般是使用js控制&#xff0c;或者使用css。下面本篇文章给大家介绍一下CSS让元素固定不变的方法。在css中&#x…...