首页
苏兮影视
随笔记
壁纸
更多
直播
时光轴
友联
关于
统计
Search
1
软件添加id功能按钮
760 阅读
2
v2ray节点搭建
728 阅读
3
typecho非常有特色的模块
548 阅读
4
QQxml消息卡片生成源码
502 阅读
5
网易云音乐歌单ID获取教程
473 阅读
谈天说地
建站源码
经验教程
资源分享
动漫美图
登录
Search
标签搜索
java
flutter
springboot
安卓
rust
linux
vue
docker
joe
快捷键
git
fish shell
maven
redis
netty
dart
groovy
js
设计模式
rpc
尽意
累计撰写
102
篇文章
累计收到
39
条评论
首页
栏目
谈天说地
建站源码
经验教程
资源分享
动漫美图
页面
苏兮影视
随笔记
壁纸
直播
时光轴
友联
关于
统计
搜索到
102
篇与
的结果
RabbitMQ 消息队列详解及 Spring Boot 实战教程
2024年11月26日
108 阅读
0 评论
3 点赞
2024-11-26
RabbitMQ 是一个高效、可靠的消息代理中间件,广泛应用于分布式系统中,用于实现微服务之间的异步通信和解耦。一、RabbitMQ 基本概念1.1 核心概念队列(Queue):存储消息的容器,消息由生产者发送到队列,消费者从队列中取出进行处理。支持点对点(P2P)模式。交换机(Exchange):负责根据路由规则将消息分发到队列。常见交换机类型:Direct、Fanout、Topic。绑定(Binding):连接交换机和队列的规则,结合路由键(Routing Key)定义消息的路由方式。消息模型:点对点模式(P2P): 一条消息只能被一个消费者消费。发布订阅模式(Pub/Sub): 一条消息可以被多个消费者消费。二、三种交换机详解2.1 Direct 交换机路由方式: 消息根据路由键(Routing Key)精准匹配到指定队列。优点: 简单、高效,适用于明确路由需求的场景。缺点: 需要为每个队列绑定具体的路由键,维护成本较高。应用场景: 点对点通知,如订单支付成功通知。2.2 Fanout 交换机路由方式: 广播消息到所有绑定队列,无需路由键。优点: 适合广播场景,无需复杂路由配置。缺点: 不支持消息过滤,容易造成资源浪费。应用场景: 全网公告或系统事件通知。2.3 Topic 交换机路由方式: 根据通配符模糊匹配路由键。* 匹配一个单词,# 匹配零个或多个单词。优点: 灵活的路由规则,支持复杂消息分发需求。缺点: 配置复杂,性能略低于其他类型交换机。应用场景: 新闻分类推送或订阅系统。三、Spring Boot 与 RabbitMQ 整合3.1 项目依赖在 pom.xml 文件中添加 RabbitMQ 依赖:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>3.2 RabbitMQ 配置两种方式方法一:使用 application.yml 配置在 application.yml 文件中配置 RabbitMQ:spring: rabbitmq: host: localhost port: 5672 username: guest password: guest方法二:通过配置类创建连接工厂import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitMQFactoryConfig { @Bean public ConnectionFactory connectionFactory() { CachingConnectionFactory factory = new CachingConnectionFactory(); factory.setHost("localhost"); factory.setPort(5672); factory.setUsername("guest"); factory.setPassword("guest"); return factory; } }3.3 交换机、队列及绑定的两种创建方式方法一:直接使用 new 创建import org.springframework.amqp.core.*; @Configuration public class RabbitMQDirectConfig { @Bean public Queue directQueue() { return new Queue("direct.queue"); } @Bean public DirectExchange directExchange() { return new DirectExchange("direct.exchange"); } @Bean public Binding bindingDirect(Queue directQueue, DirectExchange directExchange) { return BindingBuilder.bind(directQueue).to(directExchange).with("direct.key"); } }方法二:通过工厂类创建import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitMQFactoryConfig { @Bean public Queue fanoutQueue() { return QueueBuilder.durable("fanout.queue").build(); } @Bean public FanoutExchange fanoutExchange() { return ExchangeBuilder.fanoutExchange("fanout.exchange").durable(true).build(); } @Bean public Binding bindingFanout(Queue fanoutQueue, FanoutExchange fanoutExchange) { return BindingBuilder.bind(fanoutQueue).to(fanoutExchange); } }3.4 消息生产者与消费者消息生产者import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.stereotype.Component; @Component public class MessageProducer { private final RabbitTemplate rabbitTemplate; public MessageProducer(RabbitTemplate rabbitTemplate) { this.rabbitTemplate = rabbitTemplate; } public void sendDirectMessage(String message) { rabbitTemplate.convertAndSend("direct.exchange", "direct.key", message); } public void sendFanoutMessage(String message) { rabbitTemplate.convertAndSend("fanout.exchange", "", message); } public void sendTopicMessage(String message) { rabbitTemplate.convertAndSend("topic.exchange", "topic.test", message); } }消息消费者import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class MessageConsumer { @RabbitListener(queues = "direct.queue") public void receiveDirectMessage(String message) { System.out.println("Received Direct Message: " + message); } @RabbitListener(queues = "fanout.queue") public void receiveFanoutMessage(String message) { System.out.println("Received Fanout Message: " + message); } }3.5 单元测试使用 JUnit 测试消息发送功能:import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class RabbitMQTest { @Autowired private MessageProducer messageProducer; @Test public void testDirectMessage() { messageProducer.sendDirectMessage("Test Direct Exchange"); } @Test public void testFanoutMessage() { messageProducer.sendFanoutMessage("Test Fanout Exchange"); } }四、总结本文通过介绍 RabbitMQ 的核心概念,分析了三种交换机的优缺点及适用场景,并通过 Spring Boot 实现了 MQ 的完整配置与消息生产、消费功能。提供的两种实现方式(application.yml 配置和配置类方式,以及直接 new 和工厂创建方式)为不同项目需求提供了灵活选择。
Vue 3 和 WebRTC 实现点对点聊天应用
使用 Vue 3 和 WebRTC 来实现一个简单的点对点聊天应用,服务端使用 WebSocket (ws) 来建立信令服务器,用于连接和交换 WebRTC 所需的 SDP 和 ICE 候选信息。1. 项目初始化首先,我们创建一个基于 Vue 3 的项目:npm init vue@latest webrtc-chat cd webrtc-chat npm install然后,安装 WebSocket 依赖:npm install ws2. 创建 WebSocket 信令服务器我们使用 Node.js 和 ws 模块来创建信令服务器:// server.js const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws) => { ws.on('message', (message) => { // 将消息广播给其他客户端 wss.clients.forEach((client) => { if (client !== ws && client.readyState === WebSocket.OPEN) { client.send(message); } }); }); // 向新连接的客户端发送欢迎消息 ws.send(JSON.stringify({ type: 'welcome', message: 'Connected to WebSocket server' })); }); console.log('WebSocket server is running on ws://localhost:8080');运行服务器:node server.js3. 创建 Vue 3 应用在 src 文件夹中创建 components/Chat.vue 组件。<template> <div> <h2>点对点聊天</h2> <textarea v-model="message" placeholder="输入消息..."></textarea> <button @click="sendMessage">发送</button> <button @click="createOffer">发送 Offer</button> <div v-for="(msg, index) in messages" :key="index">{{ msg }}</div> </div> </template> <script> export default { data() { return { message: '', messages: [], peerConnection: null, dataChannel: null, ws: null }; }, mounted() { this.initializeWebSocket(); // 初始化 WebSocket this.initializePeerConnection(); // 初始化 PeerConnection }, methods: { // 初始化 WebSocket 连接 initializeWebSocket() { this.ws = new WebSocket('ws://localhost:8080'); // 接收来自信令服务器的消息 this.ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'offer') { this.handleOffer(data); // 处理接收到的 Offer } else if (data.type === 'answer') { this.handleAnswer(data); // 处理接收到的 Answer } else if (data.type === 'candidate') { this.addIceCandidate(data); // 添加接收到的 ICE 候选 } }; }, // 初始化 PeerConnection 和数据通道 initializePeerConnection() { this.peerConnection = new RTCPeerConnection(); // 监听 ICE 候选事件 this.peerConnection.onicecandidate = (event) => { if (event.candidate) { this.ws.send(JSON.stringify({ type: 'candidate', candidate: event.candidate })); // 将 ICE 候选发送到信令服务器 } }; // 创建数据通道 this.dataChannel = this.peerConnection.createDataChannel('chat'); this.dataChannel.onmessage = (event) => { this.messages.push(`Peer: ${event.data}`); // 接收消息并更新聊天记录 }; // 监听远程数据通道 this.peerConnection.ondatachannel = (event) => { this.dataChannel = event.channel; this.dataChannel.onmessage = (event) => { this.messages.push(`Peer: ${event.data}`); }; }; }, // 创建并发送 Offer async createOffer() { const offer = await this.peerConnection.createOffer(); await this.peerConnection.setLocalDescription(offer); this.ws.send(JSON.stringify({ type: 'offer', sdp: offer })); // 发送 Offer }, // 处理接收到的 Offer async handleOffer(data) { await this.peerConnection.setRemoteDescription(new RTCSessionDescription(data.sdp)); const answer = await this.peerConnection.createAnswer(); await this.peerConnection.setLocalDescription(answer); this.ws.send(JSON.stringify({ type: 'answer', sdp: answer })); // 发送 Answer }, // 处理接收到的 Answer async handleAnswer(data) { await this.peerConnection.setRemoteDescription(new RTCSessionDescription(data.sdp)); }, // 添加接收到的 ICE 候选 async addIceCandidate(data) { if (data.candidate) { await this.peerConnection.addIceCandidate(new RTCIceCandidate(data.candidate)); } }, // 发送消息 sendMessage() { if (this.dataChannel && this.message) { this.dataChannel.send(this.message); // 通过数据通道发送消息 this.messages.push(`Me: ${this.message}`); // 更新本地聊天记录 this.message = ''; // 清空输入框 } } } }; </script> <style> textarea { width: 100%; height: 100px; margin-bottom: 10px; } button { display: block; margin-bottom: 10px; } </style>4. 启动应用运行以下命令以启动 Vue 应用:npm run dev打开浏览器开两个窗口访问 http://localhost:3000,点击发送offer即可进行实时的点对点聊天。
2024年11月15日
146 阅读
0 评论
3 点赞
Vue 3 中使用 Tailwind CSS
1. 项目初始化与安装 Tailwind CSS在 Vue 3 项目中使用 Tailwind CSS 需要先进行初始化配置。这里以 Vue CLI 或 Vite 为例,分别介绍两种常见的初始化方式。使用 Vue CLI创建 Vue 3 项目:vue create my-project cd my-project安装 Tailwind CSS:vue add tailwind使用 Vite创建 Vue 3 项目:npm create vite@latest my-project --template vue cd my-project安装 Tailwind CSS:npm install -D tailwindcss postcss autoprefixer npx tailwindcss init配置 tailwind.config.js 和 postcss.config.js(Vite 会自动生成这些文件,确保使用默认配置即可):tailwind.config.js:module.exports = { content: [ "./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}" ], theme: { extend: {}, }, plugins: [], }postcss.config.js:module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, }2. 在 Vue 组件中使用 Tailwind CSSTailwind 的类名可以直接在 Vue 组件中使用,只需在模板中添加类名即可。比如,使用 Tailwind 设置按钮样式:<template> <button class="bg-blue-500 text-white p-2 rounded">Click Me</button> </template>你可以根据项目需求自定义类名,也可以使用 Tailwind 提供的响应式和伪类变体。例如,设置一个响应式的布局:<template> <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"> <div class="bg-gray-200 p-4">Item 1</div> <div class="bg-gray-200 p-4">Item 2</div> <div class="bg-gray-200 p-4">Item 3</div> </div> </template>3. 配置 Tailwind 的自定义主题Tailwind 提供了丰富的自定义配置选项,你可以在 tailwind.config.js 中修改颜色、字体、间距等。例如,修改默认的颜色主题:module.exports = { theme: { extend: { colors: { primary: '#FF6347', // 设置自定义颜色 }, }, }, }在 Vue 组件中使用自定义颜色:<template> <button class="bg-primary text-white p-2 rounded">Click Me</button> </template>4. 使用 Tailwind 的插件功能Tailwind 有很多官方插件,可以为你的项目带来额外的功能。例如,@tailwindcss/forms 插件可以改善表单元素的样式。安装插件:npm install @tailwindcss/forms在 tailwind.config.js 中引入插件:module.exports = { plugins: [ require('@tailwindcss/forms'), ], }使用表单插件后,你的表单元素将自动应用更好的样式:<template> <form> <input type="text" class="form-input mt-1 block w-full" /> <button class="bg-primary text-white p-2 rounded">Submit</button> </form> </template>5. 使用 Tailwind 的 JIT 模式(即时编译模式)Tailwind CSS 默认开启了 JIT 模式(在 2.x 版本后),这意味着只有在使用到的 CSS 类才会被编译到最终的 CSS 文件中,极大地减少了打包后的文件大小。若要显式开启 JIT 模式,可以在 tailwind.config.js 中进行设置:module.exports = { mode: 'jit', content: [ "./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}" ], theme: { extend: {}, }, }6. 优化 Tailwind 打包结果在生产环境中,使用 Tailwind CSS 可能会导致打包后的文件过大。为了减少文件大小,可以使用 purge 或 content 配置来确保只编译实际使用到的 CSS 类。在 tailwind.config.js 中启用内容扫描:module.exports = { content: [ "./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}" ], }这样,Tailwind 只会编译扫描到的文件中的 CSS 类,从而减少最终的 CSS 文件大小。7. 组合 Vue 3 的响应式特性与 TailwindTailwind 支持响应式设计,可以结合 Vue 3 的 v-bind 和计算属性等响应式特性来动态更新类名。通过 v-bind:class 可以动态绑定类。<template> <div :class="buttonClass"> <button>Click Me</button> </div> </template> <script setup> import { ref } from 'vue' const isPrimary = ref(true) const buttonClass = computed(() => ({ 'bg-blue-500 text-white': isPrimary.value, 'bg-gray-500 text-black': !isPrimary.value, })) </script>8. 常见问题解决CSS 样式不生效:如果发现 Tailwind 样式没有生效,检查是否正确配置了 tailwind.config.js,并确保 content 配置中包含了你的组件路径。文件过大:开启 JIT 模式和内容扫描,以确保只编译用到的类。9. 资源与参考Tailwind CSS 官方文档Vue 3 官方文档Tailwind CSS 与 Vue 3 集成教程
2024年11月15日
104 阅读
0 评论
3 点赞
v2ray节点搭建
快速开始:https://233boy.com/v2ray/v2ray-script/详细说明:https://bulianglin.com/archives/guide.html
2024年11月06日
728 阅读
0 评论
2 点赞
2024-11-06
esp32c3自动配网
在 esp32-c3 rust使用docker编译 新建项目的基础上,引入必要的依赖Cargo.toml[dependencies] log = { version = "0.4", default-features = false } esp-idf-svc = { version = "0.49", default-features = false } # 添加http的依赖 anyhow = "=1.0.86" embedded-svc = "=0.28.0" # json处理 serde = { version = "1.0", features = ["derive"] } # 序列化,支持多种,xml,json等 serde_json = "1.0" # 专门处理json格式 新建一个跟main.rs同级的配网文件,auto_set_wifi.rs然后在main.rs文件中引用mod auto_set_wifi;编写自动配网rust代码 // 引入配网页面的html文件 static INDEX_HTML: &str = include_str!("../index.html"); // 设置nvs存储的参数 static WIFI_INFO_KEY: &str = "WIFI_INFO"; static NAME_SPACE: &str = "ESP_WIFI"; // 默认ap模式下的配置信息 static AP_WIFO_INFO: WifiInfo<'static> = WifiInfo { ssid: "esp32_c3_wifi", password: "00000000", }; #[derive(Debug, Deserialize, Serialize)] struct WifiInfo<'a> { ssid: &'a str, password: &'a str, } pub fn start_auto_wifi() -> anyhow::Result<()> { // 自动配网,需要先开启ap模式,让手机连上esp32的热点 let peripherals = Peripherals::take()?; let sysloop = EspEventLoop::take()?; let nvs = EspDefaultNvsPartition::take()?; let mut wifi = BlockingWifi::wrap( EspWifi::new(peripherals.modem, sysloop.clone(), Some(nvs.clone()))?, sysloop.clone(), )?; wifi.set_configuration(&esp_idf_svc::wifi::Configuration::AccessPoint( AccessPointConfiguration { ssid: AP_WIFO_INFO.ssid.try_into().unwrap(), password: AP_WIFO_INFO.password.try_into().unwrap(), auth_method: esp_idf_svc::wifi::AuthMethod::WPA2Personal, ..Default::default() }, ))?; // 由于wifi,跟nvs在后面的函数也需要被调用,所有权不能丢弃,需要用安全的智能指针进行接管 let wifi = Arc::new(Mutex::new(wifi)); let nvs = Arc::new(Mutex::new(EspNvs::new(nvs, NAME_SPACE, true)?)); let temp_wifi = wifi.clone(); let temp_nvs = nvs.clone(); let mut buf = [0;128]; log::warn!("正在查找是否存在配网信息..."); if let Err(_) = nvs.lock().unwrap().get_raw(WIFI_INFO_KEY, &mut buf) { log::info!("不存在配网信息,开启ap模式"); let mut temp_wifi = temp_wifi.lock().unwrap(); temp_wifi.start()?; log::info!("开启wifi"); temp_wifi.wait_netif_up()?; log::info!("等待底层网络分配"); log::info!("WiFi信息为:{:?}", temp_wifi.wifi().ap_netif().get_ip_info()); } else { log::info!("存在配网信息,开启混合模式"); let _ = std::thread::spawn(|| start_muxt_mode(temp_wifi, temp_nvs)); } // 配网页面路由 let temp_nvs = nvs.clone(); let mut server = EspHttpServer::new(&ServerConfig::default())?; server.fn_handler("/", esp_idf_svc::http::Method::Get, |request| { request .into_ok_response() .unwrap() .write_all(INDEX_HTML.as_bytes()) })?; // 提交配网的表单信息 server.fn_handler( "/wifi", esp_idf_svc::http::Method::Post, move |mut request| { let len = request.content_len().unwrap() as usize; let mut buf = vec![0; len]; request.read_exact(&mut buf).unwrap(); let temp_nvs = temp_nvs.clone(); let mut response = request.into_ok_response().unwrap(); let raw = temp_nvs.lock().unwrap().set_raw(WIFI_INFO_KEY, &buf); if let true = raw.unwrap_or(false) { response.write_all(b"{\"msg\":\"success\"}").unwrap(); } else { response.write_all(b"{\"msg\":\"faiture\"}").unwrap(); } response.flush().unwrap(); Ok(()) }, )?; // 查看nvs里保存的配网信息 let temp_nvs = nvs.clone(); server.fn_handler("/nvs", esp_idf_svc::http::Method::Get, move |request| { let temp_nvs = temp_nvs.clone(); let mut buf = [0; 128]; let mut response = request.into_ok_response().unwrap(); if let Option::None = temp_nvs .lock() .unwrap() .get_raw(WIFI_INFO_KEY, &mut buf) .unwrap_or(Option::None) { response.write_all(b"{\"msg\":\"faiure\"}").unwrap(); } else { let end = buf.iter().position(|&x| x == 0).unwrap_or(buf.len()); let res = String::from_utf8_lossy(&buf[..end]); let wifi_info = serde_json::from_slice::<WifiInfo>(&buf[..end]).unwrap(); log::error!("WiFi信息为:{:?}", wifi_info); response.write_all(res.as_bytes()).unwrap(); } response.flush() })?; // 开启混合模式路由 let temp_wifi = wifi.clone(); let temp_nvs = nvs.clone(); server.fn_handler("/muxt", esp_idf_svc::http::Method::Get, move |request| { request.into_ok_response().unwrap().write(b"okk").unwrap(); let (temp_wifi, temp_nvs) = (temp_wifi.clone(), temp_nvs.clone()); let _ = std::thread::spawn(|| start_muxt_mode(temp_wifi, temp_nvs)); Ok(()) })?; // 删除nvs的配置信息 let temp_nvs = nvs.clone(); server.fn_handler("/del", esp_idf_svc::http::Method::Get, move |request| { let mut response = request.into_ok_response().unwrap(); let mut temp_nvs = temp_nvs.lock().unwrap(); if let Err(e) = temp_nvs.remove(WIFI_INFO_KEY) { response.write_all(format!("delete faiure,{e}").as_bytes()) }else{ response.write_all(b"delete success") } })?; forget(server); forget(wifi); Ok(()) } pub fn start_muxt_mode( wifi: Arc<Mutex<BlockingWifi<EspWifi>>>, nvs: Arc<Mutex<EspNvs<NvsDefault>>> )-> anyhow::Result<()> { // 启动混合模式 log::info!("延迟500毫秒执行"); FreeRtos::delay_ms(500); log::info!("开始配置"); let mut wifi = wifi.lock().unwrap(); let nvs = nvs.lock().unwrap(); // 重试连接的次数 let mut retry = 5; loop { if retry < 0 { log::error!("尝试剩余{retry}次后,连接失败"); break; } if wifi.is_started()? { if let Err(_) = wifi.stop() { log::error!("断开wifi连接失败,剩余{}次重试",retry); retry-=1; continue; } log::info!("断开wifi"); } let mut buf = [0; 128]; if let Option::None = nvs.get_raw(WIFI_INFO_KEY, &mut buf).unwrap_or(Option::None) { log::error!("获取WiFi信息失败,剩余{}次重试",retry); } let end = buf.iter().position(|&x| x == 0).unwrap_or(buf.len()); let wifi_info = serde_json::from_slice::<WifiInfo>(&buf[..end]).unwrap(); log::warn!("WiFi信息为:{:?}", wifi_info); let client_config = ClientConfiguration { ssid: wifi_info.ssid.try_into().unwrap(), password: wifi_info.password.try_into().unwrap(), ..Default::default() }; let ap_config = AccessPointConfiguration { ssid: AP_WIFO_INFO.ssid.try_into().unwrap(), password: AP_WIFO_INFO.password.try_into().unwrap(), auth_method: esp_idf_svc::wifi::AuthMethod::WPA2Personal, ..Default::default() }; wifi.set_configuration(&esp_idf_svc::wifi::Configuration::Mixed( client_config, ap_config, ))?; log::info!("启动wifi"); wifi.start()?; log::info!("等待网络连接"); if let Err(e) = wifi.connect() { log::error!("连接失败:{e},剩余{}次连接尝试", retry); retry -= 1; continue; } log::info!("等待底层网络驱动处理"); if let Err(e) = wifi.wait_netif_up() { log::error!("等待底层网络驱动处理失败,{e},剩余{}次重试",retry); } break; } log::info!("Ap模式信息为:{:?}", wifi.wifi().ap_netif().get_ip_info()); log::info!("Ip信息为:{:?}", wifi.wifi().sta_netif().get_ip_info()); Ok(()) } 在main.rs中调用fn main() { // It is necessary to call this function once. Otherwise some patches to the runtime // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 esp_idf_svc::sys::link_patches(); // Bind the log crate to the ESP Logging facilities esp_idf_svc::log::EspLogger::initialize_default(); start_auto_wifi().unwrap(); }配网的html页面代码<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>esp32配网页面</title> <style> body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f4f4f9; } .login-container { width: 300px; padding: 20px; background-color: #fff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); border-radius: 8px; } .login-container h2 { text-align: center; margin-bottom: 20px; color: #333; } .login-container input { width: 100%; padding: 10px; margin: 10px 0; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } .login-container button { width: 100%; padding: 10px; background-color: #4CAF50; border: none; color: #fff; border-radius: 4px; cursor: pointer; font-size: 16px; } .login-container button:hover { background-color: #45a049; } </style> </head> <body> <div class="login-container"> <h2>配网页面</h2> <form id="loginForm"> <input type="text" id="username" placeholder="ssid" required> <input type="password" id="password" placeholder="密码" required> <button type="button" onclick="submitForm()">提交</button> </form> </div> <script> function submitForm() { const ssid = document.getElementById('username').value; const password = document.getElementById('password').value; fetch(window.location.origin + '/wifi', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ssid, password }) }) .then(response => response.json()) .then(data => { // 在此处理响应数据 alert(data.msg); if (data.msg === "success"){ fetch(window.location.origin + '/muxt'); } console.log('成功:', data); }) .catch(error => { console.error('错误:', error); }); } </script> </body> </html>
2024年11月06日
87 阅读
0 评论
3 点赞
1
...
3
4
5
...
21